yeg
2 天以前 3773bc4f2d966fb248f96c7e5cedce58c91df689
talent-card-server/src/test/java/com/talentcard/server/business/admin/MerchantManagedOnboardingTests.java
@@ -83,7 +83,8 @@
        service.adopt(fixture.merchantId(), huifuId, APPLY_NO, true);
        verify(gateway).queryApplication(eq(huifuId), eq(APPLY_NO));
        // 两次:一次是建记录前的配对核验,一次是进度刷新本身——两处都只拿这个通道申请单号定位。
        verify(gateway, times(2)).queryApplication(eq(huifuId), eq(APPLY_NO));
        assertThat(adoptedApplicationId(fixture.merchantId(), huifuId)).isNotNull();
    }
@@ -135,7 +136,7 @@
        Fixture fixture = merchantFixture("A甲乙丙丁戊己");
        stubConvergedChannel(huifuId, "622200001234", "external-card-token", "上海汇付支付服务有限公司", "汇付支付");
        Map<String, Object> preview = service.preview(fixture.merchantId(), huifuId);
        Map<String, Object> preview = service.preview(fixture.merchantId(), huifuId, APPLY_NO);
        assertThat(String.valueOf(preview.get("nameWarning"))).contains("没有任何相同字符");
        assertThat(preview).containsEntry("channelMerchantName", "上海汇付支付服务有限公司");
@@ -161,7 +162,7 @@
        service.adopt(first.merchantId(), huifuId, APPLY_NO, true);
        // 门店名与商家名一样时不重复印一遍——多数门店就是这样,列表里不该出现「某某商户(某某商户)」。
        jdbc.update("update merchant_store set store_name = ? where id = ?", first.key(), first.storeId());
        Map<String, Object> preview = service.preview(second.merchantId(), huifuId);
        Map<String, Object> preview = service.preview(second.merchantId(), huifuId, APPLY_NO);
        assertThat(preview.get("boundMerchants"))
                .asInstanceOf(org.assertj.core.api.InstanceOfAssertFactories.list(String.class))
@@ -187,7 +188,7 @@
        assertThat(jdbc.queryForObject("select count(*) from merchant_bank_card where merchant_id = ?",
                Integer.class, fixture.merchantId())).isEqualTo(1);
        // 已经纳管过的商户号在核验里只说明会复用原记录,不能报成「门店已有进件」这种硬拦截。
        Map<String, Object> preview = service.preview(fixture.merchantId(), huifuId);
        Map<String, Object> preview = service.preview(fixture.merchantId(), huifuId, APPLY_NO);
        assertThat(preview).containsEntry("alreadyManaged", true);
        assertThat((List<?>) preview.get("blockers")).isEmpty();
    }
@@ -225,7 +226,7 @@
        service.adopt(fixture.merchantId(), firstHuifuId, APPLY_NO, true);
        assertThat(service.preview(fixture.merchantId(), secondHuifuId).get("blockers"))
        assertThat(service.preview(fixture.merchantId(), secondHuifuId, APPLY_NO).get("blockers"))
                .asInstanceOf(org.assertj.core.api.InstanceOfAssertFactories.list(String.class))
                .anySatisfy(blocker -> assertThat(blocker).contains("同一门店出现两个收款主体"));
        assertThatThrownBy(() -> service.adopt(fixture.merchantId(), secondHuifuId, APPLY_NO, true))
@@ -381,7 +382,7 @@
        jdbc.update("update merchant set can_login_merchant_app = 0 where id = ?", fixture.merchantId());
        stubConvergedChannel(huifuId, "622200001234", "external-card-token");
        Map<String, Object> preview = service.preview(fixture.merchantId(), huifuId);
        Map<String, Object> preview = service.preview(fixture.merchantId(), huifuId, APPLY_NO);
        assertThat(String.valueOf(preview.get("loginNotice"))).contains("未开启商户端登录");
        // 提示不阻挡纳管:纳管只管收款绑定,账号由「商家查询」负责。
@@ -406,7 +407,7 @@
                """, fixture.merchantId(), "t01ok" + Math.abs(System.nanoTime()));
        stubConvergedChannel(huifuId, "622200001234", "external-card-token");
        assertThat(service.preview(fixture.merchantId(), huifuId).get("loginNotice")).isEqualTo("");
        assertThat(service.preview(fixture.merchantId(), huifuId, APPLY_NO).get("loginNotice")).isEqualTo("");
    }
    /**
@@ -450,8 +451,10 @@
        Map<String, Object> detail = channelDetail(huifuId, "622200001234", "token", "Y", true);
        detail.put("qry_cash_card_info_list", List.of(Map.of("card_no", "6222****1234", "token_no", "masked-token")));
        when(gateway.queryCapabilities(huifuId)).thenReturn(detail);
        // 申请单号这一关先放行,本用例只盯"没有可用结算卡":否则先撞上的是配对校验。
        when(gateway.queryApplication(anyString(), anyString())).thenReturn(Map.of("resp_code", "00000000"));
        assertThat(service.preview(fixture.merchantId(), huifuId).get("blockers"))
        assertThat(service.preview(fixture.merchantId(), huifuId, APPLY_NO).get("blockers"))
                .asInstanceOf(org.assertj.core.api.InstanceOfAssertFactories.list(String.class))
                .anySatisfy(blocker -> assertThat(blocker).contains("结算银行卡"));
        assertThatThrownBy(() -> service.adopt(fixture.merchantId(), huifuId, APPLY_NO, true))
@@ -459,6 +462,36 @@
                .hasMessageContaining("结算银行卡");
    }
    /**
     * 申请单号填错时必须在**建记录之前**被拦下,而且要说清是哪一栏错了。
     *
     * <p>错号不会误放(基础审核确认不了、收不了款),真正的问题是它留下一具尸体:记录建出来了、
     * 刷新报的却是「汇付查询未成功,请稍后刷新」——看起来像通道抖动;而重复纳管是幂等的、不会用
     * 新号覆盖旧号,运营只能解绑重来。通道对这两个字段是**一起**校验的(填错的号,包括"真实存在但
     * 属于别的商户号"的号,都回 `00000001 申请单不存在`),所以这里配一次对就能拦住。
     */
    @Test
    void adoptingWithAnApplyNumberThatDoesNotBelongToTheMerchantIsRejected() {
        String huifuId = uniqueHuifuId();
        Fixture fixture = merchantFixture("号不匹配");
        when(gateway.queryCapabilities(huifuId)).thenReturn(channelDetail(huifuId, "622200001234", "card-token", "Y", true));
        when(gateway.queryApplication(anyString(), anyString())).thenReturn(Map.of("resp_code", "00000001", "resp_desc", "申请单不存在"));
        assertThatThrownBy(() -> service.adopt(fixture.merchantId(), huifuId, "999999999999999999", true))
                .isInstanceOf(BusinessException.class)
                .hasMessageContaining("申请单号与该商户号不匹配")
                .hasMessageContaining("申请单不存在");
        // 一具尸体都不该留下:进件记录与结算卡都不建。
        assertThat(jdbc.queryForObject("select count(*) from merchant_onboarding_application where merchant_id = ?",
                Integer.class, fixture.merchantId())).isZero();
        assertThat(jdbc.queryForObject("select count(*) from merchant_bank_card where merchant_id = ?",
                Integer.class, fixture.merchantId())).isZero();
        // 核验那一步也要提前把这条说出来,运营不用等提交。
        assertThat(service.preview(fixture.merchantId(), huifuId, "999999999999999999").get("blockers"))
                .asInstanceOf(org.assertj.core.api.InstanceOfAssertFactories.list(String.class))
                .anySatisfy(blocker -> assertThat(blocker).contains("申请单不存在"));
    }
    private Long adoptedApplicationId(Long merchantId, String huifuId) {
        return jdbc.queryForObject("select id from merchant_onboarding_application where merchant_id = ? and provider_huifu_id = ?",
                Long.class, merchantId, huifuId);