feat: HCS付款链推送(C106-3/4结算付款特批/C108-1/2履约记录/扫描编排与手动端点/特批结果人工确认)
This commit is contained in:
+21
-5
@@ -34,11 +34,13 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.transport.constant.HcsSyncConstant;
|
||||
import org.springblade.transport.support.hcs.HcsContractSyncSupport;
|
||||
import org.springblade.transport.support.hcs.HcsPaymentSyncSupport;
|
||||
import org.springblade.transport.support.hcs.HcsMasterDataSupport;
|
||||
import org.springblade.transport.support.hcs.HcsProjectSyncSupport;
|
||||
import org.springblade.transport.task.HcsPushTask;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@@ -61,44 +63,58 @@ public class HcsSyncController {
|
||||
private final HcsPushTask hcsPushTask;
|
||||
private final HcsProjectSyncSupport hcsProjectSyncSupport;
|
||||
private final HcsContractSyncSupport hcsContractSyncSupport;
|
||||
private final HcsPaymentSyncSupport hcsPaymentSyncSupport;
|
||||
private final HcsMasterDataSupport hcsMasterDataSupport;
|
||||
|
||||
/**
|
||||
* 单条重推(bizType:project=项目立项 / contract=合同)
|
||||
* 单条重推(bizType:project=项目立项 / contract=合同 / payment=付款申请)
|
||||
*/
|
||||
@PostMapping("/sync/{bizType}/{id}")
|
||||
@ApiOperationSupport(order = 1)
|
||||
@Operation(summary = "单条推送/重推合规", description = "重置待同步状态后立即推送,bizType:project=项目立项、contract=合同")
|
||||
@Operation(summary = "单条推送/重推合规", description = "重置待同步状态后立即推送,bizType:project=项目立项、contract=合同、payment=付款申请")
|
||||
public R<String> repush(@Parameter(description = "业务类型", required = true) @PathVariable String bizType,
|
||||
@Parameter(description = "单据ID", required = true) @PathVariable Long id) {
|
||||
String result = switch (bizType) {
|
||||
case HcsSyncConstant.BIZ_TYPE_PROJECT -> hcsProjectSyncSupport.repushProject(id);
|
||||
case HcsSyncConstant.BIZ_TYPE_CONTRACT -> hcsContractSyncSupport.repushContract(id);
|
||||
case HcsSyncConstant.BIZ_TYPE_PAYMENT -> hcsPaymentSyncSupport.repushPayment(id);
|
||||
default -> throw new IllegalArgumentException("不支持的HCS推送业务类型:" + bizType);
|
||||
};
|
||||
return R.data(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量推送(bizType:project=项目立项 / contract=合同;复用定时扫描逻辑,单轮批量上限 push.batch-size)
|
||||
* 批量推送(bizType:project=项目立项 / contract=合同 / payment=付款申请;复用定时扫描逻辑,单轮批量上限 push.batch-size)
|
||||
*/
|
||||
@PostMapping("/sync/{bizType}")
|
||||
@ApiOperationSupport(order = 2)
|
||||
@Operation(summary = "批量推送合规", description = "扫描待同步/待补偿单据推送一轮,bizType:project=项目立项、contract=合同")
|
||||
@Operation(summary = "批量推送合规", description = "扫描待同步/待补偿单据推送一轮,bizType:project=项目立项、contract=合同、payment=付款申请")
|
||||
public R<String> pushBatch(@Parameter(description = "业务类型", required = true) @PathVariable String bizType) {
|
||||
String result = switch (bizType) {
|
||||
case HcsSyncConstant.BIZ_TYPE_PROJECT -> hcsPushTask.pushUnsyncedProjects();
|
||||
case HcsSyncConstant.BIZ_TYPE_CONTRACT -> hcsPushTask.pushUnsyncedContracts();
|
||||
case HcsSyncConstant.BIZ_TYPE_PAYMENT -> hcsPushTask.pushUnsyncedPayments();
|
||||
default -> throw new IllegalArgumentException("不支持的HCS推送业务类型:" + bizType);
|
||||
};
|
||||
return R.data(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 特批结果人工确认(§9-U1 过渡方案:v4 无结果查询/回调接口)
|
||||
*/
|
||||
@PostMapping("/spec-pay/confirm")
|
||||
@ApiOperationSupport(order = 3)
|
||||
@Operation(summary = "特批结果人工确认", description = "传入付款申请ID与结果(approved/rejected),仅特批中(processing)允许确认")
|
||||
public R<String> confirmSpecResult(@Parameter(description = "付款申请ID", required = true) @RequestParam Long id,
|
||||
@Parameter(description = "确认结果 approved/rejected", required = true) @RequestParam String result) {
|
||||
return R.data(hcsPaymentSyncSupport.confirmSpecResult(id, result));
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新主数据缓存(C103-6 模板 / C103-7 分类 / C103-12 币种)
|
||||
*/
|
||||
@PostMapping("/master-data/refresh")
|
||||
@ApiOperationSupport(order = 3)
|
||||
@ApiOperationSupport(order = 4)
|
||||
@Operation(summary = "刷新HCS主数据缓存", description = "全量重拉模板/分类/币种列表并缓存,同时校验模板映射配置")
|
||||
public R<String> refreshMasterData() {
|
||||
return R.data(hcsMasterDataSupport.refreshAll());
|
||||
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.support.hcs;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.transport.constant.HcsSyncConstant;
|
||||
import org.springblade.transport.mapper.PaymentApplicationMapper;
|
||||
import org.springblade.transport.pojo.entity.PaymentApplication;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* HCS 付款链推送编排支撑组件(结算付款特批 C106-3/4 + 履约记录 C108-1/2,设计文档 §5.0/§5.5/§5.6)
|
||||
* <p>
|
||||
* 编排顺序:特批(额度超额标记 need 时发起)→ 履约记录(审批通过新建 / 实际付款推进更新)。
|
||||
* 特批流程ID 与履约记录ID 在各自 Support 内即时持久化;整体同步状态(hcs_sync_status)由本组件统一回写。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class HcsPaymentSyncSupport {
|
||||
|
||||
/**
|
||||
* 审批通过状态集合
|
||||
*/
|
||||
private static final List<String> APPROVED_STATUSES = List.of("approved");
|
||||
|
||||
private final PaymentApplicationMapper paymentApplicationMapper;
|
||||
private final HcsSpecPaySyncSupport hcsSpecPaySyncSupport;
|
||||
private final HcsPerfRecordSyncSupport hcsPerfRecordSyncSupport;
|
||||
|
||||
/**
|
||||
* 推送付款申请到 HCS:特批(如需)→ 履约记录(新建/更新)→ 回填整体同步状态
|
||||
*
|
||||
* @param payment 付款申请单(approvalStatus=approved)
|
||||
* @return 推送结果描述
|
||||
*/
|
||||
public String pushPayment(PaymentApplication payment) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
// ① 结算付款特批:提交时额度超额(hcs_spec_status=need)且未发起时发起 C106-3
|
||||
if (HcsSyncConstant.SPEC_STATUS_NEED.equals(payment.getHcsSpecStatus())) {
|
||||
result.append(hcsSpecPaySyncSupport.pushSpecPay(payment));
|
||||
}
|
||||
// ② 履约记录:审批通过新建 C108-1 / 实际付款推进更新 C108-2
|
||||
if (result.length() > 0) {
|
||||
result.append(";");
|
||||
}
|
||||
result.append(hcsPerfRecordSyncSupport.pushPerfRecord(payment));
|
||||
markSynced(payment, result.toString());
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 单条手动重推:重置为待同步并清零重试计数后立即推送(页面"推送合规/重推"按钮入口)
|
||||
*
|
||||
* @param id 付款申请单ID
|
||||
* @return 推送结果描述
|
||||
*/
|
||||
public String repushPayment(Long id) {
|
||||
PaymentApplication payment = paymentApplicationMapper.selectById(id);
|
||||
if (payment == null) {
|
||||
throw new ServiceException("付款申请单不存在:" + id);
|
||||
}
|
||||
if (!APPROVED_STATUSES.contains(payment.getApprovalStatus())) {
|
||||
throw new ServiceException("仅审批通过的付款申请可推送合规(当前状态:" + payment.getApprovalStatus() + ")");
|
||||
}
|
||||
payment.setHcsSyncStatus(HcsSyncConstant.HCS_STATUS_UNSYNCED);
|
||||
payment.setHcsRetryCount(0);
|
||||
paymentApplicationMapper.updateById(payment);
|
||||
try {
|
||||
return pushPayment(payment);
|
||||
} catch (Exception exception) {
|
||||
String message = StringUtil.isBlank(exception.getMessage()) ? "推送异常" : exception.getMessage();
|
||||
markFailed(payment, message);
|
||||
throw new ServiceException("HCS付款申请推送失败:" + message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 特批结果人工确认(§9-U1 过渡方案:v4 无结果查询/回调接口)
|
||||
*
|
||||
* @param id 付款申请单ID
|
||||
* @param result confirmed approved / rejected
|
||||
* @return 确认结果描述
|
||||
*/
|
||||
public String confirmSpecResult(Long id, String result) {
|
||||
PaymentApplication payment = paymentApplicationMapper.selectById(id);
|
||||
if (payment == null) {
|
||||
throw new ServiceException("付款申请单不存在:" + id);
|
||||
}
|
||||
if (!List.of(HcsSyncConstant.SPEC_STATUS_APPROVED, HcsSyncConstant.SPEC_STATUS_REJECTED).contains(result)) {
|
||||
throw new ServiceException("特批结果仅支持 approved/rejected");
|
||||
}
|
||||
if (!HcsSyncConstant.SPEC_STATUS_PROCESSING.equals(payment.getHcsSpecStatus())) {
|
||||
throw new ServiceException("仅特批中的付款申请允许确认结果(当前状态:" + payment.getHcsSpecStatus() + ")");
|
||||
}
|
||||
payment.setHcsSpecStatus(result);
|
||||
paymentApplicationMapper.updateById(payment);
|
||||
log.info("HCS特批结果人工确认:paymentNo={},specFdId={},结果={}", payment.getPaymentNo(), payment.getHcsSpecFdId(), result);
|
||||
return "特批结果已确认:" + result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 回填整体同步状态置 synced
|
||||
*/
|
||||
public void markSynced(PaymentApplication payment, String message) {
|
||||
payment.setHcsSyncStatus(HcsSyncConstant.HCS_STATUS_SYNCED);
|
||||
payment.setHcsRetryCount(0);
|
||||
payment.setHcsSyncTime(LocalDateTime.now());
|
||||
payment.setHcsSyncMsg(message);
|
||||
paymentApplicationMapper.updateById(payment);
|
||||
log.info("HCS付款申请推送成功:paymentNo={},{}", payment.getPaymentNo(), message);
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送失败回写:failed + 重试次数+1 + 失败原因
|
||||
*/
|
||||
public void markFailed(PaymentApplication payment, String message) {
|
||||
payment.setHcsSyncStatus(HcsSyncConstant.HCS_STATUS_FAILED);
|
||||
payment.setHcsRetryCount(payment.getHcsRetryCount() == null ? 1 : payment.getHcsRetryCount() + 1);
|
||||
payment.setHcsSyncTime(LocalDateTime.now());
|
||||
payment.setHcsSyncMsg(message);
|
||||
paymentApplicationMapper.updateById(payment);
|
||||
log.error("HCS付款申请推送失败:paymentNo={},retryCount={},{}", payment.getPaymentNo(), payment.getHcsRetryCount(), message);
|
||||
}
|
||||
}
|
||||
+230
@@ -0,0 +1,230 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.support.hcs;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.hcs.config.HcsProperties;
|
||||
import org.springblade.hcs.dto.HcsAttachmentRefDTO;
|
||||
import org.springblade.hcs.dto.HcsMechanismsDTO;
|
||||
import org.springblade.hcs.dto.HcsPerfRecordFormDataDTO;
|
||||
import org.springblade.hcs.dto.HcsPerfRecordPropsDTO;
|
||||
import org.springblade.hcs.dto.HcsPerfRecordSaveDTO;
|
||||
import org.springblade.hcs.dto.HcsResult;
|
||||
import org.springblade.hcs.dto.HcsFormSaveVO;
|
||||
import org.springblade.hcs.dto.HcsUserRefVO;
|
||||
import org.springblade.hcs.exception.HcsException;
|
||||
import org.springblade.hcs.feign.IHcsClient;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.system.pojo.entity.User;
|
||||
import org.springblade.transport.mapper.ContractManageMapper;
|
||||
import org.springblade.transport.mapper.HcsContractExeStageMapper;
|
||||
import org.springblade.transport.mapper.PaymentApplicationMapper;
|
||||
import org.springblade.transport.pojo.entity.ContractManage;
|
||||
import org.springblade.transport.pojo.entity.HcsContractExeStage;
|
||||
import org.springblade.transport.pojo.entity.PaymentApplication;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.ZoneId;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* HCS 履约记录同步支撑组件(C108-1 新建 / C108-2 更新,设计文档 §5.6,D11)
|
||||
* <p>
|
||||
* 付款申请为主线:审批通过 → C108-1 新增(返回 fdId 回填 hcs_perf_fd_id);
|
||||
* 实际付款推进(paidAmount 变化置待同步)→ C108-2 更新(formData.fdId=hcs_perf_fd_id 必填,响应无 data)。
|
||||
* 关联履约阶段:按合同+付款类型匹配已同步的履约规则行(付款/收款执行与计划阶段对应),
|
||||
* 无关联则不传 fd_id(§5.6)。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class HcsPerfRecordSyncSupport {
|
||||
|
||||
/**
|
||||
* 履约记录附件字段标识
|
||||
*/
|
||||
private static final String PERF_ATTACH_ENTITY_KEY = "fd_plan_att";
|
||||
|
||||
private final IHcsClient hcsClient;
|
||||
private final HcsProperties hcsProperties;
|
||||
private final ContractManageMapper contractManageMapper;
|
||||
private final HcsContractExeStageMapper hcsContractExeStageMapper;
|
||||
private final PaymentApplicationMapper paymentApplicationMapper;
|
||||
private final HcsAttachmentSupport hcsAttachmentSupport;
|
||||
|
||||
/**
|
||||
* 推送履约记录:无记录ID 走 C108-1 新增,有记录ID 走 C108-2 更新;记录ID 立即持久化
|
||||
*
|
||||
* @param payment 付款申请单(approvalStatus=approved)
|
||||
* @return 推送结果描述
|
||||
*/
|
||||
public String pushPerfRecord(PaymentApplication payment) {
|
||||
String operatorLoginName = resolveLoginName(payment);
|
||||
if (StringUtil.isBlank(payment.getHcsPerfFdId())) {
|
||||
HcsPerfRecordSaveDTO param = buildAddParam(payment, operatorLoginName);
|
||||
log.info("HCS履约记录新建开始:paymentNo={}", payment.getPaymentNo());
|
||||
HcsResult<HcsFormSaveVO> result = hcsClient.perfRecordAdd(param);
|
||||
if (result == null || !result.isSuccess() || result.getData() == null || StringUtil.isBlank(result.getData().getFdId())) {
|
||||
log.error("HCS履约记录新建失败响应:{},paymentNo={}", result == null ? "无响应" : result.getMsg(), payment.getPaymentNo());
|
||||
throw new HcsException("C108-1 履约记录新建失败:" + (result == null ? "响应为空" : result.getMsg()));
|
||||
}
|
||||
// 履约记录ID 立即持久化(调用方必须保存)
|
||||
payment.setHcsPerfFdId(result.getData().getFdId());
|
||||
paymentApplicationMapper.updateById(payment);
|
||||
log.info("HCS履约记录新建成功:paymentNo={},perfFdId={}", payment.getPaymentNo(), payment.getHcsPerfFdId());
|
||||
return "C108-1 履约记录新建成功";
|
||||
}
|
||||
// C108-2 更新:formData.fdId 必填,响应无 data,success=true 即成功
|
||||
HcsPerfRecordSaveDTO param = buildUpdateParam(payment, operatorLoginName);
|
||||
log.info("HCS履约记录更新开始:paymentNo={},perfFdId={}", payment.getPaymentNo(), payment.getHcsPerfFdId());
|
||||
HcsResult<Void> result = hcsClient.perfRecordUpdate(param);
|
||||
if (result == null || !result.isSuccess()) {
|
||||
log.error("HCS履约记录更新失败响应:{},paymentNo={}", result == null ? "无响应" : result.getMsg(), payment.getPaymentNo());
|
||||
throw new HcsException("C108-2 履约记录更新失败:" + (result == null ? "响应为空" : result.getMsg()));
|
||||
}
|
||||
return "C108-2 履约记录更新成功";
|
||||
}
|
||||
|
||||
/**
|
||||
* C108-1 新建入参(字段标注照 §3.4 规范,映射见设计文档 §5.6)
|
||||
*/
|
||||
private HcsPerfRecordSaveDTO buildAddParam(PaymentApplication payment, String operatorLoginName) {
|
||||
HcsPerfRecordSaveDTO param = new HcsPerfRecordSaveDTO();
|
||||
// 【必填】当前操作人 {fdLoginName}(付款申请创建人,§9-U2)
|
||||
param.setCurUser(HcsUserRefVO.ofLoginName(operatorLoginName));
|
||||
param.setFormData(buildFormData(payment));
|
||||
// 附件挂载:attachmentsJson 转传,fdEntityKey=fd_plan_att
|
||||
param.setMechanisms(buildMechanisms(payment, operatorLoginName));
|
||||
return param;
|
||||
}
|
||||
|
||||
/**
|
||||
* C108-2 更新入参:formData.fdId 必填,传实际金额与合同ID
|
||||
*/
|
||||
private HcsPerfRecordSaveDTO buildUpdateParam(PaymentApplication payment, String operatorLoginName) {
|
||||
HcsPerfRecordSaveDTO param = new HcsPerfRecordSaveDTO();
|
||||
param.setCurUser(HcsUserRefVO.ofLoginName(operatorLoginName));
|
||||
HcsPerfRecordFormDataDTO formData = buildFormData(payment);
|
||||
// 【必填】履约记录ID(C108-1 新建返回)
|
||||
formData.setFdId(payment.getHcsPerfFdId());
|
||||
param.setFormData(formData);
|
||||
return param;
|
||||
}
|
||||
|
||||
/**
|
||||
* 履约记录 formData(新建/更新共用)
|
||||
*/
|
||||
private HcsPerfRecordFormDataDTO buildFormData(PaymentApplication payment) {
|
||||
ContractManage contractManage = payment.getContractId() == null ? null : contractManageMapper.selectById(payment.getContractId());
|
||||
HcsContractExeStage stageRow = findPaymentStageRow(payment);
|
||||
HcsPerfRecordFormDataDTO formData = new HcsPerfRecordFormDataDTO();
|
||||
HcsPerfRecordPropsDTO props = new HcsPerfRecordPropsDTO();
|
||||
// 申请金额:appliedAmount(接口按字符串传值)
|
||||
props.setFdApplyAmount(payment.getAppliedAmount() == null ? null : payment.getAppliedAmount().toPlainString());
|
||||
// 实际金额:paidAmount(实际付款推进后更新)
|
||||
props.setFdFactAmount(payment.getPaidAmount() == null ? null : payment.getPaidAmount().toPlainString());
|
||||
// 申请编号:付款申请单号(接口文档 §8.3 字段)
|
||||
props.setFdApplyNo(payment.getPaymentNo());
|
||||
// 计划日期:applyDate(毫秒时间戳)
|
||||
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());
|
||||
// 合同ID:HCS 合同ID(合同未推送时不传)
|
||||
props.setFdContractId(contractManage == null ? null : contractManage.getHcsFdId());
|
||||
// 表单模板ID:配置 perf-record.xform-id(环境值,§9-U9,未配置不传)
|
||||
props.setFdXformId(StringUtil.isBlank(hcsProperties.getPerfRecord().getXformId()) ? null : hcsProperties.getPerfRecord().getXformId());
|
||||
formData.setDynamicProps(props);
|
||||
return formData;
|
||||
}
|
||||
|
||||
/**
|
||||
* 履约记录附件挂载(无附件或开关关闭返回 null)
|
||||
*/
|
||||
private HcsMechanismsDTO buildMechanisms(PaymentApplication payment, String operatorLoginName) {
|
||||
if (!hcsProperties.getAttachment().isTransferEnabled() || StringUtil.isBlank(payment.getAttachmentsJson())) {
|
||||
return null;
|
||||
}
|
||||
List<String> attachmentFdIds = hcsAttachmentSupport.transferAttachments(payment.getAttachmentsJson(), operatorLoginName);
|
||||
if (attachmentFdIds.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
HcsMechanismsDTO mechanisms = new HcsMechanismsDTO();
|
||||
mechanisms.setAttachment(attachmentFdIds.stream().map(fdId -> {
|
||||
HcsAttachmentRefDTO attachment = new HcsAttachmentRefDTO();
|
||||
// 附件ID(C103-2 转传返回)
|
||||
attachment.setFdId(fdId);
|
||||
// 履约记录附件字段标识
|
||||
attachment.setFdEntityKey(PERF_ATTACH_ENTITY_KEY);
|
||||
return attachment;
|
||||
}).toList());
|
||||
return mechanisms;
|
||||
}
|
||||
|
||||
/**
|
||||
* 匹配合同的付款履约规则行(perform_type=payment 且已同步,取第一条)
|
||||
*/
|
||||
private HcsContractExeStage findPaymentStageRow(PaymentApplication payment) {
|
||||
if (payment.getContractId() == null) {
|
||||
return null;
|
||||
}
|
||||
return hcsContractExeStageMapper.selectOne(Wrappers.<HcsContractExeStage>lambdaQuery()
|
||||
.eq(HcsContractExeStage::getContractId, payment.getContractId())
|
||||
.eq(HcsContractExeStage::getPerformType, "payment")
|
||||
.isNotNull(HcsContractExeStage::getHcsStageFdId)
|
||||
.last("limit 1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作人登录名(付款申请创建人,§9-U2)
|
||||
*/
|
||||
private String resolveLoginName(PaymentApplication payment) {
|
||||
if (payment.getCreateUser() == null) {
|
||||
throw new HcsException("C108 履约记录推送失败,付款申请创建人未维护(paymentNo=" + payment.getPaymentNo() + ")");
|
||||
}
|
||||
User user = UserCache.getUser(payment.getCreateUser());
|
||||
if (user == null || StringUtil.isBlank(user.getAccount())) {
|
||||
throw new HcsException("C108 履约记录推送失败,创建人登录名解析不到(createUser=" + payment.getCreateUser() + ")");
|
||||
}
|
||||
return user.getAccount();
|
||||
}
|
||||
|
||||
/**
|
||||
* LocalDate → 毫秒时间戳
|
||||
*/
|
||||
private Long toMillis(LocalDate localDate) {
|
||||
return localDate == null ? null : localDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli();
|
||||
}
|
||||
}
|
||||
+229
@@ -0,0 +1,229 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
|
||||
* not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.support.hcs;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.hcs.config.HcsProperties;
|
||||
import org.springblade.hcs.dto.HcsAttachmentRefDTO;
|
||||
import org.springblade.hcs.dto.HcsMechanismsDTO;
|
||||
import org.springblade.hcs.dto.HcsResult;
|
||||
import org.springblade.hcs.dto.HcsFormSaveVO;
|
||||
import org.springblade.hcs.dto.HcsSpecPayFormDataDTO;
|
||||
import org.springblade.hcs.dto.HcsSpecPayPropsDTO;
|
||||
import org.springblade.hcs.dto.HcsSpecPaySaveDTO;
|
||||
import org.springblade.hcs.exception.HcsException;
|
||||
import org.springblade.hcs.feign.IHcsClient;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.transport.constant.HcsSyncConstant;
|
||||
import org.springblade.system.pojo.entity.User;
|
||||
import org.springblade.transport.mapper.PaymentApplicationMapper;
|
||||
import org.springblade.transport.pojo.entity.ContractManage;
|
||||
import org.springblade.transport.pojo.entity.PaymentApplication;
|
||||
import org.springblade.transport.mapper.ContractManageMapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* HCS 结算付款特批同步支撑组件(C106-3 发起 / C106-4 更新,设计文档 §5.5)
|
||||
* <p>
|
||||
* 触发:付款申请审批通过且 hcs_spec_status=need(提交时额度超额标记)→ 扫描任务发起 C106-3,
|
||||
* 返回 data.data.fdId(双层嵌套)→ hcs_spec_fd_id,状态置 processing;
|
||||
* 单据变更且特批流程存在时走 C106-4(顶层 fdId=hcs_spec_fd_id,附件不带 fdBindType)。
|
||||
* 特批结果:v4 无结果查询/回调接口,approved/rejected 一期由页面人工确认(§9-U1)。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class HcsSpecPaySyncSupport {
|
||||
|
||||
/**
|
||||
* 特批附件字段标识
|
||||
*/
|
||||
private static final String SPEC_ATTACH_ENTITY_KEY = "fd_spc_approval_attach";
|
||||
|
||||
private final IHcsClient hcsClient;
|
||||
private final HcsProperties hcsProperties;
|
||||
private final ContractManageMapper contractManageMapper;
|
||||
private final PaymentApplicationMapper paymentApplicationMapper;
|
||||
private final HcsAttachmentSupport hcsAttachmentSupport;
|
||||
|
||||
/**
|
||||
* 推送结算付款特批:未发起走 C106-3,已发起走 C106-4;流程ID 与状态立即持久化
|
||||
*
|
||||
* @param payment 付款申请单(hcs_spec_status=need)
|
||||
* @return 推送结果描述
|
||||
*/
|
||||
public String pushSpecPay(PaymentApplication payment) {
|
||||
String operatorLoginName = resolveLoginName(payment);
|
||||
HcsSpecPaySaveDTO param = buildSpecPayParam(payment, operatorLoginName);
|
||||
if (StringUtil.isBlank(payment.getHcsSpecFdId())) {
|
||||
// C106-3 发起:返回双层嵌套 data.data.fdId
|
||||
log.info("HCS结算付款特批发起开始:paymentNo={}", payment.getPaymentNo());
|
||||
HcsResult<HcsResult<HcsFormSaveVO>> result = hcsClient.addSpecApplyPay(param);
|
||||
String specFdId = extractNestedFdId(payment, result);
|
||||
payment.setHcsSpecFdId(specFdId);
|
||||
payment.setHcsSpecStatus(HcsSyncConstant.SPEC_STATUS_PROCESSING);
|
||||
paymentApplicationMapper.updateById(payment);
|
||||
log.info("HCS结算付款特批发起成功:paymentNo={},specFdId={}", payment.getPaymentNo(), specFdId);
|
||||
return "C106-3 特批发起成功";
|
||||
}
|
||||
// C106-4 更新:顶层 fdId 必填,内层无业务数据,以内层 success 判断
|
||||
log.info("HCS结算付款特批更新开始:paymentNo={},specFdId={}", payment.getPaymentNo(), payment.getHcsSpecFdId());
|
||||
HcsResult<HcsResult<Void>> result = hcsClient.updateSpecApplyPay(param);
|
||||
if (result == null || !result.isSuccess()) {
|
||||
log.error("HCS结算付款特批更新失败响应:{},paymentNo={}", result == null ? "无响应" : result.getMsg(), payment.getPaymentNo());
|
||||
throw new HcsException("C106-4 特批更新失败:" + (result == null ? "响应为空" : result.getMsg()));
|
||||
}
|
||||
return "C106-4 特批更新成功";
|
||||
}
|
||||
|
||||
/**
|
||||
* 组装 C106-3/C106-4 入参(字段标注照 §3.4 规范,映射表见设计文档 §5.5)
|
||||
*/
|
||||
private HcsSpecPaySaveDTO buildSpecPayParam(PaymentApplication payment, String operatorLoginName) {
|
||||
ContractManage contractManage = payment.getContractId() == null ? null : contractManageMapper.selectById(payment.getContractId());
|
||||
HcsSpecPaySaveDTO param = new HcsSpecPaySaveDTO();
|
||||
// 结算付款流程ID:发起不传,更新必填(C106-4 顶层 fdId)
|
||||
param.setFdId(payment.getHcsSpecFdId());
|
||||
// 【必填】操作人登录名:付款申请创建人登录名(§9-U2)
|
||||
param.setFdLoginName(operatorLoginName);
|
||||
HcsSpecPayFormDataDTO formData = new HcsSpecPayFormDataDTO();
|
||||
HcsSpecPayPropsDTO props = new HcsSpecPayPropsDTO();
|
||||
// 【必填】合同:传合规合同ID(blade_contract_manage.hcs_fd_id,非名称)
|
||||
props.setFdContractName(requireContractFdId(payment, contractManage));
|
||||
// 合同编号
|
||||
props.setFdContractNo(payment.getContractNo());
|
||||
// 单据编号:付款申请单号(来源付款单标识)
|
||||
props.setFdDocNo(payment.getPaymentNo());
|
||||
// 单据主题:"结算付款特批-" + 付款单号
|
||||
props.setFdDocSubject("结算付款特批-" + payment.getPaymentNo());
|
||||
// 来源付款单ID:付款申请单号(§5.5 映射表)
|
||||
props.setFdId(payment.getPaymentNo());
|
||||
// 付款金额:appliedAmount
|
||||
props.setFdPayAmount(payment.getAppliedAmount());
|
||||
// 合计金额:settlementAmount
|
||||
props.setFdTotalAmount(payment.getSettlementAmount() == null ? BigDecimal.ZERO : payment.getSettlementAmount());
|
||||
// 付款人 / 收款人
|
||||
props.setFdPayer(payment.getPayerName());
|
||||
props.setFdPayee(payment.getPayeeName());
|
||||
// 收款账户名 / 收款账号 / 开户行
|
||||
props.setFdReceiveAccountName(payment.getReceiptAccountName());
|
||||
props.setFdReceiveAccount(payment.getBankAccount());
|
||||
props.setFdBankName(payment.getBankName());
|
||||
// 结算币种:TMS 无币种字段,按 currency-name-mapping 默认值(人民币)
|
||||
props.setFdSettleCurrency(hcsProperties.getContract().getCurrencyNameMapping().getOrDefault("RMB", "人民币"));
|
||||
// 付款用途 / 付款性质 / 付款类型:字典值配置(§9-U3 待联调后配置)
|
||||
props.setFdPaymentPurpose(hcsProperties.getSpecPay().getPaymentPurpose());
|
||||
props.setFdPaymentNature(hcsProperties.getSpecPay().getPaymentNature());
|
||||
props.setFdPaymentType(hcsProperties.getSpecPay().getPaymentType());
|
||||
// 事由 / 补充说明:remark
|
||||
props.setFdReason(payment.getRemark());
|
||||
props.setFdSupplementNote(payment.getRemark());
|
||||
// 单据状态:配置 spec-pay.doc-status(示例 10)
|
||||
props.setFdDocStatus(hcsProperties.getSpecPay().getDocStatus());
|
||||
// 特批附件字段标识:固定 fd_spc_approval_attach
|
||||
props.setFdSpcApprovalAttach(SPEC_ATTACH_ENTITY_KEY);
|
||||
formData.setDynamicProps(props);
|
||||
// 表单层单据主题
|
||||
formData.setFdDocSubject("结算付款特批-" + payment.getPaymentNo());
|
||||
// 表单层单据状态:配置 spec-pay.doc-status
|
||||
formData.setFdDocStatus(hcsProperties.getSpecPay().getDocStatus());
|
||||
param.setFormData(formData);
|
||||
// 附件挂载:attachmentsJson 经 C103-2 转传,特批附件 fdEntityKey=fd_spc_approval_attach,
|
||||
// 发起带 fdBindType=ADD,更新不带
|
||||
param.setMechanisms(buildMechanisms(payment, operatorLoginName));
|
||||
return param;
|
||||
}
|
||||
|
||||
/**
|
||||
* 特批附件挂载(附件转传开关关闭或无附件时返回 null)
|
||||
*/
|
||||
private HcsMechanismsDTO buildMechanisms(PaymentApplication payment, String operatorLoginName) {
|
||||
if (!hcsProperties.getAttachment().isTransferEnabled() || StringUtil.isBlank(payment.getAttachmentsJson())) {
|
||||
return null;
|
||||
}
|
||||
List<String> attachmentFdIds = hcsAttachmentSupport.transferAttachments(payment.getAttachmentsJson(), operatorLoginName);
|
||||
if (attachmentFdIds.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
HcsMechanismsDTO mechanisms = new HcsMechanismsDTO();
|
||||
boolean add = StringUtil.isBlank(payment.getHcsSpecFdId());
|
||||
mechanisms.setAttachment(attachmentFdIds.stream().map(fdId -> {
|
||||
HcsAttachmentRefDTO attachment = new HcsAttachmentRefDTO();
|
||||
// 附件ID(C103-2 转传返回)
|
||||
attachment.setFdId(fdId);
|
||||
// 特批附件字段标识
|
||||
attachment.setFdEntityKey(SPEC_ATTACH_ENTITY_KEY);
|
||||
// 绑定类型:仅发起传 ADD
|
||||
attachment.setFdBindType(add ? "ADD" : null);
|
||||
return attachment;
|
||||
}).toList());
|
||||
return mechanisms;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 C106-3 双层嵌套返回 data.data.fdId
|
||||
*/
|
||||
private String extractNestedFdId(PaymentApplication payment, HcsResult<HcsResult<HcsFormSaveVO>> result) {
|
||||
if (result == null || !result.isSuccess() || result.getData() == null || !result.getData().isSuccess()
|
||||
|| result.getData().getData() == null || StringUtil.isBlank(result.getData().getData().getFdId())) {
|
||||
log.error("HCS结算付款特批发起失败响应:{},paymentNo={}", result == null ? "无响应" : result.getMsg(), payment.getPaymentNo());
|
||||
throw new HcsException("C106-3 特批发起失败:" + (result == null ? "响应为空" : result.getMsg()));
|
||||
}
|
||||
return result.getData().getData().getFdId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 合规合同ID(fd_contract_name 必填,合同未推送时失败提示)
|
||||
*/
|
||||
private String requireContractFdId(PaymentApplication payment, ContractManage contractManage) {
|
||||
if (contractManage == null || StringUtil.isBlank(contractManage.getHcsFdId())) {
|
||||
throw new HcsException("C106 特批推送失败,关联合同尚未推送合规(paymentNo=" + payment.getPaymentNo() + ")");
|
||||
}
|
||||
return contractManage.getHcsFdId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 操作人登录名(付款申请创建人,§9-U2)
|
||||
*/
|
||||
private String resolveLoginName(PaymentApplication payment) {
|
||||
if (payment.getCreateUser() == null) {
|
||||
throw new HcsException("C106 特批推送失败,付款申请创建人未维护(paymentNo=" + payment.getPaymentNo() + ")");
|
||||
}
|
||||
User user = UserCache.getUser(payment.getCreateUser());
|
||||
if (user == null || StringUtil.isBlank(user.getAccount())) {
|
||||
throw new HcsException("C106 特批推送失败,创建人登录名解析不到(createUser=" + payment.getCreateUser() + ")");
|
||||
}
|
||||
return user.getAccount();
|
||||
}
|
||||
}
|
||||
+40
-3
@@ -33,10 +33,13 @@ import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.hcs.config.HcsProperties;
|
||||
import org.springblade.transport.constant.HcsSyncConstant;
|
||||
import org.springblade.transport.mapper.ContractManageMapper;
|
||||
import org.springblade.transport.mapper.PaymentApplicationMapper;
|
||||
import org.springblade.transport.mapper.ProjectApplyMapper;
|
||||
import org.springblade.transport.pojo.entity.ContractManage;
|
||||
import org.springblade.transport.pojo.entity.PaymentApplication;
|
||||
import org.springblade.transport.pojo.entity.ProjectApply;
|
||||
import org.springblade.transport.support.hcs.HcsContractSyncSupport;
|
||||
import org.springblade.transport.support.hcs.HcsPaymentSyncSupport;
|
||||
import org.springblade.transport.support.hcs.HcsProjectSyncSupport;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
@@ -51,9 +54,9 @@ import java.util.UUID;
|
||||
* <p>
|
||||
* 审批代码零埋点:扫描只认 approvalStatus=approved/change_approved 且 hcs_sync_status=unsynced/failed(重试未超限),
|
||||
* 与审批入口(/approve、MK 回调)无关;失败落 failed 由下轮扫描自动补偿,超限仅人工重推(D7)。
|
||||
* 任务顺序:项目 → 合同(内含风险校验→附件转传→C103-1→履约规则 C103-9 行级同步)。
|
||||
* 任务顺序:项目 → 合同(内含风险校验→附件转传→C103-1→履约规则 C103-9 行级同步)→ 付款特批/履约记录。
|
||||
* 多实例部署时以 Redis 分布式锁防重扫(照既有 BladeRedis 用法,单实例部署行为与既有金蝶任务一致)。
|
||||
* 扫描与推送逻辑收敛在 pushUnsyncedProjects/pushUnsyncedContracts,供手动端点(HcsSyncController)复用。
|
||||
* 扫描与推送逻辑收敛在 pushUnsyncedProjects/pushUnsyncedContracts/pushUnsyncedPayments,供手动端点(HcsSyncController)复用。
|
||||
* <p>
|
||||
* 调度配置(Nacos thirdParty.hcs.push.*):sync-enabled 总开关(默认关,灰度打开)、cron、batch-size、max-retry。
|
||||
*
|
||||
@@ -77,8 +80,10 @@ public class HcsPushTask {
|
||||
private final HcsProperties hcsProperties;
|
||||
private final ProjectApplyMapper projectApplyMapper;
|
||||
private final ContractManageMapper contractManageMapper;
|
||||
private final PaymentApplicationMapper paymentApplicationMapper;
|
||||
private final HcsProjectSyncSupport hcsProjectSyncSupport;
|
||||
private final HcsContractSyncSupport hcsContractSyncSupport;
|
||||
private final HcsPaymentSyncSupport hcsPaymentSyncSupport;
|
||||
private final BladeRedis bladeRedis;
|
||||
|
||||
/**
|
||||
@@ -104,9 +109,10 @@ public class HcsPushTask {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 任务顺序(设计文档 §5.0):项目 → 合同;两类互不影响
|
||||
// 任务顺序(设计文档 §5.0):项目 → 合同 → 付款;各类互不影响
|
||||
log.info("定时推送HCS项目立项:{}", pushUnsyncedProjects());
|
||||
log.info("定时推送HCS合同:{}", pushUnsyncedContracts());
|
||||
log.info("定时推送HCS付款链:{}", pushUnsyncedPayments());
|
||||
} finally {
|
||||
if (lockHolder.equals(bladeRedis.get(HcsSyncConstant.PUSH_TASK_LOCK_KEY))) {
|
||||
bladeRedis.del(HcsSyncConstant.PUSH_TASK_LOCK_KEY);
|
||||
@@ -175,4 +181,35 @@ public class HcsPushTask {
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* 扫描并推送待同步/待补偿的付款申请(C106 特批 + C108 履约记录),返回结果汇总
|
||||
*
|
||||
* @return 汇总描述
|
||||
*/
|
||||
public String pushUnsyncedPayments() {
|
||||
int maxRetry = hcsProperties.getPush().getMaxRetry();
|
||||
List<PaymentApplication> paymentList = paymentApplicationMapper.selectList(Wrappers.<PaymentApplication>lambdaQuery()
|
||||
.eq(PaymentApplication::getApprovalStatus, "approved")
|
||||
.in(PaymentApplication::getHcsSyncStatus, List.of(HcsSyncConstant.HCS_STATUS_UNSYNCED, HcsSyncConstant.HCS_STATUS_FAILED))
|
||||
.and(wrapper -> wrapper.isNull(PaymentApplication::getHcsRetryCount).or().lt(PaymentApplication::getHcsRetryCount, maxRetry))
|
||||
.last("limit " + hcsProperties.getPush().getBatchSize()));
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
for (PaymentApplication payment : paymentList) {
|
||||
try {
|
||||
hcsPaymentSyncSupport.pushPayment(payment);
|
||||
successCount++;
|
||||
} catch (Exception exception) {
|
||||
String message = StringUtil.isBlank(exception.getMessage()) ? "推送异常" : exception.getMessage();
|
||||
hcsPaymentSyncSupport.markFailed(payment, message);
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
String summary = String.format("扫描 %d 条,成功 %d 条,失败 %d 条(重试上限 %d)", paymentList.size(), successCount, failCount, maxRetry);
|
||||
if (failCount > 0) {
|
||||
log.warn("HCS付款链推送存在失败:{}", summary);
|
||||
}
|
||||
return summary;
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -100,6 +100,10 @@ public class HcsProperties {
|
||||
* 结算付款特批(C106-3/4)配置
|
||||
*/
|
||||
private SpecPay specPay = new SpecPay();
|
||||
/**
|
||||
* 履约记录(C108-1/2)配置
|
||||
*/
|
||||
private PerfRecord perfRecord = new PerfRecord();
|
||||
/**
|
||||
* 开票登记(C111)配置
|
||||
*/
|
||||
@@ -245,6 +249,14 @@ public class HcsProperties {
|
||||
private Map<String, String> invoiceTypeMapping = new HashMap<>();
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class PerfRecord {
|
||||
/**
|
||||
* 履约记录表单模板ID(fd_xform_id,环境相关值,§9-U9 待合规方提供)
|
||||
*/
|
||||
private String xformId = "";
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class Attachment {
|
||||
/**
|
||||
|
||||
@@ -138,6 +138,10 @@ thirdParty:
|
||||
receipt-bill:
|
||||
# TMS 发票类型 -> HCS 收票发票类型字典值(§9-U3 待联调后配置)
|
||||
invoice-type-mapping: {}
|
||||
# 履约记录(C108-1/2)
|
||||
perf-record:
|
||||
# 表单模板ID(fd_xform_id,环境相关值,§9-U9 待合规方提供)
|
||||
xform-id: ${HCS_PERF_RECORD_XFORM_ID:}
|
||||
# 附件转传(C103-2)
|
||||
attachment:
|
||||
# 附件转传总开关
|
||||
|
||||
Reference in New Issue
Block a user