fix: 移除付款申请A103-2连续两轮查询消失自动解锁兜底
- 回写未查得不再Redis计数解锁,保持现状等待下一轮;unlockBill 仅保留金蝶删单回调触发,避免金蝶单据实际存在时误解锁清空本地金蝶单据编号/内码 - 附三笔误解锁单据(FKSQ-00000287/88/89)还原SQL:doc/sql/transport/lqj/temp/restore_3_payment_application_kingdee_unlock_20261106.sql
This commit is contained in:
+4
-5
@@ -89,14 +89,13 @@ public interface IPaymentApplicationService extends BaseService<PaymentApplicati
|
|||||||
String retryKingdeeFailed();
|
String retryKingdeeFailed();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 金蝶单据解锁(ADR-0005):金蝶侧单据已被反审核删除时解除本地锁定——
|
* 金蝶单据解锁:金蝶侧单据已被反审核删除时解除本地锁定——
|
||||||
* 金蝶状态置生成失败、清空金蝶单据内码与编号(原值日志留档),不自动重推,等待人工核实后重新推送。
|
* 金蝶状态置生成失败、清空金蝶单据内码与编号(原值日志留档),不自动重推,等待人工核实后重新推送。
|
||||||
* 触发来源:① B901 UNLOCK 回调(待建设);② A103-2 定时回写"已同步单据连续两轮查询消失"兜底。
|
* 触发来源:金蝶删单/解锁回调(KingdeeBillLockServiceImpl 分发)。
|
||||||
* 仅对已生成/付款中单据生效,终态(已付款/已关闭)与生成失败单据自动跳过;
|
* 仅对已生成/付款中单据生效,终态(已付款/已关闭)与生成失败单据自动跳过
|
||||||
* 重推时 A103-2 勾稽预检兜底,误判场景(金蝶单据实际存在)自动回填不产生重复单据
|
|
||||||
*
|
*
|
||||||
* @param id 付款申请id
|
* @param id 付款申请id
|
||||||
* @param source 解锁来源标识(B901 / A103-2,用于日志追溯)
|
* @param source 解锁来源标识(用于日志追溯)
|
||||||
*/
|
*/
|
||||||
void unlockBill(Long id, String source);
|
void unlockBill(Long id, String source);
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -243,8 +243,8 @@ public class KingdeeBillLockServiceImpl extends ServiceImpl<KingdeeBillLockMappe
|
|||||||
log.info("金蝶删单回调源付款申请[{}]已作废(幂等跳过)", application.getPaymentNo());
|
log.info("金蝶删单回调源付款申请[{}]已作废(幂等跳过)", application.getPaymentNo());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 复用 ADR-0005 解锁口径:不作废源单,kingdeeStatus→生成失败、清编号内码(原值留档),
|
// 解锁口径:不作废源单,kingdeeStatus→生成失败、清编号内码(原值留档),
|
||||||
// 不自动重推,人工核实后重新推送(A103-2 勾稽预检防重复推单)
|
// 不自动重推,人工核实后重新推送(重推时 A103-2 勾稽预检防重复推单)
|
||||||
SpringUtil.getBean(IPaymentApplicationService.class).unlockBill(application.getId(), "金蝶删单回调");
|
SpringUtil.getBean(IPaymentApplicationService.class).unlockBill(application.getId(), "金蝶删单回调");
|
||||||
}
|
}
|
||||||
default -> log.warn("金蝶删单回调暂不支持的单据类型:targetBillType={}(映射预留)", lockRecord.getTargetBillType());
|
default -> log.warn("金蝶删单回调暂不支持的单据类型:targetBillType={}(映射预留)", lockRecord.getTargetBillType());
|
||||||
|
|||||||
+2
-63
@@ -26,7 +26,6 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springblade.core.log.exception.ServiceException;
|
import org.springblade.core.log.exception.ServiceException;
|
||||||
import org.springblade.core.mp.base.BaseServiceImpl;
|
import org.springblade.core.mp.base.BaseServiceImpl;
|
||||||
import org.springblade.core.redis.cache.BladeRedis;
|
|
||||||
import org.springblade.core.secure.utils.AuthUtil;
|
import org.springblade.core.secure.utils.AuthUtil;
|
||||||
import org.springblade.core.tool.jackson.JsonUtil;
|
import org.springblade.core.tool.jackson.JsonUtil;
|
||||||
import org.springblade.core.tool.utils.BeanUtil;
|
import org.springblade.core.tool.utils.BeanUtil;
|
||||||
@@ -135,7 +134,6 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@@ -153,12 +151,6 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
|||||||
private static final String MOCK_PAYMENT_RECORD_PREFIX = "MOCK-PAY-";
|
private static final String MOCK_PAYMENT_RECORD_PREFIX = "MOCK-PAY-";
|
||||||
/** 金蝶付款结果回写 single-flight 锁键前缀(多副本定时任务与手动触发共用) */
|
/** 金蝶付款结果回写 single-flight 锁键前缀(多副本定时任务与手动触发共用) */
|
||||||
private static final String PAY_RESULT_SYNC_LOCK_PREFIX = "transport:payment-application:kingdee-pay-result-sync:";
|
private static final String PAY_RESULT_SYNC_LOCK_PREFIX = "transport:payment-application:kingdee-pay-result-sync:";
|
||||||
/** A103-2 消失轮次 Redis 计数键前缀(金蝶侧疑似反审核删除的兜底判定,ADR-0005) */
|
|
||||||
private static final String PAY_RESULT_MISS_KEY_PREFIX = "transport:payment-application:kingdee-pay-miss:";
|
|
||||||
/** A103-2 消失解锁轮次阈值:连续两轮未查得即解锁置失败态 */
|
|
||||||
private static final int PAY_RESULT_MISS_UNLOCK_ROUNDS = 2;
|
|
||||||
/** A103-2 消失轮次计数键过期秒数:覆盖三个回写周期(10分钟/轮),超时未续计则重新累计(保守方向) */
|
|
||||||
private static final long PAY_RESULT_MISS_KEY_TTL_SECONDS = 2100L;
|
|
||||||
private final PaymentApplicationInvoiceMapper invoiceMapper;
|
private final PaymentApplicationInvoiceMapper invoiceMapper;
|
||||||
private final PaymentApplicationRecordMapper recordMapper;
|
private final PaymentApplicationRecordMapper recordMapper;
|
||||||
private final PaymentApplicationSettlementMapper settlementRelationMapper;
|
private final PaymentApplicationSettlementMapper settlementRelationMapper;
|
||||||
@@ -185,7 +177,6 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
|||||||
private final KingdeeAttachmentSyncSupport kingdeeAttachmentSyncSupport;
|
private final KingdeeAttachmentSyncSupport kingdeeAttachmentSyncSupport;
|
||||||
private final BfiProperties bfiProperties;
|
private final BfiProperties bfiProperties;
|
||||||
private final KingdeeSyncLockSupport kingdeeSyncLockSupport;
|
private final KingdeeSyncLockSupport kingdeeSyncLockSupport;
|
||||||
private final BladeRedis bladeRedis;
|
|
||||||
private final ApplicationEventPublisher eventPublisher;
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
private final HcsCreditCheckSupport hcsCreditCheckSupport;
|
private final HcsCreditCheckSupport hcsCreditCheckSupport;
|
||||||
private final HcsProperties hcsProperties;
|
private final HcsProperties hcsProperties;
|
||||||
@@ -847,7 +838,6 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
|||||||
if (id == null) return;
|
if (id == null) return;
|
||||||
PaymentApplication entity = getById(id);
|
PaymentApplication entity = getById(id);
|
||||||
if (entity == null) return;
|
if (entity == null) return;
|
||||||
clearPayApplyMissKey(id);
|
|
||||||
if (!KingdeeBfiConstant.KINGDEE_STATUS_SYNCED.equals(entity.getKingdeeStatus())
|
if (!KingdeeBfiConstant.KINGDEE_STATUS_SYNCED.equals(entity.getKingdeeStatus())
|
||||||
&& !KingdeeBfiConstant.KINGDEE_STATUS_PAYING.equals(entity.getKingdeeStatus())) {
|
&& !KingdeeBfiConstant.KINGDEE_STATUS_PAYING.equals(entity.getKingdeeStatus())) {
|
||||||
// 仅已生成/付款中单据处于"金蝶已确认收单"的锁定态;生成失败/终态无需解锁
|
// 仅已生成/付款中单据处于"金蝶已确认收单"的锁定态;生成失败/终态无需解锁
|
||||||
@@ -870,54 +860,6 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
|||||||
voidPreSettlementAdvance(entity);
|
voidPreSettlementAdvance(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* A103-2 未查得已推送单据的兜底判定(ADR-0005"连续两轮查询消失"):
|
|
||||||
* 仅对已生成/付款中单据经 Redis 计数累计消失轮次(生成失败单据金蝶查不到属正常态,等待人工重推,不参与计数),
|
|
||||||
* 连续达到阈值即解锁置失败态;重推链 A103-2 勾稽预检兜底,误判场景(金蝶单据实际存在)自动回填不产生重复单据
|
|
||||||
*
|
|
||||||
* @param application 本轮未查得的付款申请
|
|
||||||
*/
|
|
||||||
private void handlePayApplyMissed(PaymentApplication application) {
|
|
||||||
if (Func.isBlank(application.getPaymentNo())
|
|
||||||
|| (!KingdeeBfiConstant.KINGDEE_STATUS_SYNCED.equals(application.getKingdeeStatus())
|
|
||||||
&& !KingdeeBfiConstant.KINGDEE_STATUS_PAYING.equals(application.getKingdeeStatus()))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String missKey = payApplyMissKey(application.getId());
|
|
||||||
Long rounds;
|
|
||||||
try {
|
|
||||||
rounds = bladeRedis.getStringRedisTemplate().opsForValue().increment(missKey);
|
|
||||||
bladeRedis.getStringRedisTemplate().expire(missKey, PAY_RESULT_MISS_KEY_TTL_SECONDS, TimeUnit.SECONDS);
|
|
||||||
} catch (Exception exception) {
|
|
||||||
// Redis 异常时放弃本轮判定:解锁是破坏性动作(清空金蝶单号),宁可延迟不可误判
|
|
||||||
log.warn("付款申请[{}]A103-2 消失轮次累计失败,本轮跳过解锁判定", application.getPaymentNo(), exception);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (rounds != null && rounds >= PAY_RESULT_MISS_UNLOCK_ROUNDS) {
|
|
||||||
log.warn("付款申请[{}]A103-2 连续[{}]轮未查得金蝶付款申请单,判定金蝶侧已反审核删除,执行解锁",
|
|
||||||
application.getPaymentNo(), rounds);
|
|
||||||
self().unlockBill(application.getId(), "A103-2 连续" + rounds + "轮查询消失");
|
|
||||||
} else {
|
|
||||||
log.warn("付款申请[{}]A103-2 未查得金蝶付款申请单(第[{}]轮),金蝶侧可能已反审核删除,下一轮复核",
|
|
||||||
application.getPaymentNo(), rounds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A103-2 消失轮次计数键 */
|
|
||||||
private String payApplyMissKey(Long id) {
|
|
||||||
return PAY_RESULT_MISS_KEY_PREFIX + id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** 清除 A103-2 消失轮次计数(金蝶重新查得或单据解锁后调用) */
|
|
||||||
private void clearPayApplyMissKey(Long id) {
|
|
||||||
if (id == null) return;
|
|
||||||
try {
|
|
||||||
bladeRedis.getStringRedisTemplate().delete(payApplyMissKey(id));
|
|
||||||
} catch (Exception exception) {
|
|
||||||
log.warn("付款申请[{}]清除 A103-2 消失轮次计数失败", id, exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> syncKingdeeBatch(List<Long> ids) {
|
public List<String> syncKingdeeBatch(List<Long> ids) {
|
||||||
if (ids == null || ids.isEmpty()) throw new ServiceException("请至少选择一条审批通过的付款申请");
|
if (ids == null || ids.isEmpty()) throw new ServiceException("请至少选择一条审批通过的付款申请");
|
||||||
@@ -963,7 +905,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
|||||||
* 查得即回填纠正为已生成,避免人工重推产生重复单据,未查得则保持生成失败等待人工重推;
|
* 查得即回填纠正为已生成,避免人工重推产生重复单据,未查得则保持生成失败等待人工重推;
|
||||||
* ② A103-2 付款申请单查询回填:指定单据号时按金蝶单据号(billno)精确查询(无金蝶单据号兜底按来源单号),
|
* ② A103-2 付款申请单查询回填:指定单据号时按金蝶单据号(billno)精确查询(无金蝶单据号兜底按来源单号),
|
||||||
* 定时全量回写按 paymentNo(awrp_src_code)分批查询,同一来源单号取最近修改一条,
|
* 定时全量回写按 paymentNo(awrp_src_code)分批查询,同一来源单号取最近修改一条,
|
||||||
* 回填金蝶单号/内码/同步状态/已付金额并联动预结算与正式结算付款记录;连续两轮查询未命中按金蝶反审核删除解锁;
|
* 回填金蝶单号/内码/同步状态/已付金额并联动预结算与正式结算付款记录;未查得保持现状等待下一轮;
|
||||||
* ③ A103-4 付款处理查询补付款记录:付款中/已付款单据 + 已付款无付款记录单据(补偿),
|
* ③ A103-4 付款处理查询补付款记录:付款中/已付款单据 + 已付款无付款记录单据(补偿),
|
||||||
* 按付款组织 + awrp_sourcebillno(金蝶单据号)数组精确查询,双键勾稽后回填付款记录;
|
* 按付款组织 + awrp_sourcebillno(金蝶单据号)数组精确查询,双键勾稽后回填付款记录;
|
||||||
* ④ 汇总执行摘要:各计数随流程累计,单笔失败仅计数不中断整轮
|
* ④ 汇总执行摘要:各计数随流程累计,单笔失败仅计数不中断整轮
|
||||||
@@ -1002,15 +944,12 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
|||||||
batch -> payApplyMap.putAll(this.latestPayApplyBySrcCode(this.queryPayApplyPagesByBillNo(batch))));
|
batch -> payApplyMap.putAll(this.latestPayApplyBySrcCode(this.queryPayApplyPagesByBillNo(batch))));
|
||||||
this.batchForeach(fallbackSrcCodes, PAY_RESULT_QUERY_BATCH,
|
this.batchForeach(fallbackSrcCodes, PAY_RESULT_QUERY_BATCH,
|
||||||
batch -> payApplyMap.putAll(this.latestPayApplyBySrcCode(this.queryPayApplyPages(batch))));
|
batch -> payApplyMap.putAll(this.latestPayApplyBySrcCode(this.queryPayApplyPages(batch))));
|
||||||
// 逐单回写:查得即回填并清零消失计数;未查得累计消失轮次(批次其余单据正常返回,排除服务整体故障)
|
// 逐单回写:未查得保持现状等待下一轮,查得即回填(解锁仅由金蝶删单回调触发)
|
||||||
for (PaymentApplication application : applications) {
|
for (PaymentApplication application : applications) {
|
||||||
BfiPayApplyVO payApply = payApplyMap.get(application.getPaymentNo());
|
BfiPayApplyVO payApply = payApplyMap.get(application.getPaymentNo());
|
||||||
if (payApply == null) {
|
if (payApply == null) {
|
||||||
// 累计消失轮次,连续两轮视为金蝶已反审核删除,解锁置失败交人工核实重推
|
|
||||||
this.handlePayApplyMissed(application);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
this.clearPayApplyMissKey(application.getId());
|
|
||||||
try {
|
try {
|
||||||
// 返回 true 表示付款完成(终态),预结算/正式结算联动由 applyPayApplyResult 内部处理
|
// 返回 true 表示付款完成(终态),预结算/正式结算联动由 applyPayApplyResult 内部处理
|
||||||
if (self.applyPayApplyResult(application, payApply)) {
|
if (self.applyPayApplyResult(application, payApply)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user