From 75bbd829c7068450e475163c2b6aafaee53fee69 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com>
Date: Sun, 27 Sep 2026 22:54:45 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20HCS=E5=90=88=E5=90=8C=E5=90=8C=E6=AD=A5?=
=?UTF-8?q?=E6=94=AF=E6=92=91=EF=BC=88C101-1=E9=A3=8E=E9=99=A9=E6=A0=A1?=
=?UTF-8?q?=E9=AA=8C=E7=95=99=E7=97=95/C103-3=E5=88=A4=E9=87=8D/C103-2?=
=?UTF-8?q?=E9=99=84=E4=BB=B6=E8=BD=AC=E4=BC=A0/C103-1=E5=8F=91=E8=B5=B7?=
=?UTF-8?q?=E4=B8=8EC103-11=E6=9B=B4=E6=96=B0=EF=BC=89?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../support/hcs/HcsContractSyncSupport.java | 487 ++++++++++++++++++
1 file changed, 487 insertions(+)
create mode 100644 blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsContractSyncSupport.java
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsContractSyncSupport.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsContractSyncSupport.java
new file mode 100644
index 00000000..734f0e3d
--- /dev/null
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsContractSyncSupport.java
@@ -0,0 +1,487 @@
+/**
+ * BladeX Commercial License Agreement
+ * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
+ *
+ * Use of this software is governed by the Commercial License Agreement
+ * obtained after purchasing a license from BladeX.
+ *
+ * 1. This software is for development use only under a valid license
+ * from BladeX.
+ *
+ * 2. Redistribution of this software's source code to any third party
+ * without a commercial license is strictly prohibited.
+ *
+ * 3. Licensees may copyright their own code but cannot use segments
+ * from this software for such purposes. Copyright of this software
+ * remains with BladeX.
+ *
+ * Using this software signifies agreement to this License, and the software
+ * must not be used for illegal purposes.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is
+ * not liable for any claims arising from secondary or illegal development.
+ *
+ * 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.HcsContractDraftDTO;
+import org.springblade.hcs.dto.HcsContractDraftVO;
+import org.springblade.hcs.dto.HcsContractFieldJsonDTO;
+import org.springblade.hcs.dto.HcsContractVO;
+import org.springblade.hcs.dto.HcsPageQueryDTO;
+import org.springblade.hcs.dto.HcsPageVO;
+import org.springblade.hcs.dto.HcsResult;
+import org.springblade.hcs.dto.HcsRiskQueryDTO;
+import org.springblade.hcs.dto.HcsRiskVO;
+import org.springblade.hcs.dto.HcsSignerDTO;
+import org.springblade.hcs.dto.HcsUserRefVO;
+import org.springblade.hcs.dto.HcsXformFieldDTO;
+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.constant.HcsSyncConstant;
+import org.springblade.transport.mapper.ContractManageMapper;
+import org.springblade.transport.mapper.CustomerArchiveMapper;
+import org.springblade.transport.mapper.HcsContractCheckLogMapper;
+import org.springblade.transport.pojo.entity.ContractManage;
+import org.springblade.transport.pojo.entity.CustomerArchive;
+import org.springblade.transport.pojo.entity.HcsContractCheckLog;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * HCS 合同同步支撑组件(C103-1 发起 / C103-11 更新,设计文档 §5.2)
+ *
+ * 扫描任务按序执行:① 客商风险校验(可配开关,黑名单阻断)→ ② 附件转传(挂载点待 §9-U8 确认,先转传缓存)→
+ * ③ C103-3 判重(§9-U4 查询能力联调确认)→ ④ C103-1 起草 / 变更单 C103-11 更新(§9-U5 定位方式待联调)→
+ * ⑤ 履约规则 C103-9(P2 接线)。
+ * 结果留痕:风险校验写 blade_hcs_check_log 与合同 hcs_risk_* 字段;推送结果回填 hcs_fd_id/hcs_sync_status。
+ *
+ * @author Chill
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class HcsContractSyncSupport {
+
+ /**
+ * 合同金额等 xformFields 值一律字符串传值
+ */
+ private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+ /**
+ * C101-1 黑名单状态取值(示例 no 表示非黑名单,§9-U3 字典值联调确认)
+ */
+ private static final String BLACK_STATUS_YES = "yes";
+
+ private final IHcsClient hcsClient;
+ private final HcsProperties hcsProperties;
+ private final ContractManageMapper contractManageMapper;
+ private final CustomerArchiveMapper customerArchiveMapper;
+ private final HcsContractCheckLogMapper hcsContractCheckLogMapper;
+ private final HcsAttachmentSupport hcsAttachmentSupport;
+
+ /**
+ * 推送合同到 HCS:风险校验 → 附件转传 → 判重 → 起草/更新 → 回填合规 fdId 置 synced
+ *
+ * @param contractManage 合同单(approvalStatus 须为 approved/change_approved)
+ * @return 推送结果描述(供任务汇总与 hcs_sync_msg 落库)
+ */
+ public String pushContract(ContractManage contractManage) {
+ // ① 客商风险校验(可配开关),黑名单且 block-blacklist=true 时阻断推送
+ if (hcsProperties.getContract().isRequireRiskCheck()) {
+ checkRelativeRisk(contractManage);
+ }
+ // ② 附件转传(先转传并缓存合规附件ID,C103-1 挂载方式待 §9-U8 确认后接线)
+ transferAttachmentsIfEnabled(contractManage);
+ // ③④ 首推判重后起草(C103-1);已有合规ID(变更单)走更新(C103-11)
+ if (StringUtil.isBlank(contractManage.getHcsFdId())) {
+ String existFdId = findExistFdId(contractManage);
+ if (StringUtil.isNotBlank(existFdId)) {
+ markSynced(contractManage, existFdId, "判重命中,回填已有合规合同ID");
+ return "判重命中,回填已有合规合同ID";
+ }
+ String fdContractId = draftContract(contractManage);
+ // TODO 待联调(P2):履约规则 C103-9 行级同步随合同链路执行(§5.3,依赖 blade_contract_exe_stage 表)
+ markSynced(contractManage, fdContractId, "C103-1 合同发起成功");
+ return "C103-1 合同发起成功";
+ }
+ updateContract(contractManage);
+ markSynced(contractManage, contractManage.getHcsFdId(), "C103-11 合同更新成功");
+ return "C103-11 合同更新成功";
+ }
+
+ /**
+ * C101-1 客商风险校验:甲乙双方各查一次,留痕 check_log 与合同 hcs_risk_* 字段,黑名单阻断
+ */
+ private void checkRelativeRisk(ContractManage contractManage) {
+ RiskResult riskPartyA = queryRisk(contractManage, "甲方", contractManage.getPartyA());
+ RiskResult riskPartyB = queryRisk(contractManage, "乙方", contractManage.getPartyB());
+ // 风险等级:甲乙双方取先查到的非空值(无法量化比较,留痕双方原始值,联调后细化)
+ String riskLevel = StringUtil.isNotBlank(riskPartyA.riskLevel()) ? riskPartyA.riskLevel() : riskPartyB.riskLevel();
+ // 黑名单状态:任一方黑名单即为是
+ boolean blacklist = riskPartyA.blacklist() || riskPartyB.blacklist();
+ contractManage.setHcsRiskLevel(riskLevel);
+ contractManage.setHcsBlackStatus(blacklist ? BLACK_STATUS_YES : "no");
+ contractManage.setHcsRiskTime(LocalDateTime.now());
+ contractManageMapper.updateById(contractManage);
+ log.info("HCS合同风险校验完成:contractNo={},甲方[{}={}/{}],乙方[{}={}/{}]",
+ contractManage.getContractNo(), contractManage.getPartyA(), riskPartyA.blackStatus(), riskPartyA.riskLevel(),
+ contractManage.getPartyB(), riskPartyB.blackStatus(), riskPartyB.riskLevel());
+ if (blacklist && hcsProperties.getRisk().isBlockBlacklist()) {
+ String message = "客商黑名单,禁止推送(甲方[" + riskPartyA.blackStatus() + "]乙方[" + riskPartyB.blackStatus() + "])";
+ log.error("HCS合同推送被风险校验阻断:contractNo={},{}", contractManage.getContractNo(), message);
+ throw new HcsException(message);
+ }
+ }
+
+ /**
+ * 查询单方客商风险:按名称反查客商编号(§9-U6 编码体系联调确认),反查不到记 skip 留痕不阻断
+ */
+ private RiskResult queryRisk(ContractManage contractManage, String standing, String partyName) {
+ String customerCode = queryCustomerCode(partyName);
+ if (StringUtil.isBlank(customerCode)) {
+ writeCheckLog(contractManage, HcsSyncConstant.CHECK_RESULT_SKIP,
+ standing + "方[" + partyName + "]反查不到客商编号,跳过风险校验(§9-U6)", null);
+ return new RiskResult(null, null, false);
+ }
+ HcsRiskQueryDTO param = new HcsRiskQueryDTO();
+ // 【必填】客商编号:按甲乙方名称反查 CustomerArchive 客商编码(§9-U6)
+ param.setFdNumber(customerCode);
+ HcsResult result = hcsClient.getRelativeRisk(param);
+ if (result == null || !result.isSuccess()) {
+ log.error("HCS客商风险查询失败响应:{},contractNo={},standing={},fdNumber={}",
+ result == null ? "无响应" : result.getMsg(), contractManage.getContractNo(), standing, customerCode);
+ throw new HcsException("C101-1 客商风险查询失败:" + (result == null ? "响应为空" : result.getMsg()));
+ }
+ HcsRiskVO risk = result.getData();
+ String blackStatus = risk == null ? null : risk.getFdBlackStatus();
+ String riskLevel = risk == null ? null : risk.getFdRiskLevel();
+ boolean blacklist = BLACK_STATUS_YES.equalsIgnoreCase(blackStatus);
+ writeCheckLog(contractManage, blacklist ? HcsSyncConstant.CHECK_RESULT_FAIL : HcsSyncConstant.CHECK_RESULT_PASS,
+ standing + "方[" + partyName + "]黑名单=" + blackStatus + ",风险等级=" + riskLevel, result.getTraceId());
+ return new RiskResult(blackStatus, riskLevel, blacklist);
+ }
+
+ /**
+ * C103-3 合同列表判重(conditions={"fdNumber": contractNo},§9-U4 查询能力联调确认)
+ */
+ private String findExistFdId(ContractManage contractManage) {
+ HcsPageQueryDTO query = new HcsPageQueryDTO();
+ query.setColumns(List.of("fdId", "fdNumber"));
+ // 判重条件:合同编号精确匹配
+ query.setConditions(Map.of("fdNumber", Objects.toString(contractManage.getContractNo(), "")));
+ query.setOffset(0);
+ query.setPageSize(10);
+ HcsResult> result = hcsClient.contractList(query);
+ if (result == null || !result.isSuccess()) {
+ log.error("HCS合同判重查询失败响应:{},contractNo={}", result == null ? "无响应" : result.getMsg(), contractManage.getContractNo());
+ throw new HcsException("C103-3 合同判重查询失败:" + (result == null ? "响应为空" : result.getMsg()));
+ }
+ HcsPageVO page = result.getData();
+ if (page == null || page.getContent() == null || page.getContent().isEmpty()) {
+ return null;
+ }
+ return page.getContent().get(0).getFdId();
+ }
+
+ /**
+ * C103-1 合同发起,返回合规合同 fdContractId
+ */
+ private String draftContract(ContractManage contractManage) {
+ HcsContractDraftDTO param = buildDraftParam(contractManage);
+ log.info("HCS合同推送开始:{}(C103-1 发起)", contractManage.getContractNo());
+ HcsResult result = hcsClient.contractDraft(param);
+ return validateDraftResult(contractManage, result);
+ }
+
+ /**
+ * C103-11 合同更新(入参同发起;TODO 待联调(§9-U5):HCS 侧如何定位已有合同 v4 未说明)
+ */
+ private void updateContract(ContractManage contractManage) {
+ HcsContractDraftDTO param = buildDraftParam(contractManage);
+ log.info("HCS合同推送开始:{}(C103-11 更新,§9-U5 定位方式待联调)", contractManage.getContractNo());
+ HcsResult result = hcsClient.contractUpdate(param);
+ if (result == null || !result.isSuccess()) {
+ log.error("HCS合同更新失败响应:{},contractNo={}", result == null ? "无响应" : result.getMsg(), contractManage.getContractNo());
+ throw new HcsException("C103-11 合同更新失败:" + (result == null ? "响应为空" : result.getMsg()));
+ }
+ }
+
+ /**
+ * 组装 C103-1/C103-11 入参(字段标注照 §3.4 规范,映射表见设计文档 §5.2)
+ */
+ private HcsContractDraftDTO buildDraftParam(ContractManage contractManage) {
+ HcsContractDraftDTO param = new HcsContractDraftDTO();
+ // 【必填】合同模板ID:template-mapping[contractCategory](§9-U7 待合规方提供模板ID清单)
+ param.setFdTemplateId(requireTemplateId(contractManage));
+ // 【必填】外部系统标识:fd_sources,配置 thirdParty.hcs.src-system(D14)
+ param.setFdOuterSystem(hcsProperties.getSrcSystem());
+ // 【必填】操作人登录名:合同承办人登录名(§9-U2)
+ param.setFdLoginName(requireLoginName(contractManage));
+ // 【必填】保存类型:submit(提交)
+ param.setSaveType("submit");
+ HcsContractFieldJsonDTO fieldJson = new HcsContractFieldJsonDTO();
+ fieldJson.setXformFields(buildXformFields(contractManage));
+ // 相对方(签署方):甲方/乙方,编号按名称反查 CustomerArchive(§9-U6)
+ fieldJson.setSigner(buildSigners(contractManage));
+ param.setFdFieldJson(fieldJson);
+ return param;
+ }
+
+ /**
+ * xformFields 模板字段(字段名/中文名取自《桂物合同集成接口逻辑梳理》§5.1 合同字段说明)
+ */
+ private List buildXformFields(ContractManage contractManage) {
+ List fields = new ArrayList<>();
+ // 【必填】合同编号:ContractManage.contractNo
+ fields.add(field("fdNumber", "合同编号", contractManage.getContractNo()));
+ fields.add(field("CONTRACT_NO", "合同编号", contractManage.getContractNo()));
+ // 【必填】合同标题:ContractManage.contractName
+ fields.add(field("fdSubject", "合同标题", contractManage.getContractName()));
+ fields.add(field("CONTRACT_NAME", "合同名称", contractManage.getContractName()));
+ // 【必填】合同金额:contractAmount(字符串传值)
+ fields.add(field("fdAmount", "合同金额", toPlainString(contractManage.getContractAmount())));
+ fields.add(field("AMOUNT", "合同金额", toPlainString(contractManage.getContractAmount())));
+ // 币种:TMS 结算币种经 currency-name-mapping 映射(默认 RMB→人民币,照金蝶惯例)
+ fields.add(field("fdCurrencyId", "币种", mapCurrencyName(contractManage.getSettlementCurrency())));
+ fields.add(field("CURRENCY", "币种", mapCurrencyName(contractManage.getSettlementCurrency())));
+ // 合同结算类型:settlement-type-mapping[settlementMode](映射到"付款/收款"待联调,§9-U3,未配置不传)
+ String settlementType = hcsProperties.getContract().getSettlementTypeMapping().get(contractManage.getSettlementMode());
+ if (StringUtil.isNotBlank(settlementType)) {
+ fields.add(field("fdSettlementType", "合同结算类型", settlementType));
+ } else {
+ log.warn("HCS合同结算类型映射缺失:settlementMode=[{}] 不在 contract.settlement-type-mapping,fdSettlementType 不传(§9-U3)", contractManage.getSettlementMode());
+ }
+ // 特批流程:fdSepcailContract 固定 false(原文拼写笔误必须沿用,D15,配置项预留)
+ fields.add(field("fdSepcailContract", "特批流程", hcsProperties.getContract().isSpecialContractEnabled() ? "true" : "false"));
+ // 合同类型:ContractManage.contractCategory
+ fields.add(field("CONTRACT_TYPE", "合同类型", contractManage.getContractCategory()));
+ // 甲方名称/乙方名称:TMS 存名称字符串
+ fields.add(field("PARTY_A", "甲方名称", contractManage.getPartyA()));
+ fields.add(field("PARTY_B", "乙方名称", contractManage.getPartyB()));
+ // 【必填】签订日期:signDate(fdType=timestamp)
+ fields.add(timestampField("SIGN_DATE", "签订日期", contractManage.getSignDate()));
+ // 预计开始/结束时间:startDate/endDate(fdType=timestamp)
+ fields.add(timestampField("fdEstimateStartTime", "预计开始时间", contractManage.getStartDate()));
+ fields.add(timestampField("fdEstimateEndTime", "预计结束时间", contractManage.getEndDate()));
+ // 创建时间:createTime(fdType=timestamp)
+ fields.add(timestampField("CREATE_TIME", "创建时间", contractManage.getCreateTime()));
+ fields.add(timestampField("fdCreateTime", "创建时间", contractManage.getCreateTime()));
+ // 同步时间:推送时刻(fdType=timestamp)
+ fields.add(timestampField("fd_syn_time", "同步时间", LocalDateTime.now()));
+ // 承办人名称:handlerUserName
+ fields.add(field("CREATE_USER_NAME", "承办人名称", contractManage.getHandlerUserName()));
+ // TODO 待确认:合同状态 CONTRACT_STATUS,TMS 无直接对应,是否必传联调确认(§9-U3),一期不传
+ return fields;
+ }
+
+ /**
+ * signer 相对方(签署方):甲/乙双方,fdType(inner/外部)判定规则待联调(§9-U6)暂不传
+ */
+ private List buildSigners(ContractManage contractManage) {
+ List signers = new ArrayList<>();
+ // 甲方相对方:fdStanding=甲,编号按名称反查(反查不到留空,§9-U6)
+ signers.add(signer("甲", contractManage.getPartyA()));
+ // 乙方相对方:fdStanding=乙
+ signers.add(signer("乙", contractManage.getPartyB()));
+ return signers;
+ }
+
+ /**
+ * 单个相对方
+ */
+ private HcsSignerDTO signer(String fdStanding, String partyName) {
+ HcsSignerDTO signer = new HcsSignerDTO();
+ // 签署地位:甲 / 乙
+ signer.setFdStanding(fdStanding);
+ // 相对方名称:TMS 甲乙方名称
+ signer.setFdName(partyName);
+ // 相对方编号:按名称反查 CustomerArchive 客商编码,反查不到留空(§9-U6)
+ signer.setFdNumber(queryCustomerCode(partyName));
+ return signer;
+ }
+
+ /**
+ * 附件转传(可配开关):合同主文件 approved=true + 其它附件,合规附件ID 走映射缓存
+ */
+ private void transferAttachmentsIfEnabled(ContractManage contractManage) {
+ if (!hcsProperties.getAttachment().isTransferEnabled()) {
+ return;
+ }
+ List attachmentFdIds = hcsAttachmentSupport.transferContractAttachments(
+ contractManage.getContractFileJson(), contractManage.getAttachmentsJson(), requireLoginName(contractManage));
+ // TODO 待联调(§9-U8):C103-1 未定义附件字段,转传后的合规附件ID 暂存映射缓存,挂载方式确认后接线
+ log.info("HCS合同附件转传完成:contractNo={},共 {} 个附件(挂载点待 §9-U8 确认)", contractManage.getContractNo(), attachmentFdIds.size());
+ }
+
+ /**
+ * 校验 C103-1 返回并取合同 fdContractId(必须持久化)
+ */
+ private String validateDraftResult(ContractManage contractManage, HcsResult result) {
+ if (result == null || !result.isSuccess() || result.getData() == null || StringUtil.isBlank(result.getData().getFdContractId())) {
+ log.error("HCS合同发起失败响应:{},contractNo={}", result == null ? "无响应" : result.getMsg(), contractManage.getContractNo());
+ throw new HcsException("C103-1 合同发起失败:" + (result == null ? "响应为空" : result.getMsg()));
+ }
+ return result.getData().getFdContractId();
+ }
+
+ /**
+ * 回填合规 fdId 并置 synced
+ */
+ public void markSynced(ContractManage contractManage, String fdId, String message) {
+ contractManage.setHcsFdId(fdId);
+ contractManage.setHcsSyncStatus(HcsSyncConstant.HCS_STATUS_SYNCED);
+ contractManage.setHcsRetryCount(0);
+ contractManage.setHcsSyncTime(LocalDateTime.now());
+ contractManage.setHcsSyncMsg(message);
+ contractManageMapper.updateById(contractManage);
+ log.info("HCS合同推送成功:contractNo={},fdContractId={},{}", contractManage.getContractNo(), fdId, message);
+ }
+
+ /**
+ * 推送失败回写:failed + 重试次数+1 + 失败原因
+ */
+ public void markFailed(ContractManage contractManage, String message) {
+ contractManage.setHcsSyncStatus(HcsSyncConstant.HCS_STATUS_FAILED);
+ contractManage.setHcsRetryCount(contractManage.getHcsRetryCount() == null ? 1 : contractManage.getHcsRetryCount() + 1);
+ contractManage.setHcsSyncTime(LocalDateTime.now());
+ contractManage.setHcsSyncMsg(message);
+ contractManageMapper.updateById(contractManage);
+ log.error("HCS合同推送失败:contractNo={},retryCount={},{}", contractManage.getContractNo(), contractManage.getHcsRetryCount(), message);
+ }
+
+ /**
+ * 模板映射(template-mapping[contractCategory],未配置/无映射直接失败提示,§9-U7)
+ */
+ private String requireTemplateId(ContractManage contractManage) {
+ Map mapping = hcsProperties.getContract().getTemplateMapping();
+ String templateId = mapping.get(contractManage.getContractCategory());
+ if (StringUtil.isBlank(templateId)) {
+ throw new HcsException("HCS合同推送失败,合同类别 [" + contractManage.getContractCategory()
+ + "] 未配置合规模板ID(thirdParty.hcs.contract.template-mapping,§9-U7)");
+ }
+ return templateId;
+ }
+
+ /**
+ * 合同承办人登录名(fdLoginName 必填)
+ */
+ private String requireLoginName(ContractManage contractManage) {
+ if (contractManage.getHandlerUserId() == null) {
+ throw new HcsException("HCS合同推送失败,合同承办人未维护(contractNo=" + contractManage.getContractNo() + ")");
+ }
+ User user = UserCache.getUser(contractManage.getHandlerUserId());
+ if (user == null || StringUtil.isBlank(user.getAccount())) {
+ throw new HcsException("HCS合同推送失败,承办人登录名解析不到(handlerUserId=" + contractManage.getHandlerUserId() + ",§9-U2)");
+ }
+ return user.getAccount();
+ }
+
+ /**
+ * 按名称反查客商编码(照 KingdeeApBillSyncSupport#queryCustomerCode 惯例,查不到返回 null)
+ */
+ private String queryCustomerCode(String partyName) {
+ if (StringUtil.isBlank(partyName)) {
+ return null;
+ }
+ CustomerArchive customerArchive = customerArchiveMapper.selectOne(Wrappers.lambdaQuery()
+ .and(wrapper -> wrapper.eq(CustomerArchive::getFullName, partyName).or().eq(CustomerArchive::getShortName, partyName))
+ .eq(CustomerArchive::getIsDeleted, 0)
+ .last("limit 1"));
+ return customerArchive == null ? null : customerArchive.getCustomerCode();
+ }
+
+ /**
+ * 币种名称映射:currency-name-mapping 命中取映射值,未命中传原值并告警(§9-U3)
+ */
+ private String mapCurrencyName(String currency) {
+ if (StringUtil.isBlank(currency)) {
+ return null;
+ }
+ return hcsProperties.getContract().getCurrencyNameMapping().getOrDefault(currency, currency);
+ }
+
+ /**
+ * 普通模板字段(字符串传值)
+ */
+ private HcsXformFieldDTO field(String name, String label, String value) {
+ HcsXformFieldDTO field = new HcsXformFieldDTO();
+ field.setName(name);
+ field.setLabel(label);
+ field.setValue(value);
+ return field;
+ }
+
+ /**
+ * 时间模板字段(fdType=timestamp,日期传 yyyy-MM-dd,时间传 yyyy-MM-dd HH:mm:ss)
+ */
+ private HcsXformFieldDTO timestampField(String name, String label, Object value) {
+ if (value == null) {
+ return field(name, label, null);
+ }
+ String text;
+ if (value instanceof LocalDate localDate) {
+ text = localDate.format(DATE_FORMATTER);
+ } else if (value instanceof LocalDateTime localDateTime) {
+ text = localDateTime.format(DATETIME_FORMATTER);
+ } else {
+ // BaseEntity.createTime 为 Date 类型
+ text = DATETIME_FORMATTER.format(((java.util.Date) value).toInstant()
+ .atZone(java.time.ZoneId.systemDefault()).toLocalDateTime());
+ }
+ HcsXformFieldDTO field = field(name, label, text);
+ // 时间字段需带 fdType=timestamp
+ field.setFdType("timestamp");
+ return field;
+ }
+
+ /**
+ * 金额字符串(空值安全)
+ */
+ private String toPlainString(BigDecimal amount) {
+ return amount == null ? null : amount.toPlainString();
+ }
+
+ /**
+ * 风险校验留痕(blade_hcs_check_log,异常不影响主流程)
+ */
+ private void writeCheckLog(ContractManage contractManage, String result, String msg, String traceId) {
+ try {
+ HcsContractCheckLog checkLog = new HcsContractCheckLog();
+ checkLog.setTenantId(contractManage.getTenantId());
+ checkLog.setBizType(HcsSyncConstant.BIZ_TYPE_CONTRACT);
+ checkLog.setBizId(contractManage.getId());
+ checkLog.setCheckType(HcsSyncConstant.CHECK_TYPE_RISK);
+ checkLog.setResult(result);
+ checkLog.setMsg(msg);
+ checkLog.setTraceId(traceId);
+ checkLog.setCreateTime(LocalDateTime.now());
+ hcsContractCheckLogMapper.insert(checkLog);
+ } catch (Exception exception) {
+ log.error("HCS风险校验留痕写入失败:contractNo={}", contractManage.getContractNo(), exception);
+ }
+ }
+
+ /**
+ * 单方风险查询结果
+ */
+ private record RiskResult(String blackStatus, String riskLevel, boolean blacklist) {
+ }
+}