ソースを参照

平台商品类别管理

wwh 2 週間 前
コミット
431304c8c2

+ 1 - 5
baqing-shop/src/main/java/com/ruoyi/web/modules/entry/service/impl/MerchantEntryApplyServiceImpl.java

@@ -203,11 +203,7 @@ public class MerchantEntryApplyServiceImpl implements IMerchantEntryApplyService
203 203
         MerchantCreateDTO bindDto = EntryApplyFormMapper.toMerchantCreateDTO(merchant, apply.getMemberId());
204 204
         accountBindService.bindAccountOnMerchantCreate(merchant.getMerchantId(), bindDto, operator);
205 205
 
206
-        String memberName = StringUtils.isNotEmpty(member.getMemberCode()) ? member.getMemberCode() : member.getNickName();
207
-        String adminName = memberName + member.getMemberId();
208
-        String initPassword = resolveInitPassword();
209
-        ShopCreateDTO shopDto = EntryApplyFormMapper.toShopCreateDTO(apply.getFormJson(), merchant.getMerchantId(),
210
-                member.getNickName(), adminName, initPassword);
206
+        ShopCreateDTO shopDto = EntryApplyFormMapper.toShopCreateDTO(apply.getFormJson(), merchant.getMerchantId());
211 207
         Long shopId = shopService.createShop(shopDto, operator);
212 208
 
213 209
         BizMerchantEntryApply update = new BizMerchantEntryApply();

+ 1 - 5
baqing-shop/src/main/java/com/ruoyi/web/modules/entry/support/EntryApplyFormMapper.java

@@ -103,8 +103,7 @@ public final class EntryApplyFormMapper
103 103
         return dto;
104 104
     }
105 105
 
106
-    public static ShopCreateDTO toShopCreateDTO(String formJson, Long merchantId, String loginName, String adminName,
107
-            String password)
106
+    public static ShopCreateDTO toShopCreateDTO(String formJson, Long merchantId)
108 107
     {
109 108
         ParsedForm parsed = parseFormJson(formJson);
110 109
         EntryApplyShopDTO shop = parsed.getShop();
@@ -118,9 +117,6 @@ public final class EntryApplyFormMapper
118 117
         dto.setShopAvatar(shop.getShopAvatar());
119 118
         dto.setShopPhone(shop.getShopPhone());
120 119
         dto.setShopDesc(shop.getShopDesc());
121
-        dto.setLoginName(loginName);
122
-        dto.setAdminName(adminName);
123
-        dto.setPassword(password);
124 120
         return dto;
125 121
     }
126 122
 

+ 2 - 0
baqing-shop/src/main/java/com/ruoyi/web/modules/store/constant/ShopConstants.java

@@ -35,6 +35,8 @@ public final class ShopConstants
35 35
 
36 36
     public static final String MSG_MERCHANT_CANNOT_OPEN = "请先完善该商户的经营与结算信息";
37 37
 
38
+    public static final String MSG_NO_MERCHANT_ACCOUNT = "该商户尚未配置经营账号,请先在商户管理中完成入驻绑定";
39
+
38 40
     public static final String MSG_DELETE_GOODS = "请先下架或处理完商品后再删除店铺";
39 41
 
40 42
     public static final String MSG_DELETE_ORDER = "存在未完成订单,无法删除";

+ 8 - 30
baqing-shop/src/main/java/com/ruoyi/web/modules/store/dto/ShopCreateDTO.java

@@ -1,7 +1,7 @@
1 1
 package com.ruoyi.web.modules.store.dto;
2 2
 
3 3
 /**
4
- * 添加店铺请求
4
+ * 添加店铺请求(经营账号由商户入驻绑定,不在本 DTO 中维护)
5 5
  */
6 6
 public class ShopCreateDTO
7 7
 {
@@ -9,17 +9,15 @@ public class ShopCreateDTO
9 9
 
10 10
     private String shopAvatar;
11 11
 
12
+    /** 选填 */
12 13
     private String shopPhone;
13 14
 
14 15
     private Long merchantId;
15 16
 
16 17
     private String shopDesc;
17 18
 
18
-    private String loginName;
19
-
20
-    private String adminName;
21
-
22
-    private String password;
19
+    /** 选填;未传时默认开业(0) */
20
+    private String shopStatus;
23 21
 
24 22
     public String getShopName()
25 23
     {
@@ -71,33 +69,13 @@ public class ShopCreateDTO
71 69
         this.shopDesc = shopDesc;
72 70
     }
73 71
 
74
-    public String getLoginName()
75
-    {
76
-        return loginName;
77
-    }
78
-
79
-    public void setLoginName(String loginName)
80
-    {
81
-        this.loginName = loginName;
82
-    }
83
-
84
-    public String getAdminName()
85
-    {
86
-        return adminName;
87
-    }
88
-
89
-    public void setAdminName(String adminName)
90
-    {
91
-        this.adminName = adminName;
92
-    }
93
-
94
-    public String getPassword()
72
+    public String getShopStatus()
95 73
     {
96
-        return password;
74
+        return shopStatus;
97 75
     }
98 76
 
99
-    public void setPassword(String password)
77
+    public void setShopStatus(String shopStatus)
100 78
     {
101
-        this.password = password;
79
+        this.shopStatus = shopStatus;
102 80
     }
103 81
 }

+ 33 - 48
baqing-shop/src/main/java/com/ruoyi/web/modules/store/service/impl/ShopServiceImpl.java

@@ -114,35 +114,9 @@ public class ShopServiceImpl implements IShopService
114 114
         {
115 115
             throw new ServiceException(ShopConstants.MSG_SHOP_NAME_EXISTS);
116 116
         }
117
-        BizMerchantAccount existingAccount = accountMapper.selectByMerchantId(dto.getMerchantId());
118
-        if (existingAccount == null)
117
+        if (accountMapper.selectByMerchantId(dto.getMerchantId()) == null)
119 118
         {
120
-            validateAccountOnFirstShop(dto);
121
-            if (accountMapper.countByLoginName(dto.getLoginName(), null) > 0)
122
-            {
123
-                throw new ServiceException(ShopConstants.MSG_LOGIN_EXISTS);
124
-            }
125
-            BizMerchantAccount account = new BizMerchantAccount();
126
-            account.setMerchantId(dto.getMerchantId());
127
-            account.setLoginName(dto.getLoginName());
128
-            account.setAdminName(dto.getAdminName());
129
-            account.setPassword(SecurityUtils.encryptPassword(dto.getPassword()));
130
-            account.setDelFlag(ShopConstants.DEL_NORMAL);
131
-            account.setCreateBy(operator);
132
-            accountMapper.insert(account);
133
-        }
134
-        else
135
-        {
136
-            if (!existingAccount.getLoginName().equals(dto.getLoginName()))
137
-            {
138
-                throw new ServiceException(ShopConstants.MSG_ACCOUNT_MISMATCH);
139
-            }
140
-            if (StringUtils.isNotEmpty(dto.getPassword()))
141
-            {
142
-                existingAccount.setPassword(SecurityUtils.encryptPassword(dto.getPassword()));
143
-                existingAccount.setUpdateBy(operator);
144
-                accountMapper.update(existingAccount);
145
-            }
119
+            throw new ServiceException(ShopConstants.MSG_NO_MERCHANT_ACCOUNT);
146 120
         }
147 121
         BizShop shop = new BizShop();
148 122
         shop.setMerchantId(dto.getMerchantId());
@@ -150,7 +124,7 @@ public class ShopServiceImpl implements IShopService
150 124
         shop.setShopAvatar(dto.getShopAvatar());
151 125
         shop.setShopPhone(dto.getShopPhone());
152 126
         shop.setShopDesc(dto.getShopDesc());
153
-        shop.setShopStatus(ShopConstants.STATUS_OPEN);
127
+        shop.setShopStatus(resolveShopStatusOnCreate(dto.getShopStatus()));
154 128
         shop.setDelFlag(ShopConstants.DEL_NORMAL);
155 129
         shop.setCreateBy(operator);
156 130
         shopMapper.insert(shop);
@@ -378,7 +352,16 @@ public class ShopServiceImpl implements IShopService
378 352
         }
379 353
         validateShopName(dto.getShopName());
380 354
         validateShopAvatar(dto.getShopAvatar());
381
-        validateShopPhone(dto.getShopPhone());
355
+        if (StringUtils.isNotEmpty(dto.getShopPhone()))
356
+        {
357
+            validateShopPhone(dto.getShopPhone());
358
+        }
359
+        if (StringUtils.isNotEmpty(dto.getShopStatus())
360
+                && !ShopConstants.STATUS_OPEN.equals(dto.getShopStatus())
361
+                && !ShopConstants.STATUS_CLOSED.equals(dto.getShopStatus()))
362
+        {
363
+            throw new ServiceException("店铺状态无效");
364
+        }
382 365
         if (StringUtils.isNotEmpty(dto.getShopDesc()) && dto.getShopDesc().length() > 1000)
383 366
         {
384 367
             throw new ServiceException("店铺描述过长");
@@ -389,7 +372,10 @@ public class ShopServiceImpl implements IShopService
389 372
     {
390 373
         validateShopName(dto.getShopName());
391 374
         validateShopAvatar(dto.getShopAvatar());
392
-        validateShopPhone(dto.getShopPhone());
375
+        if (StringUtils.isNotEmpty(dto.getShopPhone()))
376
+        {
377
+            validateShopPhone(dto.getShopPhone());
378
+        }
393 379
         if (dto.getShopDesc() != null && dto.getShopDesc().length() > 1000)
394 380
         {
395 381
             throw new ServiceException("店铺描述过长");
@@ -426,14 +412,25 @@ public class ShopServiceImpl implements IShopService
426 412
         }
427 413
     }
428 414
 
429
-    private void validateAccountOnFirstShop(ShopCreateDTO dto)
415
+    private String resolveShopStatusOnCreate(String shopStatus)
430 416
     {
431
-        validateLoginName(dto.getLoginName());
432
-        if (StringUtils.isEmpty(dto.getAdminName()))
417
+        if (StringUtils.isEmpty(shopStatus))
433 418
         {
434
-            throw new ServiceException("请输入管理员姓名");
419
+            return ShopConstants.STATUS_OPEN;
420
+        }
421
+        return shopStatus;
422
+    }
423
+
424
+    private void validateShopPhone(String shopPhone)
425
+    {
426
+        if (StringUtils.isEmpty(shopPhone))
427
+        {
428
+            return;
429
+        }
430
+        if (!shopPhone.matches("^[0-9\\-+()\\s]{5,20}$"))
431
+        {
432
+            throw new ServiceException("商家电话格式不正确");
435 433
         }
436
-        validatePassword(dto.getPassword());
437 434
     }
438 435
 
439 436
     private void validateShopName(String shopName)
@@ -456,18 +453,6 @@ public class ShopServiceImpl implements IShopService
456 453
         }
457 454
     }
458 455
 
459
-    private void validateShopPhone(String shopPhone)
460
-    {
461
-        if (StringUtils.isEmpty(shopPhone))
462
-        {
463
-            throw new ServiceException("请输入商家电话");
464
-        }
465
-        if (!shopPhone.matches("^[0-9\\-+()\\s]{5,20}$"))
466
-        {
467
-            throw new ServiceException("商家电话格式不正确");
468
-        }
469
-    }
470
-
471 456
     private void validateLoginName(String loginName)
472 457
     {
473 458
         if (StringUtils.isEmpty(loginName))

+ 27 - 33
baqing-shop/src/test/java/com/ruoyi/web/modules/store/service/ShopServiceImplTest.java

@@ -61,15 +61,17 @@ class ShopServiceImplTest
61 61
     private ShopServiceImpl shopService;
62 62
 
63 63
     @Test
64
-    void createShop_firstShop_insertsAccountAndShop()
64
+    void createShop_withMerchantAccount_insertsShopOnly()
65 65
     {
66 66
         ShopCreateDTO dto = buildCreateDto();
67 67
         OpenShopCheckVO allowed = new OpenShopCheckVO();
68 68
         allowed.setAllowed(true);
69
+        BizMerchantAccount account = new BizMerchantAccount();
70
+        account.setMerchantId(1L);
71
+        account.setLoginName("13800138000");
69 72
         when(merchantFacade.checkOpenShop(1L)).thenReturn(allowed);
70 73
         when(shopMapper.countByShopName(eq("测试店"), isNull())).thenReturn(0);
71
-        when(accountMapper.selectByMerchantId(1L)).thenReturn(null);
72
-        when(accountMapper.countByLoginName(eq("13800138000"), isNull())).thenReturn(0);
74
+        when(accountMapper.selectByMerchantId(1L)).thenReturn(account);
73 75
         when(shopMapper.insert(any())).thenAnswer(inv -> {
74 76
             BizShop s = inv.getArgument(0);
75 77
             s.setShopId(10L);
@@ -79,53 +81,49 @@ class ShopServiceImplTest
79 81
         Long shopId = shopService.createShop(dto, "admin");
80 82
 
81 83
         assertEquals(10L, shopId);
82
-        verify(accountMapper).insert(any(BizMerchantAccount.class));
84
+        verify(accountMapper, never()).insert(any());
83 85
         verify(shopMapper).insert(any(BizShop.class));
84 86
         verify(merchantShopFacade).incrementShopCount(1L);
85 87
     }
86 88
 
87 89
     @Test
88
-    void createShop_secondShop_noNewAccount()
90
+    void createShop_withoutMerchantAccount_rejected()
89 91
     {
90 92
         ShopCreateDTO dto = buildCreateDto();
91
-        dto.setPassword(null);
92
-        BizMerchantAccount account = new BizMerchantAccount();
93
-        account.setMerchantId(1L);
94
-        account.setLoginName("13800138000");
95
-        account.setAdminName("张三");
96 93
         OpenShopCheckVO allowed = new OpenShopCheckVO();
97 94
         allowed.setAllowed(true);
98 95
         when(merchantFacade.checkOpenShop(1L)).thenReturn(allowed);
99
-        when(shopMapper.countByShopName(any(), isNull())).thenReturn(0);
100
-        when(accountMapper.selectByMerchantId(1L)).thenReturn(account);
101
-        when(shopMapper.insert(any())).thenAnswer(inv -> {
102
-            BizShop s = inv.getArgument(0);
103
-            s.setShopId(11L);
104
-            return 1;
105
-        });
106
-
107
-        shopService.createShop(dto, "admin");
96
+        when(shopMapper.countByShopName(eq("测试店"), isNull())).thenReturn(0);
97
+        when(accountMapper.selectByMerchantId(1L)).thenReturn(null);
108 98
 
109
-        verify(accountMapper, never()).insert(any());
110
-        verify(merchantShopFacade).incrementShopCount(1L);
99
+        ServiceException ex = assertThrows(ServiceException.class,
100
+                () -> shopService.createShop(dto, "admin"));
101
+        assertEquals(ShopConstants.MSG_NO_MERCHANT_ACCOUNT, ex.getMessage());
102
+        verify(shopMapper, never()).insert(any());
111 103
     }
112 104
 
113 105
     @Test
114
-    void createShop_loginMismatch_rejected()
106
+    void createShop_optionalPhoneAndStatus_allowed()
115 107
     {
116 108
         ShopCreateDTO dto = buildCreateDto();
117
-        dto.setLoginName("13900139000");
118
-        BizMerchantAccount account = new BizMerchantAccount();
119
-        account.setLoginName("13800138000");
109
+        dto.setShopPhone(null);
110
+        dto.setShopStatus(null);
120 111
         OpenShopCheckVO allowed = new OpenShopCheckVO();
121 112
         allowed.setAllowed(true);
113
+        BizMerchantAccount account = new BizMerchantAccount();
122 114
         when(merchantFacade.checkOpenShop(1L)).thenReturn(allowed);
123 115
         when(shopMapper.countByShopName(any(), isNull())).thenReturn(0);
124 116
         when(accountMapper.selectByMerchantId(1L)).thenReturn(account);
117
+        when(shopMapper.insert(any())).thenAnswer(inv -> {
118
+            BizShop s = inv.getArgument(0);
119
+            s.setShopId(11L);
120
+            assertEquals(ShopConstants.STATUS_OPEN, s.getShopStatus());
121
+            return 1;
122
+        });
125 123
 
126
-        ServiceException ex = assertThrows(ServiceException.class,
127
-                () -> shopService.createShop(dto, "admin"));
128
-        assertEquals(ShopConstants.MSG_ACCOUNT_MISMATCH, ex.getMessage());
124
+        shopService.createShop(dto, "admin");
125
+
126
+        verify(shopMapper).insert(any(BizShop.class));
129 127
     }
130 128
 
131 129
     @Test
@@ -156,14 +154,13 @@ class ShopServiceImplTest
156 154
     }
157 155
 
158 156
     @Test
159
-    void updateSellerShopProfile_updatesBizShopRow()
157
+    void updateSellerShopProfile_withoutPhone_allowed()
160 158
     {
161 159
         when(shopMapper.selectById(1L)).thenReturn(openShop(1L));
162 160
         when(shopMapper.countByShopName(eq("新店名"), eq(1L))).thenReturn(0);
163 161
         SellerShopProfileDTO dto = new SellerShopProfileDTO();
164 162
         dto.setShopName("新店名");
165 163
         dto.setShopAvatar("/new.jpg");
166
-        dto.setShopPhone("010-88886667");
167 164
         dto.setShopDesc("描述");
168 165
 
169 166
         shopService.updateSellerShopProfile(1L, dto, "seller");
@@ -225,9 +222,6 @@ class ShopServiceImplTest
225 222
         dto.setShopName("测试店");
226 223
         dto.setShopAvatar("/profile/upload/a.jpg");
227 224
         dto.setShopPhone("010-88886666");
228
-        dto.setLoginName("13800138000");
229
-        dto.setAdminName("张三");
230
-        dto.setPassword("123456");
231 225
         return dto;
232 226
     }
233 227
 

+ 1 - 1
doc/农资商城web/文档索引.md

@@ -42,7 +42,7 @@ doc/农资商城web/
42 42
 | 模块 | 功能需求 | 技术方案 | 前端技术方案 | 测试用例 | 说明 |
43 43
 |------|----------|----------|--------------|----------|------|
44 44
 | 商户管理 | [v1.4](组织管理/商户管理/商户管理功能需求.md) | [v1.4](组织管理/商户管理/商户管理技术方案.md) | [v1.0](组织管理/商户管理/商户管理前端技术方案.md) | [v1.2](组织管理/商户管理/商户管理测试用例.md) | 平台端 |
45
-| 店铺管理 | [v1.3.4](组织管理/店铺管理/店铺管理功能需求.md) | [v1.2.4](组织管理/店铺管理/店铺管理技术方案.md) | [v1.0](组织管理/店铺管理/店铺管理前端技术方案.md) | [v1.0](组织管理/店铺管理/店铺管理测试用例.md) | 平台 + 商家端店资料 |
45
+| 店铺管理 | [v1.3.5](组织管理/店铺管理/店铺管理功能需求.md) | [v1.2.5](组织管理/店铺管理/店铺管理技术方案.md) | [v1.0](组织管理/店铺管理/店铺管理前端技术方案.md) | [v1.1](组织管理/店铺管理/店铺管理测试用例.md) | 平台 + 商家端店资料 |
46 46
 | 店铺设置 | [v1.1](组织管理/店铺设置/店铺设置功能需求.md) | [v1.1.1](组织管理/店铺设置/店铺设置技术方案.md) | [v1.0](组织管理/店铺设置/店铺设置前端技术方案.md) | [v1.1](组织管理/店铺设置/店铺设置测试用例.md) | 全平台全局策略 |
47 47
 | 入驻审核 | [v1.0.2](组织管理/入驻审核/商户入驻审核功能需求.md) | [v1.0.2](组织管理/入驻审核/商户入驻审核技术方案.md) | [v1.0](组织管理/入驻审核/商户入驻审核前端技术方案.md) | [v1.0.2](组织管理/入驻审核/商户入驻审核测试用例.md) | C 端入驻 + 平台审核 |
48 48
 

+ 25 - 29
doc/农资商城web/组织管理/店铺管理/店铺管理功能需求.md

@@ -2,6 +2,7 @@
2 2
 
3 3
 > 本文档在《店铺管理功能需求-草稿》基础上整理,并关联《农资商城web》目录下 **商户管理(v1.3.1)**、商品管理、商品分类等模块需求做边界与流程对齐。  
4 4
 > 范围:平台侧 **店铺管理** 功能需求;不涉及数据库结构、接口定义及技术实现细节。  
5
+> **v1.3.5:** `biz_shop.shop_status`、`shop_phone` 改为选填;添加店铺 **不再** 采集 `login_name`/`admin_name`/`password`(经营账号由商户入驻绑定时写入 `biz_merchant_account`)。  
5 6
 > **v1.3.4:** 补充草稿第 7 条 **店铺端资料修改** 与平台列表 **同源同步**(商家端 API)。  
6 7
 > **v1.3.3:** 店铺策略已迁至 **《店铺设置》** 全局模块。  
7 8
 > **v1.3:** **不考虑会员体系**;店铺 **经营账号** 由平台在本模块配置(登录名/密码),**不校验** 会员注册、不关联会员模块。  
@@ -110,11 +111,13 @@
110 111
 |------|------|
111 112
 | 店铺名称 | 必填;**平台内未删除店铺名称不可重复** |
112 113
 | 店铺头像 | 必填;符合平台图片格式与大小限制 |
113
-| 商家电话 | 必填;店铺对外联系电话(与第 8 节编辑一致,**添加时即采集**) |
114
+| 商家电话 | **选填**;填写时须符合电话格式(与第 8 节编辑一致) |
114 115
 | 选择商户 | 必填;从商户列表选择,规则见 6.2 |
115
-| 店铺经营账号 | **必填**,见 6.3(登录名、管理员姓名、初始密码) |
116
+| 店铺状态 | **选填**;未填时默认 **开业** |
116 117
 
117
-### 6.2 选择商户规则(与商户管理 v1.3.1 对齐)
118
+> **经营账号:** 不在添加店铺时采集。商户须在 **商户管理 · 新增入驻** 时完成绑定(`biz_merchant_account`);开店前系统校验该商户 **已有** 经营账号,否则阻断并提示。
119
+
120
+### 6.2 选择商户规则(与商户管理 v1.4 对齐)
118 121
 
119 122
 可选商户须 **同时满足**:
120 123
 
@@ -126,31 +129,24 @@
126 129
 
127 130
 若提交时经营信息不完整,阻断,提示:**请先完善该商户的经营与结算信息**。
128 131
 
129
-### 6.3 店铺经营账号配置(添加店铺时 · v1.3)
130
-
131
-| 字段 | 规则 |
132
-|------|------|
133
-| 登录名 | 必填,通常为 **手机号**;须符合平台手机号格式;**平台内未删除经营账号不可重复**(`biz_merchant_account` 表维度,全平台唯一,与会员模块无关) |
134
-| 管理员姓名 | 必填,用于展示 |
135
-| 初始密码 | 必填,由平台设置;支持「生成随机密码」;商家首次登录可强制修改(若平台有统一账号策略则从其) |
132
+### 6.3 经营账号(添加店铺时 · v1.3.5)
136 133
 
137
-| 场景 | 规则 |
134
+| 规则 | 说明 |
138 135
 |------|------|
139
-| 同商户 **首家** 店 | 手工填写登录名、管理员姓名、初始密码 |
140
-| 同商户 **已有** 店 | 默认 **带出** 该商户下已有店铺的经营账号(登录名、管理员姓名),密码可重置;保存时须与同商户其他店 **登录名一致**(见 6.5) |
141
-
142
-> **不涉及会员:** **不校验** 登录名是否已在「会员模块」注册;仅维护本系统 **店铺经营账号** 数据。
136
+| 创建时机 | **商户入驻**(平台新增商户 / 入驻审核通过)时绑定,写入 `biz_merchant_account` |
137
+| 添加店铺 | **不** 再填写登录名、管理员姓名、初始密码 |
138
+| 前置校验 | 所选商户须 **已有** 经营账号;否则提示:**该商户尚未配置经营账号,请先在商户管理中完成入驻绑定** |
139
+| 账号维护 | 仍可通过 **店铺账号管理** 修改(见第 9 节) |
143 140
 
144 141
 ### 6.4 添加流程
145 142
 
146 143
 ```text
147
-填写店铺名称、头像、商家电话
148
-    → 选择商户(须经营信息完整)
149
-    → 配置店铺经营账号(登录名、管理员姓名、初始密码)
144
+填写店铺名称、头像(商家电话、店铺状态可选)
145
+    → 选择商户(须经营信息完整且已有经营账号)
150 146
     → 提交
151
-    → 系统校验(6.2、6.5、6.6)
147
+    → 系统校验(6.2、6.6)
152 148
     ├── 失败 → 提示原因
153
-    └── 成功 → 店铺状态=开业;商户「已绑定店铺数量」+1
149
+    └── 成功 → 店铺状态默认开业(未指定时);商户「已绑定店铺数量」+1
154 150
 ```
155 151
 
156 152
 ### 6.5 同商户多店经营账号一致(定稿)
@@ -158,7 +154,7 @@
158 154
 | 规则 | 说明 |
159 155
 |------|------|
160 156
 | 一致性 | 同一商户下所有未删除店铺的 **登录名(经营账号)必须相同** |
161
-| 添加 **非首家** 店 | 默认带出已有登录名与管理员姓名;若改为不同登录名,**阻断**,提示「同一商户下店铺须使用相同经营账号」 |
157
+| 添加 **非首家** 店 | 沿用该商户已有经营账号;添加店铺 **不再** 重复采集账号字段 |
162 158
 | 店铺账号管理修改 | 修改登录名/密码后,该商户下 **全部店铺** 经营账号展示 **一并更新**(见第 9 节) |
163 159
 
164 160
 ### 6.6 添加时校验
@@ -167,8 +163,8 @@
167 163
 |--------|------|----------|
168 164
 | 店铺名称 | 与所有未删除店铺不可重复 | 店铺名称已存在 |
169 165
 | 商户可开店 | 见 6.2 | 请先完善经营信息 |
170
-| 经营账号 | 登录名格式、平台唯一;同商户登录名一致 | 登录名已存在 / 同一商户下须使用相同经营账号 |
171
-| 必填与格式 | 名称、头像、**商家电话**、商户、经营账号 | 逐项提示 |
166
+| 经营账号 | 商户须已有 `biz_merchant_account` | 该商户尚未配置经营账号… |
167
+| 必填与格式 | 名称、头像;电话/状态 **选填**(填则校验格式) | 逐项提示 |
172 168
 
173 169
 提交成功后:**店铺状态 = 开业**(草稿约定)。
174 170
 
@@ -370,7 +366,7 @@
370 366
 | S1 | 可选商户:**未删除 + 认证正常 + 经营信息完整** |
371 367
 | S2 | 单商户可开设 **多个** 未删除店铺;**本期不设数量上限** |
372 368
 | S3 | 新建店铺默认 **开业** |
373
-| S4 | **添加店铺** 时必填配置 **店铺经营账号**;**同商户** 下登录名 **一致** |
369
+| S4 | **添加店铺** 须商户 **已有** 经营账号(入驻绑定);**不在** 开店接口传账号字段 |
374 370
 | S5 | 经营账号修改 **仅** 在「店铺账号管理」;同步 **同商户全部店铺** |
375 371
 | S6 | **不考虑会员体系**;登录名 **不校验** 会员注册 |
376 372
 | S7 | 创建后 **不可更换** 所属商户 |
@@ -393,10 +389,9 @@
393 389
394 390
 商户管理:编辑商户 → 补全经营与结算信息
395 391
396
-店铺管理:添加店铺 → 选择商户(校验经营信息完整)
397
-    → 配置店铺经营账号(登录名、管理员姓名、密码)
392
+店铺管理:添加店铺 → 选择商户(校验经营信息完整 **且已有经营账号**)
398 393
399
-校验名称不重复、同商户经营账号一致
394
+校验名称不重复
400 395
401 396
 成功:开业;商户已绑定店铺数量 +1
402 397
@@ -453,7 +448,7 @@ C 端:该店铺商品不可下单(含购物车结算、立即购买)
453 448
 
454 449
 | 场景 | 要求 |
455 450
 |------|------|
456
-| 添加店铺 | 选商户(经营信息完整);**须配经营账号**;同商户已有店默认带出账号 |
451
+| 添加店铺 | 选商户(经营信息完整 **且已有经营账号**);仅填店名、头像等(电话/状态选填) |
457 452
 | 编辑店铺 | 商户、经营账号只读;**改停业须二次确认** |
458 453
 | 店铺账号管理 | 展示下属店铺列表;改登录名须二次确认 |
459 454
 | 删除 | 按 11.3 二次确认 |
@@ -481,9 +476,10 @@ C 端:该店铺商品不可下单(含购物车结算、立即购买)
481 476
 | v1.1 C 端 | 定稿:**停业禁止下单** |
482 477
 | v1.3.1 | 添加店铺 **商家电话** 必填;登录名唯一口径定稿;**11.5** 末店删除后账号保留 |
483 478
 | **v1.3.2** | **取消** 单商户最多 3 店限制 |
479
+| **v1.3.5** | `shop_status`/`shop_phone` 选填;添加店铺 **不再** 采集经营账号(入驻时绑定) |
484 480
 | **v1.3.4** | 商家端店铺资料 API;§8.4 列表同源同步 |
485 481
 | **v1.3.3** | 店铺策略迁出至《店铺设置》;`biz_shop` 无策略字段 |
486 482
 
487 483
 ---
488 484
 
489
-*文档版本:v1.3.4(定稿)· 关联《商户管理功能需求.md》v1.3.1、《店铺管理技术方案.md》v1.2.3、《店铺管理测试用例.md》v1.0 · 草稿已同步。*
485
+*文档版本:v1.3.5(定稿)· 关联《商户管理功能需求.md》v1.4、《店铺管理技术方案.md》v1.2.5、《店铺管理测试用例.md》v1.1 · 草稿已同步。*

+ 32 - 31
doc/农资商城web/组织管理/店铺管理/店铺管理技术方案.md

@@ -1,9 +1,10 @@
1 1
 # 店铺管理 — 技术方案
2 2
 
3
-> **依据:** 《店铺管理功能需求.md》v1.3.4  
3
+> **依据:** 《店铺管理功能需求.md》v1.3.5  
4 4
 > **关联:** 《商户管理》《商品分类》《商品管理》功能需求(`doc/农资商城web/`)、《商户管理技术方案.md》v1.4、《店铺设置技术方案.md》v1.1.1    
5 5
 > **范围:** 本文以 **店铺模块** 的数据库、接口为主;商品/分类/订单/商家端登录给出 **表关系、Facade、联动**;不展开商品/C 端完整设计。  
6
-> **原则:** **不考虑会员**;**店铺经营账号** 由本模块维护(平台账号体系,BCrypt 密码)。  
6
+> **原则:** **不考虑会员**;**店铺经营账号** 由 **商户入驻** 写入 `biz_merchant_account`,本模块 **添加店铺时仅校验已有账号**;账号改密走 **店铺账号管理** 接口。  
7
+> **v1.2.5:** `biz_shop.shop_status`、`shop_phone` 选填;`POST /` **不再** 接收 `loginName`/`adminName`/`password`(与需求 v1.3.5 对齐)。  
7 8
 > **v1.2.3:** 商家端 **`GET/PUT /agri/seller/shop`** 维护店资料,与平台列表 **同源** `biz_shop`。  
8 9
 > **v1.2.4:** 同步《商品分类技术方案》v1.2 — `biz_goods_category.shop_id` 可空(平台分类与店铺分类同表隔离)。
9 10
 
@@ -97,7 +98,8 @@ biz_goods_category(shop_id IS NULL)  ← 平台分类(与店铺无 FK;
97 98
 | del_flag | char(1) | 0 存在 2 删除(随商户/运营策略,一般随商户生命周期) |
98 99
 | create_by/time, update_by/time | | |
99 100
 
100
-> 第 2、3 家店 **不新建** 账号行;仅校验 `login_name` 与已有账号一致。  
101
+> 第 2、3 家店 **不新建** 账号行;添加店铺 **不** 再传账号字段,仅校验商户 **已有** 账号。  
102
+> **创建时机:** 商户入驻(平台新增商户 / 入驻审核通过)时 `MerchantAccountBindService.bindAccountOnMerchantCreate` 写入。  
101 103
 > **末店逻辑删除后:** 账号行 **保留** `del_flag=0`,同商户再开店可沿用/重置(需求 §11.5)。
102 104
 
103 105
 ### 2.3 `biz_shop`
@@ -108,9 +110,9 @@ biz_goods_category(shop_id IS NULL)  ← 平台分类(与店铺无 FK;
108 110
 | merchant_id | bigint | FK;**创建后不可 UPDATE** |
109 111
 | shop_name | varchar(128) | 未删除店铺内 **唯一** |
110 112
 | shop_avatar | varchar(512) | 头像 URL |
111
-| shop_status | char(1) | **0 开业 1 停业**;新建默认 0 |
113
+| shop_status | char(1) | **0 开业 1 停业**;**选填**,未传默认 0 |
112 114
 | shop_desc | varchar(1000) | 选填 |
113
-| shop_phone | varchar(20) | 商家电话,必填 |
115
+| shop_phone | varchar(20) | 商家电话,**选填**;有值时校验格式 |
114 116
 | del_flag | char(1) | 逻辑删除 |
115 117
 | 审计字段 | | |
116 118
 
@@ -151,9 +153,9 @@ CREATE TABLE `biz_shop` (
151 153
   `merchant_id` bigint(20) NOT NULL COMMENT '商户ID',
152 154
   `shop_name` varchar(128) NOT NULL COMMENT '店铺名称',
153 155
   `shop_avatar` varchar(512) NOT NULL COMMENT '店铺头像',
154
-  `shop_status` char(1) NOT NULL DEFAULT '0' COMMENT '0开业 1停业',
156
+  `shop_status` char(1) DEFAULT '0' COMMENT '0开业 1停业,选填,默认0',
155 157
   `shop_desc` varchar(1000) DEFAULT NULL COMMENT '店铺描述',
156
-  `shop_phone` varchar(20) NOT NULL COMMENT '商家电话',
158
+  `shop_phone` varchar(20) DEFAULT NULL COMMENT '商家电话,选填',
157 159
   `del_flag` char(1) NOT NULL DEFAULT '0',
158 160
   `create_by` varchar(64) DEFAULT '',
159 161
   `create_time` datetime DEFAULT NULL,
@@ -195,7 +197,7 @@ biz_goods.shop_id       → biz_shop.shop_id
195 197
 |------|------|------|------|
196 198
 | GET | `/list` | list | 分页列表 + 检索 |
197 199
 | GET | `/{shopId}` | query | 详情 |
198
-| POST | `/` | add | 添加店铺(含经营账号) |
200
+| POST | `/` | add | 添加店铺(**不**经营账号字段) |
199 201
 | PUT | `/` | edit | 编辑店铺(**不含**账号、不含 merchantId) |
200 202
 | GET | `/{shopId}/deleteCheck` | query | 删除预检 |
201 203
 | DELETE | `/{shopIds}` | remove | 逻辑删除(批量整批失败) |
@@ -238,22 +240,19 @@ biz_goods.shop_id       → biz_shop.shop_id
238 240
   "shopAvatar": "/profile/upload/xx.jpg",
239 241
   "shopPhone": "010-88886666",
240 242
   "merchantId": 1,
241
-  "loginName": "13800138000",
242
-  "adminName": "张三",
243
-  "password": "初始密码"
243
+  "shopStatus": "0"
244 244
 }
245 245
 ```
246 246
 
247 247
 | 步骤 | 逻辑 |
248 248
 |------|------|
249 249
 | 1 | `IMerchantFacade.checkOpenShop(merchantId)` 不通过则阻断 |
250
-| 2 | 店名唯一;头像/电话必填 |
251
-| 3 | 账号:若商户 **无** 账号 → insert `biz_merchant_account`;**已有** → `loginName` 必须与现有一致,否则「同一商户下须使用相同经营账号」 |
252
-| 4 | `loginName` 平台唯一、手机号格式;**不校验会员** |
253
-| 5 | insert `biz_shop`:`shop_status=0` |
254
-| 6 | 同事务 `incrementShopCount(merchantId)` |
250
+| 2 | 店名唯一;名称、头像必填;**电话、状态选填**(填电话则校验格式) |
251
+| 3 | 校验商户 **已有** `biz_merchant_account`;无则「该商户尚未配置经营账号…」 |
252
+| 4 | insert `biz_shop`:`shop_status` 未传则 **0** |
253
+| 5 | 同事务 `incrementShopCount(merchantId)` |
255 254
 
256
-**同商户第 2、3 家店:** 可先 `GET .../merchant/{id}/account` 带出 login/admin;`password` 可空表示不修改已有密码
255
+> **不再** 在开店时 insert/更新账号;账号由商户入驻阶段创建
257 256
 
258 257
 ### 3.5 编辑店铺 `PUT /`
259 258
 
@@ -400,7 +399,7 @@ GET /agri/merchant/selectList → POST /agri/shop(账号+店)
400 399
 | S1 | `checkOpenShop` + selectList |
401 400
 | S2 | **不校验** 店铺数量上限;`shop_count` 仅 increment/decrement 统计 |
402 401
 | S3 | insert `shop_status=0` |
403
-| S4 | POST 必填账号;同商户 login 一致 |
402
+| S4 | 添加店铺 **须** 商户已有经营账号;**不** 在 POST 中传账号字段 |
404 403
 | S5 | 账号仅 `.../account` 接口;改 login 同步展示 |
405 404
 | S6 | 无会员校验 |
406 405
 | S7 | UPDATE 排除 `merchant_id` |
@@ -440,7 +439,7 @@ GET /agri/merchant/selectList → POST /agri/shop(账号+店)
440 439
 |------|-----|
441 440
 | 店名重复 | 店铺名称已存在 |
442 441
 | 登录名重复 | 登录名已存在 |
443
-| 同商户账号不一致 | 同一商户下店铺须使用相同经营账号 |
442
+| 商户无经营账号 | 该商户尚未配置经营账号,请先在商户管理中完成入驻绑定 |
444 443
 | 商户不可开店 | 请先完善该商户的经营与结算信息 |
445 444
 | 删店-商品 | 请先下架或处理完商品后再删除店铺 |
446 445
 | 删店-订单 | 存在未完成订单,无法删除 |
@@ -452,15 +451,11 @@ GET /agri/merchant/selectList → POST /agri/shop(账号+店)
452 451
 {
453 452
   "shopName": "张二店",
454 453
   "shopAvatar": "/profile/upload/y.jpg",
455
-  "shopPhone": "010-88886667",
456
-  "merchantId": 1,
457
-  "loginName": "13800138000",
458
-  "adminName": "张三",
459
-  "password": null
454
+  "merchantId": 1
460 455
 }
461 456
 ```
462 457
 
463
-`password=null`:不修改已有账号密码
458
+`shopPhone`、`shopStatus` 可省略;商户须已在入驻时绑定经营账号。
464 459
 
465 460
 ### 7.3 账号改登录名 confirm
466 461
 
@@ -478,7 +473,7 @@ GET /agri/merchant/selectList → POST /agri/shop(账号+店)
478 473
 
479 474
 ```text
480 475
 @Transactional
481
-createShop()  → insert shop [+ insert account] → incrementShopCount
476
+createShop()  → insert shop → incrementShopCount
482 477
 deleteShop()  → 预检 → del_flag=2 → decrementShopCount(账号行不删,见 §2.2)
483 478
 ```
484 479
 
@@ -493,10 +488,8 @@ deleteShop()  → 预检 → del_flag=2 → decrementShopCount(账号行不删
493 488
 |------|------|
494 489
 | shopName | 非空,长度 ≤128;未删除店铺内唯一 |
495 490
 | shopAvatar | 非空 URL;jpg/png/jpeg;≤5MB(与 RuoYi 上传一致) |
496
-| shopPhone | 非空;手机或固话(项目统一正则) |
497
-| loginName | 大陆手机号 11 位(项目统一);`del_flag=0` 全平台唯一 |
498
-| adminName | 非空,≤64 |
499
-| password | 首家店必填,长度 ≥6(与平台密码策略一致);**非首家** 可 null |
491
+| shopPhone | **选填**;有值时手机或固话(项目统一正则) |
492
+| shopStatus | **选填**;0/1;未传默认 0 |
500 493
 | shopDesc | 选填,≤1000 |
501 494
 
502 495
 ### 7.7 `checkOpenShop` / `openShopCheck`(与商户方案对齐)
@@ -540,6 +533,14 @@ deleteShop()  → 预检 → del_flag=2 → decrementShopCount(账号行不删
540 533
 
541 534
 ---
542 535
 
536
+## 13. v1.2.5 修订摘要
537
+
538
+| 项 | 内容 |
539
+|----|------|
540
+| `biz_shop` | `shop_status`、`shop_phone` **选填**(可 NULL;status 默认 0) |
541
+| `POST /agri/shop` | **移除** `loginName`/`adminName`/`password`;开店前校验商户 **已有** `biz_merchant_account` |
542
+| 账号创建 | 改由 **商户入驻** `bindAccountOnMerchantCreate`;入驻审核通过开店同理 |
543
+
543 544
 ## 13. v1.2.4 修订摘要
544 545
 
545 546
 | 项 | 内容 |
@@ -558,4 +559,4 @@ deleteShop()  → 预检 → del_flag=2 → decrementShopCount(账号行不删
558 559
 
559 560
 ---
560 561
 
561
-*文档版本:v1.2.4 · MySQL 5.7.39 · RuoYi v3.9.2-springboot2 · 关联《店铺管理功能需求.md》v1.3.4、《商品分类技术方案.md》v1.2*
562
+*文档版本:v1.2.5 · MySQL 5.7.39 · RuoYi v3.9.2-springboot2 · 关联《店铺管理功能需求.md》v1.3.5、《商品分类技术方案.md》v1.2*

+ 66 - 73
doc/农资商城web/组织管理/店铺管理/店铺管理测试用例.md

@@ -1,6 +1,6 @@
1 1
 # 店铺管理 — 测试用例
2 2
 
3
-> **依据:** 《店铺管理功能需求.md》v1.3.4、《店铺管理技术方案.md》v1.2.3  
3
+> **依据:** 《店铺管理功能需求.md》v1.3.5、《店铺管理技术方案.md》v1.2.5  
4 4
 > **范围:** 平台管理端 **店铺管理**(列表、添加、编辑、店铺账号管理、删除)及 `IShopFacade` 对外契约;商家端 **`/agri/seller/shop`** 资料维护(§8.4)  
5 5
 > **排除:** **店铺设置**(见《店铺设置测试用例.md》)、商家端完整登录与 `shop_id` 切换、子管理员 CRUD、会员体系;商品上架/审核、分类维护的独立用例(删店仅验证商品 Facade 阻断条件);C 端下单 E2E 仅通过 `isShopOpenForOrder` 单元/接口契约验证,不展开用户商城页面  
6 6
 > **环境:** RuoYi v3.9.2;接口基路径 `/agri/shop`;选商户复用 `/agri/merchant/selectList`、`openShopCheck`
@@ -19,7 +19,7 @@
19 19
 
20 20
 ## 一、单元测试
21 21
 
22
-### SHP-UT-001 首家店创建 insert 账号与店铺同事务
22
+### SHP-UT-001 商户已有账号时创建店铺
23 23
 
24 24
 | 要素 | 内容 |
25 25
 |------|------|
@@ -27,10 +27,23 @@
27 27
 | **测试项** | createShop |
28 28
 | **测试类型** | 单元测试 |
29 29
 | **测试工具** | JUnit + Mockito |
30
-| **测试目的** | 验证首家店新建 `biz_merchant_account` + `biz_shop` |
31
-| **前置条件** | 商户无账号行;`checkOpenShop` 通过 |
32
-| **测试步骤** | `createShop(dto)` 含 password |
33
-| **预期结果** | 插入 account 与 shop;`shop_status=0` |
30
+| **测试目的** | 验证有账号时仅 insert `biz_shop` |
31
+| **前置条件** | 商户已有 `biz_merchant_account`;`checkOpenShop` 通过 |
32
+| **测试步骤** | `createShop(dto)` 仅含店名、头像、merchantId |
33
+| **预期结果** | 仅插入 shop;`shop_status=0`(默认);不 insert account |
34
+
35
+### SHP-UT-001b 商户无经营账号阻断开店
36
+
37
+| 要素 | 内容 |
38
+|------|------|
39
+| **测试模块** | 店铺管理 |
40
+| **测试项** | createShop |
41
+| **测试类型** | 单元测试 |
42
+| **测试工具** | JUnit |
43
+| **测试目的** | 验证 v1.3.5 前置校验 |
44
+| **前置条件** | 商户无 account 行 |
45
+| **测试步骤** | `createShop(dto)` |
46
+| **预期结果** | 失败;msg 含「尚未配置经营账号」 |
34 47
 
35 48
 ### SHP-UT-002 创建店铺同事务 incrementShopCount
36 49
 
@@ -45,7 +58,7 @@
45 58
 | **测试步骤** | 创建成功 |
46 59
 | **预期结果** | 同一 `@Transactional` 内调用 `incrementShopCount(merchantId)` 一次 |
47 60
 
48
-### SHP-UT-003 非首家店不新建账号行
61
+### SHP-UT-003 同商户第二家店不新建账号行
49 62
 
50 63
 | 要素 | 内容 |
51 64
 |------|------|
@@ -53,12 +66,12 @@
53 66
 | **测试项** | createShop |
54 67
 | **测试类型** | 单元测试 |
55 68
 | **测试工具** | JUnit |
56
-| **测试目的** | 验证同商户多店共用账号 |
69
+| **测试目的** | 验证同商户多店共用已有账号 |
57 70
 | **前置条件** | 商户已有 account 与 1 家店 |
58
-| **测试步骤** | 第 2 家店 `loginName` 与现有一致,`password=null` |
71
+| **测试步骤** | 第 2 家店 POST 不含账号字段 |
59 72
 | **预期结果** | 仅 insert `biz_shop`;account 表行数不变 |
60 73
 
61
-### SHP-UT-004 非首家店登录名不一致阻断
74
+### SHP-UT-003b 电话与状态可选
62 75
 
63 76
 | 要素 | 内容 |
64 77
 |------|------|
@@ -66,23 +79,10 @@
66 79
 | **测试项** | createShop |
67 80
 | **测试类型** | 单元测试 |
68 81
 | **测试工具** | JUnit |
69
-| **测试目的** | 验证 S4/S5 |
70
-| **前置条件** | 已有账号 login=A |
71
-| **测试步骤** | 第 2 家店传 login=B |
72
-| **预期结果** | 抛错;msg「同一商户下店铺须使用相同经营账号」 |
73
-
74
-### SHP-UT-005 登录名全平台唯一校验
75
-
76
-| 要素 | 内容 |
77
-|------|------|
78
-| **测试模块** | 店铺管理 |
79
-| **测试项** | 账号校验 |
80
-| **测试类型** | 单元测试 |
81
-| **测试工具** | JUnit |
82
-| **测试目的** | 验证 login_name 唯一 |
83
-| **前置条件** | 其他商户已占用手机号 X |
84
-| **测试步骤** | 首家店使用 X 作为 loginName |
85
-| **预期结果** | 失败;「登录名已存在」 |
82
+| **测试目的** | 验证 shop_phone/shop_status 选填 |
83
+| **前置条件** | 商户已有账号 |
84
+| **测试步骤** | dto 省略 phone、status;再测仅传非法 phone |
85
+| **预期结果** | 省略时成功且 status=0;非法 phone 失败 |
86 86
 
87 87
 ### SHP-UT-006 店名未删除范围内唯一
88 88
 
@@ -296,18 +296,18 @@
296 296
 
297 297
 > **废止:** 逐店 settings 及 `biz_shop` 策略字段已删除。相关用例见 **《店铺设置测试用例.md》**(SSS-UT-* / SSS-API-*)。
298 298
 
299
-### SHP-UT-025 首家店 password 必填
299
+### SHP-UT-025 登录名唯一校验(账号管理接口)
300 300
 
301 301
 | 要素 | 内容 |
302 302
 |------|------|
303 303
 | **测试模块** | 店铺管理 |
304
-| **测试项** | createShop |
304
+| **测试项** | updateAccount |
305 305
 | **测试类型** | 单元测试 |
306 306
 | **测试工具** | JUnit |
307
-| **测试目的** | 验证 §7.6 |
308
-| **前置条件** | 无账号 |
309
-| **测试步骤** | `password=null` |
310
-| **预期结果** | 校验失败 |
307
+| **测试目的** | 验证 §7.6 账号改登录名唯一 |
308
+| **前置条件** | 其他商户已占用手机号 X |
309
+| **测试步骤** | `PUT .../account` 改 login 为 X |
310
+| **预期结果** | 校验失败;「登录名已存在」 |
311 311
 
312 312
 ### SHP-UT-026 冻结商户 checkOpenShop 失败
313 313
 
@@ -391,7 +391,7 @@
391 391
 | **测试步骤** | `GET /list?merchantName=张三农资` |
392 392
 | **预期结果** | 均为该商户店铺 |
393 393
 
394
-### SHP-API-005 首家店添加成功
394
+### SHP-API-005 商户已有账号时添加店铺成功
395 395
 
396 396
 | 要素 | 内容 |
397 397
 |------|------|
@@ -399,10 +399,10 @@
399 399
 | **测试项** | POST / |
400 400
 | **测试类型** | 接口测试 |
401 401
 | **测试工具** | Apifox |
402
-| **测试目的** | 验证开店主流程 S3/S4 |
403
-| **前置条件** | 经营完整正常商户;`agri:shop:add`;未占用店名/登录名 |
404
-| **测试步骤** | `POST /` 提交完整 body(含 password) |
405
-| **预期结果** | `code=200`;`shopStatus=0`;商户 `shop_count+1`;account 已建 |
402
+| **测试目的** | 验证开店主流程 S3/S4(v1.3.5) |
403
+| **前置条件** | 经营完整正常商户 **且已有** `biz_merchant_account`;`agri:shop:add`;未占用店名 |
404
+| **测试步骤** | `POST /` 提交 shopName、shopAvatar、merchantId(phone/status 可省略) |
405
+| **预期结果** | `code=200`;`shopStatus=0`;商户 `shop_count+1`;**不** 新建 account |
406 406
 
407 407
 ### SHP-API-006 同商户第二家店添加成功
408 408
 
@@ -414,7 +414,7 @@
414 414
 | **测试工具** | Apifox |
415 415
 | **测试目的** | 验证 §7.2 第二家店 |
416 416
 | **前置条件** | 商户已有 1 店及账号 |
417
-| **测试步骤** | `POST /` 同 loginName,`password=null`(§7.2 样例) |
417
+| **测试步骤** | `POST /` 仅店名、头像、merchantId(§7.2 样例) |
418 418
 | **预期结果** | 成功;account 行数不变;`shop_count` 再 +1 |
419 419
 
420 420
 ### SHP-API-007 添加缺少必填字段失败
@@ -427,7 +427,7 @@
427 427
 | **测试工具** | Apifox |
428 428
 | **测试目的** | 验证必填 |
429 429
 | **前置条件** | — |
430
-| **测试步骤** | 缺 `shopPhone` 或头像 |
430
+| **测试步骤** | 缺 `shopName` 或 `shopAvatar` |
431 431
 | **预期结果** | 失败;无新店铺 |
432 432
 
433 433
 ### SHP-API-008 店名重复失败
@@ -443,7 +443,7 @@
443 443
 | **测试步骤** | 重复 `shopName` 提交 |
444 444
 | **预期结果** | 「店铺名称已存在」 |
445 445
 
446
-### SHP-API-009 同商户登录名不一致失败
446
+### SHP-API-009 商户无经营账号不可开店
447 447
 
448 448
 | 要素 | 内容 |
449 449
 |------|------|
@@ -451,10 +451,10 @@
451 451
 | **测试项** | POST / |
452 452
 | **测试类型** | 接口测试 |
453 453
 | **测试工具** | Apifox |
454
-| **测试目的** | 验证 S4 |
455
-| **前置条件** | 已有 login=13800138000 |
456
-| **测试步骤** | 第 2 店传 13900139000 |
457
-| **预期结果** | 「同一商户下店铺须使用相同经营账号」 |
454
+| **测试目的** | 验证 S4(v1.3.5) |
455
+| **前置条件** | 经营完整但 **无** `biz_merchant_account` |
456
+| **测试步骤** | `POST /` 合法店名与头像 |
457
+| **预期结果** | 失败;「该商户尚未配置经营账号…」 |
458 458
 
459 459
 ### SHP-API-010 商户经营未完善不可开店
460 460
 
@@ -694,7 +694,7 @@
694 694
 | **测试步骤** | `POST /` |
695 695
 | **预期结果** | 403;无新数据 |
696 696
 
697
-### SHP-API-031 GET merchant account 供添加第 2 店带出
697
+### SHP-API-031 GET merchant account 供账号管理页
698 698
 
699 699
 | 要素 | 内容 |
700 700
 |------|------|
@@ -702,10 +702,10 @@
702 702
 | **测试项** | GET /merchant/{id}/account |
703 703
 | **测试类型** | 接口测试 |
704 704
 | **测试工具** | Apifox |
705
-| **测试目的** | 验证添加页带出账号 |
705
+| **测试目的** | 验证账号管理页查询 |
706 706
 | **前置条件** | 商户已有店 |
707
-| **测试步骤** | 添加前调用 account 接口 |
708
-| **预期结果** | 返回现有 loginName、adminName 供表单默认 |
707
+| **测试步骤** | 打开账号管理前调用 account 接口 |
708
+| **预期结果** | 返回 loginName、adminName 及下属 shops 列表 |
709 709
 
710 710
 ### SHP-API-032 复用 merchant selectList 仅完整商户
711 711
 
@@ -778,7 +778,7 @@
778 778
 | **测试步骤** | 点重置/清空 → 查询 |
779 779
 | **预期结果** | 恢复全量未删店铺列表 |
780 780
 
781
-### SHP-UI-005 添加首家店-完整表单提交
781
+### SHP-UI-005 添加店-完整表单提交
782 782
 
783 783
 | 要素 | 内容 |
784 784
 |------|------|
@@ -787,11 +787,11 @@
787 787
 | **测试类型** | 界面测试 |
788 788
 | **测试工具** | Playwright |
789 789
 | **测试目的** | 验证开店 UI 主流程 |
790
-| **前置条件** | 可选完整商户;有 add 权限 |
791
-| **测试步骤** | 新增 → 选商户 → 填店名/头像/电话/账号密码 → 提交 |
790
+| **前置条件** | 可选完整商户 **且已有经营账号**;有 add 权限 |
791
+| **测试步骤** | 新增 → 选商户 → 填店名/头像 → 提交(电话可选) |
792 792
 | **预期结果** | 成功;列表新店状态=开业;商户店铺数+1 |
793 793
 
794
-### SHP-UI-006 添加第二家店默认带出经营账号
794
+### SHP-UI-006 添加第二家店(同商户)
795 795
 
796 796
 | 要素 | 内容 |
797 797
 |------|------|
@@ -799,10 +799,10 @@
799 799
 | **测试项** | 添加店铺 |
800 800
 | **测试类型** | 界面测试 |
801 801
 | **测试工具** | Playwright |
802
-| **测试目的** | 验证 §6.3 带出 |
802
+| **测试目的** | 验证同商户多店 |
803 803
 | **前置条件** | 商户已有 1 店 |
804
-| **测试步骤** | 选同商户 → 观察账号区 |
805
-| **预期结果** | 登录名、管理员姓名自动填充;密码可空或可选重置 |
804
+| **测试步骤** | 选同商户 → 填新店名/头像 → 提交 |
805
+| **预期结果** | 成功;列表多一条;账号展示与首店一致 |
806 806
 
807 807
 ### SHP-UI-007 添加必填项为空校验
808 808
 
@@ -814,7 +814,7 @@
814 814
 | **测试工具** | Playwright |
815 815
 | **测试目的** | 验证前端校验 |
816 816
 | **前置条件** | 打开新增 |
817
-| **测试步骤** | 不填商家电话直接提交 |
817
+| **测试步骤** | 不填店名或头像直接提交 |
818 818
 | **预期结果** | 表单项错误提示;不提交成功 |
819 819
 
820 820
 ### SHP-UI-008 选经营未完善商户不可选或提交失败
@@ -964,18 +964,9 @@
964 964
 | **测试步骤** | 商户详情点击跳转 |
965 965
 | **预期结果** | 打开店铺列表且检索框带商户名;仅显示该商户店铺 |
966 966
 
967
-### SHP-UI-021 生成随机密码(若 UI 提供
967
+### SHP-UI-021 生成随机密码(已迁出
968 968
 
969
-| 要素 | 内容 |
970
-|------|------|
971
-| **测试模块** | 店铺管理 |
972
-| **测试项** | 添加店铺 |
973
-| **测试类型** | 界面测试 |
974
-| **测试工具** | Playwright |
975
-| **测试目的** | 验证 §6.3 随机密码 |
976
-| **前置条件** | 首家店新增页有「生成随机密码」 |
977
-| **测试步骤** | 点击生成 |
978
-| **预期结果** | 密码框填入符合长度要求的随机串 |
969
+> **废止(v1.3.5):** 添加店铺不再采集密码;随机密码若 UI 提供,见 **《商户管理测试用例》** 入驻绑定账号相关用例。
979 970
 
980 971
 ### SHP-UI-022 子管理员上限输入非法值(已迁出)
981 972
 
@@ -1029,7 +1020,7 @@
1029 1020
 | S1 | SHP-UT-007, SHP-API-010, SHP-UI-008, SHP-API-032 |
1030 1021
 | S2 | SHP-UT-008, SHP-API-012 |
1031 1022
 | S3 | SHP-UT-001, SHP-API-005 |
1032
-| S4/S5 | SHP-UT-003/004/019/020, SHP-API-006/009, SHP-UI-006/013 |
1023
+| S4/S5 | SHP-UT-001b/003/019/020/025, SHP-API-005/006/009, SHP-UI-006/013 |
1033 1024
 | S6 | SHP-UI-019 |
1034 1025
 | S7 | SHP-UT-009, SHP-API-015, SHP-UI-009 |
1035 1026
 | S8 | SHP-UT-006, SHP-API-008 |
@@ -1045,15 +1036,17 @@
1045 1036
 
1046 1037
 | 数据 | 用途 |
1047 1038
 |------|------|
1048
-| 经营完整 + 认证正常商户 | 开店、selectList |
1039
+| 经营完整 + 已有经营账号商户 | 开店、selectList |
1040
+| 经营完整但无账号商户 | SHP-API-009 / SHP-UT-001b |
1041
+| 同商户第二店 | 不新建 account |
1049 1042
 | 经营未完善 / 冻结商户 | 开店阻断 |
1050
-| 首家店 + 同商户第二店 | 账号一致、password null |
1043
+| 首家店 + 同商户第二店 | 共用已有账号 |
1051 1044
 | 多店商户(≥5) | S2 无上限 |
1052 1045
 | 开业/停业店各一 | 状态编辑、Facade |
1053 1046
 | 含出售中/待审核商品的店 | 删店阻断 |
1054 1047
 | 仅已下架商品的店 | 删店正向 |
1055
-| 未占用店名、登录手机号 | 唯一性正向 |
1048
+| 未占用店名 | 唯一性正向 |
1056 1049
 
1057 1050
 ---
1058 1051
 
1059
-*文档版本:v1.0 · 关联《店铺管理功能需求.md》v1.3.2、《店铺管理技术方案.md》v1.2.1(`biz_merchant_account`)*
1052
+*文档版本:v1.1 · 关联《店铺管理功能需求.md》v1.3.5、《店铺管理技术方案.md》v1.2.5*

+ 8 - 2
sql/biz_shop.sql

@@ -1,4 +1,4 @@
1
+-- 商户经营账号(一商户一套,多店共用;由商户入驻绑定时创建,非 biz_shop 字段)
1 2
 CREATE TABLE IF NOT EXISTS `biz_merchant_account` (
2 3
   `account_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '账号ID',
3 4
   `merchant_id` bigint(20) NOT NULL COMMENT '商户ID',
@@ -14,15 +14,15 @@ CREATE TABLE IF NOT EXISTS `biz_merchant_account` (
14 14
   KEY `idx_merchant_id` (`merchant_id`,`del_flag`)
15 15
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商户经营账号';
16 16
 
17
+-- 店铺(不含 login_name / admin_name / password,经营账号见 biz_merchant_account)
17 18
 CREATE TABLE IF NOT EXISTS `biz_shop` (
18 19
   `shop_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '店铺ID',
19 20
   `merchant_id` bigint(20) NOT NULL COMMENT '商户ID',
20 21
   `shop_name` varchar(128) NOT NULL COMMENT '店铺名称',
21 22
   `shop_avatar` varchar(512) NOT NULL COMMENT '店铺头像',
22
-  `shop_status` char(1) NOT NULL DEFAULT '0' COMMENT '0开业1停业',
23
+  `shop_status` char(1) DEFAULT '0' COMMENT '0开业1停业,选填,默认0',
23 24
   `shop_desc` varchar(1000) DEFAULT NULL COMMENT '店铺描述',
24
-  `shop_phone` varchar(20) NOT NULL COMMENT '商家电话',
25
+  `shop_phone` varchar(20) DEFAULT NULL COMMENT '商家电话,选填',
25 26
   `del_flag` char(1) NOT NULL DEFAULT '0',
26 27
   `create_by` varchar(64) DEFAULT '',
27 28
   `create_time` datetime DEFAULT NULL,
@@ -34,3 +34,7 @@ CREATE TABLE IF NOT EXISTS `biz_shop` (
34 34
   KEY `idx_shop_status` (`shop_status`,`del_flag`),
35 35
   KEY `idx_create_time` (`create_time`)
36 36
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='店铺';
37
+
38
+-- 已有库迁移(按需执行)
39
+-- ALTER TABLE `biz_shop` MODIFY COLUMN `shop_status` char(1) DEFAULT '0' NULL COMMENT '0开业1停业,选填,默认0';
40
+-- ALTER TABLE `biz_shop` MODIFY COLUMN `shop_phone` varchar(20) DEFAULT NULL COMMENT '商家电话,选填';