Просмотр исходного кода

feat: 现金纳入平台支付方式开关矩阵(031 US7 扩展)

- platformGroupsOf 不再剔除 CASH:平台设置矩阵商家维度新增现金行,
  缺行=开,保存走既有 upsert,无 SQL 变更
- isUsableForOrder 现金分支增加平台开关:平台关现金时用户自取/堂食
  现金单被拒、商家设置页可用列表不返回现金;商家建单与骑手闪送不受影响
- 测试:新增 platformDisabledCashBlocksUserDiningCash;矩阵行数 7→8;
  闸门 18/18、全量 742/742 通过
qmj 1 день назад
Родитель
Сommit
b062ada3be

+ 2 - 2
ruoyi-admin/src/main/java/com/ruoyi/app/pay/PayMethodConfigController.java

@@ -56,7 +56,7 @@ public class PayMethodConfigController extends BaseController {
         }
         List<PaymentMethodConfig> matrix = new ArrayList<>();
         for (PaymentMethodGateService.GateScope scope : PaymentMethodGateService.GateScope.values()) {
-            // 现金无平台开关行(031 US7/FR-006),矩阵只覆盖平台可管控组
+            // 现金已进商家维度矩阵(仅管控用户自取/堂食口径);骑手维度本就无现金组
             for (String group : PaymentMethodGateService.platformGroupsOf(scope)) {
                 PaymentMethodConfig row = existing.get(group + "@" + scope.name());
                 if (row == null) {
@@ -85,7 +85,7 @@ public class PayMethodConfigController extends BaseController {
             throw new ServiceException(MessageUtils.message("pay.method.config.invalid"));
         }
         for (PayMethodConfigItemDto item : dto.getItems()) {
-            // 组必须属于该维度的平台可管控集合(现金无平台开关行,CASH 提交在此拒绝)
+            // 组必须属于该维度的平台可管控集合(CASH 仅商家维度合法,骑手维度拒绝)
             if (item == null || item.getEnabled() == null
                     || (item.getEnabled() != 0 && item.getEnabled() != 1)
                     || !isValidScope(item.getScope())

+ 4 - 3
ruoyi-admin/src/test/java/com/ruoyi/app/pay/PayMethodConfigControllerTest.java

@@ -75,13 +75,14 @@ class PayMethodConfigControllerTest {
 
     @Test
     void listFillsMissingRowsAsEnabled() {
-        // 平台未配置:返回 MERCHANT 4 组 + RIDER_FLASH 3 组共 7 行,全部为开
+        // 平台未配置:返回 MERCHANT 5 组(含现金)+ RIDER_FLASH 3 组共 8 行,全部为开
         AjaxResult result = controller.list();
         assertEquals(HttpStatus.SUCCESS, result.get(AjaxResult.CODE_TAG));
         @SuppressWarnings("unchecked")
         List<PaymentMethodConfig> matrix = (List<PaymentMethodConfig>) result.get(AjaxResult.DATA_TAG);
-        assertEquals(7, matrix.size());
+        assertEquals(8, matrix.size());
         assertTrue(matrix.stream().allMatch(r -> Integer.valueOf(1).equals(r.getEnabled())));
+        assertTrue(matrix.stream().anyMatch(r -> "CASH".equals(r.getMethodCode()) && "MERCHANT".equals(r.getScope())));
     }
 
     @Test
@@ -90,7 +91,7 @@ class PayMethodConfigControllerTest {
                 .thenReturn(List.of(row("LINE_PAY", "MERCHANT", 0)));
         @SuppressWarnings("unchecked")
         List<PaymentMethodConfig> matrix = (List<PaymentMethodConfig>) controller.list().get(AjaxResult.DATA_TAG);
-        assertEquals(7, matrix.size());
+        assertEquals(8, matrix.size());
         assertEquals(0, matrix.stream()
                 .filter(r -> "LINE_PAY".equals(r.getMethodCode()) && "MERCHANT".equals(r.getScope()))
                 .findFirst().orElseThrow().getEnabled().intValue());

+ 4 - 7
ruoyi-system/src/main/java/com/ruoyi/system/service/PaymentMethodGateService.java

@@ -46,7 +46,7 @@ public class PaymentMethodGateService {
     public static final String GROUP_LINE_PAY = "LINE_PAY";
     /** 支付方式组代码:线下转账(payType=6)。 */
     public static final String GROUP_OFFLINE = "OFFLINE_TRANSFER";
-    /** 支付方式组代码:现金(payType=4);仅商家勾选管控,无平台开关行与就绪度(031 US7/FR-006)。 */
+    /** 支付方式组代码:现金(payType=4);平台开关管控用户自取/堂食口径,叠加商家勾选;无就绪度要求(031 US7/FR-006)。 */
     public static final String GROUP_CASH = "CASH";
 
     /** 现金 payType=4:原 assertUsable 路径(商家建单/闪送缺省现金)直接放行;用户单经 assertUsableForOrder 按订单类型管控。 */
@@ -143,6 +143,7 @@ public class PaymentMethodGateService {
         if (PAY_TYPE_CASH.equals(payType)) {
             if (orderType == null || scope == GateScope.RIDER_FLASH) return true;
             if (orderType != 1L && orderType != 2L) return false;
+            if (platformDisabledMap(scope).containsKey(GROUP_CASH)) return false;
             Set<String> selected = selectedGroups(scope, payeeUserId);
             return selected == null || selected.contains(GROUP_CASH);
         }
@@ -264,13 +265,9 @@ public class PaymentMethodGateService {
         return SCOPE_GROUPS.get(scope);
     }
 
-    /** 平台开关矩阵的组集合:值域剔除 CASH——现金无平台开关行(spec FR-006/US7);选择值域仍用 {@link #groupsOf}。 */
+    /** 平台开关矩阵的组集合:与维度组一致(现金进商家维度矩阵,标注仅自取/堂食;骑手维度本就无现金)。 */
     public static List<String> platformGroupsOf(GateScope scope) {
-        List<String> result = new ArrayList<>();
-        for (String group : groupsOf(scope)) {
-            if (!GROUP_CASH.equals(group)) result.add(group);
-        }
-        return result;
+        return groupsOf(scope);
     }
 
     /** payType → 组代码映射查询(保存/展示场景用);未知返回 null。 */

+ 26 - 1
ruoyi-system/src/test/java/com/ruoyi/system/service/PaymentMethodGateServiceTest.java

@@ -219,13 +219,38 @@ class PaymentMethodGateServiceTest {
         assertTrue(options.stream().noneMatch(o -> "4".equals(o.getPayType())), "商家未勾选现金时不返回现金项");
     }
 
+    @Test
+    void platformDisabledCashBlocksUserDiningCash() {
+        // 平台关现金:用户自取/堂食现金单被拒,即使商家勾选(或未设置)CASH
+        when(configMapper.selectList(any()))
+                .thenReturn(List.of(disabled("CASH", "MERCHANT")));
+        when(userMapper.selectById(7L)).thenReturn(merchant("COD,CASH"));
+        assertThrows(ServiceException.class, () ->
+                service.assertUsableForOrder("4", PaymentMethodGateService.GateScope.MERCHANT, 1L, 7L, 1L));
+        when(userMapper.selectById(7L)).thenReturn(merchant(null));
+        assertThrows(ServiceException.class, () ->
+                service.assertUsableForOrder("4", PaymentMethodGateService.GateScope.MERCHANT, 1L, 7L, 2L));
+        // 商家端设置页可用列表同样不再返回现金
+        List<PaymentMethodGateService.PayMethodOption> options =
+                service.listAvailable(PaymentMethodGateService.GateScope.MERCHANT, 1L, 7L, 1L);
+        assertTrue(options.stream().noneMatch(o -> "4".equals(o.getPayType())), "平台关现金后列表不返回现金项");
+        assertEquals(5, options.size());
+        // orderType 缺失(商家建单/闪送缺省现金)不受平台开关管控,保持现状放行
+        assertDoesNotThrow(() ->
+                service.assertUsableForOrder("4", PaymentMethodGateService.GateScope.MERCHANT, 1L, 7L, null));
+    }
+
     @Test
     void cashGroupOnlyInMerchantScopeDomains() {
-        // 值域:商家勾选可选 CASH;平台开关矩阵与骑手闪送维度不含 CASH
+        // 值域:商家勾选与平台开关矩阵均可选 CASH;骑手闪送维度不含 CASH
         assertTrue(PaymentMethodGateService.groupsOf(PaymentMethodGateService.GateScope.MERCHANT)
                 .contains(PaymentMethodGateService.GROUP_CASH));
         assertTrue(!PaymentMethodGateService.groupsOf(PaymentMethodGateService.GateScope.RIDER_FLASH)
                 .contains(PaymentMethodGateService.GROUP_CASH));
+        assertTrue(PaymentMethodGateService.platformGroupsOf(PaymentMethodGateService.GateScope.MERCHANT)
+                .contains(PaymentMethodGateService.GROUP_CASH));
+        assertTrue(!PaymentMethodGateService.platformGroupsOf(PaymentMethodGateService.GateScope.RIDER_FLASH)
+                .contains(PaymentMethodGateService.GROUP_CASH));
         assertTrue(!PaymentMethodGateService.allGroups().contains(PaymentMethodGateService.GROUP_CASH));
     }