✨ 新增金蝶客商数据定时同步任务(自动推送+使用状态停用联动)
This commit is contained in:
+15
@@ -132,6 +132,21 @@ public interface ICustomerArchiveService extends BaseService<CustomerArchive> {
|
||||
*/
|
||||
CustomerArchive syncKingdee(Long id);
|
||||
|
||||
/**
|
||||
* 定时同步金蝶客商数据:待推送客商自动推送 + 已同步客商使用状态回写
|
||||
*
|
||||
* @return 同步汇总说明
|
||||
*/
|
||||
String syncKingdeeCustomerData();
|
||||
|
||||
/**
|
||||
* 金蝶侧停用联动:停用客商档案并记录变更(仅当前启用的客商生效)
|
||||
*
|
||||
* @param id 主键
|
||||
* @return 是否执行停用
|
||||
*/
|
||||
boolean disableCustomerFromKingdee(Long id);
|
||||
|
||||
/**
|
||||
* 基于启用评分量化表生成评分记录
|
||||
*
|
||||
|
||||
+119
@@ -36,6 +36,7 @@ import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.SpringUtil;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.thirdparty.bfi.constant.BfiConstant;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiCustomerQueryDTO;
|
||||
@@ -128,6 +129,10 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
|
||||
private static final String KINGDEE_STATUS_SYNCED = "synced";
|
||||
private static final String KINGDEE_STATUS_FAILED = "failed";
|
||||
private static final String CUSTOMER_CODE_PREFIX = "KS";
|
||||
/**
|
||||
* A902-1 批量查询分批大小
|
||||
*/
|
||||
private static final int KINGDEE_CUSTOMER_QUERY_BATCH = 50;
|
||||
/**
|
||||
* 变更快照忽略字段:明细每次保存会重建主键/审计字段,不能作为“是否有修改”的依据
|
||||
*/
|
||||
@@ -391,6 +396,120 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
|
||||
return text.length() > 500 ? text.substring(0, 500) : text;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自代理:跨方法调用确保事务注解生效(单条推送、停用各自独立事务)
|
||||
*/
|
||||
private ICustomerArchiveService self() {
|
||||
return SpringUtil.getBean(ICustomerArchiveService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String syncKingdeeCustomerData() {
|
||||
// ① 待推送客商:审批通过且未推送/推送失败,复用单条推送(A902-1 复查 + A902-2 保存),单条失败不中断批次
|
||||
List<CustomerArchive> pendingList = list(Wrappers.<CustomerArchive>lambdaQuery()
|
||||
.eq(CustomerArchive::getApprovalStatus, APPROVAL_APPROVED)
|
||||
.in(CustomerArchive::getKingdeeStatus, KINGDEE_STATUS_NONE, KINGDEE_STATUS_FAILED)
|
||||
.orderByAsc(CustomerArchive::getId));
|
||||
ICustomerArchiveService proxy = self();
|
||||
int pushed = 0;
|
||||
int existed = 0;
|
||||
int pushFailed = 0;
|
||||
for (CustomerArchive customer : pendingList) {
|
||||
try {
|
||||
CustomerArchive after = proxy.syncKingdee(customer.getId());
|
||||
if (Objects.equals(after.getKingdeeStatus(), KINGDEE_STATUS_SYNCED)) {
|
||||
pushed++;
|
||||
} else if (Objects.equals(after.getKingdeeStatus(), KINGDEE_STATUS_EXIST)) {
|
||||
existed++;
|
||||
} else {
|
||||
pushFailed++;
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
pushFailed++;
|
||||
log.error("定时同步金蝶客商推送异常,id={},unifiedCreditCode={}",
|
||||
customer.getId(), customer.getUnifiedCreditCode(), exception);
|
||||
}
|
||||
}
|
||||
// ② 已同步客商使用状态回写:金蝶禁用(enable=0)→ TMS 自动停用;金蝶恢复可用不自动启用(TMS 人工控制)
|
||||
List<CustomerArchive> enabledList = list(Wrappers.<CustomerArchive>lambdaQuery()
|
||||
.eq(CustomerArchive::getApprovalStatus, APPROVAL_APPROVED)
|
||||
.in(CustomerArchive::getKingdeeStatus, KINGDEE_STATUS_SYNCED, KINGDEE_STATUS_EXIST)
|
||||
.eq(CustomerArchive::getStatus, STATUS_ENABLED)
|
||||
.orderByAsc(CustomerArchive::getId));
|
||||
int disabled = 0;
|
||||
int disableFailed = 0;
|
||||
if (!enabledList.isEmpty()) {
|
||||
List<String> creditCodes = enabledList.stream()
|
||||
.map(CustomerArchive::getUnifiedCreditCode)
|
||||
.filter(Func::isNotEmpty)
|
||||
.map(code -> code.trim().toUpperCase())
|
||||
.distinct()
|
||||
.toList();
|
||||
Map<String, BfiCustomerVO> kingdeeMap = queryKingdeeCustomerMap(creditCodes);
|
||||
for (CustomerArchive customer : enabledList) {
|
||||
BfiCustomerVO kingdeeCustomer = kingdeeMap.get(customer.getUnifiedCreditCode().trim().toUpperCase());
|
||||
if (kingdeeCustomer == null || !"0".equals(kingdeeCustomer.getEnable())) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
if (proxy.disableCustomerFromKingdee(customer.getId())) {
|
||||
disabled++;
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
disableFailed++;
|
||||
log.error("定时同步金蝶客商停用异常,id={},unifiedCreditCode={}",
|
||||
customer.getId(), customer.getUnifiedCreditCode(), exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
return "待推送 " + pendingList.size() + " 条(推送成功 " + pushed + ",金蝶已有 " + existed
|
||||
+ ",失败 " + pushFailed + "),停用联动 " + disabled + " 条(失败 " + disableFailed + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean disableCustomerFromKingdee(Long id) {
|
||||
CustomerArchive customer = ensureCustomerAccessible(id);
|
||||
if (!Objects.equals(customer.getStatus(), STATUS_ENABLED)) {
|
||||
return false;
|
||||
}
|
||||
CustomerArchive after = copyCustomer(customer);
|
||||
after.setStatus(STATUS_DISABLED);
|
||||
addChangeRecord(id, "金蝶侧客商已停用,同步停用客商档案", customer, after);
|
||||
return updateById(after);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按统一社会信用代码分批查询金蝶客商,键为大写信用代码
|
||||
*/
|
||||
private Map<String, BfiCustomerVO> queryKingdeeCustomerMap(List<String> creditCodes) {
|
||||
Map<String, BfiCustomerVO> result = new HashMap<>();
|
||||
for (int start = 0; start < creditCodes.size(); start += KINGDEE_CUSTOMER_QUERY_BATCH) {
|
||||
List<String> batch = creditCodes.subList(start, Math.min(start + KINGDEE_CUSTOMER_QUERY_BATCH, creditCodes.size()));
|
||||
if (batch.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
BfiCustomerQueryDTO param = new BfiCustomerQueryDTO();
|
||||
param.setPageNo(1);
|
||||
param.setPageSize(batch.size());
|
||||
param.setSocietycreditcode(batch);
|
||||
BfiPageVO<BfiCustomerVO> page = bfiService.queryCustomer(param);
|
||||
List<BfiCustomerVO> rows = page == null ? null : page.getRows();
|
||||
if (rows != null) {
|
||||
for (BfiCustomerVO vo : rows) {
|
||||
if (Func.isNotEmpty(vo.getSocietycreditcode())) {
|
||||
result.put(vo.getSocietycreditcode().trim().toUpperCase(), vo);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
log.error("定时同步金蝶客商批量查询异常,batchSize={}", batch.size(), exception);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean changeStatus(Long id, Integer status) {
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.task;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.transport.service.ICustomerArchiveService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 金蝶客商数据定时同步任务
|
||||
* <p>
|
||||
* 对应业财融合「客商数据定时同步子流程」(图01,ERP 泳道):
|
||||
* ① 待推送客商(审批通过且未推送/推送失败)自动执行 A902-1 复查 + A902-2 推送;
|
||||
* ② 已同步客商回写金蝶使用状态,金蝶禁用(enable=0)联动停用客商档案。
|
||||
* A901-1 供应商查询待金蝶 SDK(blade-bfi-api)提供能力后扩展。
|
||||
* 业务逻辑收敛在 {@link ICustomerArchiveService#syncKingdeeCustomerData()},便于手动触发联调。
|
||||
* <p>
|
||||
* 调度配置(Nacos thirdParty.bfi.customer.*):syncEnabled 开关、cron 表达式。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class CustomerKingdeeSyncTask {
|
||||
|
||||
private final ICustomerArchiveService customerArchiveService;
|
||||
|
||||
/** 客商金蝶数据同步开关,默认关闭 */
|
||||
@Value("${thirdParty.bfi.customer.syncEnabled:false}")
|
||||
private boolean syncEnabled;
|
||||
|
||||
/** 客商金蝶数据同步调度,默认每天 02:00 执行 */
|
||||
@Scheduled(cron = "${thirdParty.bfi.customer.cron:0 0 2 * * ?}")
|
||||
public void syncKingdeeCustomerData() {
|
||||
if (!syncEnabled) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String summary = customerArchiveService.syncKingdeeCustomerData();
|
||||
log.info("定时同步金蝶客商数据:{}", summary);
|
||||
} catch (Exception exception) {
|
||||
log.error("定时同步金蝶客商数据异常", exception);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user