From 69c16d9799df316fd5ece3396a08fca7baf51baf Mon Sep 17 00:00:00 2001 From: DaxPay Dev Date: Mon, 20 Jul 2026 09:10:48 +0800 Subject: [PATCH] =?UTF-8?q?feat(payment):=20pay=5Ftrade=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=20title=20=E5=86=97=E4=BD=99=E5=88=97,=20=E8=B5=84?= =?UTF-8?q?=E9=87=91=E5=88=97=E8=A1=A8/=E5=B7=A5=E4=BD=9C=E5=8F=B0?= =?UTF-8?q?=E5=85=8DJOIN=E5=AE=B9=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - PayTrade 实体新增 title 字段(冗余自容器, 与 source/channelMchNo 同性质) - PayTradeInitUtil.initProcessing 加 title 入参, normal/gateway 两处调用传入 - TradeOrderDetailAssembler 详情不再用容器覆盖 title, 列表/详情统一用 trade.title - PayTradeResult.title 从容器联表段迁移至凭证字段段 --- _config/sql/update-tables.sql | 80 +------------------ .../trade/TradeOrderDetailAssembler.java | 4 +- .../payment/trade/order/entity/PayTrade.java | 3 + .../trade/order/result/PayTradeResult.java | 8 +- .../pay/gateway/GatewayPayHandleService.java | 3 +- .../pay/normal/NormalPayAssistService.java | 3 +- .../payment/trade/util/PayTradeInitUtil.java | 5 +- 7 files changed, 20 insertions(+), 86 deletions(-) diff --git a/_config/sql/update-tables.sql b/_config/sql/update-tables.sql index 93655095e..d3855659b 100644 --- a/_config/sql/update-tables.sql +++ b/_config/sql/update-tables.sql @@ -1,77 +1,3 @@ --- ============================================================ --- 门店/应用默认项唯一性约束(升级脚本) --- ============================================================ --- 背景: --- 商户应用(MchAppInfo)和门店(MchStoreInfo)均引入「默认项」机制, --- 要求同一商户下至多一个默认应用 / 至多一个默认门店。 --- 原有约束仅由 Service 层 setDefault(先 clearDefault 再 set)保证, --- 并发或绕过 Service 直接改库时存在隐患。 --- 策略: --- 数据库层加 PostgreSQL 部分唯一索引(Partial Unique Index)兜底, --- 与 Service 层逻辑形成双保险。 --- 影响范围: --- - 升级前请先执行 update-datas.sql 中的诊断查询, --- 如发现「同商户多个默认应用/门店」需先人工修复, 否则建索引会失败。 --- ============================================================ - --- 商户默认应用唯一索引 (同一商户至多一个 default_app=true 且未删除的记录) -CREATE UNIQUE INDEX IF NOT EXISTS uk_mch_app_info_default - ON mch_app_info (mch_no) - WHERE default_app = TRUE AND deleted = FALSE; - --- 商户默认门店唯一索引 (同一商户至多一个 default_store=true 且未删除的记录) -CREATE UNIQUE INDEX IF NOT EXISTS uk_mch_store_info_default - ON mch_store_info (mch_no) - WHERE default_store = TRUE AND deleted = FALSE; - --- ============================================================ --- pay_trade 冗余支付渠道字段(升级脚本) --- ============================================================ --- 背景: --- 渠道分布报表/资金列表筛选需要按支付渠道(provider)维度统计, --- 原 pay_trade 仅冗余 channelMchNo, 无渠道字段, 报表 SQL 报错或需 JOIN 容器表. --- 按"轻量组织冗余"哲学(source/channelMchNo/storeNo/provider), 加 provider 列, --- 与现有冗余字段并列; 权威仍在业务容器(NormalPayOrder/GatewayPayOrder). --- 写入时机: --- 支付成功 sync 回执时由 PayUniHandleService.applyXxxSyncReceipts 同步写容器+资金凭证. --- 历史数据: --- 通过 container_id JOIN 容器表回填(normal/gateway 两种主流 trade_type). --- 其他 trade_type 历史无 provider 数据可回填, 留 NULL, 不影响新交易. --- ============================================================ - --- 1. 加列(允许 NULL, 历史数据回填前为空) -ALTER TABLE pay_trade ADD COLUMN IF NOT EXISTS provider varchar(32); - -COMMENT ON COLUMN pay_trade.provider IS '支付渠道(冗余自容器, 支付成功sync后回填; 权威在容器 provider)'; - --- 2. 历史数据回填: normal 容器 -UPDATE pay_trade t -SET provider = o.provider -FROM pay_normal_order o -WHERE t.container_id = o.id - AND t.trade_type = 'normal' - AND t.provider IS NULL - AND o.provider IS NOT NULL; - --- 3. 历史数据回填: gateway 容器 -UPDATE pay_trade t -SET provider = o.provider -FROM pay_gateway_order o -WHERE t.container_id = o.id - AND t.trade_type = 'gateway' - AND t.provider IS NULL - AND o.provider IS NOT NULL; - --- ============================================================ --- pay_md_product_config 删除 configured 字段 --- ============================================================ --- 背景: --- 该字段仅用于前端卡片"未配置"标签展示, 由前端保存时手动设置, --- 无任何后端业务逻辑读取(通道模块只读 activeEnv). --- 每个支付产品的配置存储方式各不相同(20+ 张独立 key_config/channel_merchant 表, --- 字段差异极大), 无法用统一规则判定"是否已配置", 故该字段不可靠, 一并移除. --- 影响: --- - 升级后前端不再展示"未配置"标签, 卡片仅显示产品图标与名称. --- - 无任何业务逻辑受影响(路由匹配/订单处理/通道选择均不读此字段). --- ============================================================ -ALTER TABLE pay_md_product_config DROP COLUMN IF EXISTS configured; +-- pay_trade 新增 title 列: 冗余自容器(业务订单), 资金列表/工作台免 JOIN 容器即可展示订单标题 +ALTER TABLE pay_trade ADD COLUMN title VARCHAR(255); +COMMENT ON COLUMN pay_trade.title IS '订单标题(冗余自容器, 资金列表免JOIN; 权威在容器)'; diff --git a/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/service/trade/TradeOrderDetailAssembler.java b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/service/trade/TradeOrderDetailAssembler.java index 5bb54dc7f..cf76d1496 100644 --- a/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/service/trade/TradeOrderDetailAssembler.java +++ b/daxpay-payment/daxpay-payment-admin/src/main/java/cn/daxpay/open/payment/admin/service/trade/TradeOrderDetailAssembler.java @@ -92,7 +92,7 @@ public class TradeOrderDetailAssembler { private void fillFromNormal(PayTradeResult result, NormalPayOrder order) { result.setContainerOrderNo(order.getOrderNo()); result.setBizOrderNo(order.getBizOrderNo()); - result.setTitle(order.getTitle()); + // title 不再从容器覆盖: 统一使用 PayTrade 冗余的 title(列表/详情同源) result.setContainerStatus(order.getStatus()); result.setProduct(order.getProduct()); result.setChannel(order.getChannel()); @@ -117,7 +117,7 @@ public class TradeOrderDetailAssembler { private void fillFromGateway(PayTradeResult result, GatewayPayOrder order) { result.setContainerOrderNo(order.getOrderNo()); result.setBizOrderNo(order.getBizOrderNo()); - result.setTitle(order.getTitle()); + // title 不再从容器覆盖: 统一使用 PayTrade 冗余的 title(列表/详情同源) result.setContainerStatus(order.getStatus()); result.setProduct(order.getProduct()); result.setChannel(order.getChannel()); 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 73dd72881..6f849d7e1 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 @@ -31,6 +31,9 @@ public class PayTrade extends MchBaseEntity { /// 资金交易号(平台生成,与业务单 orderNo 独立) private String tradeNo; + /// 订单标题(冗余自容器, 资金列表/工作台免JOIN; 权威在容器) + private String title; + /// 交易形态 /// @see PayTradeTypeEnum private String tradeType; diff --git a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/order/result/PayTradeResult.java b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/order/result/PayTradeResult.java index 520a3f5ae..271c9175d 100644 --- a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/order/result/PayTradeResult.java +++ b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/order/result/PayTradeResult.java @@ -43,6 +43,10 @@ public class PayTradeResult extends BaseResult { @Schema(description = "支付交易号") private String tradeNo; + /// 订单标题(冗余自容器, 资金列表/工作台免JOIN) + @Schema(description = "订单标题") + private String title; + /// 交易形态 /// @see cn.daxpay.open.payment.trade.enums.PayTradeTypeEnum @Schema(description = "交易形态") @@ -184,10 +188,6 @@ public class PayTradeResult extends BaseResult { @Schema(description = "商户业务单号") private String bizOrderNo; - /// 订单标题 - @Schema(description = "订单标题") - private String title; - /// 容器业务状态 @Schema(description = "容器业务状态") private String containerStatus; 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 e74ac6792..e649b3bdb 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 @@ -171,7 +171,8 @@ public class GatewayPayHandleService { order.getOrderNo(), source, payParam.getChannelMchNo(), - order.getStoreNo()); + order.getStoreNo(), + order.getTitle()); 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 65da4ba06..f226ace5a 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 @@ -148,7 +148,8 @@ public class NormalPayAssistService { orderNo, source, normalOrder.getChannelMchNo(), - normalOrder.getStoreNo()); + normalOrder.getStoreNo(), + normalOrder.getTitle()); } /// 注册超时关单延时消息 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 3db4ff855..a948acb92 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 @@ -30,6 +30,7 @@ public final class PayTradeInitUtil { /// @param source 订单来源(冗余自容器) /// @param channelMchNo 通道商户号(冗余自容器/路由结果,可空仅当路由未定时) /// @param storeNo 门店号(冗余自容器,可空) + /// @param title 订单标题(冗余自容器,资金列表/工作台免JOIN) public static PayTrade initProcessing( String appId, String tradeNo, @@ -39,10 +40,12 @@ public final class PayTradeInitUtil { String relationOrderNo, String source, String channelMchNo, - String storeNo) { + String storeNo, + String title) { return new PayTrade() .setAppId(appId) .setTradeNo(tradeNo) + .setTitle(title) .setTradeType(tradeType) .setContainerId(containerId) .setAmount(amount)