diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsExeStageSyncSupport.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsExeStageSyncSupport.java index 89bed20c..9cc2a52d 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsExeStageSyncSupport.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsExeStageSyncSupport.java @@ -46,19 +46,19 @@ import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.ArrayList; -import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.stream.Stream; /** * HCS 履约规则同步支撑组件(C103-9 新版行级同步,设计文档 §5.3,ADR-0002) *

- * 随合同推送链路执行(合同须已同步、hcs_fd_id 存在):聚合 row_op 非 none 的规则行 - * (add 行不带 fdId,update/delete 行必须带合规阶段ID),整体 fdEopExeplanOperateType - * 首次为 add、后续为 update;返回的 fdExeplanId 回填 blade_contract_manage.hcs_exeplan_id, - * 变更阶段 fdId 按行回填 hcs_stage_fd_id(合规返回的 fdId 必须持久化,后续行更新/删除全靠它)。 + * 随合同推送链路执行(合同须已同步、hcs_fd_id 存在):聚合 row_op 非 none 的规则行, + * 整体 fdEopExeplanOperateType 首次为 add、后续为 update;请求行必送 fdUniqueId(取 TMS 行ID,重试稳定),响应行回 fdId; + * 履约计划ID 取返回 fdChangeStages 行的 fdId 回填 blade_contract_manage.hcs_exeplan_id(实际响应无顶层 fdExeplanId), + * 各行 fdId 按行回填 hcs_stage_fd_id(必须持久化,供履约记录等接口引用)。 * * @author Chill */ @@ -123,10 +123,8 @@ public class HcsExeStageSyncSupport { List stages = new ArrayList<>(); for (HcsContractExeStage row : pendingRows) { HcsExeplanStageDTO stage = new HcsExeplanStageDTO(); - // 阶段项ID:行级 update/delete 必传(取 hcs_stage_fd_id),add 行不带 - if (!"add".equals(row.getRowOp())) { - stage.setFdId(row.getHcsStageFdId()); - } + // 阶段唯一标识:所有行必传(取 TMS 行ID,重试重发保持不变,合规按此匹配行;add 行 hcs_stage_fd_id 为空不能作值) + stage.setFdUniqueId(String.valueOf(row.getId())); // 【必填】行级操作:add / update / delete stage.setFdEopExeplanOperateType(row.getRowOp()); // 【必填】履约类型:payment=付款 / collection=收款 @@ -154,19 +152,25 @@ public class HcsExeStageSyncSupport { } /** - * 回填:fdExeplanId → 合同 hcs_exeplan_id;变更阶段 fdId 按行回填 hcs_stage_fd_id 并清除行级操作标记 + * 回填:响应行 fdId 即履约计划ID → 合同 hcs_exeplan_id(fdAddStages/fdChangeStages 取首个非空);各行 fdId 按行回填 hcs_stage_fd_id 并清除行级操作标记 *

- * TODO 待联调确认:fdChangeStages 与请求行的对应关系 v4 未明确,优先按请求行序对应(首次全 add 场景), - * 行数不一致时按业务属性组合键匹配,仍匹配不到的 add 行保持待同步(下轮重试)。 + * 新增行阶段在 fdAddStages 返回(实测不含业务属性,按 add 行出现顺序对应),变更行在 fdChangeStages 返回; + * fdChangeStages 按请求行序对应,行数不一致时按业务属性组合键匹配,仍匹配不到的 add 行保持待同步(下轮重试)。 */ private void backfill(ContractManage contractManage, List pendingRows, List requestStages, HcsExeplanVO exeplan) { - // 履约计划ID 回填(后续行更新/删除依赖,必须持久化) - if (StringUtil.isNotBlank(exeplan.getFdExeplanId()) - && !exeplan.getFdExeplanId().equals(contractManage.getHcsExeplanId())) { - contractManage.setHcsExeplanId(exeplan.getFdExeplanId()); + // 履约计划ID 回填:响应行 fdId 即履约计划ID(首次同步行在 fdAddStages、变更行在 fdChangeStages,取首个非空回填) + String fdExeplanId = Stream.of(exeplan.getFdChangeStages(), exeplan.getFdAddStages()) + .filter(Objects::nonNull) + .flatMap(List::stream) + .map(HcsExeplanStageVO::getFdId) + .filter(StringUtil::isNotBlank) + .findFirst().orElse(null); + if (StringUtil.isNotBlank(fdExeplanId)) { + contractManage.setHcsExeplanId(fdExeplanId); } backfillExeplanId(contractManage); + List addStages = exeplan.getFdAddStages() == null ? List.of() : exeplan.getFdAddStages(); List changeStages = exeplan.getFdChangeStages() == null ? List.of() : exeplan.getFdChangeStages(); // 行数一致时按请求行序一一对应;不一致时按业务属性组合键匹配 boolean positional = changeStages.size() == requestStages.size(); @@ -177,9 +181,12 @@ public class HcsExeStageSyncSupport { HcsContractExeStage row = pendingRows.get(index); String stageFdId = row.getHcsStageFdId(); if ("add".equals(row.getRowOp())) { - addRowCount++; - HcsExeplanStageVO matched = positional ? changeStages.get(index) : byAttribute.get(attributeKey(requestStages.get(index))); + HcsExeplanStageVO matched = addRowCount < addStages.size() ? addStages.get(addRowCount) : null; + if (matched == null) { + matched = positional ? changeStages.get(index) : byAttribute.get(attributeKey(requestStages.get(index))); + } stageFdId = matched == null ? null : matched.getFdId(); + addRowCount++; if (StringUtil.isBlank(stageFdId)) { unmatchedCount++; }