Bläddra i källkod

@
fix: sign_scene 改为前端下拉选择,不再硬编码

- authorizeApply/queryAgreement 接受 signScene 参数
- Controller 从请求中提取 sign_scene 传入
- 前端 AccountAPI.authorizeApply 增加 sign_scene 参数
- 前端 handleGoTab("authorize") 弹出 ElMessageBox 让用户输入场景码
- authorizeResult 新增 sign_scene 字段,默认 INDUSTRY|PURCHASE(文档预定义值)
- external_agreement_no 不为空时 sign_scene 不能为 DEFAULT|DEFAULT
@

alphaH 1 dag sedan
förälder
incheckning
973221bba1

+ 3 - 3
frontend/src/api/module_payment/account.ts

@@ -43,7 +43,7 @@ export const AccountAPI = {
     });
   },
 
-  authorizeApply(data: { enterprise_id: string }) {
+  authorizeApply(data: { enterprise_id: string; sign_scene?: string }) {
     return request<ApiResponse<{ sign_url: string; external_agreement_no: string }>>({
       url: `${API_PATH}/authorize/apply`,
       method: "post",
@@ -51,11 +51,11 @@ export const AccountAPI = {
     });
   },
 
-  queryAgreement(enterpriseId: string, externalAgreementNo: string) {
+  queryAgreement(enterpriseId: string, externalAgreementNo: string, signScene: string) {
     return request<ApiResponse<{ agreement_no: string; status: string }>>({
       url: `${API_PATH}/agreement/query`,
       method: "get",
-      params: { enterprise_id: enterpriseId, external_agreement_no: externalAgreementNo },
+      params: { enterprise_id: enterpriseId, external_agreement_no: externalAgreementNo, sign_scene: signScene },
     });
   },
 

+ 20 - 17
frontend/src/views/module_payment/account/index.vue

@@ -780,6 +780,7 @@ const authorizeResult = reactive({
   sign_url: "",
   external_agreement_no: "",
   agreement_no: "",
+  sign_scene: "INDUSTRY|PURCHASE",
   queryLoading: false,
 });
 
@@ -1222,25 +1223,11 @@ function resetBatchTransfer() {
   batchTransferFailCount.value = 0;
 }
 
-async function handleAuthorize() {
-  // if (!authorizeFormRef.value) return;
-  // await authorizeFormRef.value.validate(async (valid) => {
-  //   if (valid) {
-  //     authorizeLoading.value = true;
-  //     try {
-  //       const res = await AccountAPI.authorizeApply({
-  //         enterprise_id: authorizeForm.enterprise_id || currentEnterpriseId.value!,
-  //       });
-  //       authorizeResult.sign_url = res.data.sign_url || "";
-  //     } finally {
-  //       authorizeLoading.value = false;
-  //     }
-  //   }
-  // });
-
+async function handleAuthorize(signScene?: string) {
   try {
     const res = await AccountAPI.authorizeApply({
       enterprise_id: currentEnterpriseId.value!,
+      sign_scene: signScene || authorizeResult.sign_scene || "DEFAULT|DEFAULT",
     });
     const signHtml = res.data.data?.sign_url || "";
     const externalAgreementNo = res.data.data?.external_agreement_no || "";
@@ -1266,6 +1253,7 @@ async function handleQueryAgreement() {
     const res = await AccountAPI.queryAgreement(
       currentEnterpriseId.value!,
       authorizeResult.external_agreement_no,
+      authorizeResult.sign_scene,
     );
     const agreementNo = res.data.data?.agreement_no || "";
     if (agreementNo) {
@@ -1704,7 +1692,22 @@ function handleOverviewRefresh(data: any) {
 async function handleGoTab(tab: string) {
   console.log("goTab => ", tab);
   if (tab === "authorize") {
-    await handleAuthorize();
+    // 弹出场景码选择框(external_agreement_no 不为空时 sign_scene 不能为 DEFAULT|DEFAULT)
+    try {
+      const { value: signScene } = await ElMessageBox.prompt("请选择签约场景码", "转账授权签约", {
+        confirmButtonText: "确认签约",
+        cancelButtonText: "取消",
+        inputType: "textarea" as any,
+        inputValue: authorizeResult.sign_scene || "INDUSTRY|PURCHASE",
+        inputPlaceholder: "格式: INDUSTRY|XXXX,如 INDUSTRY|PURCHASE",
+      });
+      if (signScene) {
+        authorizeResult.sign_scene = signScene;
+        await handleAuthorize(signScene);
+      }
+    } catch {
+      // 用户取消
+    }
   } else if (tab === "create") {
     await handleCreate();
   } else if (tab === "deposit") {

+ 6 - 3
java/src/main/java/com/payment/platform/module/payment/account/controller/AccountController.java

@@ -105,15 +105,18 @@ public class AccountController {
     @PreAuthorize("@perm.hasAny('module_payment:account:authorize')")
     @PostMapping("/authorize/apply")
     public Result<Map<String, String>> authorizeApply(@RequestBody Map<String, Object> b) {
-        return Result.ok(alipayTransferService.authorizeApply((String) b.get("enterprise_id")));
+        return Result.ok(alipayTransferService.authorizeApply(
+                (String) b.get("enterprise_id"),
+                (String) b.getOrDefault("sign_scene", "DEFAULT|DEFAULT")));
     }
 
     @PreAuthorize("@perm.hasAny('module_payment:account:authorize')")
     @GetMapping("/agreement/query")
     public Result<Map<String, String>> queryAgreement(
             @RequestParam(name = "enterprise_id") String enterpriseId,
-            @RequestParam(name = "external_agreement_no") String externalAgreementNo) {
-        return Result.ok(alipayTransferService.queryAgreement(enterpriseId, externalAgreementNo));
+            @RequestParam(name = "external_agreement_no") String externalAgreementNo,
+            @RequestParam(name = "sign_scene", defaultValue = "DEFAULT|DEFAULT") String signScene) {
+        return Result.ok(alipayTransferService.queryAgreement(enterpriseId, externalAgreementNo, signScene));
     }
 
     @PreAuthorize("@perm.hasAny('module_payment:account:create')")

+ 5 - 6
java/src/main/java/com/payment/platform/module/payment/account/service/AlipayTransferService.java

@@ -108,16 +108,15 @@ public class AlipayTransferService {
     /**
      * alipay.user.agreement.page.sign — 页面跳转接口
      * 必填: personal_product_code, product_code, sign_scene, access_params
-     * 返回 sign_url + external_agreement_no(供后续 queryAgreement 反查
+     * sign_scene 由前端选择(external_agreement_no 不为空时不能为 DEFAULT|DEFAULT
      */
-    public Map<String, String> authorizeApply(String enterpriseId) {
-        // 确定性签约号,便于后续反查 — 不拼时间戳
+    public Map<String, String> authorizeApply(String enterpriseId, String signScene) {
         String externalAgreementNo = enterpriseId + "_agreement";
         try {
             AlipayUserAgreementPageSignModel model = new AlipayUserAgreementPageSignModel();
             model.setPersonalProductCode("GENERAL_WITHHOLDING_P");
             model.setProductCode("GENERAL_WITHHOLDING");
-            model.setSignScene("DEFAULT|DEFAULT");
+            model.setSignScene(signScene != null && !signScene.isBlank() ? signScene : "DEFAULT|DEFAULT");
             model.setExternalAgreementNo(externalAgreementNo);
             AccessParams accessParams = new AccessParams();
             accessParams.setChannel("ALIPAYAPP");
@@ -146,12 +145,12 @@ public class AlipayTransferService {
      * alipay.user.agreement.query — 通过 external_agreement_no 反查签约协议号
      * 用户完成签约后调用,获取支付宝侧的 agreement_no
      */
-    public Map<String, String> queryAgreement(String enterpriseId, String externalAgreementNo) {
+    public Map<String, String> queryAgreement(String enterpriseId, String externalAgreementNo, String signScene) {
         try {
             AlipayUserAgreementQueryModel model = new AlipayUserAgreementQueryModel();
             model.setExternalAgreementNo(externalAgreementNo);
             model.setPersonalProductCode("GENERAL_WITHHOLDING_P");
-            model.setSignScene("DEFAULT|DEFAULT");
+            model.setSignScene(signScene != null && !signScene.isBlank() ? signScene : "DEFAULT|DEFAULT");
             model.setThirdPartyType("PARTNER");
 
             AlipayUserAgreementQueryRequest request = new AlipayUserAgreementQueryRequest();

+ 1 - 1
java/src/main/resources/application.yml

@@ -38,7 +38,7 @@ spring:
 
   data:
     redis:
-      host: redis
+      host: localhost
       port: 6379
       database: 1
       password: '123456#xjz'

+ 3 - 0
logs/payment-platform-error.log

@@ -0,0 +1,3 @@
+2026-07-15 11:11:17.278 [scheduling-1] ERROR c.p.p.common.utils.RedisLockUtil - 获取分布式锁失败: key=retry:dealing_transfers, error=Unable to connect to Redis
+2026-07-15 11:12:19.568 [scheduling-1] ERROR c.p.p.common.utils.RedisLockUtil - 获取分布式锁失败: key=retry:dealing_transfers, error=Unable to connect to Redis
+2026-07-15 11:13:21.844 [scheduling-1] ERROR c.p.p.common.utils.RedisLockUtil - 获取分布式锁失败: key=retry:dealing_transfers, error=Unable to connect to Redis

+ 38 - 0
logs/payment-platform.log

@@ -0,0 +1,38 @@
+2026-07-15 11:11:04.900 [background-preinit] INFO  o.h.validator.internal.util.Version - HV000001: Hibernate Validator 8.0.1.Final
+2026-07-15 11:11:04.996 [main] INFO  c.p.platform.PaymentApplication - Starting PaymentApplication using Java 21.0.11 with PID 35140 (D:\project2\payment-platform\java\target\classes started by 1 in D:\project2\payment-platform)
+2026-07-15 11:11:04.997 [main] INFO  c.p.platform.PaymentApplication - The following 1 profile is active: "dev"
+2026-07-15 11:11:06.063 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Multiple Spring Data modules found, entering strict repository configuration mode
+2026-07-15 11:11:06.066 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Bootstrapping Spring Data Redis repositories in DEFAULT mode.
+2026-07-15 11:11:06.139 [main] INFO  o.s.d.r.c.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 52 ms. Found 0 Redis repository interfaces.
+2026-07-15 11:11:06.828 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port 8001 (http)
+2026-07-15 11:11:06.839 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8001"]
+2026-07-15 11:11:06.840 [main] INFO  o.a.catalina.core.StandardService - Starting service [Tomcat]
+2026-07-15 11:11:06.841 [main] INFO  o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.20]
+2026-07-15 11:11:06.895 [main] INFO  o.a.c.c.C.[.[localhost].[/api/v1] - Initializing Spring embedded WebApplicationContext
+2026-07-15 11:11:06.895 [main] INFO  o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 1722 ms
+2026-07-15 11:11:06.920 [main] INFO  c.a.d.s.b.a.DruidDataSourceAutoConfigure - Init DruidDataSource
+2026-07-15 11:11:08.360 [main] INFO  c.alibaba.druid.pool.DruidDataSource - {dataSource-1} inited
+2026-07-15 11:11:09.318 [main] WARN  c.p.p.c.alipay.AlipayClientFactory - 支付宝默认配置不完整,将仅使用服务商/租户专属客户端
+2026-07-15 11:11:09.585 [main] INFO  c.p.platform.core.oss.OssService - OSS client initialized: bucket=hunanxiaojunzioss, endpoint=oss-cn-beijing.aliyuncs.com
+2026-07-15 11:11:10.320 [main] INFO  c.p.p.m.p.e.s.EnterpriseNameSyncScheduler - [企业名称同步] 调度器启动
+2026-07-15 11:11:10.675 [main] INFO  c.p.p.m.p.n.s.NotificationService - 已注册 7 个通知处理器: [AccountHandler, BillHandler, EmployeeHandler, EnterpriseHandler, InstitutionHandler, OrderHandler, VoucherHandler]
+2026-07-15 11:11:11.422 [main] INFO  o.s.s.web.DefaultSecurityFilterChain - Will secure any request with [org.springframework.security.web.session.DisableEncodeUrlFilter@3531509c, org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@23169374, org.springframework.security.web.context.SecurityContextHolderFilter@3d0ce151, org.springframework.security.web.header.HeaderWriterFilter@7962a364, org.springframework.web.filter.CorsFilter@4f2b1a2f, org.springframework.security.web.authentication.logout.LogoutFilter@8bd9d08, com.payment.platform.core.security.JwtAuthFilter@271a140c, com.payment.platform.core.security.TenantApiKeyAuthFilter@462b239f, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@2b569858, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@1b84d03d, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@6c2a33af, org.springframework.security.web.session.SessionManagementFilter@134ec0f3, org.springframework.security.web.access.ExceptionTranslationFilter@25b87e1b, org.springframework.security.web.access.intercept.AuthorizationFilter@1390a43a]
+2026-07-15 11:11:12.046 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8001"]
+2026-07-15 11:11:12.067 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port 8001 (http) with context path '/api/v1'
+2026-07-15 11:11:12.268 [main] INFO  c.p.platform.PaymentApplication - Started PaymentApplication in 7.97 seconds (process running for 9.101)
+2026-07-15 11:11:12.313 [scheduling-1] INFO  c.p.p.m.p.f.s.F2fTradePollScheduler - [收款轮询] 定时任务开始执行
+2026-07-15 11:11:12.483 [scheduling-1] INFO  c.p.p.m.p.f.s.F2fTradePollScheduler - [收款轮询] 无待轮询交易
+2026-07-15 11:11:15.453 [main] INFO  c.p.p.core.config.AppStartupRunner - 系统配置缓存初始化完成
+2026-07-15 11:11:17.182 [main] INFO  c.p.p.core.config.AppStartupRunner - 数据字典缓存初始化完成 (10 个字典类型)
+2026-07-15 11:11:17.278 [scheduling-1] ERROR c.p.p.common.utils.RedisLockUtil - 获取分布式锁失败: key=retry:dealing_transfers, error=Unable to connect to Redis
+2026-07-15 11:12:12.494 [scheduling-1] INFO  c.p.p.m.p.f.s.F2fTradePollScheduler - [收款轮询] 定时任务开始执行
+2026-07-15 11:12:12.544 [scheduling-1] INFO  c.p.p.m.p.f.s.F2fTradePollScheduler - [收款轮询] 无待轮询交易
+2026-07-15 11:12:19.568 [scheduling-1] ERROR c.p.p.common.utils.RedisLockUtil - 获取分布式锁失败: key=retry:dealing_transfers, error=Unable to connect to Redis
+2026-07-15 11:13:12.550 [scheduling-1] INFO  c.p.p.m.p.f.s.F2fTradePollScheduler - [收款轮询] 定时任务开始执行
+2026-07-15 11:13:12.600 [scheduling-1] INFO  c.p.p.m.p.f.s.F2fTradePollScheduler - [收款轮询] 无待轮询交易
+2026-07-15 11:13:21.844 [scheduling-1] ERROR c.p.p.common.utils.RedisLockUtil - 获取分布式锁失败: key=retry:dealing_transfers, error=Unable to connect to Redis
+2026-07-15 11:13:22.907 [tomcat-handler-0] INFO  o.a.c.c.C.[.[localhost].[/api/v1] - Initializing Spring DispatcherServlet 'dispatcherServlet'
+2026-07-15 11:13:22.907 [tomcat-handler-0] INFO  o.s.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
+2026-07-15 11:13:22.909 [tomcat-handler-0] INFO  o.s.web.servlet.DispatcherServlet - Completed initialization in 2 ms
+2026-07-15 11:13:43.974 [SpringApplicationShutdownHook] INFO  c.alibaba.druid.pool.DruidDataSource - {dataSource-1} closing ...
+2026-07-15 11:13:43.976 [SpringApplicationShutdownHook] INFO  c.alibaba.druid.pool.DruidDataSource - {dataSource-1} closed