!2 金蝶解锁回调以及客商审核通过推送金蝶失败被吞异常,以及补充文档
Merge pull request !2 from mindless/feature/payment-apply-bfi-sync
This commit is contained in:
+8
@@ -372,6 +372,14 @@ public class BfiProperties {
|
||||
@Data
|
||||
public static class BillLock {
|
||||
|
||||
/**
|
||||
* 锁定/解锁总开关(thirdParty.bfi.billLock.enabled):推送后锁定 lockBusiData、
|
||||
* 金蝶解锁回调 unLockNotifyBusiData、锁定补偿重锁三处共用;
|
||||
* 默认开启保持既有锁定行为,Nacos 置 false 一键停用
|
||||
* (推送后不再锁单、解锁回调只应答不处理、补偿跳过),存量锁记录守卫(不可编辑校验)不受影响
|
||||
*/
|
||||
private boolean enabled = true;
|
||||
|
||||
/**
|
||||
* 锁定补偿开关,默认关闭(安全兜底)
|
||||
*/
|
||||
|
||||
+12
-8
@@ -136,21 +136,25 @@ public class CustomerArchiveController extends BladeController {
|
||||
}
|
||||
|
||||
/**
|
||||
* 审核通过(通过后自动推送金蝶,推送失败不影响审批结果)
|
||||
* 审核通过(通过后自动推送金蝶,推送失败不影响审批结果,失败原因以警告文案返回)
|
||||
*/
|
||||
@PostMapping("/approve")
|
||||
@ApiOperationSupport(order = 6)
|
||||
@Operation(summary = "审核通过", description = "传入id")
|
||||
public R approve(@Parameter(description = "主键", required = true) @RequestParam Long id) {
|
||||
boolean result = customerArchiveService.approve(id);
|
||||
if (result) {
|
||||
try {
|
||||
customerArchiveService.syncKingdee(id);
|
||||
} catch (Exception exception) {
|
||||
log.error("客商审批通过后推送金蝶异常,id={}", id, exception);
|
||||
}
|
||||
if (!result) {
|
||||
return R.status(false);
|
||||
}
|
||||
try {
|
||||
customerArchiveService.syncKingdee(id);
|
||||
return R.success("审核通过");
|
||||
} catch (Exception exception) {
|
||||
log.error("客商审批通过后推送金蝶异常,id={}", id, exception);
|
||||
String reason = exception.getMessage() == null ? "金蝶接口调用异常" : exception.getMessage();
|
||||
// 与结算/付款/开票审批口径一致:审批已生效,推送失败原因以 data=false 警告文案返回
|
||||
return R.data(Boolean.FALSE, "审核通过,但推送金蝶失败:" + reason);
|
||||
}
|
||||
return R.status(result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+8
@@ -196,6 +196,14 @@ public interface ICustomerArchiveService extends BaseService<CustomerArchive> {
|
||||
*/
|
||||
CustomerArchive syncKingdee(Long id);
|
||||
|
||||
/**
|
||||
* 推送失败状态落库(独立事务):推送主事务已回滚,失败状态与原因单独落库供页面展示与手动重推
|
||||
*
|
||||
* @param id 主键
|
||||
* @param reason 失败原因
|
||||
*/
|
||||
void markKingdeeSyncFailed(Long id, String reason);
|
||||
|
||||
/**
|
||||
* 定时同步金蝶客商数据:待推送客商自动推送 + 已同步客商使用状态回写
|
||||
*
|
||||
|
||||
+29
-2
@@ -9,8 +9,10 @@
|
||||
package org.springblade.transport.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.springblade.core.mp.base.BaseEntity;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiBillLockDTO;
|
||||
import org.springblade.transport.pojo.entity.KingdeeBillLock;
|
||||
import org.springblade.transport.pojo.vo.KingdeeUnlockMark;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -39,8 +41,10 @@ public interface IKingdeeBillLockService extends IService<KingdeeBillLock> {
|
||||
String lockAfterPush(String sourceBillType, Long sourceBillId, String sourceBillNo, String targetBillType, List<String> targetBillNos);
|
||||
|
||||
/**
|
||||
* 金蝶解锁回调(unLockNotifyBusiData):校验来源/目标系统与 lockAction,
|
||||
* targetBillNo 支持逗号拼接批量,逐张按 targetBillType+targetBillNo 定位最新 locked 锁记录;
|
||||
* 金蝶解锁回调(unLockNotifyBusiData):校验来源/目标系统与 lockAction。
|
||||
* 注意回调方向与锁定相反:回调 sourceBillNo=金蝶单号(即锁记录 targetBillNo,逗号拼接批量)、
|
||||
* targetBillNo=WLTMS 单号(即锁记录 sourceBillNo)、targetBillType 可能为空,
|
||||
* 故按金蝶单号+我方单号双条件反查最新 locked 锁记录;
|
||||
* operationType=DELETE 时按单据类型分发废除源单并还原关联数据
|
||||
*
|
||||
* @param param 金蝶回调参数
|
||||
@@ -66,6 +70,29 @@ public interface IKingdeeBillLockService extends IService<KingdeeBillLock> {
|
||||
*/
|
||||
Map<Long, KingdeeBillLock> mapActiveLocks(String sourceBillType, List<Long> sourceBillIds);
|
||||
|
||||
/**
|
||||
* 批量查源单解锁终态留痕(供列表/详情页填充解锁标识):
|
||||
* 每源单取最新一条 unlocked 锁记录(付款申请解锁后不作废,页面据此区分「金蝶删单/解锁」与普通推送失败)
|
||||
*
|
||||
* @param sourceBillType WLTMS 单据类型
|
||||
* @param sourceBillIds 源单主键集合
|
||||
* @return sourceBillId -> 最新 unlocked 锁记录,无解锁留痕的源单不在 Map 中
|
||||
*/
|
||||
Map<Long, KingdeeBillLock> mapUnlockedLocks(String sourceBillType, List<Long> sourceBillIds);
|
||||
|
||||
/**
|
||||
* 批量填充金蝶解锁终态标识(通用):对无活跃锁的行反查最新 unlocked 锁记录并回填解锁标识
|
||||
* (解锁时间/操作人/动作名称),供前端展示明显解锁标识;有活跃锁的单必非解锁终态,跳过。
|
||||
* 与 mapActiveLocks 配对在各单据 fillKingdeeLockStatus 中调用,列表/详情共用;
|
||||
* 后续单据(如收票单)接入锁单时,VO 实现 KingdeeUnlockMark 即可复用
|
||||
*
|
||||
* @param sourceBillType WLTMS 单据类型
|
||||
* @param rows 已填充活跃锁标识的 VO 行
|
||||
* @param activeLockMap 活跃锁 Map(mapActiveLocks 返回值)
|
||||
* @param <T> 实现 KingdeeUnlockMark 的 VO 类型(须继承 BaseEntity 以取主键)
|
||||
*/
|
||||
<T extends BaseEntity & KingdeeUnlockMark> void fillUnlockMarks(String sourceBillType, List<T> rows, Map<Long, KingdeeBillLock> activeLockMap);
|
||||
|
||||
/**
|
||||
* 锁定补偿:扫 failed 且 try_count 未达重试上限(RETRY_MAX_COUNT)的锁记录,逐张重调 lockBusiData
|
||||
* (接口 1对1:单次一个源单号对一个金蝶单号,单张成败互不影响;定时任务定时触发,亦可人工触发);
|
||||
|
||||
+52
-4
@@ -34,6 +34,7 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.utils.SpringUtil;
|
||||
import org.springblade.transport.mk.MkProcessNodeHelper;
|
||||
import org.springblade.transport.support.TransportBusinessSupport;
|
||||
import org.springblade.core.tenant.annotation.TenantIgnore;
|
||||
@@ -96,6 +97,7 @@ import org.springblade.transport.pojo.vo.CustomerReceiptAccountVO;
|
||||
import org.springblade.transport.service.ICustomerArchiveService;
|
||||
import org.springblade.transport.support.KingdeeSyncLockSupport;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
@@ -528,14 +530,52 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
|
||||
updateById(after);
|
||||
return after;
|
||||
}
|
||||
pushCustomerToKingdee(id, customer, after, detail(id));
|
||||
try {
|
||||
pushCustomerToKingdee(id, customer, after, detail(id));
|
||||
} catch (Exception exception) {
|
||||
// 推送事务已回滚,此处独立事务落"推送失败"状态与原因,供页面展示与手动重推(与结算/付款/开票口径一致)
|
||||
self().markKingdeeSyncFailed(id, exception.getMessage());
|
||||
throw exception;
|
||||
}
|
||||
updateById(after);
|
||||
return after;
|
||||
}
|
||||
|
||||
/** 自代理:跨方法调用确保事务注解生效(失败状态独立事务) */
|
||||
private ICustomerArchiveService self() {
|
||||
return SpringUtil.getBean(ICustomerArchiveService.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
|
||||
public void markKingdeeSyncFailed(Long id, String reason) {
|
||||
if (id == null) {
|
||||
return;
|
||||
}
|
||||
String failReason = truncateText(Func.toStrWithEmpty(reason, "金蝶接口调用异常"));
|
||||
CustomerArchive before = getById(id);
|
||||
// 条件更新:仅审批通过且未推送/推送失败的客商落失败状态,已推送/金蝶已有自动跳过保持原状态
|
||||
int updated = baseMapper.update(null, Wrappers.<CustomerArchive>lambdaUpdate()
|
||||
.eq(CustomerArchive::getId, id)
|
||||
.eq(CustomerArchive::getApprovalStatus, APPROVAL_APPROVED)
|
||||
.and(wrapper -> wrapper.isNull(CustomerArchive::getKingdeeStatus)
|
||||
.or()
|
||||
.notIn(CustomerArchive::getKingdeeStatus, List.of(KINGDEE_STATUS_SYNCED, KINGDEE_STATUS_EXIST)))
|
||||
.set(CustomerArchive::getKingdeeStatus, KINGDEE_STATUS_FAILED)
|
||||
.set(CustomerArchive::getKingdeeFailReason, failReason));
|
||||
if (updated > 0 && before != null) {
|
||||
// 变更记录留档(推送主事务已回滚,此处独立事务落状态与记录)
|
||||
CustomerArchive after = copyCustomer(before);
|
||||
after.setKingdeeStatus(KINGDEE_STATUS_FAILED);
|
||||
after.setKingdeeFailReason(failReason);
|
||||
addChangeRecord(id, "推送金蝶失败:" + failReason, before, after);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装 A902-2 报文推送金蝶并回写协同状态;detail 由调用方准备,
|
||||
* 定时同步批量组装报文数据,避免逐条 detail 查询的 N+1 问题
|
||||
* 定时同步批量组装报文数据,避免逐条 detail 查询的 N+1 问题;
|
||||
* 推送失败先落 failed 状态与变更记录,再抛出异常由调用方提示前端(与结算/付款/开票口径一致)
|
||||
*/
|
||||
private void pushCustomerToKingdee(Long id, CustomerArchive customer, CustomerArchive after, CustomerArchiveVO detail) {
|
||||
try {
|
||||
@@ -549,12 +589,18 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
|
||||
after.setKingdeeStatus(KINGDEE_STATUS_FAILED);
|
||||
after.setKingdeeFailReason(buildSaveFailReason(result, item));
|
||||
addChangeRecord(id, "推送金蝶失败:" + after.getKingdeeFailReason(), customer, after);
|
||||
throw new ServiceException("推送金蝶失败:" + after.getKingdeeFailReason());
|
||||
}
|
||||
} catch (ServiceException exception) {
|
||||
throw exception;
|
||||
} catch (Exception exception) {
|
||||
log.error("客商推送金蝶失败,id={},unifiedCreditCode={}", id, customer.getUnifiedCreditCode(), exception);
|
||||
after.setKingdeeStatus(KINGDEE_STATUS_FAILED);
|
||||
after.setKingdeeFailReason(truncateText(exception.getMessage() == null ? "金蝶接口调用异常" : exception.getMessage()));
|
||||
addChangeRecord(id, "推送金蝶失败:" + after.getKingdeeFailReason(), customer, after);
|
||||
throw exception instanceof RuntimeException runtimeException
|
||||
? runtimeException
|
||||
: new ServiceException("推送金蝶失败:" + after.getKingdeeFailReason());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -715,11 +761,11 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
|
||||
.map(CustomerArchive::getId)
|
||||
.toList());
|
||||
for (CustomerArchive customer : pushList) {
|
||||
CustomerArchive after = copyCustomer(customer);
|
||||
after.setKingdeeSyncTime(LocalDateTime.now());
|
||||
try {
|
||||
CustomerArchiveVO detail = buildSyncArchiveVO(customer,
|
||||
receiptAccountsMap.getOrDefault(customer.getId(), new ArrayList<>()));
|
||||
CustomerArchive after = copyCustomer(customer);
|
||||
after.setKingdeeSyncTime(LocalDateTime.now());
|
||||
pushCustomerToKingdee(customer.getId(), customer, after, detail);
|
||||
updateList.add(after);
|
||||
if (Objects.equals(after.getKingdeeStatus(), KINGDEE_STATUS_SYNCED)) {
|
||||
@@ -735,6 +781,8 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
|
||||
pushFailed++;
|
||||
log.error("定时同步金蝶客商推送异常,id={},unifiedCreditCode={}",
|
||||
customer.getId(), customer.getUnifiedCreditCode(), exception);
|
||||
// 失败状态与原因已在推送方法内回填 after,落库供页面展示与重推
|
||||
updateList.add(after);
|
||||
}
|
||||
}
|
||||
// ②③ 共用:对已推送(synced)/金蝶已有(exist)客商按信用代码批量复查金蝶档案(审核状态 + 使用状态),仅调用一次 A902-1
|
||||
|
||||
+2
@@ -233,6 +233,8 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
|
||||
vo.setKingdeeLockFailReason(lockRecord.getFailReason());
|
||||
vo.setKingdeeTryCount(lockRecord.getTryCount());
|
||||
}
|
||||
// 金蝶删单解锁会作废源单,按 unlocked 锁记录留痕补「金蝶删单」标识,区分正常作废与金蝶删单
|
||||
kingdeeBillLockService.fillUnlockMarks(KingdeeLockConstant.SOURCE_SETTLEMENT, rows, lockMap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+2
@@ -202,6 +202,8 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
|
||||
vo.setKingdeeLockFailReason(lockRecord.getFailReason());
|
||||
vo.setKingdeeTryCount(lockRecord.getTryCount());
|
||||
}
|
||||
// 金蝶删单解锁会作废源单,按 unlocked 锁记录留痕补「金蝶删单」标识,区分正常作废与金蝶删单
|
||||
kingdeeBillLockService.fillUnlockMarks(KingdeeLockConstant.SOURCE_INVOICE_APPLY, rows, lockMap);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+79
-18
@@ -13,6 +13,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.mp.base.BaseEntity;
|
||||
import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.SpringUtil;
|
||||
@@ -29,6 +30,7 @@ import org.springblade.transport.pojo.entity.FormalSettlement;
|
||||
import org.springblade.transport.pojo.entity.InvoiceApplication;
|
||||
import org.springblade.transport.pojo.entity.KingdeeBillLock;
|
||||
import org.springblade.transport.pojo.entity.PaymentApplication;
|
||||
import org.springblade.transport.pojo.vo.KingdeeUnlockMark;
|
||||
import org.springblade.transport.service.IFormalSettlementService;
|
||||
import org.springblade.transport.service.IInvoiceApplicationService;
|
||||
import org.springblade.transport.service.IKingdeeBillLockService;
|
||||
@@ -73,6 +75,11 @@ public class KingdeeBillLockServiceImpl extends ServiceImpl<KingdeeBillLockMappe
|
||||
|
||||
@Override
|
||||
public String lockAfterPush(String sourceBillType, Long sourceBillId, String sourceBillNo, String targetBillType, List<String> targetBillNos) {
|
||||
// 总开关关闭:不落锁记录、不调 lockBusiData,返回 null 不影响推送主流程
|
||||
if (!bfiProperties.getBillLock().isEnabled()) {
|
||||
log.info("金蝶单据锁定总开关关闭,跳过锁定:源单[{}:{}]", sourceBillType, sourceBillNo);
|
||||
return null;
|
||||
}
|
||||
if (Func.isEmpty(targetBillNos)) {
|
||||
return null;
|
||||
}
|
||||
@@ -145,40 +152,52 @@ public class KingdeeBillLockServiceImpl extends ServiceImpl<KingdeeBillLockMappe
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int onUnlockNotify(BfiBillLockDTO param) {
|
||||
// 总开关关闭:只应答不处理(返回 0 与「未匹配」同构,金蝶侧不会触发重试)
|
||||
if (!bfiProperties.getBillLock().isEnabled()) {
|
||||
log.warn("金蝶单据锁定总开关关闭,跳过解锁回调处理:operationType={},金蝶单号={}",
|
||||
param == null ? null : param.getOperationType(), param == null ? null : param.getSourceBillNo());
|
||||
return 0;
|
||||
}
|
||||
if (param == null || !KingdeeLockConstant.SYSTEM_XH.equals(param.getSourceSystem())
|
||||
|| !KingdeeLockConstant.SYSTEM_WLTMS.equals(param.getTargetSystem())
|
||||
|| !KingdeeLockConstant.ACTION_UNLOCK.equals(param.getLockAction())) {
|
||||
throw new ServiceException("金蝶解锁回调参数非法:sourceSystem须为XH、targetSystem须为WLTMS、lockAction须为UNLOCK");
|
||||
}
|
||||
if (Func.isBlank(param.getTargetBillNo())) {
|
||||
throw new ServiceException("金蝶解锁回调参数非法:targetBillNo不能为空");
|
||||
if (Func.isBlank(param.getSourceBillNo()) || Func.isBlank(param.getTargetBillNo())) {
|
||||
throw new ServiceException("金蝶解锁回调参数非法:sourceBillNo、targetBillNo不能为空");
|
||||
}
|
||||
// 批量回调:targetBillNo 逗号拼接,逐张处理;未匹配锁定中记录的幂等跳过(重复通知防金蝶重试风暴)
|
||||
int processed = 0;
|
||||
for (String targetBillNo : Func.toStrList(param.getTargetBillNo())) {
|
||||
KingdeeBillLock lockRecord = this.getOne(Wrappers.<KingdeeBillLock>lambdaQuery()
|
||||
.eq(KingdeeBillLock::getTargetBillType, param.getTargetBillType())
|
||||
.eq(KingdeeBillLock::getTargetBillNo, targetBillNo)
|
||||
.orderByDesc(KingdeeBillLock::getId)
|
||||
.last("limit 1"), false);
|
||||
// 回调方向与锁定相反:回调 sourceBillNo=金蝶单号(即锁记录 targetBillNo)、targetBillNo=WLTMS 单号(即锁记录 sourceBillNo)、
|
||||
// targetBillType 可能为空,故按金蝶单号+我方单号双条件反查;未匹配锁定中记录的幂等跳过(重复通知防金蝶重试风暴)
|
||||
// 一次 IN 查询取回候选记录、内存取各单号最新一条,批量更新解锁状态,避免逐张查询/更新的 N+1
|
||||
List<String> kingdeeBillNos = Func.toStrList(param.getSourceBillNo());
|
||||
Map<String, KingdeeBillLock> latestLockMap = this.list(Wrappers.<KingdeeBillLock>lambdaQuery()
|
||||
.in(KingdeeBillLock::getTargetBillNo, kingdeeBillNos)
|
||||
.eq(KingdeeBillLock::getSourceBillNo, param.getTargetBillNo())
|
||||
.orderByDesc(KingdeeBillLock::getId))
|
||||
.stream()
|
||||
.collect(Collectors.toMap(KingdeeBillLock::getTargetBillNo, Function.identity(), (first, second) -> first));
|
||||
List<KingdeeBillLock> unlockRecords = new ArrayList<>();
|
||||
for (String kingdeeBillNo : kingdeeBillNos) {
|
||||
KingdeeBillLock lockRecord = latestLockMap.get(kingdeeBillNo);
|
||||
if (lockRecord == null || !KingdeeLockConstant.STATUS_LOCKED.equals(lockRecord.getLockStatus())) {
|
||||
log.warn("金蝶解锁回调未匹配锁定中的锁记录(幂等跳过):targetBillType={},targetBillNo={}",
|
||||
param.getTargetBillType(), targetBillNo);
|
||||
log.warn("金蝶解锁回调未匹配锁定中的锁记录(幂等跳过):金蝶单号={},回调targetBillNo={}",
|
||||
kingdeeBillNo, param.getTargetBillNo());
|
||||
continue;
|
||||
}
|
||||
lockRecord.setLockStatus(KingdeeLockConstant.STATUS_UNLOCKED);
|
||||
lockRecord.setOperationType(param.getOperationType());
|
||||
lockRecord.setUnlockTime(LocalDateTime.now());
|
||||
lockRecord.setNotifyOperator(param.getOperator());
|
||||
this.updateById(lockRecord);
|
||||
processed++;
|
||||
unlockRecords.add(lockRecord);
|
||||
log.info("金蝶单据[{}:{}]解锁回调落库:operationType={},操作人={}",
|
||||
lockRecord.getSourceBillType(), lockRecord.getSourceBillNo(), param.getOperationType(), param.getOperator());
|
||||
if (KingdeeLockConstant.OPERATION_DELETE.equals(param.getOperationType())) {
|
||||
this.restoreOnKingdeeDelete(lockRecord, param.getOperator());
|
||||
}
|
||||
}
|
||||
return processed;
|
||||
this.updateBatchById(unlockRecords);
|
||||
// DELETE 分发废除还原:按源单逐张执行(各单据类型还原动作为独立业务事务,无法合并)
|
||||
if (KingdeeLockConstant.OPERATION_DELETE.equals(param.getOperationType())) {
|
||||
unlockRecords.forEach(lockRecord -> this.restoreOnKingdeeDelete(lockRecord, param.getOperator()));
|
||||
}
|
||||
return unlockRecords.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,6 +276,44 @@ public class KingdeeBillLockServiceImpl extends ServiceImpl<KingdeeBillLockMappe
|
||||
return lockRecords.stream().collect(Collectors.toMap(KingdeeBillLock::getSourceBillId, Function.identity(), (first, second) -> second));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Long, KingdeeBillLock> mapUnlockedLocks(String sourceBillType, List<Long> sourceBillIds) {
|
||||
if (Func.isEmpty(sourceBillIds)) {
|
||||
return Map.of();
|
||||
}
|
||||
List<KingdeeBillLock> lockRecords = this.list(Wrappers.<KingdeeBillLock>lambdaQuery()
|
||||
.eq(KingdeeBillLock::getSourceBillType, sourceBillType)
|
||||
.in(KingdeeBillLock::getSourceBillId, sourceBillIds)
|
||||
.eq(KingdeeBillLock::getLockStatus, KingdeeLockConstant.STATUS_UNLOCKED)
|
||||
.orderByDesc(KingdeeBillLock::getId));
|
||||
// 降序取回后首个生效:每源单保留最新一条解锁留痕
|
||||
return lockRecords.stream().collect(Collectors.toMap(KingdeeBillLock::getSourceBillId, Function.identity(), (first, second) -> first));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends BaseEntity & KingdeeUnlockMark> void fillUnlockMarks(String sourceBillType, List<T> rows, Map<Long, KingdeeBillLock> activeLockMap) {
|
||||
if (Func.isEmpty(rows)) {
|
||||
return;
|
||||
}
|
||||
List<Long> unlockedIds = rows.stream()
|
||||
.filter(row -> !activeLockMap.containsKey(row.getId()))
|
||||
.map(BaseEntity::getId).toList();
|
||||
if (Func.isEmpty(unlockedIds)) {
|
||||
return;
|
||||
}
|
||||
Map<Long, KingdeeBillLock> unlockedMap = this.mapUnlockedLocks(sourceBillType, unlockedIds);
|
||||
for (T row : rows) {
|
||||
KingdeeBillLock unlockRecord = unlockedMap.get(row.getId());
|
||||
if (unlockRecord == null) {
|
||||
continue;
|
||||
}
|
||||
row.setKingdeeUnlockTime(unlockRecord.getUnlockTime());
|
||||
row.setKingdeeUnlockOperator(unlockRecord.getNotifyOperator());
|
||||
row.setKingdeeUnlockOperationName(KingdeeLockConstant.OPERATION_DELETE.equals(unlockRecord.getOperationType())
|
||||
? "金蝶删单" : "金蝶锁单");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 锁定补偿:扫 failed 且 try_count 未达重试上限(RETRY_MAX_COUNT)的锁记录,逐张重调 lockBusiData
|
||||
* (接口 1对1:单次一个源单号对一个金蝶单号,单张成败互不影响,由定时任务定时触发,亦可人工触发)。
|
||||
@@ -265,6 +322,10 @@ public class KingdeeBillLockServiceImpl extends ServiceImpl<KingdeeBillLockMappe
|
||||
*/
|
||||
@Override
|
||||
public String retryFailedLocks() {
|
||||
// 总开关关闭:定时任务与手动补偿一并跳过
|
||||
if (!bfiProperties.getBillLock().isEnabled()) {
|
||||
return "金蝶单据锁定总开关关闭,跳过补偿";
|
||||
}
|
||||
List<KingdeeBillLock> failedList = this.list(Wrappers.<KingdeeBillLock>lambdaQuery()
|
||||
.eq(KingdeeBillLock::getLockStatus, KingdeeLockConstant.STATUS_FAILED)
|
||||
.lt(KingdeeBillLock::getTryCount, KingdeeLockConstant.RETRY_MAX_COUNT));
|
||||
|
||||
+2
@@ -240,6 +240,8 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
vo.setKingdeeLockFailReason(lockRecord.getFailReason());
|
||||
vo.setKingdeeTryCount(lockRecord.getTryCount());
|
||||
}
|
||||
// 金蝶解锁终态标识:付款申请解锁后不作废,按 unlocked 锁记录留痕区分「金蝶删单/解锁」与普通推送失败
|
||||
kingdeeBillLockService.fillUnlockMarks(KingdeeLockConstant.SOURCE_PAY_APPLY, rows, lockMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user