From 1df936896762be7631cbe0aa6429a5336ecd7583 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E8=B4=B5=E5=BC=BA?= Date: Thu, 12 Jun 2025 14:17:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=95=B0=E4=BB=93=E6=88=90=E6=9C=AC=E7=A7=91?= =?UTF-8?q?=E7=9B=AE=E5=90=8C=E6=AD=A5=E6=8E=A5=E5=8F=A31.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shkd/repc/task/DobeDWaccountTask.java | 416 +++++++++++++----- 1 file changed, 309 insertions(+), 107 deletions(-) diff --git a/main/java/shkd/repc/task/DobeDWaccountTask.java b/main/java/shkd/repc/task/DobeDWaccountTask.java index 1ca70a0..ebd5f42 100644 --- a/main/java/shkd/repc/task/DobeDWaccountTask.java +++ b/main/java/shkd/repc/task/DobeDWaccountTask.java @@ -12,7 +12,9 @@ import kd.bos.exception.KDException; import kd.bos.id.ID; import kd.bos.logging.Log; import kd.bos.logging.LogFactory; +import kd.bos.orm.query.QCP; import kd.bos.orm.query.QFilter; +import kd.bos.schedule.api.MessageHandler; import kd.bos.schedule.executor.AbstractTask; import kd.bos.servicehelper.BusinessDataServiceHelper; import kd.bos.servicehelper.QueryServiceHelper; @@ -25,11 +27,15 @@ import okhttp3.Response; import shkd.utils.DobeDWUtils; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; /** * 后台任务插件 + * 数仓成本科目同步 + * 同步规则:以编码为准,系统有则判断更新,无则新增 */ public class DobeDWaccountTask extends AbstractTask implements Plugin { @@ -45,8 +51,8 @@ public class DobeDWaccountTask extends AbstractTask implements Plugin { public void execute(RequestContext requestContext, Map map) throws KDException { //调用数仓查询接口 OkHttpClient client = new OkHttpClient(); - Request request = new Request.Builder().url(DobeDWUtils.dwUrl+dw_menthod) - .post(DobeDWUtils.createRequestBody("account",1)) + Request request = new Request.Builder().url(DobeDWUtils.dwUrl + dw_menthod) + .post(DobeDWUtils.createRequestBody("account", 1)) .header("Content-Type", "application/json") .header("Authorization", DobeDWUtils.appCode) .build(); @@ -62,127 +68,323 @@ public class DobeDWaccountTask extends AbstractTask implements Plugin { } JSONObject json_body = JSON.parseObject(resultData); //定时任务获取的参数和参数值是配置调度程序和作业的时候设置,根据参数值保存从数仓获取到的完整json - if(map != null && "yes".equals(map.get("savedwjson"))){ - DobeDWUtils.saveLog("savedwjson1","科目接口",resultData,null,true,"定时任务"); + if (map != null && "yes".equals(map.get("savedwjson"))) { + DobeDWUtils.saveLog("savedwjson1", "成本科目接口", resultData, null, true, "定时任务"); } //接口返回的数据进行了分页 // int totalNum = json_body.getIntValue("totalNum");//分页-SQL查询总数据量 //根据id查找标准成本科目的表头信息 long bzkmid; String accountId = requestContext.getAccountId();//获取账套ID - if("2008012388581769216".equals(accountId)){ - bzkmid = 2012525353133704192L;//测试id - }else{ + if ("2231954870896690176".equals(accountId)) { + bzkmid = 2234107107932663808L;//测试id + } else { bzkmid = 2037039962179791872L;//正式id } - logger.info("accountId "+accountId); - DynamicObject billinfo = BusinessDataServiceHelper.loadSingle(entityName,new QFilter[]{new QFilter("id","=",bzkmid)}); - if(billinfo == null){ + logger.info("accountId " + accountId); + DynamicObject billinfo = BusinessDataServiceHelper.loadSingle(entityName, new QFilter[]{new QFilter("id", "=", bzkmid)}); + if (billinfo == null) { //根据标准科目id未找到对应记录 - DobeDWUtils.saveLog(bzkmid+"","数仓成本科目同步","","根据标准科目ID未找到对应记录:"+bzkmid,false,"定时任务"); + DobeDWUtils.saveLog(bzkmid + "", "数仓成本科目同步", "", "根据标准科目ID未找到对应记录:" + bzkmid, false, "定时任务"); return; } -// String acctid = null;//成本科目数仓id - String number;//成本科目编号 - String name;//成本科名称 -// String ciaccountflag = null; //科目类别 非建安科目and建安科目 -// String apportionway = null; -// String taxrate = null; - String kjkm_num;//会计科目编号 - String kjkm_name;//会计科目名称 - String parentnumber;//成本科目父级编号 - DynamicObject acctinfo;//成本科目对象 - DynamicObject parentAcctinfo;//父级成本科目对象 - DynamicObject acctViewinfo;//会计科目对象 - Map parentAccts = new HashMap<>();//现有成本科目集合 - Map acctViewMap = new HashMap<>();//会计科目缓存 - JSONArray detailsJson = json_body.getJSONArray("data"); - DynamicObjectCollection doDetails = billinfo.getDynamicObjectCollection("costaccountentry");//标准科目分录数据 - for (int i = 0; i < doDetails.size(); i++) { - acctinfo = doDetails.get(i); - //以数仓科目id作为key,科目对象为value -// parentAccts.put(acctinfo.getString("caentry_srcid"),acctinfo); - if(acctinfo.getBoolean("caentry_enable")){ - //只加载已启用的科目 - parentAccts.put(acctinfo.getString("caentry_number"),acctinfo); - } - } - //循环入参,判断哪些科目该修改,哪些该新增 - for (int i = 0; i < detailsJson.size(); i++) { - json_body = detailsJson.getJSONObject(i); -// acctid = json_body.getString("costsubject_id");//科目id - number = json_body.getString("costsubject_code");//科目编号 - name = json_body.getString("costsubject_name");//科目名称 - kjkm_num = json_body.getString("accsubject_code");//会计科目编码 - kjkm_name = json_body.getString("accsubject_name");//会计科目名称 -// ciaccountflag = json_body.getString("ciaccountflag");//科目类别 非建安科目and建安科目 -// apportionway = json_body.getString("apportionway");//分摊方式 -// taxrate = json_body.getString("taxrate");//税率 - parentnumber = json_body.getString("costsubject_pcode");//科目父级code - if(DobeDWUtils.isEmpty(number) || DobeDWUtils.isEmpty(name) || DobeDWUtils.isEmpty(parentnumber)){ - //根节点数仓不体现,故,所有科目都会有父级科目编号,没有的不处理 - logger.info(String.format("成本科目入参为空异常:%s", json_body.toJSONString())); - continue; - } - //先从现有科目集合中获取,未获取到则新增 - acctinfo = parentAccts.get(number); - if(acctinfo == null){ - acctinfo = doDetails.addNew(); -// acctinfo.set("caentry_srcid", number);//源ID 用于存储数仓的科目id -// acctinfo.set("caentry_project", projectinfo.getLong("id"));//项目 - acctinfo.set("caentry_enable", true);//默认 启用 - acctinfo.set("caentry_isstdaccount", true);//是否标准科目 - acctinfo.set("caentry_standlibflag", true);//是否标准科目库 - acctinfo.set("caentry_isleaf", true);//是否叶子节点,新增默认为true - acctinfo.set("caentry_ciaccountflag", "1");//科目类别 建安 1 非建安 0 默认都是建安类 - //新增时指定一个ID,防止父级和子级都创建的情况下,子级拿不到父级id而报错 - acctinfo.set("id", ID.genLongId()); -// acctinfo.set("caentry_longnumber", number);//科目长编号 -// acctinfo.set("caentry_fullname", name);//科目长名称 - //将此次新增的科目放到现有科目集合中,方便后续科目能获取到父级科目 - parentAccts.put(number,acctinfo); - } - //已存在,做更新 编号、名称、长编号、名称、科目类别等 - acctinfo.set("caentry_number", number); - acctinfo.set("caentry_name", name); -// acctinfo.set("caentry_apportionway", null);//分摊方式 基础资料 -// acctinfo.set("caentry_taxrate", taxrate);//税率 - //创建组织 caentry_createorg - //组织 caentry_org - //业务组织 caentry_useorg - //处理父级科目 - parentAcctinfo = parentAccts.get(parentnumber); - if(parentAcctinfo != null){ - acctinfo.set("pid", parentAcctinfo.getLong("id"));//设置父级科目 - //如果数仓不指定长编码和长名称,则需要在此处手动处理 - acctinfo.set("caentry_longnumber", parentAcctinfo.getString("caentry_longnumber")+"."+number); - acctinfo.set("caentry_fullname", parentAcctinfo.getString("caentry_fullname")+"_"+name); - //科目级次=父级科目级次+1 - acctinfo.set("caentry_level", parentAcctinfo.getInt("caentry_level")+1); - //父级科目的是否叶子节点为否 - parentAcctinfo.set("caentry_isleaf", false);//是否叶子节点为否 - }else{ - logger.info("根据父级科目编号未找到对应科目"+parentnumber); - } - //设置对应会计科目字段id - if(!DobeDWUtils.isEmpty(kjkm_num)){ - acctViewinfo = acctViewMap.get(kjkm_num); - if(acctViewinfo == null){ - acctViewinfo = QueryServiceHelper.queryOne(acctViewEntity,"id,number,longnumber",new QFilter[]{new QFilter("longnumber","=",kjkm_num)}); - } - if(acctViewinfo == null){ - DobeDWUtils.saveLog(number,"数仓科目同步",json_body.toString(),kjkm_num+" 会计科目在金蝶中找不到:"+kjkm_name,false,"定时任务"); - }else{ - acctViewMap.put(kjkm_num,acctViewinfo); - acctinfo.set("caentry_account",acctViewinfo.getLong("id")); - } - } + JSONArray detailsJson = json_body.getJSONArray("data"); + if (detailsJson==null || detailsJson.isEmpty()){ + logger.info("解析data为空,不继续处理!"); + return; } + DynamicObjectCollection doDetails = billinfo.getDynamicObjectCollection("costaccountentry");//标准科目分录数据 + if (doDetails!=null && doDetails.size()!=0){ + //成本科目最少需要一级数据一条,例:CB + this.dataComparison(detailsJson,doDetails,billinfo); + } + //调用保存操作 OperationResult save = OperationServiceHelper.executeOperate("save", entityName, new DynamicObject[]{billinfo}, OperateOption.create()); - if (!save.isSuccess()){ + if (!save.isSuccess()) { logger.error(save.getMessage()); } } + + private void dataComparison(JSONArray detailsJson, DynamicObjectCollection doDetails,DynamicObject billinfo) { + // 用于存储需要新增的数据(接口中有但系统中没有的) + List toAddList = new ArrayList<>(); + int updateCount = 0; // 记录更新条数 + int addCount = 0; // 记录新增条数 + + for (int i = 0; i < detailsJson.size(); i++) { + JSONObject jsonObject = detailsJson.getJSONObject(i); + String status = jsonObject.getString("costsubject_status"); + + if ("启用".equals(status)) { + String code = jsonObject.getString("costsubject_code"); + if (code != null) { + boolean found = false; + + for (int j = 0; j < doDetails.size(); j++) { + DynamicObject dynamicObject = doDetails.get(j); + String longNumber = dynamicObject.getString("caentry_longnumber"); + + if (longNumber != null) { + // 去除系统中的"."后与接口编码比较 + String cleanedLongNumber = longNumber.replace(".", ""); + + if (code.equals(cleanedLongNumber)) { + found = true; + // 检查名称或其他字段是否有变化 + String systemName = dynamicObject.getString("caentry_name"); // 假设系统中名称字段为caentry_name + String jsonName = jsonObject.getString("costsubject_name"); + // 如果名称不同或需要检查其他字段变化,则直接更新 + if (!jsonName.equals(systemName)) { + //更新科目名称 + dynamicObject.set("caentry_name",jsonName); + updateCount++; + System.out.println("[MyLog]更新科目: " + jsonName + ", 编码: " + code); + logger.info("更新科目名称: {}, 编码: {}", jsonName, code); + } + //会计科目 + String accSubjectCode = jsonObject.getString("accsubject_code"); + if (accSubjectCode!=null){ + DynamicObject sysAccount = dynamicObject.getDynamicObject("caentry_account"); + if (sysAccount==null){ + //直接匹配会计科目赋值id + Long accountId=this.getSysAccountId(accSubjectCode); + if (accountId!=null){ + dynamicObject.set("caentry_account",accountId); + logger.info("成功关联会计科目,科目编码: {}, 会计科目ID: {}", code, accountId); + System.out.println("[MyLog]关联会计科目: " + code + " -> " + accountId); + } + }else { + //编码匹配是否改变 + String number = sysAccount.getString("number"); + if (!accSubjectCode.equals(number)){ + //匹配会计科目赋值id + Long accountId=this.getSysAccountId(accSubjectCode); + if (accountId!=null){ + dynamicObject.set("caentry_account",accountId); + logger.info("更新会计科目关联成功,科目编码: {}, 新会计科目ID: {}", code, accountId); + System.out.println("[MyLog]更新会计科目关联: " + code + " -> " + accountId); + } + } + } + } + break; // 找到匹配项后跳出循环 + } + } + } + + // 如果没有找到匹配项,则加入新增列表 + if (!found) { + toAddList.add(jsonObject); + addCount++; + System.out.println("[MyLog]准备新增科目: " + jsonObject.getString("costsubject_name") + ", 编码: " + code); + logger.info("准备新增科目: {}, 编码: {}", jsonObject.getString("costsubject_name"), code); + } + } + } + } + // 打印统计信息 + System.out.println("[MyLog]数据比对结果 - 更新条数: " + updateCount + ", 新增条数: " + addCount); + logger.info("数据比对结果 - 更新条数: {}, 新增条数: {}", updateCount, addCount); + + //更新 + if (toAddList.size()!=0){ + this.addCostSubject(toAddList,doDetails,billinfo); + } + + } + + /** + * 获取会计科目id + * @param accSubjectCode + * @return + */ + private Long getSysAccountId(String accSubjectCode) { + Long pkValue = null; + QFilter number = new QFilter("number", QCP.equals, accSubjectCode); + DynamicObject dynamicObject = BusinessDataServiceHelper.loadSingle("bd_accountview", number.toArray()); + if (dynamicObject!=null){ + pkValue= (Long) dynamicObject.getPkValue(); + } + return pkValue; + } + + /** + * 新增标准成本科目数据 + * @param toAddList 需要新增的数据 + * @param doDetails 单据体 + */ + private void addCostSubject(List toAddList, DynamicObjectCollection doDetails,DynamicObject billinfo) { + System.out.println("[MyLog]开始新增科目,数量: " + toAddList.size()); + logger.info("开始新增科目,数量: {}", toAddList.size()); + + // 用于缓存已创建的父级科目 + Map parentCache = new HashMap<>(); + + for (JSONObject jsonObject : toAddList) { + try { + // 1. 准备基础数据 + + //父级编码CB001 + String pCode = jsonObject.getString("costsubject_pcode"); + //系统需要的编码CB.001 + String formattedCode = formatParentCode(pCode); + //科目编码CB001002 + String code = jsonObject.getString("costsubject_code"); + //系统短编码002 + String shortNumber = code.substring(pCode.length()); + + // 2. 处理父级科目 + DynamicObject parentEntry = findOrCreateParent( + jsonObject, pCode, formattedCode, doDetails, parentCache,billinfo); + + // 设置父级科目行号(如果父级是新创建的) + if (!parentCache.containsKey(formattedCode)) { + parentEntry.set("seq", doDetails.size()-1); // 使用当前总条数作为行号 + parentCache.put(formattedCode, parentEntry); + System.out.println("[MyLog]新增父级科目: " + parentEntry.getString("caentry_name") + + ", 编码: " + parentEntry.getString("caentry_longnumber")); + logger.info("新增父级科目: {}, 编码: {}", + parentEntry.getString("caentry_name"), + parentEntry.getString("caentry_longnumber")); + } + + // 3. 创建当前科目 + DynamicObject dynamicObject = doDetails.addNew(); + setupDynamicObject(dynamicObject, jsonObject, parentEntry, formattedCode, shortNumber); + + // 4. 设置当前科目行号 + dynamicObject.set("seq", doDetails.size()-1); // 使用当前总条数-1作为行号 + + System.out.println("[MyLog]新增科目: " + dynamicObject.getString("caentry_name") + + ", 编码: " + dynamicObject.getString("caentry_longnumber")); + logger.info("新增科目: {}, 编码: {}", + dynamicObject.getString("caentry_name"), + dynamicObject.getString("caentry_longnumber")); + } catch (Exception e) { + System.err.println("[MyLog]新增科目失败: " + jsonObject.getString("costsubject_name") + + ", 错误: " + e.getMessage()); + logger.error("新增科目失败: {}, 错误: {}", jsonObject.getString("costsubject_name"), e.getMessage(), e); + } + } + System.out.println("[MyLog]科目新增完成,共新增: " + toAddList.size() + "条记录"); + logger.info("科目新增完成,共新增: {}条记录", toAddList.size()); + } + + /** + * 查找或创建父级科目 + */ + private DynamicObject findOrCreateParent(JSONObject jsonObject, String pCode, + String formattedCode, DynamicObjectCollection doDetails, + Map cache,DynamicObject billinfo) { + // 1. 先检查缓存 + if (cache.containsKey(formattedCode)) { + return cache.get(formattedCode); + } + + // 2. 在现有数据中查找 + for (DynamicObject entry : doDetails) { + if (formattedCode.equals(entry.getString("caentry_longnumber"))) { + return entry; + } + } + + // 3. 查找顶级父级科目(CB) + DynamicObject topParent = findTopParent(doDetails); + if (topParent == null) { + throw new RuntimeException("未找到顶级父级科目(CB)"); + } + + // 4. 创建新的父级科目 + DynamicObject parentEntry = doDetails.addNew(); + String parentShortCode = pCode.substring(2); + + + // 设置父级科目属性 + parentEntry.set("pid", topParent.getPkValue()); // 父级 ID——CB + parentEntry.set("caentry_number", parentShortCode); + parentEntry.set("caentry_longnumber", formattedCode); + parentEntry.set("caentry_name", jsonObject.getString("costsubject_pname")); + parentEntry.set("caentry_enable", true); + parentEntry.set("caentry_org", 100000L); + parentEntry.set("caentry_createorg", 100000L); + parentEntry.set("caentry_useorg", 100000L); + parentEntry.set("caentry_isleaf", false); + parentEntry.set("caentry_level", 2); + parentEntry.set("caentry_standlibflag", true); + parentEntry.set("caentry_isimportaccount", false); + parentEntry.set("caentry_isstdaccount", true); + + // 立即保存父级科目以生成pkValue + OperationResult saveResult = OperationServiceHelper.executeOperate("save", entityName, + new DynamicObject[]{billinfo}, OperateOption.create()); + + if (!saveResult.isSuccess()) { + throw new RuntimeException("保存父级科目失败: " + saveResult.getMessage()); + } + + + return parentEntry; + } + + /** + * 设置科目属性 + */ + private void setupDynamicObject(DynamicObject dynamicObject, JSONObject jsonObject, + DynamicObject parentEntry, String formattedCode, String shortNumber) { + // 1. 设置基础属性 + dynamicObject.set("pid", parentEntry.getPkValue()); + dynamicObject.set("caentry_fullname", "开发成本_" + parentEntry.getString("caentry_name") + + "_" + jsonObject.getString("costsubject_name")); + dynamicObject.set("caentry_number", shortNumber); + dynamicObject.set("caentry_longnumber", formattedCode + "." + shortNumber); + dynamicObject.set("caentry_name", jsonObject.getString("costsubject_name")); + + // 2. 设置固定属性 + dynamicObject.set("caentry_ciaccountflag", "1"); + dynamicObject.set("caentry_measureway", 1026043947790783488L); + dynamicObject.set("caentry_apportionway", 1026043947790783488L); + dynamicObject.set("caentry_enable", true); + dynamicObject.set("caentry_org", 100000L); + dynamicObject.set("caentry_createorg", 100000L); + dynamicObject.set("caentry_useorg", 100000L); + dynamicObject.set("caentry_isleaf", true); + dynamicObject.set("caentry_level", 3); + dynamicObject.set("caentry_srcid", dynamicObject.getPkValue()); + dynamicObject.set("caentry_standlibflag", true); + dynamicObject.set("caentry_isimportaccount", false); + dynamicObject.set("caentry_isstdaccount", true); + + // 3. 会计科目 + String accSubjectCode = jsonObject.getString("accsubject_code"); + if (jsonObject.getString("accsubject_code") != null) { + Long accountId=this.getSysAccountId(accSubjectCode); + if (accountId!=null){ + dynamicObject.set("caentry_account",accountId); + } + } + } + + /** + * 查找顶级父级科目(CB) + */ + private DynamicObject findTopParent(DynamicObjectCollection doDetails) { + for (DynamicObject entry : doDetails) { + if ("CB".equals(entry.getString("caentry_longnumber"))) { + return entry; + } + } + return null; + } + + /** + * 格式化父级编码(CB0010 → CB.0010;CB001 → CB.001) + */ + private String formatParentCode(String pCode) { + String prefix = pCode.substring(0, 2); // 获取前两位字母(CB) + String numberPart = pCode.substring(2); // 获取数字部分(001) + + return prefix + "." + numberPart;//CB.001 + } } \ No newline at end of file