feat(merchant): 商户端通道专属配置(微信密钥/支付宝直连应用/支付宝ISV授权)

- 新增 MchWechatDirectKeyConfigController: 微信直连密钥配置(商户端)
- 新增 MchAlipayDirectAppController: 支付宝直连应用管理+密钥+授权(商户端)
- 新增 MchAlipayDirectAppCapabilityController: 支付宝直连能力绑定(商户端)
- 新增 MchAlipayIsvAuthController: 支付宝服务商代运营授权(商户端)
- MchWxChannelAppCapabilityController: 补充 list-supported-capabilities 接口
- PermCodes: 新增 merchant:alipay-isv-auth 权限码
- 菜单 SQL: 新增业务配置分组子菜单(对接配置/通知配置/支付宝ISV授权)
This commit is contained in:
DaxPay Dev
2026-07-24 22:26:23 +08:00
parent 065eb15876
commit 69c23f9bd5
9 changed files with 687 additions and 18 deletions

View File

@@ -12,7 +12,7 @@
Target Server Version : 160014 (160014)
File Encoding : 65001
Date: 23/07/2026 21:55:31
Date: 24/07/2026 20:13:10
*/
@@ -135,7 +135,6 @@ INSERT INTO "public"."iam_perm_menu" VALUES (900001, 304, 'iam:social', 'admin',
INSERT INTO "public"."iam_perm_menu" VALUES (4040103, 4040130, 'merchant:profile', 'admin', 'MchInfoManage', 'menu.payment.merchant.profile', NULL, 't', 'f', '/payment/merchant/manage/info/MchInfoManage', '/payment/merchant/manage/info', NULL, 1, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-11 03:37:15.424776+00');
INSERT INTO "public"."iam_perm_menu" VALUES (801, 8, 'develop:trade', 'admin', 'DevelopTrade', 'menu.develop.trade', 'lucide:credit-card', 'f', 'f', '/payment/develop/trade/DevelopTrade', '/develop/trade', NULL, 1, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-23 16:00:00+00', '2026-06-23 16:00:00+00');
INSERT INTO "public"."iam_perm_menu" VALUES (802, 8, 'develop:sign', 'admin', 'DevelopSign', 'menu.develop.sign', 'lucide:file-signature', 'f', 'f', '/payment/develop/sign/DevelopSign', '/develop/sign', NULL, 2, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-23 16:00:00+00', '2026-06-23 16:00:00+00');
INSERT INTO "public"."iam_perm_menu" VALUES (40503, 40508, 'payment:isv', 'admin', 'WechatIsvAppManage', 'menu.payment.config.wechatApp', NULL, 't', 'f', '/payment/wx/WxAppHub', '/payment/wx/app', NULL, 4, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-14 00:00:00+00', '2026-07-23 13:44:16.948271+00');
INSERT INTO "public"."iam_perm_menu" VALUES (405, 4, 'payment:config', 'admin', 'PaymentConfig', 'menu.payment.config', 'lucide:settings-2', 't', 'f', NULL, '/payment/config', NULL, 2, 'f', 't', 'f', 1, 1, 0, 't', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-14 00:00:00+00', '2026-07-15 15:24:29.325656+00');
INSERT INTO "public"."iam_perm_menu" VALUES (40105, 4, 'payment:config:product-config', 'admin', 'ProductConfig', 'menu.payment.config.productConfig', 'lucide:layout-grid', 'f', 'f', '/payment/config/product/ProductConfig', '/payment/config/product', NULL, 2, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-13 00:00:00+00', '2026-07-13 13:02:20.703887+00');
INSERT INTO "public"."iam_perm_menu" VALUES (4040116, 401, 'payment:platform:provider', 'admin', 'PayProviderManage', 'menu.payment.platform.provider.manage', 'lucide:wallet', 'f', 'f', '/payment/masterdata/provider/PayProviderList', '/payment/platform/pay-provider', NULL, 0.5, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-17 10:00:31.640482+00', '2026-06-27 13:01:24.318649+00');
@@ -144,6 +143,7 @@ INSERT INTO "public"."iam_perm_menu" VALUES (309, 308, 'system:notify:notice', '
INSERT INTO "public"."iam_perm_menu" VALUES (308, 3, 'system:notify', 'admin', 'SystemNotice', 'menu.system.notify', 'lucide:bell', 'f', 'f', NULL, '/system/notify', '/system/notify/notice', 20, 'f', 't', 'f', 1, 1, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-01 07:16:30.927645+00', '2026-07-01 07:16:30.927645+00');
INSERT INTO "public"."iam_perm_menu" VALUES (40502, 40508, 'payment:isv', 'admin', 'AlipayIsvAppManage', 'menu.payment.config.alipayApp', NULL, 't', 'f', '/payment/channel/alipay/manage/app/AlipayIsvAppManage', '/payment/config/product/app-manage', NULL, 3, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-14 00:00:00+00', '2026-07-11 03:37:15.43231+00');
INSERT INTO "public"."iam_perm_menu" VALUES (4040106, 4040131, 'channel:merchant', 'admin', 'ChannelMerchant', 'menu.payment.merchant.channelMerchant', NULL, 't', 'f', '/payment/global/channel-merchant/ChannelMerchantList', '/payment/global/channel-merchant', NULL, 6, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-05-03 16:00:00+00', '2026-07-22 13:50:46.61698+00');
INSERT INTO "public"."iam_perm_menu" VALUES (40503, 40508, 'payment:isv', 'admin', 'WechatIsvAppManage', 'menu.payment.config.wechatApp', NULL, 't', 'f', '/payment/wx/WxAppHub', '/payment/wx/app', NULL, 4, 'f', 't', 'f', 1, 1, 0, 't', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-14 00:00:00+00', '2026-07-23 14:54:00.447198+00');
INSERT INTO "public"."iam_perm_menu" VALUES (30403, 301, 'system:protocol', 'admin', 'UserProtocol', 'menu.system.basic.protocol', 'lucide:file-text', 'f', 'f', '/system/basic/protocol/UserProtocolList', '/system/basic/protocol', NULL, 2, 'f', 't', 'f', 1, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-24 10:32:13.183371+00', '2026-07-16 08:43:35.038937+00');
INSERT INTO "public"."iam_perm_menu" VALUES (4040115, 4040132, 'channel:app', 'admin', 'DouyinMchAppManage', 'menu.payment.merchant.channelMerchant.douyinApp', NULL, 't', 'f', '/payment/channel/douyin/manage/app/DouyinMchAppManage', '/payment/global/channel-merchant/douyin-app-manage', NULL, 13, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-06-15 16:00:00+00', '2026-07-22 13:50:46.642335+00');
INSERT INTO "public"."iam_perm_menu" VALUES (4040119, 4040132, 'channel:app', 'admin', 'WechatIsvMchAppManage', 'menu.payment.merchant.channelMerchant.wechatIsvApp', NULL, 't', 'f', '/payment/channel/wechat/manage/mch/isv-app/WechatIsvMchAppManage', '/payment/global/channel-merchant/wechat-isv-mch-app-manage', NULL, 14, 'f', 't', 'f', 1, 1, 0, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-05 01:49:32.557374+00', '2026-07-22 13:50:46.650292+00');
@@ -179,7 +179,6 @@ INSERT INTO "public"."iam_perm_menu" VALUES (30203, 302, 'system:log:unipay', 'a
INSERT INTO "public"."iam_perm_menu" VALUES (91100, NULL, 'trade', 'merchant', 'TransactionManagement', 'menu.trade', 'lucide:arrow-left-right', 'f', 'f', NULL, '/trade', '/trade/pay-trade', 4, 'f', 't', 'f', 1, 1, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 03:48:14.886531+00', '2026-07-23 06:44:16.725707+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91101, 91100, 'trade:fund', 'merchant', 'PayTradeList', 'menu.trade.payTrade', 'lucide:arrow-left-right', 'f', 'f', '/payment/order/PayTradeList', '/trade/pay-trade', NULL, 1, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 03:48:14.896997+00', '2026-07-23 06:44:16.735074+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91103, 91100, 'trade:refund', 'merchant', 'RefundOrderList', 'menu.trade.refundOrder', 'lucide:rotate-ccw', 'f', 'f', '/payment/order/RefundOrderList', '/trade/refund-order', NULL, 2, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 03:48:14.903907+00', '2026-07-23 06:44:16.737803+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91104, 91100, 'trade:pay-order', 'merchant', 'PayOrderCatalog', 'menu.trade.payOrder', 'lucide:receipt', 'f', 'f', NULL, '/trade/pay-order', '/trade/pay-order/normal', 3, 'f', 't', 'f', 1, 1, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 06:33:41.347277+00', '2026-07-23 06:44:16.74073+00');
INSERT INTO "public"."iam_perm_menu" VALUES (607, NULL, 'trade:record', 'admin', 'TradeRecordCatalog', 'menu.trade.record', 'lucide:scroll-text', 'f', 'f', NULL, '/trade/record', '/trade/record/mch-notice', 4.5, 'f', 't', 'f', 1, 1, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-21 06:30:00+00', '2026-07-21 06:45:00+00');
INSERT INTO "public"."iam_perm_menu" VALUES (605, 607, 'trade:mch-notice', 'admin', 'MchNoticeTaskList', 'menu.trade.mchNotice', 'lucide:bell-ring', 'f', 'f', '/payment/notice/MchNoticeTaskList', '/trade/record/mch-notice', NULL, 1, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-21 03:00:00+00', '2026-07-21 06:30:00+00');
INSERT INTO "public"."iam_perm_menu" VALUES (606, 607, 'trade:callback-record', 'admin', 'PayCallbackRecordList', 'menu.trade.callbackRecord', 'lucide:inbox', 'f', 'f', '/payment/record/PayCallbackRecordList', '/trade/record/callback-record', NULL, 2, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-21 06:00:00+00', '2026-07-21 06:30:00+00');
@@ -188,15 +187,16 @@ INSERT INTO "public"."iam_perm_menu" VALUES (40402, 404, 'channel:merchant', 'ad
INSERT INTO "public"."iam_perm_menu" VALUES (91001, NULL, NULL, 'merchant', 'Dashboard', 'menu.dashboard', 'lucide:layout-dashboard', 'f', 'f', NULL, '/dashboard', '/workspace', -1, 'f', 'f', 'f', 0, NULL, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 02:30:21.982361+00', '2026-07-23 02:30:21.982361+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91002, 91001, 'dashboard:workspace', 'merchant', 'Workspace', 'menu.dashboard.workspace', 'lucide:panels-top-left', 'f', 'f', '/dashboard/workspace/index', '/workspace', NULL, 1, 'f', 'f', 't', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-23 02:30:21.991345+00', '2026-07-23 02:30:21.991345+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91003, 91001, 'dashboard:analytics', 'merchant', 'Analytics', 'menu.dashboard.analytics', 'lucide:area-chart', 'f', 'f', '/dashboard/analytics/index', '/analytics', NULL, 2, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-23 02:30:21.994392+00', '2026-07-23 02:30:21.994392+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91102, 91104, 'trade:order', 'merchant', 'NormalOrderList', 'menu.trade.normalPay', 'lucide:credit-card', 'f', 'f', '/payment/order/NormalOrderList', '/trade/pay-order/normal', NULL, 1, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 03:48:14.901128+00', '2026-07-23 06:44:16.743429+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91105, 91104, 'trade:gateway-order', 'merchant', 'GatewayOrderList', 'menu.trade.gatewayOrder', 'lucide:store', 'f', 'f', '/payment/order/GatewayOrderList', '/trade/pay-order/gateway', NULL, 2, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 06:33:41.366052+00', '2026-07-23 06:44:16.746099+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91106, NULL, 'trade:record', 'merchant', 'TradeRecordCatalog', 'menu.trade.record', 'lucide:scroll-text', 'f', 'f', NULL, '/trade/record', '/trade/record/mch-notice', 4.5, 'f', 't', 'f', 1, 1, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 06:33:41.369341+00', '2026-07-23 06:44:16.748812+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91107, 91106, 'trade:mch-notice', 'merchant', 'MchNoticeTaskList', 'menu.trade.mchNotice', 'lucide:bell-ring', 'f', 'f', '/payment/notice/MchNoticeTaskList', '/trade/record/mch-notice', NULL, 1, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 06:33:41.372431+00', '2026-07-23 06:44:16.751643+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91108, 91106, 'trade:callback-record', 'merchant', 'PayCallbackRecordList', 'menu.trade.callbackRecord', 'lucide:inbox', 'f', 'f', '/payment/record/PayCallbackRecordList', '/trade/record/callback-record', NULL, 2, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 06:33:41.375612+00', '2026-07-23 06:44:16.754274+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91104, 91100, 'trade:pay-order', 'merchant', 'PayOrderCatalog', 'menu.trade.payOrder', 'lucide:receipt', 'f', 'f', NULL, '/trade/pay-order', '/trade/pay-order/normal', 3, 'f', 't', 'f', 1, 1, 0, 't', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 06:33:41.347277+00', '2026-07-23 14:54:00.459736+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91102, 91104, 'trade:order', 'merchant', 'NormalOrderList', 'menu.trade.normalPay', 'lucide:credit-card', 'f', 'f', '/payment/order/NormalOrderList', '/trade/pay-order/normal', NULL, 1, 'f', 't', 'f', 1, 1, 0, 't', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 03:48:14.901128+00', '2026-07-23 14:54:00.459736+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91105, 91104, 'trade:gateway-order', 'merchant', 'GatewayOrderList', 'menu.trade.gatewayOrder', 'lucide:store', 'f', 'f', '/payment/order/GatewayOrderList', '/trade/pay-order/gateway', NULL, 2, 'f', 't', 'f', 1, 1, 0, 't', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 06:33:41.366052+00', '2026-07-23 14:54:00.459736+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91110, 91100, 'trade:pay-order', 'merchant', 'PayOrderCatalog', 'menu.trade.payOrder', 'lucide:receipt', 'f', 'f', NULL, '/trade/pay-order', '/trade/pay-order/normal', 3, 'f', 'f', 'f', 0, NULL, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 09:32:12.636962+00', '2026-07-23 09:32:12.636962+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91111, 91110, 'trade:order', 'merchant', 'NormalOrderList', 'menu.trade.normalPay', 'lucide:file-text', 'f', 'f', '/payment/order/NormalOrderList', '/trade/pay-order/normal', NULL, 1, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.645988+00', '2026-07-23 09:32:12.645988+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91112, 91110, 'trade:gateway-order', 'merchant', 'GatewayOrderList', 'menu.trade.gatewayOrder', 'lucide:globe', 'f', 'f', '/payment/order/GatewayOrderList', '/trade/pay-order/gateway', NULL, 2, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.648789+00', '2026-07-23 09:32:12.648789+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91200, NULL, 'trade:record', 'merchant', 'TradeRecordCatalog', 'menu.trade.record', 'lucide:scroll-text', 'f', 'f', NULL, '/trade/record', '/trade/record/mch-notice', 3, 'f', 't', 'f', 0, NULL, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 09:32:12.651967+00', '2026-07-23 09:32:12.651967+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91106, NULL, 'trade:record', 'merchant', 'TradeRecordCatalog', 'menu.trade.record', 'lucide:scroll-text', 'f', 'f', NULL, '/trade/record', '/trade/record/mch-notice', 4.5, 'f', 't', 'f', 1, 1, 0, 't', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 06:33:41.369341+00', '2026-07-23 14:54:00.459736+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91107, 91106, 'trade:mch-notice', 'merchant', 'MchNoticeTaskList', 'menu.trade.mchNotice', 'lucide:bell-ring', 'f', 'f', '/payment/notice/MchNoticeTaskList', '/trade/record/mch-notice', NULL, 1, 'f', 't', 'f', 1, 1, 0, 't', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 06:33:41.372431+00', '2026-07-23 14:54:00.459736+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91108, 91106, 'trade:callback-record', 'merchant', 'PayCallbackRecordList', 'menu.trade.callbackRecord', 'lucide:inbox', 'f', 'f', '/payment/record/PayCallbackRecordList', '/trade/record/callback-record', NULL, 2, 'f', 't', 'f', 1, 1, 0, 't', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 06:33:41.375612+00', '2026-07-23 14:54:00.459736+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91201, 91200, 'trade:mch-notice', 'merchant', 'MchNoticeTaskList', 'menu.trade.mchNotice', 'lucide:bell-ring', 'f', 'f', '/payment/notice/MchNoticeTaskList', '/trade/record/mch-notice', NULL, 1, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.655107+00', '2026-07-23 09:32:12.655107+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91202, 91200, 'trade:callback-record', 'merchant', 'PayCallbackRecordList', 'menu.trade.callbackRecord', 'lucide:webhook', 'f', 'f', '/payment/record/PayCallbackRecordList', '/trade/record/callback-record', NULL, 2, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.658982+00', '2026-07-23 09:32:12.658982+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91300, NULL, NULL, 'merchant', 'MchCenter', 'menu.mch.center', 'lucide:building-2', 'f', 'f', NULL, '/mch-center', '/mch/info', 4, 'f', 't', 'f', 0, NULL, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 09:32:12.662617+00', '2026-07-23 09:32:12.662617+00');
@@ -209,17 +209,18 @@ INSERT INTO "public"."iam_perm_menu" VALUES (91401, 91400, 'merchant:app', 'merc
INSERT INTO "public"."iam_perm_menu" VALUES (91402, 91400, 'channel:merchant', 'merchant', 'ChannelMerchantList', 'menu.payment.merchant.channelMerchant', 'lucide:store', 'f', 'f', '/payment/channel-merchant/ChannelMerchantList', '/mch/channel-merchant', NULL, 2, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.684068+00', '2026-07-23 09:32:12.684068+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91410, 91402, 'channel:merchant:create', 'merchant', 'ChannelMerchantCreate', 'menu.payment.merchant.channelMerchant.create', NULL, 't', 'f', '/payment/channel-merchant/ChannelMerchantCreate', '/mch/channel-merchant/create', NULL, 1, 'f', 'f', 'f', 0, 1, 1, 'f', 'subpage', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.687009+00', '2026-07-23 09:32:12.687009+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91411, 91402, 'channel:merchant:detail', 'merchant', 'ChannelMerchantDetail', 'menu.payment.merchant.channelMerchant.detail', NULL, 't', 'f', '/payment/channel-merchant/detail/ChannelMerchantDetailDispatch', '/mch/channel-merchant/detail', NULL, 2, 'f', 'f', 'f', 0, 1, 1, 'f', 'subpage', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.689613+00', '2026-07-23 09:32:12.689613+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91412, 91400, 'payment:wx:mch-app', 'merchant', 'MchWxAppList', 'menu.payment.wx.mchApp', 'lucide:message-circle', 'f', 'f', '/payment/wx/mch/MchWxAppList', '/mch/wx-app', NULL, 3, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-24 00:00:00+00', '2026-07-24 00:00:00+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91403, 91400, NULL, 'merchant', 'AppConfigDir', 'menu.payment.merchant.appWorkbench', 'lucide:sliders-horizontal', 'f', 'f', NULL, '/mch/app-config', NULL, 4, 'f', 'f', 'f', 0, NULL, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 09:32:12.692738+00', '2026-07-23 09:32:12.692738+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91404, 91403, 'merchant:app:route', 'merchant', 'PayRouteConfig', 'menu.payment.merchant.payRoute', 'lucide:git-branch', 'f', 'f', '/payment/route/PayRouteConfig', '/mch/route', NULL, 1, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.695589+00', '2026-07-23 09:32:12.695589+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91405, 91403, 'merchant:gateway-aggregate', 'merchant', 'AggregateScanConfig', 'menu.payment.merchant.aggregateScan', 'lucide:qr-code', 'f', 'f', '/payment/merchant/aggregate/AggregateScanConfig', '/mch/aggregate', NULL, 2, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.698644+00', '2026-07-23 09:32:12.698644+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91406, 91403, 'merchant:gateway-code', 'merchant', 'CodePayConfig', 'menu.payment.merchant.codePayConfig', 'lucide:smartphone', 'f', 'f', '/payment/merchant/code-config/CodePayConfig', '/mch/code-config', NULL, 3, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.701544+00', '2026-07-23 09:32:12.701544+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91407, 91403, 'merchant:gateway-cashier', 'merchant', 'CashierConfig', 'menu.payment.merchant.cashierConfig', 'lucide:monitor-smartphone', 'f', 'f', '/payment/merchant/cashier/CashierConfig', '/mch/cashier', NULL, 4, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.704415+00', '2026-07-23 09:32:12.704415+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91408, 91403, 'merchant:notify-config', 'merchant', 'MchAppNotifyConfig', 'menu.payment.merchant.notifyConfig', 'lucide:bell', 'f', 'f', '/payment/merchant/notify/MchAppNotifyConfigPage', '/mch/notify-config', NULL, 5, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.707422+00', '2026-07-23 09:32:12.707422+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91409, 91403, 'merchant:easypay', 'merchant', 'EasyPayConfig', 'menu.payment.merchant.easypay', 'lucide:plug', 'f', 'f', '/payment/merchant/easypay/EasyPayConfig', '/mch/easypay', NULL, 6, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.710238+00', '2026-07-23 09:32:12.710238+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91500, NULL, NULL, 'merchant', 'BusinessConfig', 'menu.business.config', 'lucide:key-round', 'f', 'f', NULL, '/mch-business', '/mch/credential', 6, 'f', 't', 'f', 0, NULL, 0, 'f', 'catalog', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 09:32:12.7131+00', '2026-07-23 09:32:12.7131+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91501, 91300, 'merchant:credential', 'merchant', 'MerchantCredentialConfig', 'menu.payment.merchant.credential', 'lucide:key', 'f', 'f', '/payment/merchant/credential/MerchantCredentialConfig', '/mch/credential', NULL, 5, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-24 10:30:00.000000+00', '2026-07-24 10:30:00.000000+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91305, 91300, 'merchant:wx-verify', 'merchant', 'MchWxDomainVerifyList', 'menu.payment.merchant.wxVerify', 'lucide:shield-check', 'f', 'f', '/payment/merchant/wx-verify/MchWxDomainVerifyList', '/mch/wx-verify', NULL, 6, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-24 10:30:00.000000+00', '2026-07-24 10:30:00.000000+00');
INSERT INTO "public"."iam_perm_menu" VALUES (40106, 4, 'payment:wx:platform-app', 'admin', 'WxAppHub', 'menu.payment.wx.app', 'lucide:app-window', 'f', 'f', '/payment/wx/WxAppHub', '/payment/wx/app', NULL, 2.5, 'f', 't', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 13:44:05.953634+00', '2026-07-23 13:44:05.953634+00');
INSERT INTO "public"."iam_perm_menu" VALUES (40107, 40106, 'payment:wx:mch-app', 'admin', 'WxMchApp', 'menu.payment.wx.mchApp', NULL, 't', 'f', NULL, NULL, NULL, 1, 'f', 'f', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 13:44:05.963399+00', '2026-07-23 13:44:05.963399+00');
INSERT INTO "public"."iam_perm_menu" VALUES (40107, 40106, 'payment:wx:mch-app', 'admin', 'WxMchApp', 'menu.payment.wx.mchApp', NULL, 't', 'f', NULL, NULL, NULL, 1, 'f', 'f', 'f', 1, 1, 0, 'f', 'menu', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 13:44:05.963399+00', '2026-07-23 14:32:14.494862+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91412, 91400, 'payment:wx:mch-app', 'merchant', 'MchWxAppList', 'menu.payment.wx.mchApp', 'lucide:message-circle', 'f', 'f', '/payment/wx/mch/MchWxAppList', '/mch/wx-app', NULL, 3, 'f', 'f', 'f', 0, 1, 1, 'f', 'menu', NULL, NULL, NULL, NULL, '', '', '2026-07-24 01:58:27.304104+00', '2026-07-24 01:58:27.304104+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91403, 91401, NULL, 'merchant', 'AppConfigDir', 'menu.payment.merchant.appWorkbench', NULL, 't', 'f', NULL, '/mch/app-config', NULL, 4, 'f', 'f', 'f', 0, NULL, 0, 'f', 'subpage_group', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-23 09:32:12.692738+00', '2026-07-23 09:32:12.692738+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91413, 91403, 'merchant:app:workbench', 'merchant', 'MchAppWorkbench', 'menu.payment.merchant.appWorkbench', NULL, 't', 'f', '/payment/merchant/app/MchAppWorkbench', '/mch/app/manage', NULL, 0, 'f', 'f', 'f', 0, 1, 1, 'f', 'subpage', NULL, NULL, NULL, NULL, NULL, NULL, '2026-07-24 10:20:36.109679+00', '2026-07-24 10:20:36.109679+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91404, 91403, 'merchant:app:route', 'merchant', 'PayRouteConfig', 'menu.payment.merchant.payRoute', 'lucide:git-branch', 't', 'f', '/payment/route/PayRouteConfig', '/mch/route', NULL, 1, 'f', 'f', 'f', 0, 1, 1, 'f', 'subpage', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.695589+00', '2026-07-23 09:32:12.695589+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91405, 91403, 'merchant:gateway-aggregate', 'merchant', 'AggregateScanConfig', 'menu.payment.merchant.aggregateScan', 'lucide:qr-code', 't', 'f', '/payment/merchant/aggregate/AggregateScanConfig', '/mch/aggregate', NULL, 2, 'f', 'f', 'f', 0, 1, 1, 'f', 'subpage', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.698644+00', '2026-07-23 09:32:12.698644+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91406, 91403, 'merchant:gateway-code', 'merchant', 'CodePayConfig', 'menu.payment.merchant.codePayConfig', 'lucide:smartphone', 't', 'f', '/payment/merchant/code-config/CodePayConfig', '/mch/code-config', NULL, 3, 'f', 'f', 'f', 0, 1, 1, 'f', 'subpage', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.701544+00', '2026-07-23 09:32:12.701544+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91407, 91403, 'merchant:gateway-cashier', 'merchant', 'CashierConfig', 'menu.payment.merchant.cashierConfig', 'lucide:monitor-smartphone', 't', 'f', '/payment/merchant/cashier/CashierConfig', '/mch/cashier', NULL, 4, 'f', 'f', 'f', 0, 1, 1, 'f', 'subpage', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.704415+00', '2026-07-23 09:32:12.704415+00');
INSERT INTO "public"."iam_perm_menu" VALUES (91409, 91403, 'merchant:easypay', 'merchant', 'EasyPayConfig', 'menu.payment.merchant.easypay', 'lucide:plug', 't', 'f', '/payment/merchant/easypay/EasyPayConfig', '/mch/easypay', NULL, 6, 'f', 'f', 'f', 0, 1, 1, 'f', 'subpage', NULL, NULL, NULL, NULL, '', '', '2026-07-23 09:32:12.710238+00', '2026-07-23 09:32:12.710238+00');
-- ----------------------------
-- Primary Key structure for table iam_perm_menu

View File

@@ -0,0 +1,174 @@
-- 商户端应用工作台菜单改造:配置项从独立菜单收敛为应用工作台隐藏子页面
-- 1. AppConfigDir(91403) 从可见目录改为透明分组容器(subpage_group),挂到应用配置(91401)下
-- 2. 新增应用工作台(91413)隐藏子页面,作为配置入口聚合页
-- 3. 通道路由/聚合/码牌/收银台/易支付 改为隐藏子页面(subpage)
-- 4. 通知配置(91408)删除独立路由,改由工作台抽屉承载
-- AppConfigDir 改为透明分组容器,挂到应用配置下
UPDATE "public"."iam_perm_menu"
SET pid = 91401, icon = NULL, hidden = true, menu_type = 'subpage_group'
WHERE id = 91403;
-- 新增应用工作台隐藏子页面(配置入口聚合页)
INSERT INTO "public"."iam_perm_menu"
(id, pid, menu_code, client_code, name, i18n_key, icon, hidden, hide_children_menu,
component, path, redirect, sort_no, root, keep_alive, affix_tab,
creator, last_modifier, version, deleted, menu_type,
active_icon, badge, badge_type, badge_variants, iframe_src, link,
create_time, last_modified_time)
VALUES (91413, 91403, 'merchant:app:workbench', 'merchant', 'MchAppWorkbench',
'menu.payment.merchant.appWorkbench', NULL, true, false,
'/payment/merchant/app/MchAppWorkbench', '/mch/app/manage', NULL, 0, false, false, false,
0, 1, 1, false, 'subpage',
NULL, NULL, NULL, NULL, NULL, NULL,
now(), now())
ON CONFLICT (id) DO UPDATE SET
pid = EXCLUDED.pid,
menu_code = EXCLUDED.menu_code,
name = EXCLUDED.name,
i18n_key = EXCLUDED.i18n_key,
icon = EXCLUDED.icon,
hidden = EXCLUDED.hidden,
component = EXCLUDED.component,
path = EXCLUDED.path,
sort_no = EXCLUDED.sort_no,
menu_type = EXCLUDED.menu_type,
last_modified_time = now();
-- 通道路由/聚合/码牌/收银台/易支付 改为隐藏子页面
UPDATE "public"."iam_perm_menu"
SET hidden = true, menu_type = 'subpage'
WHERE id IN (91404, 91405, 91406, 91407, 91409);
-- 通知配置删除独立路由(改由应用工作台抽屉承载)
DELETE FROM "public"."iam_perm_menu" WHERE id = 91408;
-- ===== 商户端「业务配置」分组(BusinessConfig 91500)子菜单 =====
-- 91500 catalog 已在全量 iam_perm_menu.sql 中,此处仅补子菜单
-- 1. 对接配置(91501):商户 API 密钥配置,页面已迁移,补菜单注册让 /mch/credential 路由生效
-- 对接配置商户级menu 类型,可见)
INSERT INTO "public"."iam_perm_menu"
(id, pid, menu_code, client_code, name, i18n_key, icon, hidden, hide_children_menu,
component, path, redirect, sort_no, root, keep_alive, affix_tab,
creator, last_modifier, version, deleted, menu_type,
active_icon, badge, badge_type, badge_variants, iframe_src, link,
create_time, last_modified_time)
VALUES (91501, 91500, 'merchant:credential', 'merchant', 'MerchantCredentialConfig',
'menu.payment.merchant.credential', 'lucide:key', false, false,
'/payment/merchant/credential/MerchantCredentialConfig', '/mch/credential', NULL, 1, false, false, false,
0, 1, 1, false, 'menu',
NULL, NULL, NULL, NULL, NULL, NULL,
now(), now())
ON CONFLICT (id) DO UPDATE SET
pid = EXCLUDED.pid,
menu_code = EXCLUDED.menu_code,
name = EXCLUDED.name,
i18n_key = EXCLUDED.i18n_key,
icon = EXCLUDED.icon,
hidden = EXCLUDED.hidden,
component = EXCLUDED.component,
path = EXCLUDED.path,
sort_no = EXCLUDED.sort_no,
menu_type = EXCLUDED.menu_type,
last_modified_time = now();
-- 通知配置(应用级汇总页:列出商户所有应用,逐个配置异步通知)
INSERT INTO "public"."iam_perm_menu"
(id, pid, menu_code, client_code, name, i18n_key, icon, hidden, hide_children_menu,
component, path, redirect, sort_no, root, keep_alive, affix_tab,
creator, last_modifier, version, deleted, menu_type,
active_icon, badge, badge_type, badge_variants, iframe_src, link,
create_time, last_modified_time)
VALUES (91502, 91500, 'merchant:notify-config', 'merchant', 'MchNotifyConfigOverview',
'menu.payment.merchant.notifyConfig', 'lucide:bell', false, false,
'/payment/merchant/notify-config/MchNotifyConfigOverview', '/mch/notify-config', NULL, 2, false, false, false,
0, 1, 1, false, 'menu',
NULL, NULL, NULL, NULL, NULL, NULL,
now(), now())
ON CONFLICT (id) DO UPDATE SET
pid = EXCLUDED.pid,
menu_code = EXCLUDED.menu_code,
name = EXCLUDED.name,
i18n_key = EXCLUDED.i18n_key,
icon = EXCLUDED.icon,
hidden = EXCLUDED.hidden,
component = EXCLUDED.component,
path = EXCLUDED.path,
sort_no = EXCLUDED.sort_no,
menu_type = EXCLUDED.menu_type,
last_modified_time = now();
-- 支付宝服务商代运营授权(商户级独立页:列出商户名下支付宝通道商户,扫码完成代运营授权)
INSERT INTO "public"."iam_perm_menu"
(id, pid, menu_code, client_code, name, i18n_key, icon, hidden, hide_children_menu,
component, path, redirect, sort_no, root, keep_alive, affix_tab,
creator, last_modifier, version, deleted, menu_type,
active_icon, badge, badge_type, badge_variants, iframe_src, link,
create_time, last_modified_time)
VALUES (91503, 91500, 'merchant:alipay-isv-auth', 'merchant', 'AlipayIsvAuth',
'menu.business.alipayIsvAuth', 'lucide:shield-check', false, false,
'/payment/merchant/alipay-isv-auth/AlipayIsvAuth', '/mch/alipay-isv-auth', NULL, 3, false, false, false,
0, 1, 1, false, 'menu',
NULL, NULL, NULL, NULL, NULL, NULL,
now(), now())
ON CONFLICT (id) DO UPDATE SET
pid = EXCLUDED.pid,
menu_code = EXCLUDED.menu_code,
name = EXCLUDED.name,
i18n_key = EXCLUDED.i18n_key,
icon = EXCLUDED.icon,
hidden = EXCLUDED.hidden,
component = EXCLUDED.component,
path = EXCLUDED.path,
sort_no = EXCLUDED.sort_no,
menu_type = EXCLUDED.menu_type,
last_modified_time = now();
-- ===== 商户端菜单结构重构:业务配置目录拆分(数据归属对齐) =====
-- 背景:「业务配置」(91500) 混淆了商户级与应用级配置,按数据模型唯一键拆分:
-- 对接配置(91501, mchNo 唯一) → 商户中心(91300) 下
-- 通知配置(91502, appId 唯一) → 删除独立总览页,已由应用工作台抽屉(MchAppNotifyConfig)承载
-- 支付宝授权(91503, channelMchNo 唯一) → 删除独立菜单,改由通道商户详情页抽屉(AlipayIsvAuthDrawer)承载
-- 微信域名验证(91305, mchNo 唯一) → 新增到商户中心下(原为孤儿页,页面已存在未挂菜单)
-- 业务配置目录(91500) → 子菜单全部迁出后删除
-- 对接配置移至商户中心下(商户级密钥,与具体应用无关)
UPDATE "public"."iam_perm_menu"
SET pid = 91300, sort_no = 5
WHERE id = 91501;
-- 新增微信域名验证菜单(商户中心下,商户级)
INSERT INTO "public"."iam_perm_menu"
(id, pid, menu_code, client_code, name, i18n_key, icon, hidden, hide_children_menu,
component, path, redirect, sort_no, root, keep_alive, affix_tab,
creator, last_modifier, version, deleted, menu_type,
active_icon, badge, badge_type, badge_variants, iframe_src, link,
create_time, last_modified_time)
VALUES (91305, 91300, 'merchant:wx-verify', 'merchant', 'MchWxDomainVerifyList',
'menu.payment.merchant.wxVerify', 'lucide:shield-check', false, false,
'/payment/merchant/wx-verify/MchWxDomainVerifyList', '/mch/wx-verify', NULL, 6, false, false, false,
0, 1, 1, false, 'menu',
NULL, NULL, NULL, NULL, NULL, NULL,
now(), now())
ON CONFLICT (id) DO UPDATE SET
pid = EXCLUDED.pid,
menu_code = EXCLUDED.menu_code,
name = EXCLUDED.name,
i18n_key = EXCLUDED.i18n_key,
icon = EXCLUDED.icon,
hidden = EXCLUDED.hidden,
component = EXCLUDED.component,
path = EXCLUDED.path,
sort_no = EXCLUDED.sort_no,
menu_type = EXCLUDED.menu_type,
last_modified_time = now();
-- 删除通知配置独立总览页(应用级配置,已由应用工作台抽屉承载)
DELETE FROM "public"."iam_perm_menu" WHERE id = 91502;
-- 删除支付宝授权独立菜单(通道商户级配置,改由通道商户详情抽屉承载)
DELETE FROM "public"."iam_perm_menu" WHERE id = 91503;
-- 删除业务配置目录(子菜单已全部迁出)
DELETE FROM "public"."iam_perm_menu" WHERE id = 91500;

View File

@@ -0,0 +1,70 @@
package cn.daxpay.open.channel.alipay.controller.direct;
import cn.daxpay.open.channel.alipay.param.direct.AlipayDirectAppCapabilityBatchParam;
import cn.daxpay.open.channel.alipay.result.direct.AlipayDirectAppCapabilityResult;
import cn.daxpay.open.channel.alipay.result.direct.AlipayDirectCapabilityOption;
import cn.daxpay.open.channel.alipay.service.direct.AlipayDirectAppCapabilityService;
import cn.daxpay.open.payment.common.context.PaymentContext;
import cn.daxpay.open.platform.core.code.CommonCode;
import cn.daxpay.open.platform.core.exception.BizInfoException;
import cn.daxpay.open.platform.core.rest.Res;
import cn.daxpay.open.platform.core.rest.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/// # 支付宝直连商户应用支付能力关联管理(商户端)
///
/// 对照运营端 [AlipayDirectAppCapabilityController],路径前缀 `/mch/alipay/direct-app/capability`。
/// 商户号一律取自 [PaymentContext],忽略请求中的 mchNo防越权。
@Validated
@Tag(name = "支付宝直连商户应用支付能力关联管理(商户端)")
@RestController
@RequestMapping("/mch/alipay/direct-app/capability")
@RequiredArgsConstructor
public class MchAlipayDirectAppCapabilityController {
private final AlipayDirectAppCapabilityService alipayDirectAppCapabilityService;
private final PaymentContext paymentContext;
/// 当前登录商户号(上下文必有;缺则视为会话异常)
private String requireMchNo() {
String mchNo = paymentContext.getMchNo();
if (mchNo == null || mchNo.isBlank()) {
// 商户上下文缺失
throw new BizInfoException(CommonCode.FAIL_CODE, "pay.error.assist.mchContextMissing");
}
return mchNo;
}
@Operation(summary = "查询通道商户的能力应用关联列表")
@GetMapping("/list-by-channel-mch-no")
public Result<List<AlipayDirectAppCapabilityResult>> listByChannelMchNo(
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo) {
return Res.ok(alipayDirectAppCapabilityService.listByChannelMchNo(requireMchNo(), channelMchNo));
}
@Operation(summary = "全量保存能力应用关联")
@PostMapping("/save-batch")
public Result<Void> saveBatch(@RequestBody @Validated AlipayDirectAppCapabilityBatchParam param) {
// 强制当前商户号,忽略客户端传入(防越权)
param.setMchNo(requireMchNo());
alipayDirectAppCapabilityService.saveBatch(param);
return Res.ok();
}
@Operation(summary = "查询支付宝直连支持的支付能力候选")
@GetMapping("/list-supported-capabilities")
public Result<List<AlipayDirectCapabilityOption>> listSupportedCapabilities() {
return Res.ok(alipayDirectAppCapabilityService.listSupportedCapabilities());
}
}

View File

@@ -0,0 +1,166 @@
package cn.daxpay.open.channel.alipay.controller.direct;
import cn.daxpay.open.channel.alipay.param.direct.AlipayDirectAppAuthConfigParam;
import cn.daxpay.open.channel.alipay.param.direct.AlipayDirectAppKeyConfigParam;
import cn.daxpay.open.channel.alipay.param.direct.AlipayDirectAppParam;
import cn.daxpay.open.channel.alipay.result.direct.AlipayDirectAppAuthConfigResult;
import cn.daxpay.open.channel.alipay.result.direct.AlipayDirectAppKeyConfigResult;
import cn.daxpay.open.channel.alipay.result.direct.AlipayDirectAppResult;
import cn.daxpay.open.channel.alipay.service.direct.AlipayDirectAppAuthConfigService;
import cn.daxpay.open.channel.alipay.service.direct.AlipayDirectAppKeyConfigService;
import cn.daxpay.open.channel.alipay.service.direct.AlipayDirectAppService;
import cn.daxpay.open.payment.common.context.PaymentContext;
import cn.daxpay.open.platform.core.code.CommonCode;
import cn.daxpay.open.platform.core.exception.BizInfoException;
import cn.daxpay.open.platform.core.rest.Res;
import cn.daxpay.open.platform.core.rest.result.Result;
import cn.daxpay.open.platform.core.util.ValidationUtil;
import cn.daxpay.open.platform.core.validation.ValidationGroup;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Objects;
/// # 支付宝直连商户应用管理(商户端)
///
/// 对照运营端 [AlipayDirectAppController],路径前缀 `/mch/alipay/direct-app`。
/// 商户号一律取自 [PaymentContext],忽略请求中的 mchNo防越权。
@Validated
@Tag(name = "支付宝直连商户应用管理(商户端)")
@RestController
@RequestMapping("/mch/alipay/direct-app")
@RequiredArgsConstructor
public class MchAlipayDirectAppController {
private final AlipayDirectAppService alipayDirectAppService;
private final AlipayDirectAppKeyConfigService alipayDirectAppKeyConfigService;
private final AlipayDirectAppAuthConfigService alipayDirectAppAuthConfigService;
private final PaymentContext paymentContext;
/// 当前登录商户号(上下文必有;缺则视为会话异常)
private String requireMchNo() {
String mchNo = paymentContext.getMchNo();
if (mchNo == null || mchNo.isBlank()) {
// 商户上下文缺失
throw new BizInfoException(CommonCode.FAIL_CODE, "pay.error.assist.mchContextMissing");
}
return mchNo;
}
/// 校验应用归属当前商户
private void assertOwned(AlipayDirectAppResult result) {
if (!Objects.equals(result.getMchNo(), requireMchNo())) {
// 支付宝直连商户应用不属于当前商户
throw new BizInfoException(CommonCode.FAIL_CODE, "error.channel.alipay.mchAppNotFound");
}
}
@Operation(summary = "根据通道商户号查询应用列表")
@GetMapping("/list-by-channel-mch-no")
public Result<List<AlipayDirectAppResult>> listByChannelMchNo(
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo) {
return Res.ok(alipayDirectAppService.listByMchNoAndChannelMchNo(requireMchNo(), channelMchNo));
}
@Operation(summary = "查询应用详情")
@GetMapping("/find-by-id")
public Result<AlipayDirectAppResult> findById(
@NotNull(message = "{validation.field.id.notNull}") Long id) {
AlipayDirectAppResult result = alipayDirectAppService.findById(id);
this.assertOwned(result);
return Res.ok(result);
}
@Operation(summary = "同一通道商户下支付宝应用ID是否已存在")
@GetMapping("/exists-ali-app-id-by-channel")
public Result<Boolean> existsAliAppIdByChannel(
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo,
@NotBlank(message = "{validation.field.aliAppId.notBlank}") String aliAppId) {
return Res.ok(alipayDirectAppService.existsAliAppIdByChannel(requireMchNo(), channelMchNo, aliAppId, null));
}
@Operation(summary = "同一通道商户下支付宝应用ID是否已存在(排除自身)")
@GetMapping("/exists-ali-app-id-by-channel-not-id")
public Result<Boolean> existsAliAppIdByChannelNotId(
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo,
@NotBlank(message = "{validation.field.aliAppId.notBlank}") String aliAppId,
@NotNull(message = "{validation.field.id.notNull}") Long id) {
return Res.ok(alipayDirectAppService.existsAliAppIdByChannel(requireMchNo(), channelMchNo, aliAppId, id));
}
@Operation(summary = "新增直连商户应用")
@PostMapping("/add")
public Result<Void> add(@RequestBody @Validated(ValidationGroup.add.class) AlipayDirectAppParam param) {
// 强制当前商户号,忽略客户端传入(防越权)
param.setMchNo(requireMchNo());
ValidationUtil.validateParam(param, ValidationGroup.add.class);
alipayDirectAppService.add(param);
return Res.ok();
}
@Operation(summary = "修改直连商户应用")
@PostMapping("/update")
public Result<Void> update(@RequestBody @Validated(ValidationGroup.edit.class) AlipayDirectAppParam param) {
this.assertOwned(alipayDirectAppService.findById(param.getId()));
// 强制当前商户号,忽略客户端传入(防越权)
param.setMchNo(requireMchNo());
ValidationUtil.validateParam(param, ValidationGroup.edit.class);
alipayDirectAppService.update(param);
return Res.ok();
}
@Operation(summary = "删除直连商户应用")
@PostMapping("/delete")
public Result<Void> delete(@NotNull(message = "{validation.field.id.notNull}") Long id) {
this.assertOwned(alipayDirectAppService.findById(id));
alipayDirectAppService.delete(id);
return Res.ok();
}
@Operation(summary = "查询应用密钥配置")
@GetMapping("/find-key-config-by-app-id")
public Result<AlipayDirectAppKeyConfigResult> findKeyConfigByAppId(
@NotNull(message = "{validation.field.alipayDirectAppId.notNull}") Long alipayDirectAppId,
@NotNull(message = "{validation.field.sandbox.notNull}") Boolean sandbox) {
// 先校验应用归属当前商户
this.assertOwned(alipayDirectAppService.findById(alipayDirectAppId));
return Res.ok(alipayDirectAppKeyConfigService.findByAlipayDirectAppId(alipayDirectAppId, sandbox).toResult());
}
@Operation(summary = "保存应用密钥配置")
@PostMapping("/save-key-config")
public Result<Void> saveKeyConfig(@RequestBody @Validated AlipayDirectAppKeyConfigParam param) {
// 先校验应用归属当前商户
this.assertOwned(alipayDirectAppService.findById(param.getAlipayDirectAppId()));
alipayDirectAppKeyConfigService.save(param);
return Res.ok();
}
@Operation(summary = "查询应用授权认证配置")
@GetMapping("/find-auth-config-by-app-id")
public Result<AlipayDirectAppAuthConfigResult> findAuthConfigByAppId(
@NotNull(message = "{validation.field.alipayDirectAppId.notNull}") Long alipayDirectAppId) {
// 先校验应用归属当前商户
this.assertOwned(alipayDirectAppService.findById(alipayDirectAppId));
return Res.ok(alipayDirectAppAuthConfigService.findByAlipayDirectAppId(alipayDirectAppId).toResult());
}
@Operation(summary = "保存应用授权认证配置")
@PostMapping("/save-auth-config")
public Result<Void> saveAuthConfig(@RequestBody @Validated AlipayDirectAppAuthConfigParam param) {
// 先校验应用归属当前商户
this.assertOwned(alipayDirectAppService.findById(param.getAlipayDirectAppId()));
alipayDirectAppAuthConfigService.save(param);
return Res.ok();
}
}

View File

@@ -0,0 +1,126 @@
package cn.daxpay.open.channel.alipay.controller.isv;
import cn.daxpay.open.channel.alipay.dao.isv.AlipayIsvChannelMerchantManager;
import cn.daxpay.open.channel.alipay.entity.isv.AlipayIsvChannelMerchant;
import cn.daxpay.open.channel.alipay.param.isv.AlipayIsvAuthParam;
import cn.daxpay.open.channel.alipay.result.isv.AlipayIsvAuthUrlResult;
import cn.daxpay.open.channel.alipay.result.isv.MchAlipayIsvAuthResult;
import cn.daxpay.open.channel.alipay.service.isv.AlipayIsvAuthService;
import cn.daxpay.open.payment.common.context.PaymentContext;
import cn.daxpay.open.platform.core.annotation.PermCode;
import cn.daxpay.open.platform.core.code.CommonCode;
import cn.daxpay.open.platform.core.code.PermCodes;
import cn.daxpay.open.platform.core.exception.BizInfoException;
import cn.daxpay.open.platform.core.exception.DataNotExistException;
import cn.daxpay.open.platform.core.rest.Res;
import cn.daxpay.open.platform.core.rest.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Objects;
/// # 支付宝服务商代运营授权(商户端)
///
/// 对照运营端 [AlipayIsvChannelMerchantController],路径前缀 `/mch/alipay/isv-auth`。
/// 商户号一律取自 [PaymentContext],忽略请求中的 mchNo防越权。
/// 复用 [AlipayIsvAuthService] 的授权链接生成能力;查询结果脱敏,不返回 appAuthToken。
/// 供通道商户详情页「授权操作」抽屉([findByChannelMchNo])与列表视图([list])共用。
@PermCode(menuCode = PermCodes.Merchant.AlipayIsvAuth.MENU)
@Validated
@Tag(name = "支付宝服务商代运营授权(商户端)")
@RestController
@RequestMapping("/mch/alipay/isv-auth")
@RequiredArgsConstructor
public class MchAlipayIsvAuthController {
private final AlipayIsvAuthService alipayIsvAuthService;
private final AlipayIsvChannelMerchantManager alipayIsvChannelMerchantManager;
private final PaymentContext paymentContext;
/// 当前登录商户号(上下文必有;缺则视为会话异常)
private String requireMchNo() {
String mchNo = paymentContext.getMchNo();
if (mchNo == null || mchNo.isBlank()) {
// 商户上下文缺失
throw new BizInfoException(CommonCode.FAIL_CODE, "pay.error.assist.mchContextMissing");
}
return mchNo;
}
/// 列表:当前商户名下所有支付宝服务商通道商户绑定(脱敏,仅返回是否已授权)
@PermCode(code = PermCodes.Action.VIEW)
@Operation(summary = "当前商户的支付宝服务商通道商户列表")
@GetMapping("/list")
public Result<List<MchAlipayIsvAuthResult>> list() {
String mchNo = requireMchNo();
List<AlipayIsvChannelMerchant> entities = alipayIsvChannelMerchantManager
.lambdaQuery()
.eq(AlipayIsvChannelMerchant::getMchNo, mchNo)
.orderByAsc(AlipayIsvChannelMerchant::getCreateTime)
.list();
List<MchAlipayIsvAuthResult> list = entities.stream()
.map(this::toResult)
.toList();
return Res.ok(list);
}
/// 查询单个通道商户的授权状态(供通道商户详情页抽屉展示,脱敏)
@PermCode(code = PermCodes.Action.VIEW)
@Operation(summary = "查询单个通道商户授权状态")
@GetMapping("/find")
public Result<MchAlipayIsvAuthResult> findByChannelMchNo(
@RequestParam("channelMchNo") String channelMchNo) {
return Res.ok(toResult(requireOwned(channelMchNo)));
}
/// 生成代运营授权链接(先校验归属当前商户,再复用核心 Service
@PermCode(code = PermCodes.Action.MANAGE)
@Operation(summary = "生成代运营授权链接")
@PostMapping("/gen-auth-url")
public Result<AlipayIsvAuthUrlResult> genAuthUrl(@RequestBody @Validated AlipayIsvAuthParam param) {
// 显式防越权channelMchNo 必须归属当前商户
requireOwned(param.getChannelMchNo());
return Res.ok(alipayIsvAuthService.genAuthUrl(param));
}
/// 获取代运营授权回调地址(用于支付宝开放平台配置参考)
@PermCode(code = PermCodes.Action.VIEW)
@Operation(summary = "获取代运营授权回调地址")
@GetMapping("/auth-callback-url")
public Result<String> getAuthCallbackUrl() {
return Res.ok(alipayIsvAuthService.getAuthCallbackUrl());
}
/// 校验通道商户号归属当前商户并返回实体TenantLine 兜底之外的显式防越权)
private AlipayIsvChannelMerchant requireOwned(String channelMchNo) {
AlipayIsvChannelMerchant entity = alipayIsvChannelMerchantManager
.lambdaQuery()
.eq(AlipayIsvChannelMerchant::getChannelMchNo, channelMchNo)
.oneOpt()
.orElseThrow(() -> new DataNotExistException("error.payment.channel.channelMerchantNotExist"));
if (!Objects.equals(entity.getMchNo(), requireMchNo())) {
// 通道商户不属于当前商户(复用通用归属校验文案)
throw new BizInfoException(CommonCode.FAIL_CODE, "error.payment.merchant.storeNoMatch");
}
return entity;
}
/// 实体转脱敏结果(不下发 appAuthToken
private MchAlipayIsvAuthResult toResult(AlipayIsvChannelMerchant entity) {
var result = new MchAlipayIsvAuthResult();
result.setChannelMchNo(entity.getChannelMchNo());
result.setProduct(entity.getProduct());
result.setAlipayUserId(entity.getAlipayUserId());
result.setAuthorized(entity.getAppAuthToken() != null && !entity.getAppAuthToken().isBlank());
return result;
}
}

View File

@@ -0,0 +1,32 @@
package cn.daxpay.open.channel.alipay.result.isv;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/// # 支付宝服务商代运营授权列表项(商户端,脱敏)
///
/// 供商户端「业务配置 > 支付宝ISV授权」使用。
/// 不返回应用授权令牌(对照 [AlipayIsvChannelMerchantResult#getAppAuthToken]
/// 仅暴露是否已授权,避免敏感凭据下发到商户端。
@Data
@Accessors(chain = true)
@Schema(title = "支付宝服务商代运营授权列表项(商户端)")
public class MchAlipayIsvAuthResult {
/// 通道商户号
@Schema(description = "通道商户号")
private String channelMchNo;
/// 所属支付产品
@Schema(description = "所属支付产品")
private String product;
/// 子商户支付宝识别码(2088开头的16位数字)
@Schema(description = "子商户支付宝识别码")
private String alipayUserId;
/// 是否已授权(appAuthToken 是否已绑定)
@Schema(description = "是否已授权")
private Boolean authorized;
}

View File

@@ -0,0 +1,80 @@
package cn.daxpay.open.channel.wechat.controller.direct;
import cn.daxpay.open.channel.wechat.dao.direct.WechatDirectChannelMerchantManager;
import cn.daxpay.open.channel.wechat.entity.direct.WechatDirectChannelMerchant;
import cn.daxpay.open.channel.wechat.param.direct.WechatDirectKeyConfigParam;
import cn.daxpay.open.channel.wechat.result.direct.WechatDirectKeyConfigResult;
import cn.daxpay.open.channel.wechat.service.direct.WechatDirectKeyConfigService;
import cn.daxpay.open.payment.common.context.PaymentContext;
import cn.daxpay.open.platform.core.code.CommonCode;
import cn.daxpay.open.platform.core.exception.BizInfoException;
import cn.daxpay.open.platform.core.exception.DataNotExistException;
import cn.daxpay.open.platform.core.rest.Res;
import cn.daxpay.open.platform.core.rest.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Objects;
/// # 微信直连密钥配置(商户端)
///
/// 对照运营端 [WechatDirectChannelMerchantController],路径前缀 `/mch/wechat/direct-key-config`。
/// 商户号一律取自 [PaymentContext],忽略请求中的 mchNo防越权。
@Validated
@Tag(name = "微信直连密钥配置(商户端)")
@RestController
@RequestMapping("/mch/wechat/direct-key-config")
@RequiredArgsConstructor
public class MchWechatDirectKeyConfigController {
private final WechatDirectKeyConfigService wechatDirectKeyConfigService;
private final WechatDirectChannelMerchantManager wechatDirectChannelMerchantManager;
private final PaymentContext paymentContext;
/// 当前登录商户号(上下文必有;缺则视为会话异常)
private String requireMchNo() {
String mchNo = paymentContext.getMchNo();
if (mchNo == null || mchNo.isBlank()) {
// 商户上下文缺失
throw new BizInfoException(CommonCode.FAIL_CODE, "pay.error.assist.mchContextMissing");
}
return mchNo;
}
/// 校验通道商户号归属当前商户TenantLine 兜底之外的显式防越权)
private void assertOwned(String channelMchNo) {
WechatDirectChannelMerchant entity = wechatDirectChannelMerchantManager
.findByChannelMchNo(channelMchNo)
.orElseThrow(() -> new DataNotExistException("error.payment.channel.channelMerchantNotExist"));
if (!Objects.equals(entity.getMchNo(), requireMchNo())) {
// 通道商户不属于当前商户
throw new BizInfoException(CommonCode.FAIL_CODE, "error.payment.merchant.storeNoMatch");
}
}
@Operation(summary = "根据通道商户号查询密钥配置")
@GetMapping("/find-key-config")
public Result<WechatDirectKeyConfigResult> findKeyConfig(
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}") String channelMchNo) {
this.assertOwned(channelMchNo);
return Res.ok(wechatDirectKeyConfigService.findByChannelMchNo(channelMchNo).toResult());
}
@Operation(summary = "保存密钥配置")
@PostMapping("/save-key-config")
public Result<Void> saveKeyConfig(@RequestBody @Validated WechatDirectKeyConfigParam param) {
this.assertOwned(param.getChannelMchNo());
// 强制当前商户号,忽略客户端传入(防越权)
param.setMchNo(requireMchNo());
wechatDirectKeyConfigService.save(param);
return Res.ok();
}
}

View File

@@ -4,8 +4,10 @@ import cn.daxpay.open.payment.common.context.PaymentContext;
import cn.daxpay.open.payment.merchant.dao.channel.ChannelMerchantManager;
import cn.daxpay.open.payment.merchant.entity.channel.ChannelMerchant;
import cn.daxpay.open.payment.wx.param.WxChannelAppCapabilityBatchParam;
import cn.daxpay.open.payment.wx.result.WxCapabilityOption;
import cn.daxpay.open.payment.wx.result.WxChannelAppCapabilityResult;
import cn.daxpay.open.payment.wx.service.WxChannelAppCapabilityService;
import cn.daxpay.open.payment.wx.service.WxPlatformAppCapabilityService;
import cn.daxpay.open.platform.core.annotation.PermCode;
import cn.daxpay.open.platform.core.code.CommonCode;
import cn.daxpay.open.platform.core.code.PermCodes;
@@ -14,6 +16,7 @@ import cn.daxpay.open.platform.core.exception.config.ConfigErrorException;
import cn.daxpay.open.platform.core.rest.Res;
import cn.daxpay.open.platform.core.rest.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
@@ -36,6 +39,7 @@ import java.util.Objects;
public class MchWxChannelAppCapabilityController {
private final WxChannelAppCapabilityService wxChannelAppCapabilityService;
private final WxPlatformAppCapabilityService wxPlatformAppCapabilityService;
private final ChannelMerchantManager channelMerchantManager;
private final PaymentContext paymentContext;
@@ -69,6 +73,16 @@ public class MchWxChannelAppCapabilityController {
return Res.ok(wxChannelAppCapabilityService.listByChannelMchNo(channelMchNo));
}
@PermCode(code = PermCodes.Action.VIEW)
@Operation(summary = "按产品查询可绑定能力候选")
@GetMapping("/list-supported-capabilities")
public Result<List<WxCapabilityOption>> listSupportedCapabilities(
@NotBlank(message = "{validation.field.product.notBlank}")
@Parameter(description = "支付产品编码") @RequestParam String product) {
// 只读产品元数据,无需通道商户归属校验
return Res.ok(wxPlatformAppCapabilityService.listSupportedCapabilities(product));
}
@PermCode(code = PermCodes.Action.MANAGE)
@Operation(summary = "全量保存能力绑定")
@PostMapping("/save-batch")

View File

@@ -122,6 +122,12 @@ public interface PermCodes {
String MENU = "merchant:easypay";
}
/// 支付宝服务商代运营授权 menuCode=merchant:alipay-isv-auth
interface AlipayIsvAuth {
/// 菜单编码
String MENU = "merchant:alipay-isv-auth";
}
/// 门店 menuCode=merchant:store
interface Store {
/// 菜单编码