fix: 修正HCS履约阶段与履约记录接口字段传值

- C103-9 履约规则行新增 fdId(update/delete 定位用),fdUniqueId 仅 add 行传值

- 履约计划ID 回填优先取响应顶层 fdExeplanId,兜底响应行 fdId

- C108 履约记录补传 fdPlanId(必填)、fdJdDocNo、fdDocSubject,附件补 fdBindType=ADD

- 履约计划行未同步时抛异常交由付款链置 failed 重试

- 结算付款特批补传 fd_source_sys 外部系统编号
This commit is contained in:
刘泉佳
2026-10-27 19:43:01 +08:00
parent cc465d4cee
commit f3d598f796
6 changed files with 42 additions and 13 deletions
@@ -57,7 +57,7 @@ import java.util.stream.Stream;
* <p>
* 随合同推送链路执行(合同须已同步、hcs_fd_id 存在):聚合 row_op 非 none 的规则行,
* 整体 fdEopExeplanOperateType 首次为 add、后续为 update;请求行必送 fdUniqueId(取 TMS 行ID,重试稳定),响应行回 fdId;
* 履约计划ID 取返回 fdChangeStages 行的 fdId 回填 blade_contract_manage.hcs_exeplan_id(实际响应无顶层 fdExeplanId),
* 履约计划ID 取响应顶层 fdExeplanId 回填 blade_contract_manage.hcs_exeplan_id,
* 各行 fdId 按行回填 hcs_stage_fd_id(必须持久化,供履约记录等接口引用)。
*
* @author Chill
@@ -123,8 +123,12 @@ public class HcsExeStageSyncSupport {
List<HcsExeplanStageDTO> stages = new ArrayList<>();
for (HcsContractExeStage row : pendingRows) {
HcsExeplanStageDTO stage = new HcsExeplanStageDTO();
// 阶段唯一标识:所有行必传(取 TMS 行ID,重试重发保持不变,合规按此匹配行;add 行 hcs_stage_fd_id 为空不能作值)
stage.setFdUniqueId(String.valueOf(row.getId()));
// 阶段行ID:update/delete 行必传(HCS 凭首次 add 返回的 fdId 定位行,缺失报"履约计划已不存在"),add 行无值自动省略
stage.setFdId(row.getHcsStageFdId());
// 阶段唯一标识:仅 add 行必传(取 TMS 行ID,HCS 回显存储);update/delete 行实测无需携带,以 fdId 定位
if ("add".equals(row.getRowOp())) {
stage.setFdUniqueId(String.valueOf(row.getId()));
}
// 【必填】行级操作:add / update / delete
stage.setFdEopExeplanOperateType(row.getRowOp());
// 【必填】履约类型:payment=付款 / collection=收款
@@ -159,8 +163,9 @@ public class HcsExeStageSyncSupport {
*/
private void backfill(ContractManage contractManage, List<HcsContractExeStage> pendingRows,
List<HcsExeplanStageDTO> requestStages, HcsExeplanVO exeplan) {
// 履约计划ID 回填:响应行 fdId 即履约计划ID(首次同步行在 fdAddStages、变更行在 fdChangeStages,取首个非空回填)
String fdExeplanId = Stream.of(exeplan.getFdChangeStages(), exeplan.getFdAddStages())
// 履约计划ID 回填:优先取响应顶层 fdExeplanId(实测 add/update 均有返回),兜底取响应行 fdId
String fdExeplanId = StringUtil.isNotBlank(exeplan.getFdExeplanId()) ? exeplan.getFdExeplanId()
: Stream.of(exeplan.getFdChangeStages(), exeplan.getFdAddStages())
.filter(Objects::nonNull)
.flatMap(List::stream)
.map(HcsExeplanStageVO::getFdId)
@@ -157,6 +157,8 @@ public class HcsPaymentSyncSupport {
String message = StringUtil.isBlank(exception.getMessage()) ? "履约数据推送异常" : exception.getMessage();
markFailed(payment, message);
log.error("付款特批通过后履约数据推送失败:paymentNo={},{}", payment.getPaymentNo(), message, exception);
// todo
throw new ServiceException("付款特批通过后履约数据推送失败:" + message);
}
}
@@ -58,7 +58,7 @@ import java.util.List;
* 付款申请为主线:审批通过 → C108-1 新增(返回 fdId 回填 hcs_perf_fd_id);
* 实际付款推进(paidAmount 变化置待同步)→ C108-2 更新(formData.fdId=hcs_perf_fd_id 必填,响应无 data)。
* 关联履约阶段:按合同+付款类型匹配已同步的履约规则行(付款/收款执行与计划阶段对应),
* 无关联则不传 fd_id(§5.6)。
* fd_plan_id(履约计划id)接口必填,无已同步计划行时抛出重试(合同链路首推后自愈)。
*
* @author Chill
*/
@@ -149,6 +149,10 @@ public class HcsPerfRecordSyncSupport {
private HcsPerfRecordFormDataDTO buildFormData(PaymentApplication payment) {
ContractManage contractManage = payment.getContractId() == null ? null : contractManageMapper.selectById(payment.getContractId());
HcsContractExeStage stageRow = findPaymentStageRow(payment);
// 【必填】fd_plan_id 履约计划id:无已同步计划行时无值可传,抛出由付款链置 failed 重试(合同链路首推后下轮自愈)
if (stageRow == null || StringUtil.isBlank(stageRow.getHcsStageFdId())) {
throw new HcsException("C108 履约记录未匹配到已同步履约计划行,fd_plan_id 必传:paymentNo=" + payment.getPaymentNo());
}
HcsPerfRecordFormDataDTO formData = new HcsPerfRecordFormDataDTO();
HcsPerfRecordPropsDTO props = new HcsPerfRecordPropsDTO();
// 申请金额:appliedAmount(接口按字符串传值)
@@ -161,10 +165,13 @@ public class HcsPerfRecordSyncSupport {
props.setFdPlanDate(toMillis(payment.getApplyDate()));
// 计划类型:paymentType(原文拼写 fd_paln_type 必须沿用)
props.setFdPalnType(payment.getPaymentType());
// 性质:款项性质取关联履约规则行的款项性质(无关联不传)
props.setFdProperty(stageRow == null ? null : stageRow.getFundNature());
// 来源履约计划项ID:关联履约阶段 hcs_stage_fd_id(无关联则不传,§5.6)
props.setFdId(stageRow == null ? null : stageRow.getHcsStageFdId());
// 性质:款项性质取关联履约计划行的款项性质
props.setFdProperty(stageRow.getFundNature());
// 履约计划id / 来源履约计划项ID:履约计划行合规阶段ID(HCS 只认自己的阶段ID)
props.setFdId(stageRow.getHcsStageFdId());
props.setFdPlanId(stageRow.getHcsStageFdId());
// 金蝶单据号:付款推送金蝶返回的单号(溯源用)
props.setFdJdDocNo(payment.getKingdeeBillNo());
// 【必填】合同ID:HCS 合同ID(合同未推送时为空,联调确认兜底口径)
props.setFdContractId(contractManage == null ? null : contractManage.getHcsFdId());
// 业务ID:TMS 付款申请单主键(方便溯源,v4 新增)
@@ -176,6 +183,8 @@ public class HcsPerfRecordSyncSupport {
props.setFdXformId(xformId);
// 【必填】表单模板ID(formData 顶层 fd_xform_id,v4 必填)
formData.setFdXformId(xformId);
// 标题:付款履约-付款申请单号(参照结算付款特批标题风格)
formData.setFdDocSubject("付款履约-" + payment.getPaymentNo());
formData.setDynamicProps(props);
return formData;
}
@@ -194,8 +203,9 @@ public class HcsPerfRecordSyncSupport {
}
HcsMechanismsDTO mechanisms = new HcsMechanismsDTO();
mechanisms.setAttachment(attachmentRefs.stream().map(ref -> {
// 附件ID(C103-2 转传返回)
// 附件ID(C103-2 转传返回);C108-1 为发起类接口,绑定类型固定 ADD
ref.setFdEntityKey(PERF_ATTACH_ENTITY_KEY);
ref.setFdBindType("ADD");
return ref;
}).toList());
return mechanisms;
@@ -135,6 +135,8 @@ public class HcsSpecPaySyncSupport {
props.setFdDocSubject("结算付款特批-" + payment.getPaymentNo());
// 来源付款单ID:付款申请单号(§5.5 映射表)
props.setFdId(payment.getPaymentNo());
props.setFdSourceSys(hcsProperties.getSrcSystem());
// 【必填】付款金额:appliedAmount
props.setFdPayAmount(payment.getAppliedAmount());
// 【必填】合计金额:settlementAmount
@@ -36,7 +36,8 @@ import java.math.BigDecimal;
/**
* 合同合规系统(HCS)C103-9(新) 履约规则同步 行级操作项(数据源 blade_contract_exe_stage,ADR-0002)
* <p>
* 新增行可不带 fdId;更新/删除行必须带 fdId(合规阶段ID,回填自首次同步返回)。
* add 行必传 fdUniqueId(TMS 行唯一标识,HCS 回显存储);update/delete 行必传 fdId(首次 add 返回的阶段行ID,
* 持久化于 hcs_stage_fd_id),实测无需 fdUniqueId。
*
* @author Chill
*/
@@ -47,9 +48,13 @@ public class HcsExeplanStageDTO implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 阶段项ID(行级 update/delete 必传,取 blade_contract_exe_stage.hcs_stage_fd_id)
* 阶段行ID(HCS 返回的 fdId,持久化于 hcs_stage_fd_id;update/delete 行必传,add 行可不传)
*/
private String fdId;
/**
* 阶段唯一标识(仅 add 行必传,取 TMS 行ID,HCS 回显存储;update/delete 行以 fdId 定位,无需此值)
*/
private String fdUniqueId;
/**
* 行级操作:add / update / delete
*/
@@ -55,6 +55,11 @@ public class HcsSpecPayPropsDTO implements Serializable {
*/
@JsonProperty("fd_contract_no")
private String fdContractNo;
/**
* 外部系统编号
*/
@JsonProperty("fd_source_sys")
private String fdSourceSys;
/**
* 收款账户名
*/