:refactor: 合同特批改为随起草报文 fdSepcailContract 发起,删除 C103-8 链路并修正结果回调报文
- C103-8 独立特批发起接口合规未提供(联调 404):删除 IHcsClient#addSpecContract、SPEC_CONTRACT_ADD_URL、HcsSpecContractSaveDTO、applySpec 及辅助方法、人工端点 POST /hcs/contract/spec-apply - 高风险/黑名单客商合同推送时 C103-1/C103-11 报文携带 fdSepcailContract=true,合规侧随起草自动走特批流程;推送门禁与手动重推仅拦截 processing,need 存量卡单重推自愈 - 推送成功后 hangSpecReview 挂起"特批审核"(processing),含 approved/rejected 终态守卫防合规秒批回调竞态覆盖 - 特批附件随 C103-1 附件面板 eopCdraftSpecial 挂载(原全部走 eopCdraftOther) - contractLegal/result 回调报文按合规方最新字段表修正(HcsContractLegalResultDTO:contractCode/success/kingdeeId/sourceSystem=WCCK),按合同编号定位合同,success=true 联动放行、false 联动审批驳回
This commit is contained in:
+3
-3
@@ -85,7 +85,7 @@ public final class HcsSyncConstant {
|
||||
public static final String CHECK_TYPE_CREDIT = "credit";
|
||||
|
||||
/**
|
||||
* 校验类型:合同特批(C103-8 发起 / 合规特批结果推送留痕)
|
||||
* 校验类型:合同特批(合规特批结果推送/查询联动留痕;原 C103-8 发起留痕随接口删除)
|
||||
*/
|
||||
public static final String CHECK_TYPE_SPEC = "spec";
|
||||
|
||||
@@ -176,9 +176,9 @@ public final class HcsSyncConstant {
|
||||
public static final String ATTACHMENT_TYPE_FIELD = "fileType";
|
||||
|
||||
/**
|
||||
* 附件类型:特批说明(高风险/黑名单客商合同必须上传;中文匹配沿用 FILE_TYPE_SEAL_ARCHIVE="双章归档文件" 惯例)
|
||||
* 附件类型:特批附件(高风险/黑名单客商合同必须上传;值取业务字典 contract_attachment_type,中文匹配沿用 FILE_TYPE_SEAL_ARCHIVE="双章归档文件" 惯例)
|
||||
*/
|
||||
public static final String ATTACHMENT_TYPE_SPEC_EXPLANATION = "特批说明";
|
||||
public static final String ATTACHMENT_TYPE_SPEC_EXPLANATION = "特批附件";
|
||||
|
||||
/**
|
||||
* 附件类型:用印附件(双章归档文件,取值沿用 ContractManageServiceImpl#FILE_TYPE_SEAL_ARCHIVE)
|
||||
|
||||
+3
-31
@@ -38,6 +38,7 @@ import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.api.R;
|
||||
import org.springblade.core.tool.utils.StringUtil;
|
||||
import org.springblade.hcs.dto.HcsAttachmentRefDTO;
|
||||
import org.springblade.hcs.dto.HcsContractLegalResultDTO;
|
||||
import org.springblade.hcs.dto.HcsCurrencyVO;
|
||||
import org.springblade.hcs.dto.HcsLegalResultDTO;
|
||||
import org.springblade.hcs.dto.HcsTemplateVO;
|
||||
@@ -216,35 +217,6 @@ public class HcsSyncController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 合同发起特批(C103-8,前端接入设计 §3.4):body = { contractId, attachments: [{fdId, name}] }
|
||||
*/
|
||||
@PostMapping("/contract/spec-apply")
|
||||
@ApiOperationSupport(order = 13)
|
||||
@Operation(summary = "合同发起特批", description = "C103-8:高风险客商合同特批发起,attachments 为经 /hcs/attachment/upload 上传的合规附件引用")
|
||||
@SuppressWarnings("unchecked")
|
||||
public R<String> applyContractSpec(@RequestBody Map<String, Object> body) {
|
||||
Object contractIdValue = body == null ? null : body.get("contractId");
|
||||
if (contractIdValue == null || StringUtil.isBlank(String.valueOf(contractIdValue))) {
|
||||
return R.fail("contractId 不能为空");
|
||||
}
|
||||
Long contractId = Long.valueOf(String.valueOf(contractIdValue));
|
||||
Object attachmentsValue = body == null ? null : body.get("attachments");
|
||||
List<HcsAttachmentRefDTO> attachments = (attachmentsValue instanceof List<?> list ? list : List.<Object>of()).stream()
|
||||
.filter(Map.class::isInstance)
|
||||
.map(item -> {
|
||||
Map<String, Object> attachment = (Map<String, Object>) item;
|
||||
HcsAttachmentRefDTO ref = new HcsAttachmentRefDTO();
|
||||
// 附件ID:C103-2 上传返回的合规附件ID
|
||||
ref.setFdId(String.valueOf(attachment.get("fdId")));
|
||||
// 附件名
|
||||
ref.setFdFileName(attachment.get("name") == null ? null : String.valueOf(attachment.get("name")));
|
||||
return ref;
|
||||
})
|
||||
.toList();
|
||||
return R.data(hcsContractSpecSyncSupport.applySpec(contractId, attachments));
|
||||
}
|
||||
|
||||
/**
|
||||
* 合同特批结果人工确认(§6-U1 过渡方案:与合规推送等效)
|
||||
*/
|
||||
@@ -266,8 +238,8 @@ public class HcsSyncController {
|
||||
*/
|
||||
@PostMapping("/contractLegal/result")
|
||||
@ApiOperationSupport(order = 15)
|
||||
@Operation(summary = "合同特批结果回传接收", description = "合规系统推送合同特批结果,按合同ID定位并联动特批状态,原文留痕")
|
||||
public R<String> contractLegalResult(@RequestBody(required = false) HcsLegalResultDTO param) {
|
||||
@Operation(summary = "合同特批结果回传接收", description = "合规系统推送合同特批(金蝶同步)结果,按合同编号定位并联动特批状态,原文留痕")
|
||||
public R<String> contractLegalResult(@RequestBody(required = false) HcsContractLegalResultDTO param) {
|
||||
return R.data(hcsContractSpecSyncSupport.handleLegalResult(param));
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -66,7 +66,7 @@ import java.util.Objects;
|
||||
* 任一附件转传失败 → 抛出异常由整单推送失败进补偿重试(失败原因落单据 hcs_sync_msg)。
|
||||
* <p>
|
||||
* 合同链路挂载字段 fdEntityKey(v4 枚举):主文件 approved=true → eopCdraftSign(用印)、
|
||||
* 其它附件 → eopCdraftOther、特批说明 → eopCdraftSpecial(C103-8 特批链路)。
|
||||
* 特批附件(fileType=特批附件)→ eopCdraftSpecial(随 C103-1 fdSepcailContract=true 特批流程)、其它附件 → eopCdraftOther。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@@ -124,8 +124,10 @@ public class HcsAttachmentSupport {
|
||||
}
|
||||
}
|
||||
for (Map<?, ?> attachment : parseJsonArray(attachmentsJson)) {
|
||||
attachmentRefs.add(this.transferOne(bizType, bizId, attachment, operatorLoginName,
|
||||
HcsSyncConstant.CONTRACT_ATTACH_KEY_OTHER));
|
||||
// 特批附件(fileType=特批附件)挂特批面板字段,其余挂其他附件字段(2026-10-13:特批改随 C103-1 fdSepcailContract 发起)
|
||||
String entityKey = HcsSyncConstant.ATTACHMENT_TYPE_SPEC_EXPLANATION.equals(attachment.get(HcsSyncConstant.ATTACHMENT_TYPE_FIELD))
|
||||
? HcsSyncConstant.CONTRACT_ATTACH_KEY_SPECIAL : HcsSyncConstant.CONTRACT_ATTACH_KEY_OTHER;
|
||||
attachmentRefs.add(this.transferOne(bizType, bizId, attachment, operatorLoginName, entityKey));
|
||||
}
|
||||
return attachmentRefs;
|
||||
}
|
||||
|
||||
+101
-154
@@ -25,20 +25,17 @@
|
||||
*/
|
||||
package org.springblade.transport.support.hcs;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
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.hcs.config.HcsProperties;
|
||||
import org.springblade.hcs.dto.HcsAttachmentRefDTO;
|
||||
import org.springblade.hcs.dto.HcsContractDetailQueryDTO;
|
||||
import org.springblade.hcs.dto.HcsContractDetailVO;
|
||||
import org.springblade.hcs.dto.HcsContractLegalResultDTO;
|
||||
import org.springblade.hcs.dto.HcsLegalResultDTO;
|
||||
import org.springblade.hcs.dto.HcsMechanismsDTO;
|
||||
import org.springblade.hcs.dto.HcsResult;
|
||||
import org.springblade.hcs.dto.HcsSpecContractSaveDTO;
|
||||
import org.springblade.hcs.dto.HcsFormSaveVO;
|
||||
import org.springblade.hcs.exception.HcsException;
|
||||
import org.springblade.hcs.feign.IHcsClient;
|
||||
import org.springblade.transport.constant.HcsSyncConstant;
|
||||
import org.springblade.transport.mapper.ContractManageMapper;
|
||||
@@ -51,15 +48,14 @@ import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* HCS 合同特批同步支撑组件(C103-8 发起特批 / 合规特批结果推送接收,前端接入设计 §3.4)
|
||||
* HCS 合同特批同步支撑组件(合规特批结果接收:contractLegal/result 推送 + C103-4 定时查询兜底 + 人工确认)。
|
||||
* <p>
|
||||
* 业务闭环:合同推送时 C101-1 风险校验发现黑名单 → 置 hcs_spec_status=need 挂起(推送链路门禁跳过)→
|
||||
* 内部特批审批通过后,前端上传特批材料(C103-2 取合规附件ID)并发起 C103-8 → 状态 processing(流程挂起)→
|
||||
* 集团审批结果经合规 contractLegal/result 接口推送 TMS(替代未建设的 B103)→
|
||||
* approved 恢复流程,rejected 可重新发起。
|
||||
* 业务闭环(2026-10-13 起):高风险/黑名单客商合同随 C103-1/C103-11 报文 fdSepcailContract=true 在合规侧走特批流程
|
||||
* (C103-8 独立发起接口合规未提供,已删除),TMS 挂起"特批审核"(processing)→
|
||||
* 集团审批结果经合规 contractLegal/result 接口推送 TMS(替代未建设的 B103),或定时 C103-4 查询兜底联动 →
|
||||
* approved 恢复流程,rejected 联动合同审批驳回。
|
||||
* <p>
|
||||
* TODO 待联调确认(前端接入设计 §6-U1):C103-8 报文 v4 未提供,按 C106-3 模式预估(双层嵌套返回);
|
||||
* 结果推送报文 {sourceSystem, requestId=合同ID, status=APPROVED/REJECTED, reason},原文留痕 blade_hcs_check_log。
|
||||
* 结果推送报文 {contractType, contractCode, requestId, success, reason, kingdeeId, sourceSystem=WCCK},原文留痕 blade_hcs_check_log。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@@ -69,9 +65,19 @@ import java.util.List;
|
||||
public class HcsContractSpecSyncSupport {
|
||||
|
||||
/**
|
||||
* 特批附件字段标识(照 C106-3 fd_spc_approval_attach 惯例预估,§6-U1 以合规方报文为准)
|
||||
* C103-4 合同审批状态:已审批(fdApprovalStatus,小写枚举)
|
||||
*/
|
||||
private static final String SPEC_ATTACH_ENTITY_KEY = "fd_spc_approval_attach";
|
||||
private static final String LEGAL_STATUS_APPROVED = "approved";
|
||||
|
||||
/**
|
||||
* C103-4 合同审批状态:审批作废(特批驳回映射待联调确认,暂按作废联动驳回)
|
||||
*/
|
||||
private static final String LEGAL_STATUS_INVALID = "approvalinvalid";
|
||||
|
||||
/**
|
||||
* 合同特批结果回传来源系统标识(合规方接口文档约定固定传 WCCK)
|
||||
*/
|
||||
private static final String LEGAL_CALLBACK_SRC_SYSTEM = "WCCK";
|
||||
|
||||
/**
|
||||
* 结果推送原文留痕最大长度(blade_hcs_check_log.msg 为 varchar(512))
|
||||
@@ -79,55 +85,56 @@ public class HcsContractSpecSyncSupport {
|
||||
private static final int CALLBACK_MSG_MAX_LENGTH = 500;
|
||||
|
||||
private final IHcsClient hcsClient;
|
||||
private final HcsProperties hcsProperties;
|
||||
private final ContractManageMapper contractManageMapper;
|
||||
private final HcsContractCheckLogMapper hcsContractCheckLogMapper;
|
||||
|
||||
/**
|
||||
* 发起合同特批(C103-8):校验合同与特批状态 → 组装预估报文 → 回填特批流程ID,状态置 processing
|
||||
* 特批结果定时查询兜底(合规推送 contractLegal/result 未达/未配置时的补偿,2026-10-13):
|
||||
* 扫描特批中(processing)的合同,按 C103-4 合同详情 fdApprovalStatus 联动——
|
||||
* approved 恢复"审批通过"、approvalinvalid 联动审批驳回(特批驳回的合规侧状态待联调确认,暂按作废映射)、其余等待中跳过;
|
||||
* 与结果推送互不冲突(推送先到则状态已非 processing,查询自然跳过)
|
||||
*
|
||||
* @param contractId 合同ID
|
||||
* @param attachments 特批附件引用(fdId=C103-2 合规附件ID,fdFileName=文件名)
|
||||
* @return 结果描述
|
||||
* @return 结果汇总(供任务日志)
|
||||
*/
|
||||
public String applySpec(Long contractId, List<HcsAttachmentRefDTO> attachments) {
|
||||
ContractManage contractManage = requireAppliableContract(contractId);
|
||||
if (attachments == null || attachments.isEmpty()) {
|
||||
throw new ServiceException("请先上传特批申请材料(含内部审批流程附件)");
|
||||
public String syncProcessingSpecResults() {
|
||||
List<ContractManage> contracts = contractManageMapper.selectList(Wrappers.<ContractManage>lambdaQuery()
|
||||
.eq(ContractManage::getHcsSpecStatus, HcsSyncConstant.SPEC_STATUS_PROCESSING)
|
||||
.isNotNull(ContractManage::getHcsFdId)
|
||||
.last("limit 50"));
|
||||
if (contracts.isEmpty()) {
|
||||
return "特批结果查询:无特批中合同";
|
||||
}
|
||||
HcsSpecContractSaveDTO param = new HcsSpecContractSaveDTO();
|
||||
// 【必填】操作人登录名:配置 thirdParty.hcs.operator-login-name(默认 srvadmin,§9-U2)
|
||||
param.setFdLoginName(hcsProperties.getOperatorLoginName());
|
||||
// 【必填】合规合同ID:blade_contract_manage.hcs_fd_id(非名称,照 C106-3 fd_contract_name 惯例)
|
||||
param.setFdContractId(requireContractFdId(contractManage));
|
||||
// 【必填】合同编号
|
||||
param.setFdContractNo(contractManage.getContractNo());
|
||||
// 【必填】单据主题:"合同特批-" + 合同编号
|
||||
param.setFdDocSubject("合同特批-" + contractManage.getContractNo());
|
||||
// 特批事由:合同名称(非必传)
|
||||
param.setFdReason(contractManage.getContractName());
|
||||
// 特批附件:发起带 fdBindType=ADD(entityKey 预估照 C106-3,§6-U1)
|
||||
param.setMechanisms(buildMechanisms(attachments));
|
||||
// 特批材料留痕(含合规附件ID,供重新发起与排查)
|
||||
contractManage.setHcsSpecFilesJson(JSONUtil.toJsonStr(attachments));
|
||||
try {
|
||||
log.info("HCS合同特批发起开始:contractNo={},附件 {} 个", contractManage.getContractNo(), attachments.size());
|
||||
// TODO 待联调(§6-U1):报文按 C106-3 模式预估,双层嵌套 data.data.fdId
|
||||
HcsResult<HcsResult<HcsFormSaveVO>> result = hcsClient.addSpecContract(param);
|
||||
String specFdId = extractNestedFdId(contractManage, result);
|
||||
contractManage.setHcsSpecFdId(specFdId);
|
||||
markSpec(contractManage, HcsSyncConstant.SPEC_STATUS_PROCESSING, "C103-8 合同特批发起成功");
|
||||
writeCheckLog(contractManage, HcsSyncConstant.CHECK_RESULT_PASS, "C103-8 特批发起成功,特批流程ID=" + specFdId, result.getTraceId());
|
||||
log.info("HCS合同特批发起成功:contractNo={},specFdId={}", contractManage.getContractNo(), specFdId);
|
||||
return "特批发起成功,等待集团审批";
|
||||
} catch (Exception exception) {
|
||||
String message = StringUtil.isBlank(exception.getMessage()) ? "特批发起异常" : exception.getMessage();
|
||||
// 失败保持待特批(need),允许重试发起
|
||||
markSpec(contractManage, HcsSyncConstant.SPEC_STATUS_NEED, "C103-8 特批发起失败:" + message);
|
||||
writeCheckLog(contractManage, HcsSyncConstant.CHECK_RESULT_FAIL, "C103-8 特批发起失败:" + message, null);
|
||||
throw exception instanceof ServiceException serviceException ? serviceException
|
||||
: new ServiceException("合同特批发起失败:" + message);
|
||||
int approvedCount = 0;
|
||||
int rejectedCount = 0;
|
||||
for (ContractManage contractManage : contracts) {
|
||||
try {
|
||||
HcsContractDetailQueryDTO query = new HcsContractDetailQueryDTO();
|
||||
// 【定位】合规合同ID:C103-4 fdId 与 fdNumber 二选一
|
||||
query.setFdId(contractManage.getHcsFdId());
|
||||
HcsResult<HcsContractDetailVO> result = hcsClient.contractDetail(query);
|
||||
if (result == null || !result.isSuccess() || result.getData() == null) {
|
||||
log.warn("HCS特批结果查询失败响应:contractNo={},{}", contractManage.getContractNo(),
|
||||
result == null ? "无响应" : result.getMsg());
|
||||
continue;
|
||||
}
|
||||
String approvalStatus = result.getData().getFdApprovalStatus();
|
||||
if (LEGAL_STATUS_APPROVED.equalsIgnoreCase(approvalStatus)) {
|
||||
syncContractOnSpecResult(contractManage, true);
|
||||
markSpec(contractManage, HcsSyncConstant.SPEC_STATUS_APPROVED, "特批结果查询联动:fdApprovalStatus=approved");
|
||||
writeCheckLog(contractManage, HcsSyncConstant.CHECK_RESULT_PASS, "特批结果查询联动:fdApprovalStatus=approved", null);
|
||||
approvedCount++;
|
||||
} else if (LEGAL_STATUS_INVALID.equalsIgnoreCase(approvalStatus)) {
|
||||
syncContractOnSpecResult(contractManage, false);
|
||||
markSpec(contractManage, HcsSyncConstant.SPEC_STATUS_REJECTED, "特批结果查询联动:fdApprovalStatus=approvalinvalid");
|
||||
writeCheckLog(contractManage, HcsSyncConstant.CHECK_RESULT_FAIL, "特批结果查询联动:fdApprovalStatus=approvalinvalid", null);
|
||||
rejectedCount++;
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
// 单条失败不影响其余,下轮扫描自动重试
|
||||
log.warn("HCS特批结果查询单条失败(下轮重试):contractNo={},{}", contractManage.getContractNo(), exception.getMessage());
|
||||
}
|
||||
}
|
||||
return "特批结果查询:扫描 " + contracts.size() + " 单,联动通过 " + approvedCount + ",联动驳回 " + rejectedCount;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,120 +165,60 @@ public class HcsContractSpecSyncSupport {
|
||||
}
|
||||
|
||||
/**
|
||||
* 合同特批结果接收处理(合规 contractLegal/result 推送,替代未建设的 B103,D15 回传机制):
|
||||
* sourceSystem 须与 third-party.hcs.src-system 配置一致(不符抛出"来源系统标识错误"异常),
|
||||
* requestId=TMS 合同ID、status=APPROVED/REJECTED,
|
||||
* approved 恢复合同"审批通过"展示;rejected 联动合同审批驳回;原文全量留痕
|
||||
* 合同特批结果接收处理(合规 contractLegal/result 推送,2026-10-13 按合规方最新字段表修正报文结构):
|
||||
* 特批完成后合规侧同步金蝶并回调,报文 {contractType, contractCode, requestId, success, reason, kingdeeId, sourceSystem=WCCK};
|
||||
* sourceSystem 须为 WCCK(不符抛"来源系统标识错误"异常);contractCode=TMS 合同编号定位(requestId 仅请求头回显不可定位);
|
||||
* success=true 联动恢复"审批通过"(kingdeeId 随留痕保存);success=false 联动合同审批驳回走重新提交闭环(reason 随原文留痕)
|
||||
*
|
||||
* @param param 合规推送报文
|
||||
* @return 处理结果描述
|
||||
*/
|
||||
public String handleLegalResult(HcsLegalResultDTO param) {
|
||||
// 来源系统校验:仅接受 third-party.hcs.src-system 配置的来源,防伪造回传
|
||||
if (!StrUtil.equalsIgnoreCase(param == null ? null : param.getSourceSystem(), hcsProperties.getSrcSystem())) {
|
||||
public String handleLegalResult(HcsContractLegalResultDTO param) {
|
||||
// 来源系统校验:仅接受合规方约定的 WCCK,防伪造回传
|
||||
if (!LEGAL_CALLBACK_SRC_SYSTEM.equalsIgnoreCase(param == null ? null : param.getSourceSystem())) {
|
||||
log.warn("合同特批结果推送来源系统标识错误:sourceSystem={}", param == null ? null : param.getSourceSystem());
|
||||
throw new ServiceException("来源系统标识错误");
|
||||
}
|
||||
String raw = param == null ? "" : JSONUtil.toJsonStr(param);
|
||||
String requestId = param == null ? null : param.getRequestId();
|
||||
ContractManage contractManage = locateContract(requestId);
|
||||
String raw = JSONUtil.toJsonStr(param);
|
||||
ContractManage contractManage = locateContractByNo(param.getContractCode());
|
||||
if (contractManage == null) {
|
||||
log.warn("合同特批结果推送未定位到合同:requestId={},报文已留痕", requestId);
|
||||
log.warn("合同特批结果推送未定位到合同:contractCode={},报文已留痕", param.getContractCode());
|
||||
writeCallbackLog(null, HcsSyncConstant.CHECK_RESULT_SKIP, "合同特批结果推送未定位到合同:" + raw);
|
||||
return "已留痕:未定位到合同(requestId=" + requestId + ")";
|
||||
return "已留痕:未定位到合同(contractCode=" + param.getContractCode() + ")";
|
||||
}
|
||||
String normalizedStatus = param.getStatus() == null ? "" : param.getStatus().trim();
|
||||
boolean approved;
|
||||
if (HcsSyncConstant.LEGAL_RESULT_APPROVED.equalsIgnoreCase(normalizedStatus)) {
|
||||
approved = true;
|
||||
} else if (HcsSyncConstant.LEGAL_RESULT_REJECTED.equalsIgnoreCase(normalizedStatus)) {
|
||||
approved = false;
|
||||
} else {
|
||||
log.warn("合同特批结果状态未识别:contractNo={},status={},报文已留痕", contractManage.getContractNo(), param.getStatus());
|
||||
writeCallbackLog(contractManage, HcsSyncConstant.CHECK_RESULT_SKIP, "合同特批结果推送状态未识别:" + raw);
|
||||
return "已留痕:状态字段未识别(contractNo=" + contractManage.getContractNo() + ")";
|
||||
if (param.getSuccess() == null) {
|
||||
log.warn("合同特批结果推送 success 字段缺失:contractNo={},报文已留痕", contractManage.getContractNo());
|
||||
writeCallbackLog(contractManage, HcsSyncConstant.CHECK_RESULT_SKIP, "合同特批结果推送 success 字段缺失:" + raw);
|
||||
return "已留痕:success 字段缺失(contractNo=" + contractManage.getContractNo() + ")";
|
||||
}
|
||||
String status = approved ? HcsSyncConstant.SPEC_STATUS_APPROVED : HcsSyncConstant.SPEC_STATUS_REJECTED;
|
||||
// 特批结果联动合同审批状态(通过恢复展示,驳回联动审批驳回),随 markSpec 一并落库(reason 随原文留痕)
|
||||
syncContractOnSpecResult(contractManage, approved);
|
||||
markSpec(contractManage, status, "合同特批结果推送:" + raw);
|
||||
writeCallbackLog(contractManage, approved ? HcsSyncConstant.CHECK_RESULT_PASS : HcsSyncConstant.CHECK_RESULT_FAIL,
|
||||
"合同特批结果推送:" + raw);
|
||||
log.info("合同特批结果推送处理完成:contractNo={},结果={},reason={}", contractManage.getContractNo(), status, param.getReason());
|
||||
return "特批结果已处理:" + status;
|
||||
boolean success = param.getSuccess();
|
||||
// 成功:特批完成且金蝶同步成功,恢复"审批通过";kingdeeId 随 hcs_spec_msg/留痕保存
|
||||
if (success) {
|
||||
syncContractOnSpecResult(contractManage, true);
|
||||
markSpec(contractManage, HcsSyncConstant.SPEC_STATUS_APPROVED,
|
||||
"合同特批结果推送:金蝶同步成功,kingdeeId=" + param.getKingdeeId());
|
||||
writeCallbackLog(contractManage, HcsSyncConstant.CHECK_RESULT_PASS, "合同特批结果推送:" + raw);
|
||||
log.info("合同特批结果推送处理完成:contractNo={},结果=approved,kingdeeId={}", contractManage.getContractNo(), param.getKingdeeId());
|
||||
return "特批结果已处理:approved";
|
||||
}
|
||||
// 失败:特批流程已完成但金蝶同步失败,联动合同审批驳回,走重新编辑→提交→审批→重新特批闭环(失败单独重试机制待联调确认)
|
||||
syncContractOnSpecResult(contractManage, false);
|
||||
markSpec(contractManage, HcsSyncConstant.SPEC_STATUS_REJECTED, "合同特批结果推送:金蝶同步失败:" + param.getReason());
|
||||
writeCallbackLog(contractManage, HcsSyncConstant.CHECK_RESULT_FAIL, "合同特批结果推送:" + raw);
|
||||
log.info("合同特批结果推送处理完成:contractNo={},结果=rejected,reason={}", contractManage.getContractNo(), param.getReason());
|
||||
return "特批结果已处理:rejected";
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验可发起特批的合同:存在、审批通过、特批状态为待特批/已驳回
|
||||
* 按 ERP 合同编号定位合同(合规推送 contractCode,合同编号唯一)
|
||||
*/
|
||||
private ContractManage requireAppliableContract(Long contractId) {
|
||||
ContractManage contractManage = contractManageMapper.selectById(contractId);
|
||||
if (contractManage == null) {
|
||||
throw new ServiceException("合同单不存在:" + contractId);
|
||||
}
|
||||
if (!List.of("approved", "change_approved").contains(contractManage.getApprovalStatus())) {
|
||||
throw new ServiceException("仅审批通过的合同可发起特批(当前状态:" + contractManage.getApprovalStatus() + ")");
|
||||
}
|
||||
if (!List.of(HcsSyncConstant.SPEC_STATUS_NEED, HcsSyncConstant.SPEC_STATUS_REJECTED).contains(contractManage.getHcsSpecStatus())) {
|
||||
throw new ServiceException("仅待特批/已驳回的合同可发起特批(当前状态:" + contractManage.getHcsSpecStatus() + ")");
|
||||
}
|
||||
return contractManage;
|
||||
}
|
||||
|
||||
/**
|
||||
* 合规合同ID(合同须已推送合规回填 hcs_fd_id)
|
||||
*/
|
||||
private String requireContractFdId(ContractManage contractManage) {
|
||||
if (StringUtil.isBlank(contractManage.getHcsFdId())) {
|
||||
throw new ServiceException("合同尚未推送合规,无法发起特批(contractNo=" + contractManage.getContractNo() + ")");
|
||||
}
|
||||
return contractManage.getHcsFdId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 特批附件挂载(发起带 fdBindType=ADD)
|
||||
*/
|
||||
private HcsMechanismsDTO buildMechanisms(List<HcsAttachmentRefDTO> attachments) {
|
||||
HcsMechanismsDTO mechanisms = new HcsMechanismsDTO();
|
||||
mechanisms.setAttachment(attachments.stream().map(ref -> {
|
||||
HcsAttachmentRefDTO attachment = new HcsAttachmentRefDTO();
|
||||
// 附件ID:C103-2 转传返回的合规附件ID
|
||||
attachment.setFdId(ref.getFdId());
|
||||
// 附件名(预估字段,§6-U1)
|
||||
attachment.setFdFileName(ref.getFdFileName());
|
||||
// 特批附件字段标识
|
||||
attachment.setFdEntityKey(SPEC_ATTACH_ENTITY_KEY);
|
||||
// 绑定类型:发起传 ADD
|
||||
attachment.setFdBindType("ADD");
|
||||
return attachment;
|
||||
}).toList());
|
||||
return mechanisms;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 C103-8 双层嵌套返回 data.data.fdId(照 C106-3 模式预估)
|
||||
*/
|
||||
private String extractNestedFdId(ContractManage contractManage, 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合同特批发起失败响应:{},contractNo={}", result == null ? "无响应" : String.format("msg=%s, traceId=%s", result.getMsg(), result.getTraceId()), contractManage.getContractNo());
|
||||
throw new HcsException("C103-8 合同特批发起失败:" + (result == null ? "响应为空" : result.getMsg()));
|
||||
}
|
||||
return result.getData().getData().getFdId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 按 TMS 合同ID 定位合同(合规推送 requestId)
|
||||
*/
|
||||
private ContractManage locateContract(String requestId) {
|
||||
if (StringUtil.isBlank(requestId)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return contractManageMapper.selectById(Long.valueOf(requestId.trim()));
|
||||
} catch (NumberFormatException exception) {
|
||||
private ContractManage locateContractByNo(String contractCode) {
|
||||
if (StringUtil.isBlank(contractCode)) {
|
||||
return null;
|
||||
}
|
||||
return contractManageMapper.selectOne(Wrappers.<ContractManage>lambdaQuery()
|
||||
.eq(ContractManage::getContractNo, contractCode.trim())
|
||||
.last("limit 1"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+40
-83
@@ -71,8 +71,9 @@ import java.util.Objects;
|
||||
* <p>
|
||||
* 扫描任务按序执行:⓪ 特批挂起门禁 → ① 客商风险校验(可配开关,仅留痕,高风险不再阻断)→
|
||||
* ② 附件转传(v4 挂载点 fdFieldJson.attachment,随 C103-1 挂载)→ ③ C103-3 判重(§9-U4 查询能力联调确认)→
|
||||
* ④ C103-1 起草 / 变更单 C103-11 更新(§9-U5 定位方式待联调)→ ⑤ 高风险/黑名单自动特批 C103-8(外部客商管控,
|
||||
* {@link HcsContractSpecSyncSupport},挂起"特批审核"待合规推送结果)→ ⑥ 履约规则 C103-9 行级同步({@link HcsExeStageSyncSupport})。
|
||||
* ④ C103-1 起草 / 变更单 C103-11 更新(§9-U5 定位方式待联调)→ ⑤ 高风险/黑名单挂起"特批审核"
|
||||
* (外部客商管控:报文随 fdSepcailContract=true 在合规侧走特批,{@link HcsContractSpecSyncSupport} 接收结果)→
|
||||
* ⑥ 履约规则 C103-9 行级同步({@link HcsExeStageSyncSupport})。
|
||||
* 结果留痕:风险校验写 blade_hcs_check_log 与合同 hcs_risk_* 字段;推送结果回填 hcs_fd_id/hcs_sync_status。
|
||||
*
|
||||
* @author Chill
|
||||
@@ -103,7 +104,7 @@ public class HcsContractSyncSupport {
|
||||
|
||||
/**
|
||||
* 推送合同到 HCS:特批挂起门禁 → 风险校验(仅留痕,高风险不再阻断)→ 附件转传 → 判重 → 起草/更新 → 回填合规 fdId 置 synced
|
||||
* → 高风险/黑名单客商合同挂起"特批审核"并自动发起 C103-8 特批(外部客商管控)
|
||||
* → 高风险/黑名单客商合同挂起"特批审核"等待合规特批结果(报文随 fdSepcailContract=true,外部客商管控)
|
||||
*
|
||||
* @param contractManage 合同单(approvalStatus 须为 approved/change_approved)
|
||||
* @return 推送结果描述(供任务汇总与 hcs_sync_msg 落库)
|
||||
@@ -112,10 +113,11 @@ public class HcsContractSyncSupport {
|
||||
log.info("HCS合同推送开始:contractNo={},approvalStatus={},首推={},hcsSpecStatus={}",
|
||||
contractManage.getContractNo(), contractManage.getApprovalStatus(),
|
||||
StringUtil.isBlank(contractManage.getHcsFdId()), contractManage.getHcsSpecStatus());
|
||||
// ⓪ 特批挂起门禁:待特批/特批中的合同跳过推送,等待合规推送特批结果后恢复(C103-8,前端接入设计 §3.4)
|
||||
if (List.of(HcsSyncConstant.SPEC_STATUS_NEED, HcsSyncConstant.SPEC_STATUS_PROCESSING).contains(contractManage.getHcsSpecStatus())) {
|
||||
// ⓪ 特批挂起门禁:特批中(processing)的合同跳过推送,等待合规推送/定时查询特批结果后恢复;
|
||||
// need(存量待特批/历史卡单)不拦截——重推时随起草报文带 fdSepcailContract=true 自愈转特批
|
||||
if (HcsSyncConstant.SPEC_STATUS_PROCESSING.equals(contractManage.getHcsSpecStatus())) {
|
||||
log.info("HCS合同推送跳过(特批挂起中):contractNo={},hcsSpecStatus={}", contractManage.getContractNo(), contractManage.getHcsSpecStatus());
|
||||
return "合同特批挂起中(" + contractManage.getHcsSpecStatus() + "),跳过推送";
|
||||
return "合同特批挂起中(processing),跳过推送";
|
||||
}
|
||||
// ① 客商风险校验(可配开关):留痕 hcs_risk_* 字段并返回高风险/黑名单标志,不再阻断推送(高风险合同起草后自动转特批)
|
||||
boolean risky = false;
|
||||
@@ -132,20 +134,20 @@ public class HcsContractSyncSupport {
|
||||
markSynced(contractManage, existFdId, "判重命中,回填已有合规合同ID");
|
||||
resultMessage = "判重命中,回填已有合规合同ID";
|
||||
} else {
|
||||
String fdContractId = draftContract(contractManage, attachmentRefs);
|
||||
String fdContractId = draftContract(contractManage, attachmentRefs, risky);
|
||||
// 先持久化合规合同ID(调用方必须保存),履约规则同步失败时下轮凭其走更新补偿
|
||||
markSynced(contractManage, fdContractId, "C103-1 合同发起成功");
|
||||
resultMessage = "C103-1 合同发起成功";
|
||||
}
|
||||
} else {
|
||||
updateContract(contractManage);
|
||||
updateContract(contractManage, risky);
|
||||
markSynced(contractManage, contractManage.getHcsFdId(), "C103-11 合同更新成功");
|
||||
resultMessage = "C103-11 合同更新成功";
|
||||
}
|
||||
// ⑤ 高风险/黑名单客商(外部客商管控):C103-1 起草成功后挂起"特批审核"并自动发起 C103-8;
|
||||
// 特批已通过的合同不重复发起(单次有效),发起失败保持待特批可人工重发
|
||||
// ⑤ 高风险/黑名单客商(外部客商管控):起草/更新报文已随 fdSepcailContract=true 在合规侧发起特批流程,
|
||||
// 此处挂起"特批审核"等待结果(合规推送 contractLegal/result 或定时 C103-4 查询联动恢复/驳回)
|
||||
if (risky) {
|
||||
autoApplySpecIfNeeded(contractManage);
|
||||
hangSpecReview(contractManage);
|
||||
}
|
||||
// ⑥ 履约规则 C103-9 行级同步(随合同链路,§5.3;一期暂不对接默认关,打开后失败抛出由任务落 failed,下轮凭 hcs_fd_id 走更新补偿)
|
||||
if (!hcsProperties.getExeStage().isSyncEnabled()) {
|
||||
@@ -158,7 +160,7 @@ public class HcsContractSyncSupport {
|
||||
/**
|
||||
* C101-1 客商风险校验:甲乙双方各查一次,留痕 check_log 与合同 hcs_risk_* 字段;
|
||||
* 返回是否高风险/黑名单(风险等级=2 或任一方黑名单),不再阻断推送——
|
||||
* 推送成功后由自动特批链路挂起处理(外部客商管控,见 {@link #autoApplySpecIfNeeded})
|
||||
* 推送成功后由自动特批链路挂起处理(外部客商管控,见 {@link #})
|
||||
*
|
||||
* @return true=高风险或黑名单客商(触发自动特批)
|
||||
*/
|
||||
@@ -181,72 +183,26 @@ public class HcsContractSyncSupport {
|
||||
}
|
||||
|
||||
/**
|
||||
* 高风险/黑名单客商合同自动特批(C103-8,外部客商管控):C103-1 起草成功后置待特批并自动发起,
|
||||
* TMS 流程挂起展示"特批审核"(currentNode),合规推送特批结果(contractLegal/result)后恢复或驳回;
|
||||
* 特批已通过(单次有效)的合同不重复发起;发起失败保持待特批仅留痕,可人工重发或重新推送兜底
|
||||
* 高风险/黑名单客商合同挂起"特批审核":合规侧特批流程已随 C103-1/C103-11 报文 fdSepcailContract=true 发起
|
||||
* (C103-8 独立发起接口合规未提供,2026-10-13 联调确认),TMS 挂起展示"特批审核"(currentNode),
|
||||
* 结果经合规推送(contractLegal/result)或定时 C103-4 查询联动后恢复或驳回;特批已通过(单次有效)不再重复挂起
|
||||
*/
|
||||
private void autoApplySpecIfNeeded(ContractManage contractManage) {
|
||||
if (HcsSyncConstant.SPEC_STATUS_APPROVED.equals(contractManage.getHcsSpecStatus())) {
|
||||
log.info("HCS合同高风险/黑名单但特批已通过(单次有效),跳过自动特批:contractNo={},hcsSpecStatus={}",
|
||||
private void hangSpecReview(ContractManage contractManage) {
|
||||
// 终态守卫:合规回调(秒批/秒驳)先于挂起执行时,不得覆盖已落地的结果
|
||||
if (List.of(HcsSyncConstant.SPEC_STATUS_APPROVED, HcsSyncConstant.SPEC_STATUS_REJECTED).contains(contractManage.getHcsSpecStatus())) {
|
||||
log.info("HCS合同高风险/黑名单但特批已有终态(单次有效),跳过挂起:contractNo={},hcsSpecStatus={}",
|
||||
contractManage.getContractNo(), contractManage.getHcsSpecStatus());
|
||||
return;
|
||||
}
|
||||
contractManage.setHcsSpecStatus(HcsSyncConstant.SPEC_STATUS_NEED);
|
||||
contractManage.setHcsSpecStatus(HcsSyncConstant.SPEC_STATUS_PROCESSING);
|
||||
contractManage.setHcsSpecTime(LocalDateTime.now());
|
||||
contractManage.setHcsSpecMsg("高风险/黑名单客商,合同推送合规成功后自动发起特批(C103-8)");
|
||||
contractManage.setHcsSpecMsg("高风险/黑名单客商,合同报文随 fdSepcailContract=true 走合规特批流程");
|
||||
// TMS 流程挂起为"特批审核",特批通过恢复展示、驳回联动合同审批驳回
|
||||
contractManage.setCurrentNode(HcsSyncConstant.SPEC_REVIEW_NODE);
|
||||
contractManageMapper.updateById(contractManage);
|
||||
log.info("HCS合同高风险/黑名单触发自动特批(C103-8):contractNo={},riskLevel={},blackStatus={},挂起节点={}",
|
||||
log.info("HCS合同高风险/黑名单挂起特批审核:contractNo={},riskLevel={},blackStatus={},挂起节点={}",
|
||||
contractManage.getContractNo(), contractManage.getHcsRiskLevel(), contractManage.getHcsBlackStatus(),
|
||||
contractManage.getCurrentNode());
|
||||
try {
|
||||
List<HcsAttachmentRefDTO> attachments = transferSpecExplanationAttachments(contractManage);
|
||||
hcsContractSpecSyncSupport.applySpec(contractManage.getId(), attachments);
|
||||
} catch (Exception exception) {
|
||||
log.warn("HCS合同自动特批发起失败(保持待特批,可人工重发):contractNo={},{}",
|
||||
contractManage.getContractNo(), exception.getMessage(), exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 筛选合同「特批附件」类型附件(fileType=特批附件,业务字典 contract_attachment_type)并转传合规(C103-2),
|
||||
* 返回特批附件引用(fdEntityKey=eopCdraftSpecial);无特批说明附件返回空列表(提交时已强制校验,此处由 C103-8 防御性拦截)
|
||||
*/
|
||||
private List<HcsAttachmentRefDTO> transferSpecExplanationAttachments(ContractManage contractManage) {
|
||||
List<Map<?, ?>> specAttachments = new ArrayList<>();
|
||||
for (Object item : parseJsonArray(contractManage.getAttachmentsJson())) {
|
||||
if (item instanceof Map<?, ?> attachment
|
||||
&& HcsSyncConstant.ATTACHMENT_TYPE_SPEC_EXPLANATION.equals(attachment.get(HcsSyncConstant.ATTACHMENT_TYPE_FIELD))) {
|
||||
specAttachments.add(attachment);
|
||||
}
|
||||
}
|
||||
if (specAttachments.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
String specJson = JsonUtil.toJson(specAttachments);
|
||||
String loginName = hcsProperties.getOperatorLoginName();
|
||||
List<HcsAttachmentRefDTO> attachmentRefs = hcsAttachmentSupport.transferAttachments(
|
||||
HcsSyncConstant.BIZ_TYPE_CONTRACT, contractManage.getId(), specJson, loginName);
|
||||
for (int index = 0; index < attachmentRefs.size(); index++) {
|
||||
// 特批附件字段 key:与元数据行序一致
|
||||
attachmentRefs.get(index).setFdEntityKey(HcsSyncConstant.CONTRACT_ATTACH_KEY_SPECIAL);
|
||||
attachmentRefs.get(index).setFdFileName(resolveAttachmentName(specAttachments.get(index)));
|
||||
}
|
||||
return attachmentRefs;
|
||||
}
|
||||
|
||||
/**
|
||||
* 附件元数据文件名(name/originalName/fileName 依次取首个非空,照 HcsAttachmentSupport 惯例)
|
||||
*/
|
||||
private String resolveAttachmentName(Map<?, ?> attachment) {
|
||||
for (String field : List.of("name", "originalName", "fileName")) {
|
||||
Object value = attachment.get(field);
|
||||
if (value != null && !String.valueOf(value).isBlank()) {
|
||||
return String.valueOf(value);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -321,21 +277,21 @@ public class HcsContractSyncSupport {
|
||||
}
|
||||
|
||||
/**
|
||||
* C103-1 合同发起,返回合规合同 fdContractId
|
||||
* C103-1 合同发起,返回合规合同 fdContractId;risky=true 时报文带 fdSepcailContract=true 走合规特批流程
|
||||
*/
|
||||
private String draftContract(ContractManage contractManage, List<HcsAttachmentRefDTO> attachmentRefs) {
|
||||
HcsContractDraftDTO param = buildDraftParam(contractManage, attachmentRefs);
|
||||
log.info("HCS合同推送开始:{}(C103-1 发起,挂载附件 {} 个)", contractManage.getContractNo(), attachmentRefs.size());
|
||||
private String draftContract(ContractManage contractManage, List<HcsAttachmentRefDTO> attachmentRefs, boolean risky) {
|
||||
HcsContractDraftDTO param = buildDraftParam(contractManage, attachmentRefs, risky);
|
||||
log.info("HCS合同推送开始:{}(C103-1 发起,挂载附件 {} 个,特批流程={})", contractManage.getContractNo(), attachmentRefs.size(), risky);
|
||||
HcsResult<HcsContractDraftVO> result = hcsClient.contractDraft(param);
|
||||
return validateDraftResult(contractManage, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* C103-11 合同更新(入参同发起,v4 新增顶层 fdId 定位已有合同)
|
||||
* C103-11 合同更新(入参同发起,v4 新增顶层 fdId 定位已有合同);risky=true 时报文带 fdSepcailContract=true
|
||||
*/
|
||||
private void updateContract(ContractManage contractManage) {
|
||||
HcsContractDraftDTO param = buildDraftParam(contractManage, null);
|
||||
log.info("HCS合同推送开始:{}(C103-11 更新,附件不重复挂载待联调确认)", contractManage.getContractNo());
|
||||
private void updateContract(ContractManage contractManage, boolean risky) {
|
||||
HcsContractDraftDTO param = buildDraftParam(contractManage, null, risky);
|
||||
log.info("HCS合同推送开始:{}(C103-11 更新,附件不重复挂载待联调确认,特批流程={})", contractManage.getContractNo(), risky);
|
||||
HcsResult<Void> result = hcsClient.contractUpdate(param);
|
||||
if (result == null || !result.isSuccess()) {
|
||||
log.error("HCS合同更新失败响应:{},contractNo={}", result == null ? "无响应" : String.format("msg=%s, traceId=%s", result.getMsg(), result.getTraceId()), contractManage.getContractNo());
|
||||
@@ -347,7 +303,7 @@ public class HcsContractSyncSupport {
|
||||
* 组装 C103-1/C103-11 入参(字段标注照 §3.4 规范,映射表见设计文档 §5.2);
|
||||
* attachmentRefs 新建传合规附件引用列表(fdId+fdEntityKey,必填,无附件传空列表),更新传 null 不挂载
|
||||
*/
|
||||
private HcsContractDraftDTO buildDraftParam(ContractManage contractManage, List<HcsAttachmentRefDTO> attachmentRefs) {
|
||||
private HcsContractDraftDTO buildDraftParam(ContractManage contractManage, List<HcsAttachmentRefDTO> attachmentRefs, boolean risky) {
|
||||
HcsContractDraftDTO param = new HcsContractDraftDTO();
|
||||
// 【必填】合同ID:C103-11 更新顶层必填(传 C103-1 返回的 fdContractId),C103-1 新建不传(v4 新增)
|
||||
param.setFdId(contractManage.getHcsFdId());
|
||||
@@ -360,7 +316,7 @@ public class HcsContractSyncSupport {
|
||||
// 【必填】保存类型:submit(提交)
|
||||
param.setSaveType("submit");
|
||||
HcsContractFieldJsonDTO fieldJson = new HcsContractFieldJsonDTO();
|
||||
fieldJson.setXformFields(buildXformFields(contractManage));
|
||||
fieldJson.setXformFields(buildXformFields(contractManage, risky));
|
||||
// 相对方(签署方):甲方/乙方,编号按名称反查 CustomerArchive(§9-U6)
|
||||
fieldJson.setSigner(buildSigners(contractManage));
|
||||
// 【必填】附件数据:C103-1 必填(无附件传空列表),fdEntityKey=特批 eopCdraftSpecial/用印 eopCdraftSign/其他 eopCdraftOther,
|
||||
@@ -379,7 +335,7 @@ public class HcsContractSyncSupport {
|
||||
/**
|
||||
* xformFields 模板字段(字段名取 {@link HcsContractFieldConstant},中文名取自《桂物合同集成接口逻辑梳理》§5.1 合同字段说明)
|
||||
*/
|
||||
private List<HcsXformFieldDTO> buildXformFields(ContractManage contractManage) {
|
||||
private List<HcsXformFieldDTO> buildXformFields(ContractManage contractManage, boolean risky) {
|
||||
List<HcsXformFieldDTO> fields = new ArrayList<>();
|
||||
// 【必填】合同ID:更新时传合同发起返回的 fdContractId 的值(v4 合同字段说明)
|
||||
fields.add(field(HcsContractFieldConstant.FIELD_FD_ID, "合同ID", contractManage.getHcsFdId()));
|
||||
@@ -409,8 +365,9 @@ public class HcsContractSyncSupport {
|
||||
}
|
||||
// 结算模式:TMS 合同固定开口合同(open,v4 字段说明)
|
||||
fields.add(field(HcsContractFieldConstant.FIELD_FD_CONTRACT_GW_NATURE, "结算模式", HcsContractFieldConstant.CONTRACT_GW_NATURE_OPEN));
|
||||
// 特批流程:fdSepcailContract 固定 false(原文拼写笔误必须沿用,D15,配置项预留)
|
||||
fields.add(field(HcsContractFieldConstant.FIELD_FD_SEPCAIL_CONTRACT, "特批流程", hcsProperties.getContract().isSpecialContractEnabled() ? "true" : "false"));
|
||||
// 特批流程:高风险/黑名单客商报文带 true(合规侧随起草自动走特批流程,C103-8 独立接口未提供);
|
||||
// 配置项 contract.special-contract-enabled 可强制全量走特批(原文拼写笔误必须沿用,D15)
|
||||
fields.add(field(HcsContractFieldConstant.FIELD_FD_SEPCAIL_CONTRACT, "特批流程", risky || hcsProperties.getContract().isSpecialContractEnabled() ? "true" : "false"));
|
||||
// 合同类型:ContractManage.contractCategory
|
||||
fields.add(field(HcsContractFieldConstant.FIELD_CONTRACT_TYPE, "合同类型", contractManage.getContractCategory()));
|
||||
// 甲方名称/乙方名称:TMS 存名称字符串
|
||||
@@ -515,8 +472,8 @@ public class HcsContractSyncSupport {
|
||||
if (!List.of("approved", "change_approved").contains(contractManage.getApprovalStatus())) {
|
||||
throw new ServiceException("仅审批通过的合同可推送合规(当前状态:" + contractManage.getApprovalStatus() + ")");
|
||||
}
|
||||
if (List.of(HcsSyncConstant.SPEC_STATUS_NEED, HcsSyncConstant.SPEC_STATUS_PROCESSING).contains(contractManage.getHcsSpecStatus())) {
|
||||
throw new ServiceException("高风险客商合同需先完成特批(C103-8,当前特批状态:" + contractManage.getHcsSpecStatus() + ")");
|
||||
if (HcsSyncConstant.SPEC_STATUS_PROCESSING.equals(contractManage.getHcsSpecStatus())) {
|
||||
throw new ServiceException("合同特批审核中,禁止重推(等待合规特批结果后恢复)");
|
||||
}
|
||||
contractManage.setHcsSyncStatus(HcsSyncConstant.HCS_STATUS_UNSYNCED);
|
||||
contractManage.setHcsRetryCount(0);
|
||||
|
||||
+18
-18
@@ -33,45 +33,45 @@ import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 合同合规系统(HCS)C103-8 合同发起特批流程入参
|
||||
* 合同特批完成后合规系统回传 TMS 的结果报文(POST /hcs/contractLegal/result,iPaaS 网关 /gwzh/HTHG/contractLegal/result)
|
||||
* <p>
|
||||
* TODO 待联调确认(前端接入设计 §6-U1):C103-8 报文 v4 未提供,本 DTO 按 C106-3 结算付款特批的
|
||||
* formData/mechanisms 模式预估,字段名以合规方最新接口说明为准。
|
||||
* 报文为"合同同步结果"结构:合规特批完成后同步金蝶,回调携带金蝶同步结果;
|
||||
* 定位合同用 contractCode(ERP 合同编号),requestId 仅是 X-ERP-Request-Id 请求头原样回传、不能用于定位。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Data
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class HcsSpecContractSaveDTO implements Serializable {
|
||||
public class HcsContractLegalResultDTO implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 1L;
|
||||
/**
|
||||
* 特批流程ID(更新必填,发起不传)
|
||||
* 合同类型:deliverer=投送人;gc=钢厂;sett_center=结算中心
|
||||
*/
|
||||
private String fdId;
|
||||
private String contractType;
|
||||
/**
|
||||
* 【必填】操作人登录名(§9-U2)
|
||||
* ERP 本次同步使用的合同编号(TMS blade_contract_manage.contract_no,定位合同用)
|
||||
*/
|
||||
private String fdLoginName;
|
||||
private String contractCode;
|
||||
/**
|
||||
* 【必填】合规合同ID(blade_contract_manage.hcs_fd_id,非名称)
|
||||
* 本次合同同步请求头 X-ERP-Request-Id,原样回传(不用于定位)
|
||||
*/
|
||||
private String fdContractId;
|
||||
private String requestId;
|
||||
/**
|
||||
* 【必填】合同编号
|
||||
* 金蝶同步成功传 true,失败传 false
|
||||
*/
|
||||
private String fdContractNo;
|
||||
private Boolean success;
|
||||
/**
|
||||
* 【必填】单据主题:"合同特批-" + 合同编号
|
||||
* 金蝶同步失败原因;失败时建议填写
|
||||
*/
|
||||
private String fdDocSubject;
|
||||
private String reason;
|
||||
/**
|
||||
* 特批事由(合同名称/特批原因,非必传)
|
||||
* 金蝶实际返回的合同 ID;成功时用于保存留痕
|
||||
*/
|
||||
private String fdReason;
|
||||
private String kingdeeId;
|
||||
/**
|
||||
* 特批附件(C103-2 转传返回的合规附件ID 引用)
|
||||
* 来源系统标识,约定传 WCCK(最长 64 字符)
|
||||
*/
|
||||
private HcsMechanismsDTO mechanisms;
|
||||
private String sourceSystem;
|
||||
}
|
||||
+1
-16
@@ -55,7 +55,6 @@ import org.springblade.hcs.dto.HcsReceiptBillSaveDTO;
|
||||
import org.springblade.hcs.dto.HcsResult;
|
||||
import org.springblade.hcs.dto.HcsRiskQueryDTO;
|
||||
import org.springblade.hcs.dto.HcsRiskVO;
|
||||
import org.springblade.hcs.dto.HcsSpecContractSaveDTO;
|
||||
import org.springblade.hcs.dto.HcsSpecPaySaveDTO;
|
||||
import org.springblade.hcs.dto.HcsTemplateVO;
|
||||
import org.springframework.cloud.openfeign.FeignClient;
|
||||
@@ -79,7 +78,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@FeignClient(name = "HCS", url = "${thirdParty.hcs.baseUrl}", configuration = HcsFeignClientConfig.class, fallbackFactory = HcsClientFallbackFactory.class)
|
||||
@FeignClient(name = "HCS", url = "${thirdParty.hcs.baseUrl}", configuration = HcsFeignClientConfig.class, fallbackFactory = HcsClientFallbackFactory.class, primary = false)
|
||||
public interface IHcsClient {
|
||||
|
||||
String PROJECT_ADD_URL = "${thirdParty.hcs.projectAddUrl:/openapi/sys-modeling/apis/data/km-project1/V1/projectApproval/add}";
|
||||
@@ -99,7 +98,6 @@ public interface IHcsClient {
|
||||
String CREDIT_CHECK_URL = "${thirdParty.hcs.creditCheckUrl:/openapi/eop-cdraft/lcode/ext-cdraft/extEopCdraftMainOpen/contractCreditCheck}";
|
||||
String SPEC_PAY_ADD_URL = "${thirdParty.hcs.specPayAddUrl:/openapi/sys-modeling/lcode/ext-pay-receive/extPayReceiveOpenService/addSpecApplyPay}";
|
||||
String SPEC_PAY_UPDATE_URL = "${thirdParty.hcs.specPayUpdateUrl:/openapi/sys-modeling/lcode/ext-pay-receive/extPayReceiveOpenService/updateSpecApplyPay}";
|
||||
String SPEC_CONTRACT_ADD_URL = "${thirdParty.hcs.specContractAddUrl:/openapi/sys-modeling/lcode/ext-cdraft/extEopCdraftMainOpen/addSpecApplyContract}";
|
||||
String PERF_RECORD_ADD_URL = "${thirdParty.hcs.perfRecordAddUrl:/openapi/sys-modeling/apis/data/km-pay-receive/V1/dataSync/add}";
|
||||
String PERF_RECORD_UPDATE_URL = "${thirdParty.hcs.perfRecordUpdateUrl:/openapi/sys-modeling/apis/data/km-pay-receive/V1/dataSync/update}";
|
||||
String RECEIPT_BILL_ADD_URL = "${thirdParty.hcs.receiptBillAddUrl:/openapi/sys-modeling/lcode/ext-invoice-receive/extInvoiceReceiveOpenService/addReceiveBill}";
|
||||
@@ -267,19 +265,6 @@ public interface IHcsClient {
|
||||
@PostMapping(SPEC_PAY_UPDATE_URL)
|
||||
HcsResult<HcsResult<Void>> updateSpecApplyPay(@RequestBody HcsSpecPaySaveDTO param);
|
||||
|
||||
/**
|
||||
* C103-8 合同发起特批流程(高风险客商签约特批)
|
||||
* <p>
|
||||
* TODO 待联调确认(前端接入设计 §6-U1):C103-8 报文与路径均未在 v4 接口说明中定义,
|
||||
* 本端点按 C106-3 结算付款特批的报文结构预估(双层嵌套 data.data 承接返回),
|
||||
* 路径可经 thirdParty.hcs.specContractAddUrl 覆盖,字段以合规方最新接口说明为准。
|
||||
*
|
||||
* @param param 请求参数
|
||||
* @return data.data.{fdId, fdXFormId, docViewUrl}(fdId 为特批流程ID)
|
||||
*/
|
||||
@PostMapping(SPEC_CONTRACT_ADD_URL)
|
||||
HcsResult<HcsResult<HcsFormSaveVO>> addSpecContract(@RequestBody HcsSpecContractSaveDTO param);
|
||||
|
||||
/**
|
||||
* C108-1 履约记录新建
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user