| | |
| | | import static org.mockito.ArgumentMatchers.anyString; |
| | | import static org.mockito.ArgumentMatchers.eq; |
| | | import static org.mockito.Mockito.never; |
| | | import static org.mockito.Mockito.times; |
| | | import static org.mockito.Mockito.verify; |
| | | import static org.mockito.Mockito.when; |
| | | |
| | |
| | | assertThat(recordsOf(fetch(null, null, null, "P"))).isEmpty(); |
| | | } |
| | | |
| | | /** |
| | | * 多家商家共用同一个汇付商户号时,同一条结算流水只能出现一次。 |
| | | * |
| | | * <p>结算流水属于**商户号**(一个收款主体),不属于商家:连锁各门店是独立商家、共用同一个收款主体, |
| | | * 通道里就是一条流水。按商家逐家去问通道,会把同一条流水按共用它的商家数重复印出来——页面上看起来 |
| | | * 像结了 N 笔钱(7 家共用就是 7 行一模一样的流水号/金额/到账卡)。 |
| | | */ |
| | | @Test |
| | | void oneSettlementFlowIsNotRepeatedForEveryMerchantThatSharesTheHuifuId() { |
| | | String sharedHuifuId = "TF" + suffix + "SHARED"; |
| | | approveMerchantOn(sharedHuifuId, "Settlement chain A " + suffix, "NORMAL", "1", true, "SHARED-A", "餐饮服务", "13800000005"); |
| | | approveMerchantOn(sharedHuifuId, "Settlement chain B " + suffix, "NORMAL", "1", true, "SHARED-B", "餐饮服务", "13800000006"); |
| | | |
| | | List<String> sharedFlows = recordsOf(fetch(null, null, null, null)).stream() |
| | | .map(record -> String.valueOf(record.get("providerTransactionNo"))) |
| | | .filter(flow -> flow.endsWith(sharedHuifuId)) |
| | | .toList(); |
| | | |
| | | assertThat(sharedFlows) |
| | | .as("一条流水只该出现一次,不能按共用它的商家数重复") |
| | | .containsExactlyInAnyOrder("TX-S-" + sharedHuifuId, "TX-F-" + sharedHuifuId); |
| | | verify(settlementRecordGateway, times(1)).query(eq(sharedHuifuId), any(), any(), anyInt()); |
| | | // 格子放不下几家商家的名字(7 家就 50 多个字符),所以显示成「共用收款主体(N 家)」, |
| | | // 名单随行带给前端做悬停提示。 |
| | | assertThat(recordsOf(fetch(null, null, null, null))) |
| | | .filteredOn(record -> String.valueOf(record.get("providerTransactionNo")).endsWith(sharedHuifuId)) |
| | | .isNotEmpty() |
| | | .allSatisfy(record -> { |
| | | assertThat(record).containsEntry("merchantName", "共用收款主体(2 家)"); |
| | | assertThat(record.get("merchantNames")) |
| | | .asInstanceOf(org.assertj.core.api.InstanceOfAssertFactories.list(String.class)) |
| | | .containsExactly("Settlement chain A " + suffix, "Settlement chain B " + suffix); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 按商家搜索照样能搜到共用的那条流水。 |
| | | * |
| | | * <p>筛选是在**数据库那一层**做的(先定"查哪些商家的收款主体",再去问通道),不是拿格子里的显示 |
| | | * 文本去匹配;所以格子改成「共用收款主体(N 家)」不会让搜索失效。而且筛到只剩一家时,那一格就 |
| | | * 显示这一家的名字——运营按商家查的时候,看到的就是自己查的那家。 |
| | | */ |
| | | @Test |
| | | void searchingByOneOfTheSharingMerchantsStillFindsTheSharedFlow() { |
| | | String sharedHuifuId = "TF" + suffix + "SEARCH"; |
| | | approveMerchantOn(sharedHuifuId, "Settlement search A " + suffix, "NORMAL", "1", true, "SEARCH-A", "餐饮服务", "13800000007"); |
| | | approveMerchantOn(sharedHuifuId, "Settlement search B " + suffix, "NORMAL", "1", true, "SEARCH-B", "餐饮服务", "13800000008"); |
| | | |
| | | List<Map<String, Object>> rows = recordsOf(fetch("Settlement search A " + suffix, null, null, null)); |
| | | |
| | | assertThat(rows) |
| | | .as("按商家筛的是「看谁名下的收款主体」,共用主体下也能搜到") |
| | | .filteredOn(record -> String.valueOf(record.get("providerTransactionNo")).endsWith(sharedHuifuId)) |
| | | .isNotEmpty() |
| | | .allSatisfy(record -> assertThat(record).containsEntry("merchantName", "Settlement search A " + suffix)); |
| | | verify(settlementRecordGateway).query(eq(sharedHuifuId), any(), any(), anyInt()); |
| | | } |
| | | |
| | | /** 关闭的子商户回的是通道原话;它只在真的被查到时才会成为一条失败提示。 */ |
| | | private void stubProvider() { |
| | | when(settlementRecordGateway.query(anyString(), anyString(), anyString(), anyInt())) |
| | |
| | | /** 建一家已进件通过的商户并写下通道能力快照,返回它的斗拱子商户号。 */ |
| | | private String approveMerchant(String merchantName, String merchantStat, String cashSwitch, boolean verified, |
| | | String tag, String merchantType, String contactMobile) { |
| | | return approveMerchantOn("TF" + suffix + tag, merchantName, merchantStat, cashSwitch, verified, tag, merchantType, contactMobile); |
| | | } |
| | | |
| | | /** 同一个商户号可以被多家商家共用(连锁各门店各是一个独立商家),所以商户号由调用方给。 */ |
| | | private String approveMerchantOn(String huifuId, String merchantName, String merchantStat, String cashSwitch, |
| | | boolean verified, String tag, String merchantType, String contactMobile) { |
| | | jdbc.update("insert into merchant(merchant_name, merchant_type, contact_mobile, status, deleted) values (?, ?, ?, 1, 0)", |
| | | merchantName, merchantType, contactMobile); |
| | | Long merchantId = jdbc.queryForObject("select max(id) from merchant", Long.class); |
| | | jdbc.update("insert into merchant_store(merchant_id, store_name, status, deleted) values (?, ?, 1, 0)", merchantId, merchantName); |
| | | Long storeId = jdbc.queryForObject("select max(id) from merchant_store", Long.class); |
| | | String huifuId = "TF" + suffix + tag; |
| | | jdbc.update(""" |
| | | insert into merchant_onboarding_application(merchant_id, store_id, application_no, subject_type, subject_name, |
| | | contact_name, contact_mobile, idempotency_key, status, provider_huifu_id, |