| | |
| | | jdbc.update("update merchant_onboarding_application set status='pending', wechat_config_status='failed', wechat_config_response_code=?, wechat_config_response_message=?, reject_reason=?, reviewed_at=null where id=?", code, message, "微信商户配置失败:" + reason, application.get("id")); |
| | | } else { |
| | | jdbc.update("update merchant_onboarding_application set wechat_config_status='approved', wechat_config_response_code=?, wechat_config_response_message=?, reject_reason=null where id=?", code, message, application.get("id")); |
| | | continuePostOnboardingProvisioning(clean(application.get("applicationNo"))); |
| | | continueProvisioningAfterCommit(clean(application.get("applicationNo"))); |
| | | } |
| | | return true; |
| | | } |
| | |
| | | jdbc.update("update merchant_onboarding_application set status='pending', business_open_status='failed', business_open_apply_no=coalesce(nullif(?,''),business_open_apply_no), business_open_response_code=?, business_open_response_message=?, reject_reason=?, reviewed_at=null where id=?", applyNo, code, message, "商户业务开通失败:" + reason, application.get("id")); |
| | | } else if ("S".equalsIgnoreCase(businessStatus)) { |
| | | jdbc.update("update merchant_onboarding_application set business_open_status='approved', business_open_apply_no=coalesce(nullif(?,''),business_open_apply_no), business_open_response_code=?, business_open_response_message=?, reject_reason=null where id=?", applyNo, code, message, application.get("id")); |
| | | completePostOnboardingProvisioning(application.get("id")); |
| | | completeProvisioningAfterCommit(application.get("id")); |
| | | } else { |
| | | jdbc.update("update merchant_onboarding_application set business_open_status='processing', business_open_apply_no=coalesce(nullif(?,''),business_open_apply_no), business_open_response_code=?, business_open_response_message=? where id=?", applyNo, code, message, application.get("id")); |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * 把「继续做后续配置」推迟到当前事务提交之后。 |
| | | * |
| | | * <p>两个回调都是先 {@code select ... for update} 锁住进件记录,再走到后续配置;而后续配置会 |
| | | * **同步调用汇付的 HTTP 接口**(微信小程序配置、商户业务开通)。在事务里调外部接口意味着: |
| | | * 拿着这条进件记录的行锁和一条数据库连接,去等一个不受控的网络响应。慢一点就把连接池占住, |
| | | * 进而让**整张 merchant_onboarding_application 上的查询全部排队**——2026-09-15 后台全站 |
| | | * 不可用就是这个形态(一个空闲未提交事务持着元数据锁,DDL 无限等锁,10 条连接被借光)。 |
| | | * |
| | | * <p>提交后再做还有第二个好处:回调只负责把通道结果落库,后续配置自己有重试入口 |
| | | * (业务开通回调、主动轮询),不会被回调事务的成败牵连。 |
| | | */ |
| | | private void continueProvisioningAfterCommit(String applicationNo) { |
| | | runAfterCommit(() -> continuePostOnboardingProvisioning(applicationNo)); |
| | | } |
| | | |
| | | private void completeProvisioningAfterCommit(Object applicationId) { |
| | | runAfterCommit(() -> completePostOnboardingProvisioning(applicationId)); |
| | | } |
| | | |
| | | /** 有活动事务就注册 afterCommit,否则立即执行(例如被非事务入口直接调用)。 */ |
| | | private void runAfterCommit(Runnable task) { |
| | | if (!TransactionSynchronizationManager.isSynchronizationActive()) { |
| | | task.run(); |
| | | return; |
| | | } |
| | | TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { |
| | | @Override |
| | | public void afterCommit() { |
| | | task.run(); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 进件审核通过后补一次收款能力快照。 |
| | | * |
| | | * <p>必须在当前事务提交之后再刷新:能力快照用 {@code REQUIRES_NEW} 在独立事务里写,而微信商户配置 |
| | |
| | | * {@code CannotAcquireLockException} 失败,快照永远补不上,商户虽然进件通过却始终无法收款。 |
| | | */ |
| | | private void refreshCapabilityAfterCommit(Long applicationId) { |
| | | if (!TransactionSynchronizationManager.isSynchronizationActive()) { |
| | | paymentReadinessService.refreshQuietly(applicationId); |
| | | return; |
| | | } |
| | | TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { |
| | | @Override |
| | | public void afterCommit() { |
| | | paymentReadinessService.refreshQuietly(applicationId); |
| | | } |
| | | }); |
| | | runAfterCommit(() -> paymentReadinessService.refreshQuietly(applicationId)); |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |