diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PaymentApplication.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PaymentApplication.java index e4021397..b5b00417 100644 --- a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PaymentApplication.java +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/entity/PaymentApplication.java @@ -141,6 +141,8 @@ public class PaymentApplication extends TenantEntity { private String hcsSpecFdId; /** 特批状态:none/need/pushing/processing/approved/rejected */ private String hcsSpecStatus; + /** 合规付款特批结果附件ID(paymentLegal/result 回调 fileUrl,C103-5 下载入参、金蝶附件联带用) */ + private String hcsSpecAttachFdId; /** 履约记录ID(C108-1 返回,C108-2 更新必传) */ private String hcsPerfFdId; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/HcsSyncConstant.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/HcsSyncConstant.java index 42eaaf09..5585ea46 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/HcsSyncConstant.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/constant/HcsSyncConstant.java @@ -190,6 +190,11 @@ public final class HcsSyncConstant { */ public static final String PAYMENT_ATTACH_TYPE_SPECIAL = "special_approval"; + /** + * 付款申请附件类型值:合规特批结果附件(paymentLegal/result 回传展示行标记,详情展示用,不参与特批附件提交校验) + */ + public static final String PAYMENT_ATTACH_TYPE_SPEC_RESULT = "spec_result"; + /** * 附件类型:用印附件(双章归档文件,取值沿用 ContractManageServiceImpl#FILE_TYPE_SEAL_ARCHIVE) */ diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/HcsSyncController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/HcsSyncController.java index e192d772..2526f7dc 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/HcsSyncController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/HcsSyncController.java @@ -307,7 +307,7 @@ public class HcsSyncController { *
* 免登免授权同合同接口(blade.secure 放行);
* iPaaS 网关注册映射:/gwzh/HTHG/paymentLegal/result → /hcs/paymentLegal/result;
- * 报文 {sourceSystem, requestId=TMS付款申请ID, status=APPROVED/REJECTED, reason},
+ * 报文 {sourceSystem, requestId=合规特批流程ID(hcs_spec_fd_id), status=APPROVED/REJECTED, reason},
* approved 恢复"审批通过"放行,rejected 联动付款审批驳回走重新提交闭环。
*/
@PostMapping("/paymentLegal/result")
diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java
index 98777d55..4f957049 100644
--- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java
+++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java
@@ -116,6 +116,8 @@ import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.math.RoundingMode;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@@ -261,6 +263,8 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl
附件来源两类:用户附件(attachmentsJson 数组项,按 url 下载,幂等键=url)与 + *
附件来源三类:用户附件(attachmentsJson 数组项,按 url 下载,幂等键=url,仅接受 http(s) 绝对地址)、 * 票池镜像发票文件(attachmentsJson 对象 {source,path,name},经 A801-2 下载回传,幂等键=path, - * path 变化(红冲重开)视为新文件自动触发重传)。
+ * path 变化(红冲重开)视为新文件自动触发重传)与 + * HCS 合规附件(付款特批结果附件等,按合规附件ID 经 C103-5 下载,幂等键=fdId)。 * * @author Chill */ @@ -80,6 +82,7 @@ public class KingdeeAttachmentSyncSupport { private final IBfiService bfiService; private final KingdeeAttachmentSyncMapper attachmentSyncMapper; + private final HcsAttachmentSupport hcsAttachmentSupport; /** 附件下载客户端(无状态,进程内复用) */ private final HttpClient downloadClient = HttpClient.newBuilder() @@ -91,11 +94,30 @@ public class KingdeeAttachmentSyncSupport { * 待同步附件项 * * @param name 附件名称(含扩展名,展示/金蝶侧文件名) - * @param key 幂等键:用户附件=附件 url;发票文件=A801-2 的 path + * @param key 幂等键:用户附件=附件 url;发票文件=A801-2 的 path;合规附件=合规附件ID fdId * @param size 附件大小(字节,可空:空时下载后按实际字节校验) * @param kingdeePathSource true 表示 key 为星瀚侧文件 path(经 A801-2 下载);false 表示 http(s) 附件 url + * @param hcsFdIdSource true 表示 key 为合规附件ID fdId(经 C103-5 下载) */ - public record KingdeeAttachmentItem(String name, String key, Long size, boolean kingdeePathSource) { + public record KingdeeAttachmentItem(String name, String key, Long size, boolean kingdeePathSource, boolean hcsFdIdSource) { + + /** + * 兼容既有四参构造(默认非合规附件来源) + */ + public KingdeeAttachmentItem(String name, String key, Long size, boolean kingdeePathSource) { + this(name, key, size, kingdeePathSource, false); + } + + /** + * HCS 合规附件项(C103-5 按合规附件ID 下载) + * + * @param name 附件名称 + * @param fdId 合规附件ID + * @return 附件项 + */ + public static KingdeeAttachmentItem hcsAttachment(String name, String fdId) { + return new KingdeeAttachmentItem(name, fdId, null, false, true); + } } /** @@ -225,7 +247,8 @@ public class KingdeeAttachmentSyncSupport { continue; } String url = firstText(map, "url", "link", "fileUrl", "downloadUrl", "domain", "src"); - if (Func.isBlank(url)) { + // 仅接受 http(s) 绝对地址:相对/代理路径(如合规特批附件展示行)无法直下,跳过防下载必败 + if (Func.isBlank(url) || !(url.startsWith("http://") || url.startsWith("https://"))) { continue; } String name = firstText(map, "originalName", "name", "fileName"); @@ -362,10 +385,18 @@ public class KingdeeAttachmentSyncSupport { } /** - * 拉取附件内容:星瀚侧文件经 A801-2 下载(流式读取,防大文件全量进内存失控); - * 用户附件按 http(s) url 下载(流式读取)。调用方负责按 20MB 上限校验 + * 拉取附件内容:合规附件经 C103-5 按合规附件ID 下载(流式读取,防大文件全量进内存失控); + * 星瀚侧文件经 A801-2 下载(流式读取);用户附件按 http(s) url 下载(流式读取)。调用方负责按 20MB 上限校验 */ private byte[] fetchContent(KingdeeAttachmentItem item) throws Exception { + if (item.hcsFdIdSource()) { + Response response = hcsAttachmentSupport.downloadRaw(item.key()); + try (InputStream inputStream = response.body().asInputStream()) { + return readCapped(inputStream); + } finally { + response.close(); + } + } if (item.kingdeePathSource()) { Response response = bfiService.downloadInvoice(item.key()); try (InputStream inputStream = response.body().asInputStream()) { diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsPaymentSyncSupport.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsPaymentSyncSupport.java index 46059777..3bb56b68 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsPaymentSyncSupport.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/hcs/HcsPaymentSyncSupport.java @@ -26,6 +26,7 @@ package org.springblade.transport.support.hcs; import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springblade.core.log.exception.ServiceException; @@ -219,7 +220,7 @@ public class HcsPaymentSyncSupport { /** * 付款特批结果接收处理(合规 paymentLegal/result 回调通知特批审批结果): * sourceSystem 须与 third-party.hcs.src-system 配置一致(不符抛出"来源系统标识错误"异常), - * requestId=TMS 付款申请ID、status=APPROVED/REJECTED; + * requestId=合规特批流程ID(hcs_spec_fd_id,C106-3 回存值)、status=APPROVED/REJECTED; * 特批结果联动付款审批状态:approved 恢复"审批通过"放行(后续金蝶推送放行), * rejected 联动付款审批驳回(returned),走"重新编辑→提交→审批→重新特批"闭环 * @@ -250,6 +251,11 @@ public class HcsPaymentSyncSupport { } syncPaymentOnSpecResult(payment, HcsSyncConstant.SPEC_STATUS_APPROVED.equals(status)); payment.setHcsSpecStatus(status); + // 特批通过回传特批结果附件(fileUrl 值=合规附件ID):落库供 C103-5 下载与金蝶附件联带; + // 回调无附件时保留旧值(shortcut: 驳回重批期间旧附件仍展示,重批回调会覆盖,金蝶侧特批中门禁不会误传) + if (HcsSyncConstant.SPEC_STATUS_APPROVED.equals(status) && StringUtil.isNotBlank(param.getFileUrl())) { + payment.setHcsSpecAttachFdId(param.getFileUrl().trim()); + } paymentApplicationMapper.updateById(payment); // 特批通过后补推履约计划与履约记录(驳回不产生履约数据),失败不阻断后续金蝶补推 if (HcsSyncConstant.SPEC_STATUS_APPROVED.equals(status)) { @@ -288,17 +294,16 @@ public class HcsPaymentSyncSupport { } /** - * 按 TMS 付款申请ID 定位付款申请(合规推送 requestId) + * 按合规特批流程ID定位付款申请(合规推送 requestId=hcs_spec_fd_id,即 C106-3 返回并回存的流程 fdId; + * 与合同链路 locateContractByFdId 同口径,C106-3 发起报文未传 TMS 付款申请ID,合规无法回传数字ID) */ private PaymentApplication locatePayment(String requestId) { if (StringUtil.isBlank(requestId)) { return null; } - try { - return paymentApplicationMapper.selectById(Long.valueOf(requestId.trim())); - } catch (NumberFormatException exception) { - return null; - } + return paymentApplicationMapper.selectOne(Wrappers.