talent-card-server/src/main/java/com/talentcard/server/business/merchant/MerchantPaymentReadinessService.java
@@ -153,7 +153,8 @@ // The payment path never calls the provider itself: an unfilled snapshot is filled by the // onboarding completion, the scheduler, or an explicit admin refresh. if (unknown) return Readiness.blocked("商户收款能力待确认,暂时无法收款"); if (!isAutoSettleOff(application.autoSettleStatus())) return Readiness.blocked("商户自动结算尚未关闭,暂时无法收款"); // 不再要求「自动结算必须关闭」:未核销的钱由延迟户兜住(不可结算、不可取现), // 只有核销确认后才进基本户,因此自动结算开着也不会把钱提前结走。 if (!isCashAvailable(application.cashSwitchStatus())) return Readiness.blocked("商户取现能力不可用,暂时无法收款"); if (!isMerchantNormal(application.merchantStatus())) { return Readiness.blocked("斗拱子商户状态异常(" + application.merchantStatus() + "),暂时无法收款"); @@ -163,9 +164,6 @@ /** The payment integration is switched off, so provider-side network checks cannot be required. */ private Readiness localOnly(Application application) { if (!application.autoSettleStatus().isBlank() && !isAutoSettleOff(application.autoSettleStatus())) { return Readiness.blocked("商户自动结算尚未关闭,暂时无法收款"); } if (!application.cashSwitchStatus().isBlank() && !isCashAvailable(application.cashSwitchStatus())) { return Readiness.blocked("商户取现能力不可用,暂时无法收款"); } talent-card-server/src/test/java/com/talentcard/server/business/merchant/MerchantPaymentReadinessServiceTests.java
@@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mockingDetails; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -98,13 +99,15 @@ assertThat(merchant.huifuId()).isNotBlank(); } /** * 自动结算开着不再阻止收款:未核销的钱由延迟户兜住(不可结算、不可取现), * 只有核销确认后才进基本户,因此自动结算不会把未核销的钱提前结走。 */ @Test void rejectsANewPaidOrderOnceAutoSettlementIsBackOn() { void allowsANewPaidOrderEvenWhenAutoSettlementIsOn() { confirmSnapshot("NORMAL", "1", "1"); assertThatThrownBy(() -> readinessService.requireCollectingMerchant(merchantId, storeId)) .isInstanceOf(BusinessException.class) .hasMessageContaining("自动结算"); assertThat(readinessService.requireCollectingMerchant(merchantId, storeId).huifuId()).isNotBlank(); } @Test @@ -175,9 +178,10 @@ void reusesTheConfirmedSnapshotInsteadOfQueryingTheProviderAgain() { confirmSnapshot("NORMAL", "0", "1"); // 快照可能被定时刷新任务碰过,所以只断言「这次判定本身没有再查通道」。 int before = mockingDetails(capabilityGateway).getInvocations().size(); assertThat(readinessService.readiness(merchantId, storeId).ready()).isTrue(); verify(capabilityGateway, times(1)).query(any(HuifuMerchantCapabilityQueryCommand.class)); assertThat(mockingDetails(capabilityGateway).getInvocations().size()).isEqualTo(before); } @Test talent-card-server/src/test/java/com/talentcard/server/business/portal/AppOrderPortalServiceTests.java
@@ -126,14 +126,14 @@ assertThat(jdbc.queryForObject("select coupon_id from benefit_order where order_no = ?", Long.class, replacement.get("orderNo"))).isEqualTo(couponId); } /** 自动结算开着不再阻止下单:未核销的钱由延迟户兜住,不会被自动结算提前结走。 */ @Test void refusesPaidOrderWhileMerchantSettlementCapabilityIsMissing() { void allowsPaidOrderEvenWhenMerchantAutoSettlementIsOn() { jdbc.update("update merchant_onboarding_application set auto_settle_status='1' where id=?", onboardingId); assertThatThrownBy(() -> appOrderPortalService.create(talentId, new AppOrderCreateRequest(merchantId, productId, null), "app-order-blocked-" + System.nanoTime())) .isInstanceOf(BusinessException.class) .hasMessageContaining("自动结算"); assertThat(jdbc.queryForObject("select count(*) from benefit_order where merchant_id=?", Long.class, merchantId)).isZero(); Map<String, Object> created = appOrderPortalService.create(talentId, new AppOrderCreateRequest(merchantId, productId, null), "app-order-auto-settle-" + System.nanoTime()); assertThat(created.get("orderNo")).isNotNull(); } @Test