390 lines
19 KiB
Java
390 lines
19 KiB
Java
package shkd.repc.task;
|
||
|
||
import com.alibaba.fastjson.JSON;
|
||
import com.alibaba.fastjson.JSONArray;
|
||
import com.alibaba.fastjson.JSONObject;
|
||
import kd.bos.context.RequestContext;
|
||
import kd.bos.dataentity.OperateOption;
|
||
import kd.bos.dataentity.entity.DynamicObject;
|
||
import kd.bos.dataentity.entity.DynamicObjectCollection;
|
||
import kd.bos.entity.operate.result.OperationResult;
|
||
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;
|
||
import kd.bos.servicehelper.operation.OperationServiceHelper;
|
||
import kd.bos.servicehelper.operation.SaveServiceHelper;
|
||
import kd.sdk.plugin.Plugin;
|
||
import okhttp3.OkHttpClient;
|
||
import okhttp3.Request;
|
||
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 {
|
||
|
||
private static final Log logger = LogFactory.getLog(DobeDWaccountTask.class);
|
||
private static final String entityName = "recos_stdcostaccount";//标准成本科目实体 供应链库 表名 t_recos_scostaccount
|
||
private static final String acctViewEntity = "bd_accountview";//会计科目实体 财务库 表名 T_BD_Account
|
||
private static final String dw_menthod = "mdm_costsubject";
|
||
|
||
// private static final String accEntity = "costaccountentry";//科目分录实体 供应链库 表名 t_recos_costaccount
|
||
// private static final String projectEntity = "repmd_projectbill";//项目实体 表名 t_repmd_projectbill
|
||
|
||
@Override
|
||
public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
|
||
//调用数仓查询接口
|
||
OkHttpClient client = new OkHttpClient();
|
||
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();
|
||
|
||
String resultData;
|
||
try {
|
||
Response response = client.newCall(request).execute();
|
||
resultData = response.body().string();
|
||
logger.info("成本科目接口返回结果:\n{}", resultData);
|
||
} catch (IOException e) {
|
||
logger.info(String.format("成本科目接口异常:%s", e.getMessage()));
|
||
throw new RuntimeException("获取数仓成本科目数据失败", e);
|
||
}
|
||
JSONObject json_body = JSON.parseObject(resultData);
|
||
//定时任务获取的参数和参数值是配置调度程序和作业的时候设置,根据参数值保存从数仓获取到的完整json
|
||
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 ("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) {
|
||
//根据标准科目id未找到对应记录
|
||
DobeDWUtils.saveLog(bzkmid + "", "数仓成本科目同步", "", "根据标准科目ID未找到对应记录:" + bzkmid, false, "定时任务");
|
||
return;
|
||
}
|
||
|
||
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()) {
|
||
logger.error(save.getMessage());
|
||
}
|
||
}
|
||
|
||
private void dataComparison(JSONArray detailsJson, DynamicObjectCollection doDetails,DynamicObject billinfo) {
|
||
// 用于存储需要新增的数据(接口中有但系统中没有的)
|
||
List<JSONObject> 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<JSONObject> toAddList, DynamicObjectCollection doDetails,DynamicObject billinfo) {
|
||
System.out.println("[MyLog]开始新增科目,数量: " + toAddList.size());
|
||
logger.info("开始新增科目,数量: {}", toAddList.size());
|
||
|
||
// 用于缓存已创建的父级科目
|
||
Map<String, DynamicObject> 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<String, DynamicObject> 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
|
||
}
|
||
} |