From 2b838bb7f81ff19049bd45f1fa34dd028148675f Mon Sep 17 00:00:00 2001 From: daxpay Date: Wed, 5 Aug 2026 11:56:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(trade):=20pay=5Ftrade=20=E5=86=97=E4=BD=99?= =?UTF-8?q?=E6=94=AF=E4=BB=98=E9=80=9A=E9=81=93(channel)=E5=AD=97=E6=AE=B5?= =?UTF-8?q?,=20=E6=8C=89=E6=8E=A5=E5=85=A5=E9=80=9A=E9=81=93=E7=BB=B4?= =?UTF-8?q?=E5=BA=A6=E7=9C=8B=E8=B5=84=E9=87=91=E5=85=8DJOIN?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PayTrade 新增 channel 字段(冗余自容器 product→channel, 创建即终值) - 下单链路写入 channel(PayTradeInitUtil/NormalPayAssistService/GatewayPayHandleService) - PayTradeQuery 开放 channel 筛选 - table.sql 建表加列, update-tables.sql 升级脚本+存量回填 - 与 TransferTrade 的 channel+provider 双冗余口径对齐 --- _config/sql/table.sql | 2 ++ _config/sql/update-tables.sql | 22 +++++++++++++++++++ .../payment/trade/order/entity/PayTrade.java | 11 +++++++--- .../trade/order/param/PayTradeQuery.java | 8 ++++++- .../pay/gateway/GatewayPayHandleService.java | 5 +++-- .../pay/normal/NormalPayAssistService.java | 5 +++-- .../payment/trade/util/PayTradeInitUtil.java | 10 ++++++--- 7 files changed, 52 insertions(+), 11 deletions(-) diff --git a/_config/sql/table.sql b/_config/sql/table.sql index d2d3d4401..cb9710e28 100644 --- a/_config/sql/table.sql +++ b/_config/sql/table.sql @@ -3266,6 +3266,7 @@ CREATE TABLE "public"."pay_trade" ( "version" int4 NOT NULL DEFAULT 0, "deleted" bool NOT NULL DEFAULT false, "provider" varchar(32) COLLATE "pg_catalog"."default", + "channel" varchar(32) COLLATE "pg_catalog"."default", "title" varchar(255) COLLATE "pg_catalog"."default" ) ; @@ -3294,6 +3295,7 @@ COMMENT ON COLUMN "public"."pay_trade"."last_modified_time" IS '最后修改时 COMMENT ON COLUMN "public"."pay_trade"."version" IS '乐观锁版本'; COMMENT ON COLUMN "public"."pay_trade"."deleted" IS '逻辑删除'; COMMENT ON COLUMN "public"."pay_trade"."provider" IS '支付渠道(冗余自容器, 支付成功sync后回填; 权威在容器 provider)'; +COMMENT ON COLUMN "public"."pay_trade"."channel" IS '支付通道(冗余自容器 product→channel; 创建即终值; 权威在容器 channel)'; COMMENT ON COLUMN "public"."pay_trade"."title" IS '订单标题'; COMMENT ON TABLE "public"."pay_trade" IS '资金交易凭证'; diff --git a/_config/sql/update-tables.sql b/_config/sql/update-tables.sql index 4ab2ee3f4..f0a00f3c0 100644 --- a/_config/sql/update-tables.sql +++ b/_config/sql/update-tables.sql @@ -17,3 +17,25 @@ CREATE UNIQUE INDEX IF NOT EXISTS "uk_adapay_direct_key_config_sandbox" ON "publ "sandbox" "pg_catalog"."bool_ops" ASC NULLS LAST ) WHERE deleted = false; COMMENT ON INDEX "public"."uk_adapay_direct_key_config_sandbox" IS '同一通道商户同一环境密钥唯一'; + +-- ===== pay_trade 增加支付通道冗余字段 ===== +-- 冗余自容器 product→channel, 用于按接入通道维度看资金/报表免 JOIN; 与 pay_transfer_trade 口径对齐 +ALTER TABLE "public"."pay_trade" ADD COLUMN "channel" varchar(32) COLLATE "pg_catalog"."default"; +COMMENT ON COLUMN "public"."pay_trade"."channel" IS '支付通道(冗余自容器 product→channel; 创建即终值; 权威在容器 channel)'; + +-- 存量数据回填: 从业务容器反向填充 channel +-- 普通支付容器(normal) +UPDATE "public"."pay_trade" t +SET "channel" = c."channel" +FROM "public"."pay_normal_order" c +WHERE t."container_id" = c."id" + AND t."trade_type" = 'normal' + AND t."channel" IS NULL; + +-- 网关支付容器(gateway) +UPDATE "public"."pay_trade" t +SET "channel" = c."channel" +FROM "public"."pay_gateway_order" c +WHERE t."container_id" = c."id" + AND t."trade_type" = 'gateway' + AND t."channel" IS NULL; diff --git a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/order/entity/PayTrade.java b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/order/entity/PayTrade.java index 48252d505..eda8cdc94 100644 --- a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/order/entity/PayTrade.java +++ b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/order/entity/PayTrade.java @@ -17,11 +17,12 @@ import java.time.OffsetDateTime; /// 统一资金交易表,记录每一笔资金动作(普通支付/预授权冻结/预授权捕获/周期代扣/合单子单) /// 与容器层(业务单/协议)分离,通过 trade_type + 容器关联字段建立联系。 /// 保留资金动作固有属性、通道反查命脉字段,以及 **轻量组织冗余** -///(source / channelMchNo / storeNo / provider),便于资金列表与汇总免 JOIN 容器; +///(source / channel / channelMchNo / storeNo / provider),便于资金列表与汇总免 JOIN 容器; /// 完整业务上下文/路由细节/payBody/回执仍以容器为准。 /// 过期时间只在容器,本表不存 expiredTime。 -/// 注意: 本表不冗余 channel(支付通道), 通道反查用 channelMchNo; -/// 渠道分布报表/资金列表筛选走 provider(支付渠道)。 +/// channel(接入通道, B端机构维度) 与 provider(支付渠道, C端钱包维度) 是正交两个维度: +/// channel 用于按接入通道(拉卡拉/银联商务等聚合)维度看资金, provider 用于按付款钱包(微信/支付宝等)维度看资金; +/// 与 [cn.daxpay.open.payment.trade.transfer.entity.TransferTrade] 的 channel+provider 双冗余口径对齐。 @Data @EqualsAndHashCode(callSuper = true) @Accessors(chain = true) @@ -78,6 +79,10 @@ public class PayTrade extends MchBaseEntity { /// @see cn.daxpay.open.platform.core.enums.pay.channel.PayProviderEnum private String provider; + /// 支付通道(冗余自容器 product→[ChannelEnum]; 创建即终值, 与 provider 不同无需成功路径兜底) + /// @see cn.daxpay.open.platform.core.enums.pay.channel.ChannelEnum + private String channel; + /// 门店号(冗余自业务容器, 可空; 权威在容器 storeNo) private String storeNo; diff --git a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/order/param/PayTradeQuery.java b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/order/param/PayTradeQuery.java index 8e3d85d38..c51594e53 100644 --- a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/order/param/PayTradeQuery.java +++ b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/order/param/PayTradeQuery.java @@ -46,7 +46,13 @@ public class PayTradeQuery { @Schema(description = "交易形态") private String tradeType; - // 注意: pay_trade 表无 channel/method/product 列,禁止在此加无效筛选 + /// 支付通道 + /// @see cn.daxpay.open.platform.core.enums.pay.channel.ChannelEnum + @QueryParam(type = QueryParam.CompareTypeEnum.EQ) + @Schema(description = "支付通道") + private String channel; + + // 注意: pay_trade 表无 method/product 列,禁止在此加无效筛选 /// 通道商户号 @QueryParam(type = QueryParam.CompareTypeEnum.EQ) diff --git a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/pay/gateway/GatewayPayHandleService.java b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/pay/gateway/GatewayPayHandleService.java index 1aec1a7ab..6cce9ff06 100644 --- a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/pay/gateway/GatewayPayHandleService.java +++ b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/pay/gateway/GatewayPayHandleService.java @@ -166,7 +166,7 @@ public class GatewayPayHandleService { } // 默认上送网关业务单号; 特殊通道返回后可覆盖 - // channelMchNo 取自路由后的 payParam; storeNo 冗余自预下单容器; provider 由 method 派生 + // channelMchNo 取自路由后的 payParam; storeNo 冗余自预下单容器; provider 由 method 派生; channel 由 product 派生 String provider = PayTradeProviderUtil.resolveProviderByMethod(payParam.getMethod()); PayTrade trade = PayTradeInitUtil.initProcessing( order.getAppId(), @@ -180,7 +180,8 @@ public class GatewayPayHandleService { payParam.getChannelMchNo(), order.getStoreNo(), order.getTitle(), - provider); + provider, + channel); payTradeManager.save(trade); this.fillRouteOnOrder(order, payParam, clientEnv, device); diff --git a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/pay/normal/NormalPayAssistService.java b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/pay/normal/NormalPayAssistService.java index 8bf638d30..3ac5fa840 100644 --- a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/pay/normal/NormalPayAssistService.java +++ b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/runtime/service/pay/normal/NormalPayAssistService.java @@ -142,7 +142,7 @@ public class NormalPayAssistService { } /// 组装资金交易(未落库); relationOrderNo 默认=orderNo, 特殊通道支付返回后可覆盖 - /// channelMchNo / storeNo / provider 冗余自容器,资金列表与渠道报表免 JOIN + /// channel / channelMchNo / storeNo / provider 冗余自容器,资金列表与通道/渠道报表免 JOIN private PayTrade buildPayTrade(NormalPayParam payParam, NormalPayOrder normalOrder, String tradeNo, String orderNo, String source) { return PayTradeInitUtil.initProcessing( @@ -157,7 +157,8 @@ public class NormalPayAssistService { normalOrder.getChannelMchNo(), normalOrder.getStoreNo(), normalOrder.getTitle(), - normalOrder.getProvider()); + normalOrder.getProvider(), + normalOrder.getChannel()); } /// 注册超时关单延时消息 diff --git a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/util/PayTradeInitUtil.java b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/util/PayTradeInitUtil.java index 15079d5ac..c1a5c59ef 100644 --- a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/util/PayTradeInitUtil.java +++ b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/util/PayTradeInitUtil.java @@ -7,7 +7,7 @@ import cn.daxpay.open.platform.core.enums.pay.channel.CurrencyEnum; /// # 资金交易初始化工具 /// /// PROCESSING 态资金凭证公共字段的单一事实源。 -/// 调用方负责 tradeType / container / source / channelMchNo / storeNo / provider 等业务差异,本工具只填 +/// 调用方负责 tradeType / container / source / channel / channelMchNo / storeNo / provider 等业务差异,本工具只填 /// 「待处理结算类交易」公共规则: /// - currency = 传入币种(可空, 缺省 cny) /// - postedAmount = 0(成功后由 [cn.daxpay.open.payment.trade.runtime.service.pay.common.PayUniHandleService] @@ -33,6 +33,7 @@ public final class PayTradeInitUtil { /// @param storeNo 门店号(冗余自容器,可空) /// @param title 订单标题(冗余自容器,资金列表/工作台免JOIN) /// @param provider 支付渠道(由 method 派生,可空;权威亦可在容器 provider) + /// @param channel 支付通道(由 product→channel 派生,创建即终值;权威在容器 channel) public static PayTrade initProcessing( String appId, String tradeNo, @@ -45,7 +46,8 @@ public final class PayTradeInitUtil { String channelMchNo, String storeNo, String title, - String provider) { + String provider, + String channel) { // 币种: 显式透传优先, 缺省 cny(向后兼容国内通道) String resolvedCurrency = (currency == null || currency.isBlank()) ? CurrencyEnum.CNY.getCode() : currency; @@ -67,6 +69,8 @@ public final class PayTradeInitUtil { .setChannelMchNo(channelMchNo) .setStoreNo(storeNo) // 支付渠道: 下单时由 method 派生, 渠道分布报表免 JOIN - .setProvider(provider); + .setProvider(provider) + // 支付通道: 由 product 派生, 创建即终值, 按接入通道维度看资金免 JOIN + .setChannel(channel); } }