From 4a76e65b4d31369083997ae975dcd6e00a12e8a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com> Date: Thu, 29 Oct 2026 11:19:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=90=88=E8=A7=84=E8=AE=A4=E9=A2=86?= =?UTF-8?q?=E6=8E=A8=E9=80=81=E6=94=B9=E4=B8=BA=E6=8C=89=E8=AE=A4=E9=A2=86?= =?UTF-8?q?=E4=B8=BB=E5=8D=95=E8=A7=A6=E5=8F=91=E5=B9=B6=E6=96=B0=E5=A2=9E?= =?UTF-8?q?C107-3=E4=BD=9C=E5=BA=9F=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../support/hcs/HcsClaimSyncSupport.java | 276 +++++++++++++----- 1 file changed, 209 insertions(+), 67 deletions(-) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsClaimSyncSupport.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsClaimSyncSupport.java index 8577b62d..973fdbea 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsClaimSyncSupport.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsClaimSyncSupport.java @@ -11,8 +11,8 @@ * 2. Redistribution of this software's source code to any third party * is strictly prohibited. *

- * 3. Licensees may copyright their own code but cannot use segments - * from this software for their own commercial purposes. + * 3. Licensees may copyright their own code but cannot use segments of this software + * for their own commercial purposes. *

* Using this software signifies agreement to this license agreement and terms of use, * and you expressly agree to be bound by the terms of use of this software. @@ -33,17 +33,24 @@ import org.springblade.core.tool.utils.StringUtil; import org.springblade.hcs.config.HcsProperties; import org.springblade.hcs.dto.HcsClaimRecordAddDTO; import org.springblade.hcs.dto.HcsCollectionFlowVO; +import org.springblade.hcs.dto.HcsFormSaveVO; import org.springblade.hcs.dto.HcsIdVO; +import org.springblade.hcs.dto.HcsMechanismsDTO; import org.springblade.hcs.dto.HcsPageQueryDTO; import org.springblade.hcs.dto.HcsPageVO; +import org.springblade.hcs.dto.HcsPerfRecordFormDataDTO; +import org.springblade.hcs.dto.HcsPerfRecordPropsDTO; +import org.springblade.hcs.dto.HcsPerfRecordSaveDTO; import org.springblade.hcs.dto.HcsRefVO; import org.springblade.hcs.dto.HcsResult; +import org.springblade.hcs.dto.HcsUserRefVO; import org.springblade.hcs.exception.HcsException; import org.springblade.hcs.feign.IHcsClient; import org.springblade.transport.constant.HcsSyncConstant; import org.springblade.transport.mapper.ContractManageMapper; import org.springblade.transport.mapper.FormalSettlementMapper; import org.springblade.transport.mapper.KingdeeReceiptFlowMapper; +import org.springblade.transport.mapper.ReceiptClaimHcsRecordMapper; import org.springblade.transport.mapper.ReceiptClaimMapper; import org.springblade.transport.mapper.ReceiptClaimSettlementMapper; import org.springblade.transport.pojo.entity.ContractManage; @@ -51,12 +58,14 @@ import org.springblade.transport.pojo.entity.FormalSettlement; import org.springblade.transport.pojo.entity.HcsContractExeStage; import org.springblade.transport.pojo.entity.KingdeeReceiptFlow; import org.springblade.transport.pojo.entity.ReceiptClaim; +import org.springblade.transport.pojo.entity.ReceiptClaimHcsRecord; import org.springblade.transport.pojo.entity.ReceiptClaimSettlement; import org.springblade.transport.service.IContractExeStageService; import org.springframework.stereotype.Component; import java.math.BigDecimal; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.ZoneId; import java.util.LinkedHashMap; import java.util.List; @@ -67,13 +76,14 @@ import java.util.stream.Collectors; /** * HCS 收款认领链推送编排支撑组件(C103-9 更新履约计划 + C107-1 认领记录 + C108-1 履约流水) *

- * 触发点:金蝶认领推送(A301-3)成功之后(手动按钮与定时任务共用钩子)。 + * 触发点:收款流水认领事务提交后按认领主单推送(作废认领事务提交后推 C107-3 作废同步)。 * 开关复用既有配置(不单独新增):exe-stage.sync-enabled 门禁 C103-9/C107-1(与合同链履约计划同步同开关), * perf-record.sync-enabled 门禁 C108-1(与付款链履约记录同步同开关)。 * 编排顺序:先 C103-9(认领金额累加到收款履约计划行并重推,C107-1/C108-1 引用其返回的阶段ID), - * 再按「认领主单 × 合同」推 C107-1(合规流水ID 经 C104-2 按明细流水号定位),最后按合同推 C108-1 履约流水。 + * 再按「认领主单 × 合同」推 C107-1(合规流水ID 经 C104-2 按明细流水号定位,返回 fdId 落库 + * blade_receipt_claim_hcs_record 供作废 C107-3 引用),最后按合同推 C108-1 履约流水。 * 合同未推送合规(hcs_fd_id 空)时仅累加计划行金额,随合同链路首推。 - * 推送失败仅记日志不回滚金蝶推送结果(单合同失败不影响其余合同)。 + * 推送失败仅落失败标记与日志,不回滚认领主流程(单合同失败不影响其余合同,不做定时重试)。 * * @author Chill */ @@ -86,60 +96,80 @@ public class HcsClaimSyncSupport { * 认领完成状态(与 ReceiptFlowServiceImpl.CLAIMED 同值) */ private static final String CLAIMED = "claimed"; + /** + * 推送状态:推送成功 + */ + private static final String PUSH_SUCCESS = "success"; + /** + * 推送状态:推送失败(仅记标记,不做定时重试) + */ + private static final String PUSH_FAILED = "failed"; + /** + * 推送状态:已作废(C107-3 同步成功) + */ + private static final String PUSH_VOIDED = "voided"; + /** + * 认领记录状态:已认领 + */ + private static final int CLAIM_STATUS_CLAIMED = 0; + /** + * 认领记录状态:已作废 + */ + private static final int CLAIM_STATUS_VOIDED = 1; private final IHcsClient hcsClient; private final HcsProperties hcsProperties; private final KingdeeReceiptFlowMapper receiptFlowMapper; private final ReceiptClaimMapper claimMapper; private final ReceiptClaimSettlementMapper claimSettlementMapper; + private final ReceiptClaimHcsRecordMapper claimHcsRecordMapper; private final FormalSettlementMapper formalSettlementMapper; private final ContractManageMapper contractManageMapper; private final IContractExeStageService contractExeStageService; private final HcsExeStageSyncSupport hcsExeStageSyncSupport; - private final HcsPerfRecordAddSupport hcsPerfRecordAddSupport; /** - * 金蝶认领推送成功后推送合同合规(C103-9 → C107-1 → C108-1) + * 认领事务提交后推送合同合规(C103-9 → C107-1 → C108-1,按认领主单维度) *

* 复用既有开关:exe-stage.sync-enabled(履约计划同步)门禁 C103-9/C107-1, * perf-record.sync-enabled(履约记录同步)门禁 C108-1;开关关闭或推送异常均静默返回, - * 不影响金蝶认领推送主流程与事务。 + * 不影响认领主流程与事务。 * - * @param flowId 收款流水ID + * @param claimId 认领主单ID */ - public void pushClaimToHcs(Long flowId) { + public void pushClaimToHcs(Long claimId) { if (!hcsProperties.getExeStage().isSyncEnabled()) { return; } try { - doPushClaimToHcs(flowId); + doPushClaimToHcs(claimId); } catch (Exception exception) { - log.error("HCS收款认领链推送异常 flowId:{}", flowId, exception); + log.error("HCS收款认领链推送异常 claimId:{}", claimId, exception); } } /** - * 推送主体:归集有效认领分摊(按合同分组)→ 逐合同推送(单合同失败不影响其余合同) + * 推送主体:归集本认领主单有效分摊(按合同分组)→ 逐合同推送(单合同失败不影响其余合同) */ - private void doPushClaimToHcs(Long flowId) { - KingdeeReceiptFlow flow = receiptFlowMapper.selectById(flowId); + private void doPushClaimToHcs(Long claimId) { + ReceiptClaim claim = claimMapper.selectById(claimId); + if (claim == null || !CLAIMED.equals(claim.getClaimStatus())) { + log.info("HCS收款认领链推送跳过:认领主单不存在或已作废 claimId={}", claimId); + return; + } + KingdeeReceiptFlow flow = receiptFlowMapper.selectById(claim.getReceiptFlowId()); if (flow == null) { + log.warn("HCS收款认领链推送跳过:收款流水不存在 claimId={},flowId={}", claimId, claim.getReceiptFlowId()); return; } - // 未作废认领主单(作废认领不同步合规) - List claims = claimMapper.selectList(Wrappers.lambdaQuery() - .eq(ReceiptClaim::getReceiptFlowId, flowId) - .eq(ReceiptClaim::getClaimStatus, CLAIMED)); - if (claims.isEmpty()) { - return; - } - // 有效分摊行(作废时已置 status=0) + // 本认领主单有效分摊行(作废时已置 status=0) List allocations = claimSettlementMapper.selectList( Wrappers.lambdaQuery() - .eq(ReceiptClaimSettlement::getReceiptFlowId, flowId) + .eq(ReceiptClaimSettlement::getReceiptClaimId, claimId) .eq(ReceiptClaimSettlement::getStatus, 1) .eq(ReceiptClaimSettlement::getIsDeleted, 0)); if (allocations.isEmpty()) { + log.info("HCS收款认领链推送跳过:认领主单无有效分摊 claimId={}", claimId); return; } // 结算单批量定位合同(一次 IN 查询,避免逐行 N+1) @@ -147,20 +177,18 @@ public class HcsClaimSyncSupport { allocations.stream().map(ReceiptClaimSettlement::getFormalSettlementId) .filter(Objects::nonNull).distinct().toList()).stream() .collect(Collectors.toMap(FormalSettlement::getId, item -> item, (first, second) -> first)); - // 合同id → 认领主单id → 分摊金额 - Map> amountByContractByClaim = new LinkedHashMap<>(); + // 合同id → 本认领主单分摊金额 + Map amountByContract = new LinkedHashMap<>(); for (ReceiptClaimSettlement allocation : allocations) { FormalSettlement settlement = settlements.get(allocation.getFormalSettlementId()); if (settlement == null || settlement.getContractId() == null - || allocation.getReceiptClaimId() == null || allocation.getAllocatedReceiptAmount() == null) { + || allocation.getAllocatedReceiptAmount() == null) { continue; } - amountByContractByClaim - .computeIfAbsent(settlement.getContractId(), key -> new LinkedHashMap<>()) - .merge(allocation.getReceiptClaimId(), allocation.getAllocatedReceiptAmount(), BigDecimal::add); + amountByContract.merge(settlement.getContractId(), allocation.getAllocatedReceiptAmount(), BigDecimal::add); } - if (amountByContractByClaim.isEmpty()) { - log.info("HCS收款认领链推送跳过:分摊行未关联合同 flowId={}", flowId); + if (amountByContract.isEmpty()) { + log.info("HCS收款认领链推送跳过:分摊行未关联合同 claimId={},flowId={}", claimId, flow.getId()); return; } // 合规侧收款流水ID(C107-1 必填引用,一次定位全部合同共用;定位失败仅跳过 C107-1,不阻塞履约计划更新) @@ -168,47 +196,47 @@ public class HcsClaimSyncSupport { try { hcsCollectionFlowId = locateHcsCollectionFlowId(flow); } catch (Exception exception) { - log.error("HCS收款流水定位失败 flowId:{},跳过C107-1认领记录推送", flowId, exception); + log.error("HCS收款流水定位失败 flowId:{},跳过C107-1认领记录推送", flow.getId(), exception); } - for (Map.Entry> contractEntry : amountByContractByClaim.entrySet()) { + for (Map.Entry contractEntry : amountByContract.entrySet()) { try { - pushOneContract(flow, claims, contractEntry.getKey(), contractEntry.getValue(), hcsCollectionFlowId); + pushOneContract(flow, claim, contractEntry.getKey(), contractEntry.getValue(), hcsCollectionFlowId); } catch (Exception exception) { - log.error("HCS收款认领链推送失败 flowId:{},contractId:{}", flowId, contractEntry.getKey(), exception); + log.error("HCS收款认领链推送失败 claimId:{},flowId:{},contractId:{}", + claimId, flow.getId(), contractEntry.getKey(), exception); } } } /** - * 推送单个合同:① C103-9 更新履约计划(认领金额累加重推)→ ② C107-1 认领记录(按认领主单分组)→ ③ C108-1 履约流水 + * 推送单个合同:① C103-9 更新履约计划(认领金额累加重推)→ ② C107-1 认领记录(结果落库)→ ③ C108-1 履约流水 */ - private void pushOneContract(KingdeeReceiptFlow flow, List claims, Long contractId, - Map amountByClaim, String hcsCollectionFlowId) { + private void pushOneContract(KingdeeReceiptFlow flow, ReceiptClaim claim, Long contractId, + BigDecimal claimAmount, String hcsCollectionFlowId) { ContractManage contractManage = contractManageMapper.selectById(contractId); if (contractManage == null) { log.warn("HCS收款认领链推送跳过:合同不存在 contractId={}", contractId); return; } - BigDecimal claimedTotal = amountByClaim.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add); // ① C103-9 更新合同履约计划:认领金额累加到收款计划行;已同步行置 update 凭阶段ID 重推 - contractExeStageService.accumulateClaimedAmountOnPush(contractId, claimedTotal); + contractExeStageService.accumulateClaimedAmountOnPush(contractId, claimAmount); HcsContractExeStage stageRow = contractExeStageService.findCollectionStageRow(contractId, HcsSyncConstant.FUND_NATURE_PROGRESS); if (StringUtil.isNotBlank(contractManage.getHcsFdId())) { // 行级重推(无待同步行返回提示,不视为失败);合同未推送合规时随合同链路首推 hcsExeStageSyncSupport.syncExeStages(contractManage); } - // ② C107-1 认领记录:按「认领主单 × 合同」推送,金额为该认领主单分摊到本合同的金额 + // ② C107-1 认领记录:本认领主单分摊到本合同的金额,返回 fdId 落库供 C107-3 作废引用 if (StringUtil.isBlank(hcsCollectionFlowId)) { - log.warn("HCS认领记录推送跳过:未定位到合规收款流水 flowId={},detailSerialNo={}", flow.getId(), flow.getDetailSerialNo()); + log.warn("HCS认领记录推送跳过:未定位到合规收款流水 claimId={},flowId={},detailSerialNo={}", + claim.getId(), flow.getId(), flow.getDetailSerialNo()); + saveClaimHcsRecord(claim, flow, contractManage, claimAmount, null, PUSH_FAILED, "C104-2未定位到合规收款流水"); } else { - Map claimsById = claims.stream() - .collect(Collectors.toMap(ReceiptClaim::getId, item -> item, (first, second) -> first)); - for (Map.Entry claimEntry : amountByClaim.entrySet()) { - pushClaimRecord(flow, claimsById.get(claimEntry.getKey()), contractManage, stageRow, claimEntry.getValue(), hcsCollectionFlowId); - } + String claimRecordFdId = pushClaimRecord(flow, claim, contractManage, stageRow, claimAmount, hcsCollectionFlowId); + saveClaimHcsRecord(claim, flow, contractManage, claimAmount, claimRecordFdId, + StringUtil.isBlank(claimRecordFdId) ? PUSH_FAILED : PUSH_SUCCESS, null); } // ③ C108-1 履约流水:按合同一条(开关 perf-record.sync-enabled,与付款链履约记录同步同开关), - // 须已推送合规且存在已同步收款计划行(fd_plan_id 必填);履约日期为收款流水交易日期 + // 须已推送合规且存在已同步收款计划行(fd_plan_id 必填) if (!hcsProperties.getPerfRecord().isSyncEnabled()) { log.info("HCS履约流水推送跳过:履约记录同步开关未开启 contractNo={}", contractManage.getContractNo()); return; @@ -221,16 +249,18 @@ public class HcsClaimSyncSupport { log.info("HCS履约流水推送跳过:无已同步收款履约计划行 contractNo={}", contractManage.getContractNo()); return; } - pushPerfRecord(flow, contractManage, stageRow, claimedTotal); + pushPerfRecord(flow, contractManage, stageRow, claimAmount, claim.getClaimDate()); } /** * C107-1 认领记录推送(单认领主单 × 单合同) + * + * @return 合规侧认领记录 fdId(响应未返回返回 null) */ - private void pushClaimRecord(KingdeeReceiptFlow flow, ReceiptClaim claim, ContractManage contractManage, - HcsContractExeStage stageRow, BigDecimal claimAmount, String hcsCollectionFlowId) { - if (claim == null || claimAmount == null || claimAmount.signum() <= 0) { - return; + private String pushClaimRecord(KingdeeReceiptFlow flow, ReceiptClaim claim, ContractManage contractManage, + HcsContractExeStage stageRow, BigDecimal claimAmount, String hcsCollectionFlowId) { + if (claimAmount == null || claimAmount.signum() <= 0) { + return null; } HcsClaimRecordAddDTO param = new HcsClaimRecordAddDTO(); // 【必填】收款流水引用(合规侧流水ID,C104-2 按明细流水号定位) @@ -259,7 +289,7 @@ public class HcsClaimSyncSupport { param.setFdClaimDate(toMillis(claim.getClaimDate())); param.setFdRemark(claim.getRemark()); // 状态 0=已认领(仅推送未作废认领) - param.setFdStatus(0); + param.setFdStatus(CLAIM_STATUS_CLAIMED); log.info("HCS认领记录推送开始 flowId:{},claimId:{},contractNo:{},认领金额 {}", flow.getId(), claim.getId(), contractManage.getContractNo(), claimAmount); HcsResult result = hcsClient.claimRecordAdd(param); @@ -267,25 +297,137 @@ public class HcsClaimSyncSupport { log.error("HCS认领记录推送失败响应:{},claimId={}", result == null ? "无响应" : String.format("msg=%s, traceId=%s", result.getMsg(), result.getTraceId()), claim.getId()); throw new HcsException("C107-1 认领记录推送失败:" + (result == null ? "响应为空" : result.getMsg())); } - // shortcut: C107-1 返回 fdId 未持久化(认领作废同步 C107-3 未实现,重推会重复);需作废联动时先落库 + String claimRecordFdId = result.getData() == null ? null : result.getData().getFdId(); log.info("HCS认领记录推送成功 flowId:{},claimId:{},contractNo:{},claimRecordFdId:{}", flow.getId(), claim.getId(), contractManage.getContractNo(), - result.getData() == null ? "响应未返回" : result.getData().getFdId()); + StringUtil.isBlank(claimRecordFdId) ? "响应未返回" : claimRecordFdId); + return claimRecordFdId; } /** - * C108-1 履约流水推送(单合同一条:申请金额=实际金额=该合同累计认领金额,履约日期=收款流水交易日期) + * C107-1 推送结果落库(认领主单 × 合同一条):成功持久化合规 fdId,失败落标记与原因 + */ + private void saveClaimHcsRecord(ReceiptClaim claim, KingdeeReceiptFlow flow, ContractManage contractManage, + BigDecimal claimAmount, String claimRecordFdId, String pushStatus, String failReason) { + ReceiptClaimHcsRecord record = new ReceiptClaimHcsRecord(); + record.setReceiptClaimId(claim.getId()); + record.setReceiptFlowId(flow.getId()); + record.setContractId(contractManage.getId()); + record.setContractNo(contractManage.getContractNo()); + record.setClaimAmount(claimAmount); + record.setHcsClaimRecordFdId(claimRecordFdId); + record.setPushStatus(pushStatus); + record.setFailReason(failReason); + record.setPushedTime(PUSH_SUCCESS.equals(pushStatus) ? LocalDateTime.now() : null); + claimHcsRecordMapper.insert(record); + } + + /** + * 认领作废事务提交后同步合规 C107-3(修改认领记录 fdStatus=1 已作废) + *

+ * 仅同步本认领主单推送成功的合规记录;单条失败独立记日志,不影响其余合同。 + * 作废失败时本地记录保持 success(合规侧仍为已认领),需人工在合规侧处置。 + * + * @param claimId 认领主单ID + */ + public void voidClaimToHcs(Long claimId) { + if (!hcsProperties.getExeStage().isSyncEnabled()) { + return; + } + try { + List records = claimHcsRecordMapper.selectList( + Wrappers.lambdaQuery() + .eq(ReceiptClaimHcsRecord::getReceiptClaimId, claimId) + .eq(ReceiptClaimHcsRecord::getPushStatus, PUSH_SUCCESS)); + if (records.isEmpty()) { + log.info("HCS认领作废同步跳过:无推送成功的合规认领记录 claimId={}", claimId); + return; + } + for (ReceiptClaimHcsRecord record : records) { + try { + voidOneClaimRecord(record); + } catch (Exception exception) { + log.error("HCS认领作废同步失败 claimId:{},recordId:{},hcsFdId:{}", + claimId, record.getId(), record.getHcsClaimRecordFdId(), exception); + } + } + } catch (Exception exception) { + log.error("HCS认领作废同步异常 claimId:{}", claimId, exception); + } + } + + /** + * C107-3 单条作废:fdId 必填,fdStatus=1 已作废;成功后本地记录置 voided + */ + private void voidOneClaimRecord(ReceiptClaimHcsRecord record) { + HcsClaimRecordAddDTO param = new HcsClaimRecordAddDTO(); + // 【必填】合规侧认领记录ID(C107-1 推送成功时落库) + param.setFdId(record.getHcsClaimRecordFdId()); + // 状态 1=已作废 + param.setFdStatus(CLAIM_STATUS_VOIDED); + log.info("HCS认领作废同步开始 claimId:{},contractNo:{},hcsFdId:{}", + record.getReceiptClaimId(), record.getContractNo(), record.getHcsClaimRecordFdId()); + HcsResult result = hcsClient.claimRecordUpdate(param); + if (result == null || !result.isSuccess()) { + throw new HcsException("C107-3 认领作废同步失败:" + (result == null ? "响应为空" : result.getMsg())); + } + // 本地推送记录置已作废 + claimHcsRecordMapper.update(null, Wrappers.lambdaUpdate() + .eq(ReceiptClaimHcsRecord::getId, record.getId()) + .set(ReceiptClaimHcsRecord::getPushStatus, PUSH_VOIDED)); + log.info("HCS认领作废同步成功 claimId:{},contractNo:{},hcsFdId:{}", + record.getReceiptClaimId(), record.getContractNo(), record.getHcsClaimRecordFdId()); + } + + /** + * C108-1 履约流水推送(单合同一条:申请金额=实际金额=本次认领分摊金额) */ private void pushPerfRecord(KingdeeReceiptFlow flow, ContractManage contractManage, - HcsContractExeStage stageRow, BigDecimal claimedTotal) { - // 履约日期:交易日期(需求口径),交易时间为空兜底当天 - LocalDate perfDate = flow.getTransactionTime() != null ? flow.getTransactionTime().toLocalDate() : LocalDate.now(); - String perfFdId = hcsPerfRecordAddSupport.pushPerfRecordAdd(contractManage, stageRow, - firstNonBlank(flow.getReceiptNoticeNo(), flow.getDetailSerialNo()), claimedTotal, toMillis(perfDate), - HcsSyncConstant.PERF_TYPE_COLLECTION, flow.getKingdeeBillNo(), flow.getId(), - "收款履约-" + firstNonBlank(flow.getReceiptNoticeNo(), flow.getDetailSerialNo())); - // shortcut: 履约流水 fdId 未持久化(认领链无 C108-2 更新场景,重推会重复);需推进实际金额更新时先落库 - log.info("HCS履约流水新建完成 flowId:{},contractNo:{},perfFdId:{}", flow.getId(), contractManage.getContractNo(), perfFdId); + HcsContractExeStage stageRow, BigDecimal claimAmount, LocalDate planDate) { + // 表单模板ID(配置 perf-record.xform-id,环境值 §9-U9,未配置不传) + String xformId = StringUtil.isBlank(hcsProperties.getPerfRecord().getXformId()) ? null : hcsProperties.getPerfRecord().getXformId(); + HcsPerfRecordPropsDTO props = new HcsPerfRecordPropsDTO(); + // 【必填】申请编号:收款通知单号,兜底明细流水号 + props.setFdApplyNo(firstNonBlank(flow.getReceiptNoticeNo(), flow.getDetailSerialNo())); + // 申请金额 / 实际金额:本次认领分摊金额 + props.setFdApplyAmount(claimAmount.toPlainString()); + props.setFdFactAmount(claimAmount.toPlainString()); + // 【必填】履约日期:认领日期(毫秒时间戳) + props.setFdPlanDate(toMillis(planDate)); + // 履约类型:收款 + props.setFdPalnType(HcsSyncConstant.PERFORM_TYPE_COLLECTION); + // 履约性质:收款计划行款项性质 + props.setFdProperty(stageRow.getFundNature()); + // 履约计划id / 来源履约计划项ID:收款计划行合规阶段ID(HCS 只认自己的阶段ID) + props.setFdId(stageRow.getHcsStageFdId()); + props.setFdPlanId(stageRow.getHcsStageFdId()); + // 金蝶单据号:金蝶收款单号(A301-1 同步落库) + props.setFdJdDocNo(flow.getKingdeeBillNo()); + // 【必填】合同ID:HCS 合同ID + props.setFdContractId(contractManage.getHcsFdId()); + // 业务ID:TMS 收款流水ID(溯源) + props.setFdBusinessId(String.valueOf(flow.getId())); + props.setFdCreateTime(System.currentTimeMillis()); + props.setFdXformId(xformId); + HcsPerfRecordFormDataDTO formData = new HcsPerfRecordFormDataDTO(); + formData.setFdXformId(xformId); + formData.setDynamicProps(props); + // 机制数据(C108-1 必填):认领附件暂不转传,传空列表 + HcsMechanismsDTO mechanisms = new HcsMechanismsDTO(); + mechanisms.setAttachment(List.of()); + HcsPerfRecordSaveDTO param = new HcsPerfRecordSaveDTO(); + param.setCurUser(HcsUserRefVO.ofLoginName(hcsProperties.getOperatorLoginName())); + param.setFormData(formData); + param.setMechanisms(mechanisms); + log.info("HCS履约流水新建开始 flowId:{},contractNo:{},认领金额 {}", flow.getId(), contractManage.getContractNo(), claimAmount); + HcsResult result = hcsClient.perfRecordAdd(param); + if (result == null || !result.isSuccess() || result.getData() == null || StringUtil.isBlank(result.getData().getFdId())) { + log.error("HCS履约流水新建失败响应:{},contractNo={}", result == null ? "无响应" : String.format("msg=%s, traceId=%s", result.getMsg(), result.getTraceId()), contractManage.getContractNo()); + throw new HcsException("C108-1 履约流水新建失败:" + (result == null ? "响应为空" : result.getMsg())); + } + // shortcut: 履约流水 fdId 未持久化(认领链无 C108-2 更新场景);需推进实际金额更新时先落库 + log.info("HCS履约流水新建成功 flowId:{},contractNo:{},perfFdId:{}", + flow.getId(), contractManage.getContractNo(), result.getData().getFdId()); } /**