wwh недель назад: 2
Родитель
Сommit
2d8dae9f2b

+ 496 - 0
doc/消费者APP/商品分类/商品分类功能需求.md

@@ -0,0 +1,496 @@
1
+# 商品分类 — 功能需求说明(C 端)
2
+
3
+> 本文档在《商品分类功能需求-草稿》基础上整理,并关联 **《关联需求分析.md》** v1.6、平台侧 **《商品分类功能需求》** v1.5、**《商品审核功能需求》** v1.0、**《店铺管理功能需求》** v1.3.5、**《会员管理功能需求》** v1.0.1 及 C 端兄弟模块 **《商城首页功能需求》** v1.0.1 做边界与流程对齐。  
4
+> 范围:**C 端(消费者 APP / 用户商城)· 商品分类** 的浏览、导航与分类下商品列表;**不涉及** 数据库结构、接口定义及技术实现细节。  
5
+> **说明:** 本模块 **只消费** 平台维护的 **平台商品分类**(全平台一份);**不使用** 店铺商品分类。分类下商品为 **全平台跨店铺** 聚合展示。  
6
+> **v1.0:** 首版定稿;搜索入口、全部分类页、一级分类落地页、分类商品列表与排序;与供给链/浏览层规则对齐。  
7
+> **v1.0.1:** 定稿 A→独立列表页(A1);B 切换 Tab 列表回顶(C1);独立列表页含搜索栏;排序区 UI 说明;与商城首页一级可见口径对齐。
8
+
9
+---
10
+
11
+## 1. 模块定位
12
+
13
+### 1.1 业务目标
14
+
15
+为 **C 端买家/访客** 提供按 **平台商品分类** 浏览农资商品的能力,使其能够:
16
+
17
+- 通过 **顶部搜索入口** 进入全站检索(检索能力另册);
18
+- 在 **全部分类页** 查看完整 **一级 + 二级** 平台分类,并快速切换大类与小类;
19
+- 从 **商城首页** 进入 **一级分类商品页**,通过 **二级 Tab** 切换查看各小类下的商品;
20
+- 在 **分类商品列表** 中按 **价格、销量** 排序浏览,并进入 **商品详情**。
21
+
22
+**不包含(本期或与它模块分工):**
23
+
24
+| 能力 | 说明 |
25
+|------|------|
26
+| **搜索执行与结果页** | 本模块仅提供 **搜索入口**;关键词检索、结果排序等见 **搜索模块**(待建设) |
27
+| **平台/店铺分类维护** | 见平台 **商品分类**、商家 **店铺商品分类**;C 端 **只读消费** |
28
+| **商品发品/审核/上下架** | 见商家 **商品列表**、平台 **商品审核** |
29
+| **购物车、下单、支付** | 见 **购物车**、**订单管理** 等;本模块 **不直接** 完成交易 |
30
+| **按店铺商品分类导航** | 店铺商品分类为商家内部组织字段;**C 端分类浏览不走该体系** |
31
+| **会员注册/登录页** | 见 **会员管理**;本模块可提供入口跳转,**不在此定义** 注册表单 |
32
+
33
+### 1.2 在业务链中的位置
34
+
35
+```text
36
+【供给链】平台维护平台分类 → 商家发品挂平台二级 → 审核/出售中
37
+                              ↓
38
+【C 端 · 商城首页】一级快捷导航、「更多」入口
39
+                              ↓
40
+【C 端 · 本模块】全部分类页 / 一级分类商品页 / 分类商品列表
41
+                              ↓
42
+                      商品详情 → 加购/下单(须满足可购条件,见 §9)
43
+
44
+【用户链 · 并行】访客与会员均可浏览;下单须登录且会员启用
45
+```
46
+
47
+| 关联模块 | 关系 | 边界说明 |
48
+|----------|------|----------|
49
+| **商城首页** v1.0.1 | **上游入口** | 点击一级分类 → **一级分类商品页**;点击「更多」→ **全部分类页**(HM7) |
50
+| **商品分类(平台)** v1.5 | **类目来源** | 仅 **平台分类**(一级 + 二级);**是否显示** 控制 C 端可见性 |
51
+| **商品审核 / 商品管理** | **商品来源** | 列表仅展示 **出售中** 商品 |
52
+| **店铺管理** v1.3.5 | **间接** | 停业店铺商品 **可出现在** 浏览列表;**下单** 在详情/结算拦截 |
53
+| **会员管理** v1.0.1 | **用户链** | 访客与会员 **均可** 浏览;禁用会员 **不影响** 分类页展示 |
54
+| **关联需求分析** v1.6 §11 | **可购规则** | 本模块 **展示 ≠ 可下单**;详情/结算校验四条件 |
55
+
56
+### 1.3 与《关联需求分析》的对应
57
+
58
+| 关联分析要点 | 本模块 |
59
+|--------------|--------|
60
+| C 端浏览与下单分离 | 分类页、商品列表属 **浏览层** |
61
+| 平台分类 vs 店铺分类 | **仅用平台分类**(G1);**不展示** 店铺商品分类 |
62
+| C 端可售四条件 | 列表 **不校验** 店态/库存;详情/下单模块校验 |
63
+| 分类「不显示」 | C 端 **无** 该分类入口;已挂商品 **不自动下架**(C-P5) |
64
+| 店铺停业 | 商品 **可浏览**;**禁止下单**(S-P3、G10) |
65
+| 状态不级联 | 平台改分类显示/排序 **即时** 影响 C 端展示,**无** 单独「发布」动作 |
66
+
67
+### 1.4 使用端与角色
68
+
69
+| 端 | 角色 | 能力 |
70
+|----|------|------|
71
+| **C 端** | **访客** | 浏览全部分类、分类下商品列表、进详情 |
72
+| **C 端** | **已登录会员** | 同访客;加购/下单在其它页面 |
73
+| 平台管理端 | 平台管理员 | **不操作** 本页面;维护 **平台** 分类树 |
74
+| 店铺经营管理端 | 商家 | **不操作** 本页面;维护 **店铺商品分类**(与本模块无关) |
75
+
76
+---
77
+
78
+## 2. 页面与入口
79
+
80
+本模块包含 **三个页面**;其中 **商品列表展示规则** 在 B 内嵌区与 C 独立页 **共用**(§7):
81
+
82
+```text
83
+C 端商品分类
84
+├── A. 全部分类页(左右分栏:一级 + 二级)
85
+├── B. 一级分类商品页(二级 Tab + 同页下方商品列表)
86
+└── C. 二级分类商品列表页(独立页;仅 A 路径进入)
87
+```
88
+
89
+| 入口 | 目标页面 | 说明 |
90
+|------|----------|------|
91
+| 商城首页 · 点击某一 **一级分类** | **B. 一级分类商品页** | 带入该 **一级分类**;默认选中排序最靠前的 **二级 Tab** |
92
+| 商城首页 · 点击 **「更多」** | **A. 全部分类页** | 展示完整可见分类树 |
93
+| A · 点击某一 **二级分类** | **C. 二级分类商品列表页** | **跳转独立页**;无 Tab;顶栏展示 **一级 > 二级** 路径 |
94
+| B · 切换 **二级 Tab** | B 内 **同页** 商品列表区刷新 | **不跳转**;列表规则见 §7 |
95
+
96
+> **定稿(v1.0.1):**  
97
+> - **A1:** A 点二级 → **C 独立商品列表页**(**非** 在 A 页右侧直接展示商品)。  
98
+> - **B 与 C 区别:** B = Tab + 同页列表;C = 无 Tab 的独立列表页,带面包屑/路径标题。  
99
+> - 草稿「点击小类进入商品列表」= **A→C**;B 内 Tab 切换 **共用 §7 列表规则**。
100
+
101
+---
102
+
103
+## 3. 顶部搜索栏
104
+
105
+### 3.1 展示规则
106
+
107
+| 项 | 定稿 |
108
+|----|------|
109
+| 出现页面 | **A. 全部分类页**、**B. 一级分类商品页**、**C. 二级分类商品列表页(独立)** 顶部 **固定**(滚动时保持可见,具体吸顶样式以前台 UI 为准) |
110
+| 默认文案 | 占位提示:**「搜索兽药、饲料、店铺」**(与商城首页 HM3 **一致**) |
111
+| 可输入性 | 可为 **只读样式**;点击整栏进入 **搜索输入页** |
112
+
113
+### 3.2 交互流程
114
+
115
+```text
116
+用户在分类相关页面
117
+    → 点击搜索栏
118
+        → 进入「搜索输入页」(关键词、历史等 **另册**)
119
+        → 检索逻辑 **不在本模块定义**
120
+```
121
+
122
+### 3.3 边界
123
+
124
+| 规则 | 说明 |
125
+|------|------|
126
+| GC-S1 | 本模块 **不承担** 搜索逻辑与结果展示 |
127
+| GC-S2 | 占位文案 **固定** 为草稿约定文案(运营 **不可** 在平台后台配置,若需可配置另版本) |
128
+
129
+---
130
+
131
+## 4. 分类数据口径(只读)
132
+
133
+### 4.1 分类体系
134
+
135
+| 项 | 定稿 |
136
+|----|------|
137
+| 数据来源 | **平台商品分类**(平台管理员在 **平台 · 商品分类** 维护;全平台一份) |
138
+| 层级 | **一级 + 二级**;商品 **仅挂二级**,一级 **不直接** 挂商品(对齐平台 C5、C7) |
139
+| 不展示 | **店铺商品分类** 及任意 **店铺维度** 分类树 |
140
+
141
+### 4.2 可见性
142
+
143
+| 层级 | C 端展示条件 |
144
+|------|--------------|
145
+| **一级** | **是否显示 = 是**;**未删除**;且其下 **至少存在一个** 满足可见条件的 **二级**(避免空壳一级) |
146
+| **二级** | **是否显示 = 是**;**未删除**;且其 **所属一级** **是否显示 = 是** |
147
+
148
+> **说明:** 父级隐藏则子级 **一律不可见**,即使子级单独设为显示(对齐平台 §8.4、关联分析 C-P5)。
149
+
150
+### 4.3 排序
151
+
152
+| 层级 | 规则 |
153
+|------|------|
154
+| 一级、二级 | 同层 **排序值升序**;相同则 **创建时间升序**(与平台商品分类模块一致) |
155
+
156
+### 4.4 展示字段
157
+
158
+| 字段 | 说明 |
159
+|------|------|
160
+| 分类名称 | 必展示 |
161
+| 分类图片 | 有则展示缩略图;**缺失时** 使用 **默认占位图**(以前台 UI 为准) |
162
+| 是否热门 | 平台配置 **是否热门 = 是** 时,可在分类条目上展示 **热门标识**(样式以前台 UI 为准;**非必须**) |
163
+
164
+---
165
+
166
+## 5. 全部分类页(A)
167
+
168
+### 5.1 布局(对应草稿 §2)
169
+
170
+| 项 | 定稿 |
171
+|----|------|
172
+| 布局 | **左右分栏**:**左侧** 一级分类列表;**右侧** 当前选中一级下的 **二级分类** 列表 |
173
+| 默认选中 | 进入页面时,左侧默认选中 **排序最靠前** 的第一个 **可见一级**;右侧展示其下二级 |
174
+| 切换一级 | 点击左侧某一一级 → 右侧 **切换** 为对应二级列表(**不跳转** 新页) |
175
+
176
+### 5.2 交互流程
177
+
178
+```text
179
+用户从首页「更多」进入全部分类页
180
+    → 左侧展示全部可见一级
181
+    → 右侧展示默认一级的二级列表
182
+    → 用户点击左侧其他一级
183
+        → 右侧刷新为对应二级列表
184
+    → 用户点击右侧某一二级
185
+        → **跳转** 至 **C. 二级分类商品列表页(独立)**(§7)
186
+            → 页顶展示 **搜索栏** + **一级 > 二级** 路径(面包屑或标题栏,以前台 UI 为准)
187
+            → **无** 二级 Tab(与 B 页区分)
188
+```
189
+
190
+### 5.3 空态
191
+
192
+| 情形 | 行为 |
193
+|------|------|
194
+| 无任何可见一级(或均无可见二级) | 展示 **空态**(如「暂无分类」);**不展示** 虚假分类条目 |
195
+| 某一级下无可见二级 | 该一级 **不出现在** 左侧列表(见 §4.2 空壳过滤) |
196
+| 分类图片缺失 | 占位图 + 名称 |
197
+
198
+### 5.4 加载失败
199
+
200
+| 情形 | 行为 |
201
+|------|------|
202
+| 分类数据加载失败 / 超时 | 页面展示 **空态或错误提示**;**不白屏**;是否提供 **整页重试** 以前台 UX 为准(**不提供** 模块内局部重试按钮为默认定稿,与首页 HM14 一致) |
203
+
204
+---
205
+
206
+## 6. 一级分类商品页(B)
207
+
208
+承接 **商城首页** 点击某一 **一级分类** 的落地页(HM7、商城首页 §5.3)。
209
+
210
+### 6.1 布局
211
+
212
+| 项 | 定稿 |
213
+|----|------|
214
+| 页顶 | **顶部搜索栏**(§3) |
215
+| 一级上下文 | 展示当前 **一级分类名称**(及可选图片/面包屑,以前台 UI 为准) |
216
+| 二级导航 | 该一级下全部 **可见二级** 以 **Tab**(或等效横向标签)展示 |
217
+| 默认 Tab | 进入时选中 **排序最靠前** 的第一个 **可见二级** |
218
+| 列表区 | Tab 下方为 **分类商品列表**(§7) |
219
+
220
+### 6.2 交互流程
221
+
222
+```text
223
+用户从首页点击一级分类「兽药」
224
+    → 进入一级分类商品页,上下文 = 兽药
225
+    → 展示其下二级 Tab(如 抗生素、驱虫药…)
226
+    → 默认选中第一个 Tab,下方加载该二级商品列表
227
+    → 用户切换 Tab
228
+        → 下方列表 **回顶** 并 **重新加载** 对应二级商品(**C1**;Tab 条位置 **保持不变**)
229
+    → 用户点击某一商品
230
+        → 进入商品详情页
231
+```
232
+
233
+### 6.3 Tab 切换与滚动(定稿 C1)
234
+
235
+| 项 | 定稿 |
236
+|----|------|
237
+| 切换 Tab | 商品列表区 **滚动回顶**;按新 Tab 对应二级 **重新加载** 列表 |
238
+| Tab 条 | **保持** 当前横向位置(**不** 因切换 Tab 而重置 Tab 滚动位置) |
239
+| 排序状态 | 切换 Tab 后 **保留** 用户已选排序方式(如仍为「销量降序」) |
240
+
241
+### 6.4 边界
242
+
243
+| 情形 | 行为 |
244
+|------|------|
245
+| 一级存在但 **无可见二级** | **不应** 从首页进入(首页一级已过滤);若直达则展示 **空态** |
246
+| 一级被平台改为 **不显示** | 用户 **刷新或再进入** 时该入口消失;已打开页按 **空态/失效提示** 处理 |
247
+
248
+---
249
+
250
+## 7. 商品列表规则(B 内嵌区 / C 独立页共用)
251
+
252
+对应草稿 §3「对应商品列表」;**B 页 Tab 下方列表区** 与 **C 独立页** **共用** 下列规则。
253
+
254
+### 7.0 C 独立页结构(A1)
255
+
256
+| 项 | 定稿 |
257
+|----|------|
258
+| 进入方式 | 仅 **A. 全部分类页** 点击某一 **二级分类** |
259
+| 页顶 | **搜索栏**(§3)+ **一级 > 二级** 路径展示 |
260
+| 主体 | **商品列表**(本节 §7.1~§7.6) |
261
+| 无 Tab | **不展示** 同级其它二级 Tab;返回 **A 页** 可再选其它二级 |
262
+
263
+### 7.1 数据范围
264
+
265
+列表展示 **当前所选平台二级分类** 下、**全平台跨店铺** 聚合的可浏览商品。
266
+
267
+参与列表的商品须 **同时满足**:
268
+
269
+| # | 条件 | 来源 |
270
+|---|------|------|
271
+| 1 | 商品状态 = **出售中** | 商品管理 / 商品审核 |
272
+| 2 | 商品 **未逻辑删除** | 商品管理 |
273
+| 3 | 所属店铺 **未逻辑删除** | 店铺管理 |
274
+| 4 | 商品已挂 **当前所选平台二级分类** | 商品分类 C5 |
275
+
276
+> **定稿(浏览层):** **不要求** 店铺 **开业** 才可 **出现在列表**;**不要求** 库存 > 0 才可 **展示**(与商城首页 §6.2、关联分析「浏览 vs 下单分离」一致)。
277
+
278
+**不要求:** 店铺商品分类已填、商品服务项已选 — **不影响** 是否进入列表。
279
+
280
+### 7.2 单条展示字段
281
+
282
+| 字段 | 说明 |
283
+|------|------|
284
+| 商品主图 | 列表主图 |
285
+| 商品名称 | 单行或双行截断以前台 UI 为准 |
286
+| 售价 | 当前 **出售中** 售价 |
287
+| 店铺名称 | 所属 **店铺名称**;**实时读取** 店铺档案(店改名后刷新列表即更新) |
288
+
289
+**不展示(本期列表卡片):** 库存、销量数字、服务标签、规格 — 若需展示销量另版本。
290
+
291
+### 7.3 排序(对应草稿 §3)
292
+
293
+用户可在列表区切换排序方式:
294
+
295
+| 排序项 | 规则 |
296
+|--------|------|
297
+| **销量** | 累计销量 **降序** 或 **升序**(用户二选一) |
298
+| **价格** | 售价 **降序** 或 **升序**(用户二选一) |
299
+
300
+| 项 | 定稿 |
301
+|----|------|
302
+| 默认排序 | 进入列表时默认 **销量降序**(以前台 UX 可标注「销量」为当前选中态) |
303
+| 切换排序 | 用户切换后 **重新排序** 当前列表;**不切换** 分类上下文 |
304
+| 稳定性 | 同排序值下,以 **商品编号升序** 保证顺序稳定 |
305
+| 排序区 UI | 提供 **销量**、**价格** 两个维度及 **升序/降序**(共四种组合);宜采用 **排序条 / 下拉面板** 等单一入口切换(具体样式以前台 UI 为准) |
306
+
307
+### 7.4 分页与加载
308
+
309
+| 项 | 定稿 |
310
+|----|------|
311
+| 加载方式 | 支持 **分页** 或 **上拉加载更多**(以前台 UX 选定一种或组合) |
312
+| 空列表 | 展示 **空态**(如「该分类下暂无商品」);**不** 用其它分类商品补齐 |
313
+
314
+### 7.5 交互流程
315
+
316
+```text
317
+用户进入某二级分类的商品列表
318
+    → 默认按销量降序展示出售中商品
319
+    → 用户切换「价格升序/降序」或「销量升序/降序」
320
+        → 列表重排/重新加载
321
+    → 用户点击商品卡片
322
+        → 进入商品详情页
323
+    → 用户上拉/翻页
324
+        → 加载更多(若有)
325
+```
326
+
327
+### 7.6 加载失败
328
+
329
+| 情形 | 行为 |
330
+|------|------|
331
+| 商品列表加载失败 | 列表区 **空态或错误提示**;**不阻塞** 分类导航/Tab;是否整页重试以前台 UX 为准 |
332
+
333
+---
334
+
335
+## 8. 页面间导航关系
336
+
337
+```text
338
+                    ┌── 商城首页 ──┐
339
+                    │  一级 / 更多  │
340
+                    └──────┬───────┘
341
+           ┌───────────────┼───────────────┐
342
+           ▼                               ▼
343
+   B. 一级分类商品页                  A. 全部分类页
344
+   (Tab + 同页列表)                 (左一级 / 右二级)
345
+           │                               │
346
+           │ 切换 Tab(列表回顶 C1)          │ 点击二级
347
+           │                               ▼
348
+           │                       C. 二级分类商品列表页
349
+           │                       (独立 · 面包屑 + 列表)
350
+           └───────────┬───────────────────┘
351
+                       │ 共用 §7 列表规则
352
+                       ▼
353
+                 点击商品 → 商品详情页(另册)
354
+```
355
+
356
+| 导航 | 定稿 |
357
+|------|------|
358
+| 返回 | 遵循系统 **返回栈**;从 B 返回首页 **保留** 首页滚动位置(对齐商城首页 §7.3) |
359
+| 深度链接 | 若 APP 支持外链/分享进入某分类列表,须 **校验分类可见性**;不可见则 **空态或友好提示** |
360
+
361
+---
362
+
363
+## 9. 与下单、会员的协作(浏览 vs 可购)
364
+
365
+本模块 **仅浏览与跳转**,**不在此** 完成加购或支付。用户进入商品详情后,须满足 **《关联需求分析》§11 C 端可售条件** 方可下单:
366
+
367
+| # | 条件 |
368
+|---|------|
369
+| 1 | 商品 = **出售中** |
370
+| 2 | 店铺 = **开业** |
371
+| 3 | 商品所属 **平台分类可见**(一级、二级 **是否显示** 均为是) |
372
+| 4 | 库存 **> 0** |
373
+
374
+| 场景 | 行为 |
375
+|------|------|
376
+| 访客浏览分类与列表 | **允许**;加购/下单引导 **登录/注册** |
377
+| 会员禁用 | 分类页 **仍可浏览**;登录/下单被会员模块拦截 |
378
+| 停业店铺商品出现在列表 | 可 **浏览** 详情;下单提示 **店铺已停业** |
379
+| 库存为 0 | 可 **浏览** 详情;加购/下单提示 **库存不足** |
380
+| 分类被改为 **不显示** | 入口消失;已收藏/历史链接进入时 **失效提示** |
381
+
382
+---
383
+
384
+## 10. 与平台后台配置的协作
385
+
386
+```text
387
+【平台 · 商品分类】
388
+    维护一级/二级、图片、排序、是否显示、是否热门
389
+        ↓ 即时生效(无 C 端单独发布)
390
+【C 端 · 本模块】
391
+    全部分类页 / 一级分类页 / 商品列表 只读展示
392
+【平台 · 供给链】
393
+    商户 → 店铺 → 发品挂平台二级 → 出售中
394
+        ↓
395
+    分类下列表仅展示出售中商品
396
+```
397
+
398
+| 平台操作 | 对本模块的影响 |
399
+|----------|----------------|
400
+| 新增/编辑分类名称、图片、排序 | 分类页 **即时** 更新展示 |
401
+| 分类 **是否显示** 改为否 | C 端 **隐藏** 该分类入口;列表 **不再** 纳入新浏览(已打开页刷新后生效) |
402
+| 商品上架为出售中 / 下架 | 分类列表 **增删** 可浏览商品 |
403
+| 店铺改名 | 列表 **店铺名称** 即时更新 |
404
+| 店铺停业 | **不** 自动从列表移除;下单在详情拦截 |
405
+| 会员禁用 | **不影响** 分类浏览 |
406
+
407
+---
408
+
409
+## 11. 异常与提示(产品层)
410
+
411
+| 场景 | 要求 |
412
+|------|------|
413
+| 网络异常 | 使用 C 端 **统一网络提示**;不在本模块单独定义文案体系 |
414
+| 业务校验失败 | 提示语 **明确**(如「分类不存在或不可见」「排序方式无效」);与 C 端其它模块 **体验一致** |
415
+| 草稿 §4「code、msg、data」 | **不在本文定义**;属实现层约定 |
416
+
417
+---
418
+
419
+## 12. 业务规则汇总
420
+
421
+| 编号 | 规则 |
422
+|------|------|
423
+| **GC1** | C 端分类浏览 **仅使用平台商品分类**;**不使用** 店铺商品分类 |
424
+| **GC2** | 访客与已登录会员 **均可** 浏览本模块页面 |
425
+| **GC3** | 搜索栏占位 **「搜索兽药、饲料、店铺」**;点击进 **搜索输入页**(GC-S1、GC-S2) |
426
+| **GC4** | **全部分类页 A**:左一级、右二级;点二级 **跳转 C 独立列表页**(A1) |
427
+| **GC5** | **一级分类商品页 B**:二级 **Tab** + 同页列表;切换 Tab **列表回顶重载**(C1) |
428
+| **GC5a** | **C 独立页**:含 **搜索栏** + **一级>二级** 路径;**无 Tab** |
429
+| **GC6** | 分类可见性:**显示=是** 且未删;二级须 **父级一级亦显示** |
430
+| **GC7** | 分类排序:同层 **排序值升序** → **创建时间升序** |
431
+| **GC8** | 商品列表范围:**当前二级** 下 **出售中** 且商品/店铺 **未删**;**全平台** 跨店 |
432
+| **GC9** | 列表 **默认销量降序**;用户可切换 **价格/销量** 升序或降序 |
433
+| **GC10** | 列表卡片:**主图、名称、售价、店铺名称**(店铺名 **实时读档**) |
434
+| **GC11** | 点击商品 → **商品详情页** |
435
+| **GC12** | 本模块 **展示 ≠ 可下单**;可购四条件在 **详情/结算** 校验 |
436
+| **GC13** | 列表 **可含** 停业店、零库存商品(浏览层) |
437
+| **GC14** | 分类/列表加载失败 **不白屏**;分类区与列表区 **可独立空态** |
438
+| **GC15** | 本模块 **不提供** 分类或商品的 **编辑** 能力 |
439
+
440
+---
441
+
442
+## 13. 页面与交互要点
443
+
444
+| 场景 | 要求 |
445
+|------|------|
446
+| 全部分类页 | 左右分栏 **一级联动二级**;点二级 **跳转 C 独立页** |
447
+| 一级分类页 | Tab 多时可 **横向滑动**;切换 Tab **列表回顶**(C1) |
448
+| C 独立列表页 | **搜索栏 + 路径标题 + 列表**;**无 Tab** |
449
+| 商品列表 | 排序区含 **销量/价格 × 升/降**;切换排序 **即时反馈** |
450
+| 图片缺失 | 分类/商品均可用 **占位图** |
451
+| 空分类 / 空商品 | **明确空态文案** |
452
+| 从首页进入 | 与 HM7 **路径一致**,无第二套交互 |
453
+
454
+---
455
+
456
+## 14. 草稿对照与修订说明
457
+
458
+| 草稿条目 | 本文档处理 |
459
+|----------|------------|
460
+| §1 搜索栏、占位、进搜索页 | §3 |
461
+| §2 二级分类、左右分栏、点大类切换小类 | §5(全部分类页) |
462
+| §3 点小类进商品列表、价格销量排序 | §7;**B 页 Tab 切换** 同规则 |
463
+| §4 异常返回 code/msg/data | §11 **不纳入** |
464
+| §5 技术栈 RuoYi/MySQL | **不纳入** |
465
+| 未写:首页入口、平台口径、可购边界 | §2、§4、§9 补充 |
466
+| 未写:一级分类 Tab 落地页 | §6;对齐商城首页 v1.0.1 **定稿 A** |
467
+| v1.0.1 | A1 独立列表页;C1 Tab 切换回顶;C 页搜索栏;排序 UI §7.3 |
468
+
469
+---
470
+
471
+## 15. 非本期范围
472
+
473
+| 项 | 说明 |
474
+|------|------|
475
+| 搜索输入页、搜索结果、搜索历史 | 待建设搜索模块 |
476
+| C 端 **商品详情** 正式需求 | 本模块仅跳转 |
477
+| 按 **店铺商品分类** 浏览 | — |
478
+| 按 **店铺** 维度的分类馆 / 店铺首页 | — |
479
+| 列表展示 **销量数字、库存、服务标签** | 本期卡片 **不展示** |
480
+| 三级及以上分类 | 平台 **禁止**(C1) |
481
+| 分类页 **加购、领券、分享** | 在详情或另册 |
482
+| 分类/排序 **后台可配置**(C 端默认排序、占位文案) | 本期产品定稿;若需配置另版本 |
483
+| 下拉刷新 | 默认 **不做**(与商城首页 v1.0.1 一致);若分类页单独需要另版本 |
484
+
485
+---
486
+
487
+## 16. 修订记录
488
+
489
+| 版本 | 说明 |
490
+|------|------|
491
+| **v1.0** | 首版定稿:搜索入口、全部分类页、一级分类 Tab 页、分类商品列表与排序;关联平台商品分类 v1.5、《关联需求分析》v1.6、商城首页 v1.0.1 |
492
+| **v1.0.1** | A1 A→C 独立列表页;C1 B 切换 Tab 列表回顶;C 页含搜索栏;排序区 UI;与首页一级可见口径对齐 |
493
+
494
+---
495
+
496
+*文档版本:v1.0.1(定稿)· 关联《关联需求分析.md》v1.6、《商品分类功能需求.md》v1.5、《商城首页功能需求.md》v1.0.2 · 草稿《商品分类功能需求-草稿.md》保持不变。*

+ 372 - 0
doc/消费者APP/商品分类/商品分类技术方案.md

@@ -0,0 +1,372 @@
1
+# 商品分类 — 技术方案(C 端)
2
+
3
+> **依据:** 《商品分类功能需求.md》v1.0.1  
4
+> **关联:** 平台《商品分类技术方案.md》v1.2、《商品管理技术方案.md》v1.3、《店铺管理技术方案.md》v1.2.6、《商城首页技术方案.md》v1.1、《关联需求分析.md》v1.6  
5
+> **范围:** C 端 **`/api/category/**`** 只读(全部分类树、一级下二级 Tab、二级下商品分页列表);**无** 平台/店铺分类 CRUD、**无** 搜索执行、**无** 新建业务主表。  
6
+> **原则:** **无新建表**;复用 `biz_goods_category`、`biz_goods`、`biz_shop`;全部 **`@Anonymous`**;搜索栏 **无接口**(GC-S1)。
7
+
8
+---
9
+
10
+## 1. 技术架构
11
+
12
+| 项 | 选型 |
13
+|----|------|
14
+| 基础框架 | RuoYi **v3.9.2**(`springboot2` 分支) |
15
+| 数据库 | **MySQL 5.7.39** |
16
+| ORM / 响应 | MyBatis;分类树 `AjaxResult`;商品列表 `TableDataInfo`(分页) |
17
+| 鉴权 | C 端 **`@Anonymous`**(访客/会员均可读) |
18
+| 分页 | RuoYi **`PageHelper`** + `startPage()`(`/goods`) |
19
+| 缓存 | **本期不做** Redis;读库即时生效 |
20
+
21
+### 1.1 模块落位
22
+
23
+```text
24
+baqing-shop/src/main/java/com/ruoyi/web/modules/home/
25
+├── controller/CategoryAppController.java     # /api/category/**
26
+├── service/ICategoryAppService.java
27
+├── service/impl/CategoryAppServiceImpl.java
28
+├── mapper/CategoryAppMapper.java             # 二级 Tab、分类商品 SQL
29
+├── dto/CategoryGoodsQuery.java
30
+├── constant/CategoryAppConstants.java        # sortBy 枚举值
31
+└── vo/HomeHotGoodsVO.java                    # 商品卡片(与首页热销共用)
32
+
33
+已有(category 模块 · Facade):
34
+└── com.ruoyi.web.modules.category.facade.ICategoryFacade
35
+        listVisibleByShopId(null)               # 平台可见分类树(A 页 / tree)
36
+        isCategoryVisible(categoryId)         # 二级校验(/goods)
37
+
38
+已有(home 模块 · 首页一级导航,勿混用):
39
+└── HomeAppController GET /api/home/categories
40
+        仅一级扁平列表,供首页横向导航;**不含** children
41
+
42
+resources/mapper/home/
43
+└── CategoryAppMapper.xml
44
+```
45
+
46
+> **说明:** C 端分类与 C 端首页 **同属 `home` 包**、**不同基路径**(`/api/category` vs `/api/home`)。平台分类 CRUD 仍在 `/agri/category`(`PlatformCategoryController`)。
47
+
48
+### 1.2 协作链
49
+
50
+```text
51
+biz_goods_category(shop_id IS NULL)
52
+    ├── GET /api/category/tree              → A 页 · 一级+二级树
53
+    ├── GET /api/category/{level1Id}/level2-tabs → B 页 · 二级 Tab
54
+    └── (校验)isCategoryVisible
55
+
56
+biz_goods + biz_shop
57
+    └── GET /api/category/goods?categoryId=&sortBy=&pageNum=&pageSize=
58
+            → B 内嵌列表 / C 独立列表(共用)
59
+
60
+【页面与接口】
61
+    首页 HM7 ──一级──► B:level2-tabs + goods(默认首 Tab)
62
+    首页「更多」──► A:tree ──点二级──► C:goods(同 goods 接口)
63
+    搜索栏:纯前端 GC-S1(无接口)
64
+```
65
+
66
+| 关联模块 | 协作 |
67
+|----------|------|
68
+| **平台 · 商品分类** | 维护 `biz_goods_category`;C 端只读 `show_flag` / 排序 |
69
+| **商品分类 Facade** | `listVisibleByShopId(null)` 过滤空壳一级、父隐子隐(GC6) |
70
+| **商城首页** | `/api/home/categories` 一级快捷导航;点击进入本模块 B 页 |
71
+| **商品 / 审核** | 列表 `goods_status=2`(出售中) |
72
+| **店铺管理** | JOIN `shop_name` 实时;**不过滤** 停业/库存(GC13) |
73
+| **会员管理** | **不校验** Token |
74
+| **商品详情** | 列表点击跳转;可购四条件在详情/Facade 校验(GC12) |
75
+
76
+### 1.3 跨模块 Facade
77
+
78
+| 接口 | 本模块用法 |
79
+|------|------------|
80
+| **`ICategoryFacade.listVisibleByShopId(null)`** | `/tree` 平台全量可见树(含 children) |
81
+| **`ICategoryFacade.isCategoryVisible`** | `/goods` 校验二级 `categoryId` |
82
+| **`IPlatformCategoryService`** | **不直接** 暴露 C 端 HTTP |
83
+| **`IGoodsPurchaseFacade` / 详情** | 本模块 **不调用**;下单层校验 |
84
+
85
+---
86
+
87
+## 2. 数据库设计
88
+
89
+### 2.1 原则
90
+
91
+**无新建表。** 只读下列已有表;DDL 见 `sql/`。
92
+
93
+| 表 | 本模块用途 | 权威 DDL |
94
+|----|------------|----------|
95
+| `biz_goods_category` | 平台一/二级导航 | `sql/biz_goods_category.sql` |
96
+| `biz_goods` | 二级下商品列表 | `sql/biz_goods.sql` |
97
+| `biz_shop` | 商品卡片店铺名 | `sql/biz_shop.sql` |
98
+
99
+### 2.2 本模块读字段与条件
100
+
101
+**`biz_goods_category`** — 平台分类(`shop_id IS NULL`)
102
+
103
+| 场景 | 条件 | 输出字段 |
104
+|------|------|----------|
105
+| **tree**(Facade 内存组装) | 一级 `show=1` 且下有可见二级;二级 `isCategoryVisible` | `categoryId, categoryName, categoryPic, sortNo, hotFlag` + `children[]` |
106
+| **level2-tabs** | `level=2`, `parent_id=#{level1Id}`, `show=1`, `del=0`;且一级可见 | 同上(无 children) |
107
+
108
+**`biz_goods` + `biz_shop`** — 分类商品列表
109
+
110
+| 字段 | 条件 / 说明 |
111
+|------|-------------|
112
+| g.category_id | **=** 所选二级 ID |
113
+| g.goods_status | **`2`** 出售中(GC8) |
114
+| g.del_flag | **`0`** |
115
+| s.del_flag | **`0`**(JOIN) |
116
+| g.sales_count, g.sale_price, g.goods_sn | 排序键(GC9) |
117
+| g.goods_id, g.goods_sn, g.goods_name, g.main_pic, g.sale_price | 输出 |
118
+| s.shop_id, s.shop_name | JOIN;**实时**(GC10) |
119
+
120
+> **浏览层:** SQL **不过滤** `shop_status`、**不过滤** `stock`(GC13)。
121
+
122
+### 2.3 与首页 `/api/home/categories` 差异
123
+
124
+| 项 | `/api/home/categories` | `/api/category/tree` |
125
+|----|------------------------|----------------------|
126
+| 层级 | 仅 **一级** 扁平 | **一级 + 二级** 树 |
127
+| 空壳一级 | SQL 仅 `show=1`(首页导航宜与 GC6 对齐:无可见二级的不展示) | Facade **排除** 无可见二级的一级 |
128
+| 用途 | 首页横向导航 +「更多」 | A 全部分类页 |
129
+
130
+### 2.4 建议索引(可选)
131
+
132
+```sql
133
+-- 可选:与首页热销共用
134
+-- idx_category_id on biz_goods(category_id)
135
+-- idx_shop_status 等现有索引一般够用
136
+```
137
+
138
+---
139
+
140
+## 3. C 端接口设计
141
+
142
+**基路径:** `/api/category`  
143
+**鉴权:** 全部 **`@Anonymous`**
144
+
145
+### 3.1 接口一览
146
+
147
+| 方法 | 路径 | 页面 | 说明 |
148
+|------|------|------|------|
149
+| GET | `/tree` | **A** 全部分类 | 平台可见分类树 |
150
+| GET | `/{level1Id}/level2-tabs` | **B** Tab 区 | 指定一级下二级列表 |
151
+| GET | `/goods` | **B/C** 商品列表 | 二级下出售中商品 **分页** |
152
+
153
+**本期不提供:** 搜索 API、聚合 `/api/category/index`、店铺分类读接口。
154
+
155
+### 3.2 可见分类树 `GET /api/category/tree`
156
+
157
+| 项 | 说明 |
158
+|----|------|
159
+| Query | **无** |
160
+| 实现 | `ICategoryFacade.listVisibleByShopId(null)` |
161
+
162
+**`data`:** `CategoryVisibleVO[]`(`category` 模块已有 VO)
163
+
164
+| 字段 | 说明 |
165
+|------|------|
166
+| categoryId | 一级 ID |
167
+| categoryName | 名称 |
168
+| categoryPic | 图片 |
169
+| sortNo | 排序 |
170
+| hotFlag | 是否热门 |
171
+| children | `CategoryVisibleChildVO[]`(二级) |
172
+
173
+**children 元素:** `categoryId, categoryName, categoryPic, sortNo, hotFlag`
174
+
175
+**前端(A 页):** 左列 = `data`;右列 = 当前选中一级的 `children`;点 child → 路由 C 页并带 `categoryId`。
176
+
177
+### 3.3 一级下二级 Tab `GET /api/category/{level1Id}/level2-tabs`
178
+
179
+| 项 | 说明 |
180
+|----|------|
181
+| Path | `level1Id` — 平台一级 ID |
182
+| 校验 | 一级存在、`shop_id IS NULL`、`level=1`、`show=1`;否则 `ServiceException` |
183
+
184
+**`data`:** `CategoryVisibleChildVO[]`(按 `sort_no, create_time` 升序)
185
+
186
+**SQL 片段:**
187
+
188
+```sql
189
+SELECT category_id, category_name, category_pic, hot_flag, sort_no
190
+FROM biz_goods_category
191
+WHERE shop_id IS NULL
192
+  AND category_level = '2'
193
+  AND parent_id = #{level1Id}
194
+  AND show_flag = '1'
195
+  AND del_flag = '0'
196
+ORDER BY sort_no ASC, create_time ASC
197
+```
198
+
199
+**前端(B 页):** 首页带入 `level1Id` → 本接口渲染 Tab → 默认首 Tab 调 `/goods`;切换 Tab(**C1**)仅改 `categoryId` 重新请求 `/goods`。
200
+
201
+### 3.4 分类商品列表 `GET /api/category/goods`
202
+
203
+| 项 | 说明 |
204
+|----|------|
205
+| Query | 见下表 |
206
+| 分页 | `pageNum`、`pageSize`(RuoYi 默认) |
207
+| 响应 | **`TableDataInfo`**:`{ code, msg, rows, total }` |
208
+
209
+**Query:**
210
+
211
+| 参数 | 必填 | 说明 |
212
+|------|:----:|------|
213
+| categoryId | 是 | **平台二级** 分类 ID |
214
+| sortBy | 否 | 默认 `sales_desc`(`CategoryAppConstants.DEFAULT_SORT`) |
215
+| pageNum | 否 | 页码 |
216
+| pageSize | 否 | 每页条数 |
217
+
218
+**sortBy 合法值:**
219
+
220
+| 值 | 排序 |
221
+|----|------|
222
+| `sales_desc` | 销量降序 → `goods_sn` 升序 |
223
+| `sales_asc` | 销量升序 → `goods_sn` 升序 |
224
+| `price_desc` | 售价降序 → `goods_sn` 升序 |
225
+| `price_asc` | 售价升序 → `goods_sn` 升序 |
226
+
227
+**校验:**
228
+
229
+1. `categoryId` 非空  
230
+2. `categoryFacade.isCategoryVisible(categoryId)` 为 true,否则「分类不存在或不可见」  
231
+3. `sortBy` 非法 →「排序参数无效」
232
+
233
+**`rows` 元素:** `HomeHotGoodsVO`(与 `/api/home/hot-goods` 同结构)
234
+
235
+| 字段 | 类型 | 说明 |
236
+|------|------|------|
237
+| goodsId | long | |
238
+| goodsSn | string | |
239
+| goodsName | string | |
240
+| mainPic | string | |
241
+| salePrice | decimal | |
242
+| shopId | long | |
243
+| shopName | string | 实时 |
244
+
245
+**SQL 片段:**
246
+
247
+```sql
248
+SELECT g.goods_id, g.goods_sn, g.goods_name, g.main_pic, g.sale_price,
249
+       s.shop_id, s.shop_name
250
+FROM biz_goods g
251
+INNER JOIN biz_shop s ON g.shop_id = s.shop_id AND s.del_flag = '0'
252
+WHERE g.goods_status = '2'
253
+  AND g.del_flag = '0'
254
+  AND g.category_id = #{categoryId}
255
+ORDER BY
256
+  -- 动态 sortBy,同键 goods_sn ASC
257
+```
258
+
259
+### 3.5 搜索栏
260
+
261
+| 项 | 说明 |
262
+|----|------|
263
+| 占位 | 前端写死「搜索兽药、饲料、店铺」(GC3) |
264
+| 接口 | **无** |
265
+
266
+---
267
+
268
+## 4. Service 分层
269
+
270
+```text
271
+CategoryAppController
272
+    → ICategoryAppService.listCategoryTree()
273
+    → ICategoryAppService.listLevel2Tabs(level1Id)
274
+    → ICategoryAppService.listCategoryGoods(query)   # 分页由 Controller startPage
275
+
276
+CategoryAppServiceImpl
277
+    → ICategoryFacade.listVisibleByShopId(null)
278
+    → ICategoryFacade.isCategoryVisible(categoryId)
279
+    → BizGoodsCategoryMapper.selectById(level1Id, null)   # 一级校验
280
+    → CategoryAppMapper.selectPlatformLevel2Tabs(level1Id)
281
+    → CategoryAppMapper.selectGoodsByCategory(categoryId, sortBy)
282
+```
283
+
284
+| 方法 | 说明 |
285
+|------|------|
286
+| `listCategoryTree()` | 透传 Facade;`shopId=null` → 平台树 |
287
+| `listLevel2Tabs()` | 校验一级可见 → Mapper 查二级 |
288
+| `listCategoryGoods()` | 校验二级可见 + sortBy → Mapper;**不** 校验可购四条件 |
289
+
290
+---
291
+
292
+## 5. 与平台后台对照
293
+
294
+| 平台操作 | 影响的 C 端接口 |
295
+|----------|-----------------|
296
+| 平台分类改 show/排序/名称/图 | `/tree`、`/level2-tabs` |
297
+| 平台分类改 show=0 | 树/Tab 即时隐藏;`/goods` 校验失败 |
298
+| 商品上下架 | `/goods` 增删行 |
299
+| 店铺改名 | `/goods` 的 `shopName` |
300
+| 店铺停业 | **仍出现在** `/goods`;下单在详情拦截 |
301
+| 会员禁用 | **不影响** 三接口 |
302
+
303
+---
304
+
305
+## 6. 业务规则映射
306
+
307
+| 规则 | 实现 |
308
+|------|------|
309
+| GC1 | 仅 `shop_id IS NULL` / Facade 平台树 |
310
+| GC2 | `@Anonymous` |
311
+| GC3~GC-S2 | 无搜索 API |
312
+| GC4 | `/tree` + 前端 A→C 路由 |
313
+| GC5 | `/level2-tabs` + `/goods`;Tab 切换前端 C1 |
314
+| GC5a | `/goods` + 前端面包屑(路径非接口字段) |
315
+| GC6 | Facade `listVisibleByShopId` + `isCategoryVisible` |
316
+| GC7 | SQL / Facade 排序 |
317
+| GC8~GC10 | `/goods` SQL + `HomeHotGoodsVO` |
318
+| GC9 | `sortBy` 四档 + 默认 `sales_desc` |
319
+| GC12 | Service **不** 调可购 Facade |
320
+| GC13 | SQL 不过滤店态/库存 |
321
+| GC14 | 三接口 **独立**;前端模块级空态 |
322
+| GC15 | 无写接口 |
323
+
324
+---
325
+
326
+## 7. 前端接口调用建议
327
+
328
+| 页面 | 调用顺序 |
329
+|------|----------|
330
+| **A** 全部分类 | `GET /tree` → 点二级 → `GET /goods?categoryId=` |
331
+| **B** 一级分类 | `GET /{level1Id}/level2-tabs` → `GET /goods?categoryId=首Tab` → 切换 Tab 仅重调 `/goods` |
332
+| **C** 独立列表 | `GET /goods?categoryId=`(`level1Id` 由路由/状态展示面包屑) |
333
+
334
+> **C1:** Tab 切换时前端 **列表回顶** 后再请求 `/goods`(产品层,非后端字段)。
335
+
336
+---
337
+
338
+## 8. 实现状态
339
+
340
+| 项 | 状态 |
341
+|----|------|
342
+| `GET /api/category/tree` | **待实现** |
343
+| `GET /api/category/{level1Id}/level2-tabs` | **待实现** |
344
+| `GET /api/category/goods` | **待实现** |
345
+| `CategoryAppController` / `CategoryAppServiceImpl` | **待创建** |
346
+| `CategoryAppMapper.xml` | **待创建** |
347
+| C 端分类前端 | **未实现** |
348
+
349
+---
350
+
351
+## 9. 非本期
352
+
353
+| 项 | 说明 |
354
+|------|------|
355
+| `GET /api/category/index` 聚合 | 前端分调三接口即可 |
356
+| Redis 缓存 | — |
357
+| `/api/search/**` | 搜索模块 |
358
+| 店铺商品分类 C 端读 | GC1 排除 |
359
+| 列表返回销量/库存字段 | GC10 卡片不展示 |
360
+| 商品详情 API | 商品详情模块 |
361
+
362
+---
363
+
364
+## 10. 修订记录
365
+
366
+| 版本 | 说明 |
367
+|------|------|
368
+| **v1.0** | 首版;无新建表;`/api/category` 三读接口;关联平台分类 Facade、商城首页 v1.1 |
369
+
370
+---
371
+
372
+*文档版本:v1.0 · MySQL 5.7.39 · RuoYi v3.9.2-springboot2 · 关联《商品分类功能需求.md》v1.0.1、《商品分类技术方案.md》v1.2(平台)、《商城首页技术方案.md》v1.1*

+ 774 - 0
doc/消费者APP/商品分类/商品分类测试用例.md

@@ -0,0 +1,774 @@
1
+# 商品分类 — 测试用例(C 端)
2
+
3
+> **依据:** 《商品分类功能需求.md》v1.0.1、《商品分类技术方案.md》v1.0  
4
+> **关联:** 平台《商品分类测试用例.md》CAT-UT-013/014(同源 `isCategoryVisible`)、C 端《商城首页测试用例.md》MHP-UI-008/009(HM7 入口)、《关联需求分析.md》§11  
5
+> **范围:** C 端 `/api/category/tree`、`/api/category/{level1Id}/level2-tabs`、`/api/category/goods`;`CategoryAppServiceImpl` / `CategoryAppController`  
6
+> **代码落位:** `baqing-shop/src/main/java/com/ruoyi/web/modules/home/`  
7
+> **排除:** 搜索执行与结果页、商品详情可购四条件校验、平台/店铺分类 CRUD、店铺商品分类 C 端导航、聚合 `/api/category/index`  
8
+> **环境:** RuoYi v3.9.2-springboot2;MySQL 5.7.39;C 端 **无需 Token**(`@Anonymous`);UI 使用 Playwright **`channel: 'chrome'`**
9
+
10
+---
11
+
12
+## 用例编号说明
13
+
14
+| 前缀 | 类型 | 工具 |
15
+|------|------|------|
16
+| CGC-UT-xxx | 单元测试 | JUnit 5 + Mockito |
17
+| CGC-API-xxx | 接口测试 | MockMvc / Apifox |
18
+| CGC-UI-xxx | 界面测试 | Playwright(Google Chrome) |
19
+
20
+> **命名:** **CGC** = Consumer Goods Category(C 端商品分类);与平台 **CAT-** 区分。
21
+
22
+---
23
+
24
+## 测试数据约定
25
+
26
+| 实体 | 说明 |
27
+|------|------|
28
+| 平台一级 | L1-A「兽药」show=1 sort=0;L1-B「饲料」show=1 sort=1;L1-H **隐藏** show=0;L1-E **空壳**(无可见二级);L1-S 店铺101一级(**不应出现**) |
29
+| 平台二级 | L1-A 下 L2-A1「抗生素」、L2-A2「驱虫药」show=1;L2-H **隐藏** show=0;L2-P 父级 L1-H(**不可见**) |
30
+| 店铺 | S101 开业;S102 **停业**;S103 已删 |
31
+| 出售中商品 | G-A1@L2-A1 销量100 价88;G-A2 销量100 价99(同销量测排序);G-A3 销量50;G-B1@S102 停业;G-Z1 stock=0;G-O1 已下架/待审核(**不进列表**) |
32
+| 会员 | M1 正常;M2 **禁用**(分类仍可读) |
33
+
34
+---
35
+
36
+## 一、单元测试
37
+
38
+### CGC-UT-001 分类树委托平台 Facade
39
+
40
+| 要素 | 内容 |
41
+|------|------|
42
+| **用例编号** | CGC-UT-001 |
43
+| **测试模块** | C 端商品分类 |
44
+| **测试项** | listCategoryTree |
45
+| **测试类型** | 单元测试 |
46
+| **测试工具** | JUnit 5 + Mockito |
47
+| **测试目的** | 验证 GC1、GC6 |
48
+| **前置条件** | Mock `ICategoryFacade.listVisibleByShopId(null)` 返回含 L1-A 树 |
49
+| **测试步骤** | 调用 `listCategoryTree()` |
50
+| **预期结果** | 调用 `listVisibleByShopId(null)`;返回与 Facade 一致 |
51
+
52
+### CGC-UT-002 分类树空列表
53
+
54
+| 要素 | 内容 |
55
+|------|------|
56
+| **用例编号** | CGC-UT-002 |
57
+| **测试模块** | C 端商品分类 |
58
+| **测试项** | listCategoryTree 空态 |
59
+| **测试类型** | 单元测试 |
60
+| **测试工具** | JUnit 5 + Mockito |
61
+| **测试目的** | 验证 §5.3 边界 |
62
+| **前置条件** | Facade 返回 empty |
63
+| **测试步骤** | `listCategoryTree()` |
64
+| **预期结果** | 空 List;**不** 抛异常 |
65
+
66
+### CGC-UT-003 二级 Tab 一级可见时返回
67
+
68
+| 要素 | 内容 |
69
+|------|------|
70
+| **用例编号** | CGC-UT-003 |
71
+| **测试模块** | C 端商品分类 |
72
+| **测试项** | listLevel2Tabs |
73
+| **测试类型** | 单元测试 |
74
+| **测试工具** | JUnit 5 + Mockito |
75
+| **测试目的** | 验证 GC5、GC7 |
76
+| **前置条件** | L1-A 一级 show=1;Mapper 返回 L2-A1、L2-A2 |
77
+| **测试步骤** | `listLevel2Tabs(L1-A.id)` |
78
+| **预期结果** | 返回 2 条二级;顺序与 Mapper 一致 |
79
+
80
+### CGC-UT-004 二级 Tab 一级隐藏拒绝
81
+
82
+| 要素 | 内容 |
83
+|------|------|
84
+| **用例编号** | CGC-UT-004 |
85
+| **测试模块** | C 端商品分类 |
86
+| **测试项** | listLevel2Tabs 校验 |
87
+| **测试类型** | 单元测试 |
88
+| **测试工具** | JUnit 5 + Mockito |
89
+| **测试目的** | 验证 GC6 |
90
+| **前置条件** | L1-H show=0 |
91
+| **测试步骤** | `listLevel2Tabs(L1-H.id)` |
92
+| **预期结果** | 抛 `ServiceException`;msg 含「不可见」或「不存在」 |
93
+
94
+### CGC-UT-005 商品列表缺 categoryId
95
+
96
+| 要素 | 内容 |
97
+|------|------|
98
+| **用例编号** | CGC-UT-005 |
99
+| **测试模块** | C 端商品分类 |
100
+| **测试项** | listCategoryGoods 必填 |
101
+| **测试类型** | 单元测试 |
102
+| **测试工具** | JUnit 5 + Mockito |
103
+| **测试目的** | 验证 §3.4 校验 |
104
+| **前置条件** | query.categoryId 为空 |
105
+| **测试步骤** | `listCategoryGoods(query)` |
106
+| **预期结果** | 抛 `ServiceException`;「请选择商品分类」类提示 |
107
+
108
+### CGC-UT-006 商品列表分类不可见拒绝
109
+
110
+| 要素 | 内容 |
111
+|------|------|
112
+| **用例编号** | CGC-UT-006 |
113
+| **测试模块** | C 端商品分类 |
114
+| **测试项** | isCategoryVisible |
115
+| **测试类型** | 单元测试 |
116
+| **测试工具** | JUnit 5 + Mockito |
117
+| **测试目的** | 验证 GC6 |
118
+| **前置条件** | `isCategoryVisible(L2-H)=false` |
119
+| **测试步骤** | `listCategoryGoods({categoryId:L2-H})` |
120
+| **预期结果** | 抛 `ServiceException`;「分类不存在或不可见」 |
121
+
122
+### CGC-UT-007 商品列表默认销量降序
123
+
124
+| 要素 | 内容 |
125
+|------|------|
126
+| **用例编号** | CGC-UT-007 |
127
+| **测试模块** | C 端商品分类 |
128
+| **测试项** | sortBy 默认 |
129
+| **测试类型** | 单元测试 |
130
+| **测试工具** | JUnit 5 + Mockito |
131
+| **测试目的** | 验证 GC9 |
132
+| **前置条件** | L2-A1 可见;sortBy 未传 |
133
+| **测试步骤** | `listCategoryGoods(query)` |
134
+| **预期结果** | Mapper 调用 `sortBy=sales_desc` |
135
+
136
+### CGC-UT-008 商品列表价格升序排序
137
+
138
+| 要素 | 内容 |
139
+|------|------|
140
+| **用例编号** | CGC-UT-008 |
141
+| **测试模块** | C 端商品分类 |
142
+| **测试项** | sortBy price_asc |
143
+| **测试类型** | 单元测试 |
144
+| **测试工具** | JUnit 5 + Mockito |
145
+| **测试目的** | 验证 GC9 |
146
+| **前置条件** | query.sortBy=`price_asc` |
147
+| **测试步骤** | `listCategoryGoods(query)` |
148
+| **预期结果** | Mapper 入参 `price_asc` |
149
+
150
+### CGC-UT-009 非法 sortBy 拒绝
151
+
152
+| 要素 | 内容 |
153
+|------|------|
154
+| **用例编号** | CGC-UT-009 |
155
+| **测试模块** | C 端商品分类 |
156
+| **测试项** | sortBy 校验 |
157
+| **测试类型** | 单元测试 |
158
+| **测试工具** | JUnit 5 + Mockito |
159
+| **测试目的** | 验证 §3.4 |
160
+| **前置条件** | sortBy=`invalid`;分类可见 |
161
+| **测试步骤** | `listCategoryGoods(query)` |
162
+| **预期结果** | 抛 `ServiceException`;「排序参数无效」 |
163
+
164
+### CGC-UT-010 商品列表透传 Mapper 顺序
165
+
166
+| 要素 | 内容 |
167
+|------|------|
168
+| **用例编号** | CGC-UT-010 |
169
+| **测试模块** | C 端商品分类 |
170
+| **测试项** | 列表顺序 |
171
+| **测试类型** | 单元测试 |
172
+| **测试工具** | JUnit 5 + Mockito |
173
+| **测试目的** | 验证 GC8 |
174
+| **前置条件** | Mapper 返回 G-A1 在 G-A3 前 |
175
+| **测试步骤** | `listCategoryGoods(query)` |
176
+| **预期结果** | 结果顺序与 Mapper 一致 |
177
+
178
+### CGC-UT-011 商品列表零条
179
+
180
+| 要素 | 内容 |
181
+|------|------|
182
+| **用例编号** | CGC-UT-011 |
183
+| **测试模块** | C 端商品分类 |
184
+| **测试项** | 列表空态 |
185
+| **测试类型** | 单元测试 |
186
+| **测试工具** | JUnit 5 + Mockito |
187
+| **测试目的** | 验证 §7.4 |
188
+| **前置条件** | Mapper 返回 empty |
189
+| **测试步骤** | `listCategoryGoods(query)` |
190
+| **预期结果** | 空 List |
191
+
192
+### CGC-UT-012 Facade null 树返回空
193
+
194
+| 要素 | 内容 |
195
+|------|------|
196
+| **用例编号** | CGC-UT-012 |
197
+| **测试模块** | C 端商品分类 |
198
+| **测试项** | listCategoryTree null |
199
+| **测试类型** | 单元测试 |
200
+| **测试工具** | JUnit 5 + Mockito |
201
+| **测试目的** | 验证健壮性 |
202
+| **前置条件** | Facade 返回 null |
203
+| **测试步骤** | `listCategoryTree()` |
204
+| **预期结果** | 空 List;**不** NPE |
205
+
206
+---
207
+
208
+## 二、接口测试
209
+
210
+### CGC-API-001 分类树无需 Token
211
+
212
+| 要素 | 内容 |
213
+|------|------|
214
+| **用例编号** | CGC-API-001 |
215
+| **测试模块** | C 端商品分类 |
216
+| **测试项** | GET /tree 匿名 |
217
+| **测试类型** | 接口测试 |
218
+| **测试工具** | MockMvc / Apifox |
219
+| **测试目的** | 验证 GC2 |
220
+| **前置条件** | 不传 Authorization |
221
+| **测试步骤** | GET `/api/category/tree` |
222
+| **预期结果** | `code=200`;`data` 为数组 |
223
+
224
+### CGC-API-002 分类树含 children 契约
225
+
226
+| 要素 | 内容 |
227
+|------|------|
228
+| **用例编号** | CGC-API-002 |
229
+| **测试模块** | C 端商品分类 |
230
+| **测试项** | tree VO |
231
+| **测试类型** | 接口测试 |
232
+| **测试工具** | MockMvc |
233
+| **测试目的** | 验证 §3.2 |
234
+| **前置条件** | 存在 L1-A 含 L2-A1 |
235
+| **测试步骤** | GET `/tree` |
236
+| **预期结果** | 含 `categoryId/categoryName/children`;child 含 `categoryId/categoryName` |
237
+
238
+### CGC-API-003 分类树不含 show=0 一级
239
+
240
+| 要素 | 内容 |
241
+|------|------|
242
+| **用例编号** | CGC-API-003 |
243
+| **测试模块** | C 端商品分类 |
244
+| **测试项** | tree 过滤 |
245
+| **测试类型** | 接口测试 |
246
+| **测试工具** | Apifox |
247
+| **测试目的** | 验证 GC6 |
248
+| **前置条件** | 库中 L1-H show=0 |
249
+| **测试步骤** | GET `/tree` |
250
+| **预期结果** | **不含** L1-H |
251
+
252
+### CGC-API-004 分类树不含空壳一级
253
+
254
+| 要素 | 内容 |
255
+|------|------|
256
+| **用例编号** | CGC-API-004 |
257
+| **测试模块** | C 端商品分类 |
258
+| **测试项** | 空壳一级 |
259
+| **测试类型** | 接口测试 |
260
+| **测试工具** | Apifox |
261
+| **测试目的** | 验证 GC6、§4.2 |
262
+| **前置条件** | L1-E show=1 但无可见二级 |
263
+| **测试步骤** | GET `/tree` |
264
+| **预期结果** | **不含** L1-E |
265
+
266
+### CGC-API-005 分类树不含店铺分类
267
+
268
+| 要素 | 内容 |
269
+|------|------|
270
+| **用例编号** | CGC-API-005 |
271
+| **测试模块** | C 端商品分类 |
272
+| **测试项** | 平台隔离 |
273
+| **测试类型** | 接口测试 |
274
+| **测试工具** | Apifox |
275
+| **测试目的** | 验证 GC1 |
276
+| **前置条件** | 存在 L1-S(shop_id=101) |
277
+| **测试步骤** | GET `/tree` |
278
+| **预期结果** | **不含** L1-S |
279
+
280
+### CGC-API-006 二级 Tab 正常返回
281
+
282
+| 要素 | 内容 |
283
+|------|------|
284
+| **用例编号** | CGC-API-006 |
285
+| **测试模块** | C 端商品分类 |
286
+| **测试项** | GET level2-tabs |
287
+| **测试类型** | 接口测试 |
288
+| **测试工具** | MockMvc |
289
+| **测试目的** | 验证 GC5 |
290
+| **前置条件** | L1-A 可见;下有 L2-A1、L2-A2 |
291
+| **测试步骤** | GET `/api/category/{L1-A}/level2-tabs` |
292
+| **预期结果** | `code=200`;`data.length=2` |
293
+
294
+### CGC-API-007 二级 Tab 排序升序
295
+
296
+| 要素 | 内容 |
297
+|------|------|
298
+| **用例编号** | CGC-API-007 |
299
+| **测试模块** | C 端商品分类 |
300
+| **测试项** | Tab sort_no |
301
+| **测试类型** | 接口测试 |
302
+| **测试工具** | Apifox |
303
+| **测试目的** | 验证 GC7 |
304
+| **前置条件** | L2-A1 sort=0;L2-A2 sort=1 |
305
+| **测试步骤** | GET level2-tabs |
306
+| **预期结果** | 第一条 `categoryId=L2-A1` |
307
+
308
+### CGC-API-008 二级 Tab 一级不存在
309
+
310
+| 要素 | 内容 |
311
+|------|------|
312
+| **用例编号** | CGC-API-008 |
313
+| **测试模块** | C 端商品分类 |
314
+| **测试项** | level1 校验 |
315
+| **测试类型** | 接口测试 |
316
+| **测试工具** | MockMvc |
317
+| **测试目的** | 验证 §3.3 |
318
+| **前置条件** | level1Id=99999 |
319
+| **测试步骤** | GET `/api/category/99999/level2-tabs` |
320
+| **预期结果** | 业务失败;提示分类不存在/不可见 |
321
+
322
+### CGC-API-009 二级 Tab 一级隐藏
323
+
324
+| 要素 | 内容 |
325
+|------|------|
326
+| **用例编号** | CGC-API-009 |
327
+| **测试模块** | C 端商品分类 |
328
+| **测试项** | level1 show=0 |
329
+| **测试类型** | 接口测试 |
330
+| **测试工具** | Apifox |
331
+| **测试目的** | 验证 GC6 |
332
+| **前置条件** | L1-H show=0 |
333
+| **测试步骤** | GET `/api/category/{L1-H}/level2-tabs` |
334
+| **预期结果** | 业务失败 |
335
+
336
+### CGC-API-010 商品列表分页成功
337
+
338
+| 要素 | 内容 |
339
+|------|------|
340
+| **用例编号** | CGC-API-010 |
341
+| **测试模块** | C 端商品分类 |
342
+| **测试项** | GET /goods |
343
+| **测试类型** | 接口测试 |
344
+| **测试工具** | MockMvc |
345
+| **测试目的** | 验证 §3.4 |
346
+| **前置条件** | L2-A1 可见;≥1 出售中商品 |
347
+| **测试步骤** | GET `/api/category/goods?categoryId=&pageNum=1&pageSize=10` |
348
+| **预期结果** | `code=200`;含 `rows`、`total` |
349
+
350
+### CGC-API-011 商品列表字段契约
351
+
352
+| 要素 | 内容 |
353
+|------|------|
354
+| **用例编号** | CGC-API-011 |
355
+| **测试模块** | C 端商品分类 |
356
+| **测试项** | goods VO |
357
+| **测试类型** | 接口测试 |
358
+| **测试工具** | MockMvc |
359
+| **测试目的** | 验证 GC10 |
360
+| **前置条件** | G-A1 出售中 |
361
+| **测试步骤** | GET `/goods?categoryId=L2-A1` |
362
+| **预期结果** | row 含 `goodsId/goodsName/mainPic/salePrice/shopId/shopName` |
363
+
364
+### CGC-API-012 商品列表缺 categoryId
365
+
366
+| 要素 | 内容 |
367
+|------|------|
368
+| **用例编号** | CGC-API-012 |
369
+| **测试模块** | C 端商品分类 |
370
+| **测试项** | categoryId 必填 |
371
+| **测试类型** | 接口测试 |
372
+| **测试工具** | MockMvc |
373
+| **测试目的** | 验证 §3.4 |
374
+| **前置条件** | — |
375
+| **测试步骤** | GET `/api/category/goods` |
376
+| **预期结果** | 业务失败 |
377
+
378
+### CGC-API-013 商品列表分类不可见
379
+
380
+| 要素 | 内容 |
381
+|------|------|
382
+| **用例编号** | CGC-API-013 |
383
+| **测试模块** | C 端商品分类 |
384
+| **测试项** | 隐藏二级 |
385
+| **测试类型** | 接口测试 |
386
+| **测试工具** | Apifox |
387
+| **测试目的** | 验证 GC6 |
388
+| **前置条件** | L2-H show=0 |
389
+| **测试步骤** | GET `/goods?categoryId=L2-H` |
390
+| **预期结果** | 业务失败;「分类不存在或不可见」 |
391
+
392
+### CGC-API-014 商品列表默认销量降序
393
+
394
+| 要素 | 内容 |
395
+|------|------|
396
+| **用例编号** | CGC-API-014 |
397
+| **测试模块** | C 端商品分类 |
398
+| **测试项** | 默认排序 |
399
+| **测试类型** | 接口测试 |
400
+| **测试工具** | Apifox |
401
+| **测试目的** | 验证 GC9 |
402
+| **前置条件** | G-A1 销量100;G-A3 销量50 |
403
+| **测试步骤** | GET `/goods?categoryId=L2-A1`(无 sortBy) |
404
+| **预期结果** | 首条 `goodsId=G-A1` |
405
+
406
+### CGC-API-015 商品列表价格升序
407
+
408
+| 要素 | 内容 |
409
+|------|------|
410
+| **用例编号** | CGC-API-015 |
411
+| **测试模块** | C 端商品分类 |
412
+| **测试项** | sortBy price_asc |
413
+| **测试类型** | 接口测试 |
414
+| **测试工具** | Apifox |
415
+| **测试目的** | 验证 GC9 |
416
+| **前置条件** | G-A1 价88;G-A2 价99 |
417
+| **测试步骤** | GET `/goods?categoryId=L2-A1&sortBy=price_asc` |
418
+| **预期结果** | 首条价 **更低** 的商品 |
419
+
420
+### CGC-API-016 商品列表非法 sortBy
421
+
422
+| 要素 | 内容 |
423
+|------|------|
424
+| **用例编号** | CGC-API-016 |
425
+| **测试模块** | C 端商品分类 |
426
+| **测试项** | sortBy 校验 |
427
+| **测试类型** | 接口测试 |
428
+| **测试工具** | MockMvc |
429
+| **测试目的** | 验证 §3.4 |
430
+| **前置条件** | L2-A1 可见 |
431
+| **测试步骤** | GET `/goods?categoryId=L2-A1&sortBy=foo` |
432
+| **预期结果** | 业务失败;「排序参数无效」 |
433
+
434
+### CGC-API-017 商品列表仅出售中
435
+
436
+| 要素 | 内容 |
437
+|------|------|
438
+| **用例编号** | CGC-API-017 |
439
+| **测试模块** | C 端商品分类 |
440
+| **测试项** | goods_status |
441
+| **测试类型** | 接口测试 |
442
+| **测试工具** | Apifox |
443
+| **测试目的** | 验证 GC8 |
444
+| **前置条件** | G-O1 已下架同分类 |
445
+| **测试步骤** | GET `/goods?categoryId=L2-A1` |
446
+| **预期结果** | **不含** G-O1 |
447
+
448
+### CGC-API-018 商品列表含停业店商品
449
+
450
+| 要素 | 内容 |
451
+|------|------|
452
+| **用例编号** | CGC-API-018 |
453
+| **测试模块** | C 端商品分类 |
454
+| **测试项** | 浏览层店态 |
455
+| **测试类型** | 接口测试 |
456
+| **测试工具** | Apifox |
457
+| **测试目的** | 验证 GC13 |
458
+| **前置条件** | G-B1@S102 停业、出售中 |
459
+| **测试步骤** | GET `/goods?categoryId=对应二级` |
460
+| **预期结果** | **含** G-B1 |
461
+
462
+### CGC-API-019 禁用会员仍可读
463
+
464
+| 要素 | 内容 |
465
+|------|------|
466
+| **用例编号** | CGC-API-019 |
467
+| **测试模块** | C 端商品分类 |
468
+| **测试项** | 会员禁用 |
469
+| **测试类型** | 接口测试 |
470
+| **测试工具** | Apifox |
471
+| **测试目的** | 验证 GC2 |
472
+| **前置条件** | M2 禁用 Token |
473
+| **测试步骤** | 带 Token 调 `/tree`、`/goods` |
474
+| **预期结果** | 均 `code=200` |
475
+
476
+### CGC-API-020 平台改 show 后 goods 拒绝
477
+
478
+| 要素 | 内容 |
479
+|------|------|
480
+| **用例编号** | CGC-API-020 |
481
+| **测试模块** | C 端商品分类 |
482
+| **测试项** | 分类联动 |
483
+| **测试类型** | 接口测试 |
484
+| **测试工具** | Apifox |
485
+| **测试目的** | 验证 §10 |
486
+| **前置条件** | 平台将 L2-A1 show 改为 0 |
487
+| **测试步骤** | GET `/goods?categoryId=L2-A1` |
488
+| **预期结果** | 业务失败 |
489
+
490
+---
491
+
492
+## 三、界面测试(Playwright · Chrome)
493
+
494
+> **路由建议:** A `/#/mall/category/all`;B `/#/mall/category/l1/{id}`;C `/#/mall/category/goods?categoryId=`(以实际为准)
495
+
496
+### CGC-UI-001 首页点一级进 B 页
497
+
498
+| 要素 | 内容 |
499
+|------|------|
500
+| **用例编号** | CGC-UI-001 |
501
+| **测试模块** | C 端商品分类 |
502
+| **测试项** | HM7 入口 B |
503
+| **测试类型** | 界面测试 |
504
+| **测试工具** | Playwright(Chrome) |
505
+| **测试目的** | 验证 GC5、HM7 |
506
+| **前置条件** | 首页已加载;存在 L1-A |
507
+| **测试步骤** | 点击首页一级「兽药」 |
508
+| **预期结果** | 进入 B 页;可见二级 Tab 与商品列表区 |
509
+
510
+### CGC-UI-002 首页点更多进 A 页
511
+
512
+| 要素 | 内容 |
513
+|------|------|
514
+| **用例编号** | CGC-UI-002 |
515
+| **测试模块** | C 端商品分类 |
516
+| **测试项** | HM7 入口 A |
517
+| **测试类型** | 界面测试 |
518
+| **测试工具** | Playwright(Chrome) |
519
+| **测试目的** | 验证 GC4 |
520
+| **前置条件** | 首页已加载 |
521
+| **测试步骤** | 点击类目区「更多」 |
522
+| **预期结果** | 进入 A 全部分类页;左右分栏可见 |
523
+
524
+### CGC-UI-003 A 页左切一级联动右侧
525
+
526
+| 要素 | 内容 |
527
+|------|------|
528
+| **用例编号** | CGC-UI-003 |
529
+| **测试模块** | C 端商品分类 |
530
+| **测试项** | A 页联动 |
531
+| **测试类型** | 界面测试 |
532
+| **测试工具** | Playwright(Chrome) |
533
+| **测试目的** | 验证 GC4、§5.1 |
534
+| **前置条件** | A 页已打开;有 L1-A、L1-B |
535
+| **测试步骤** | 点击左侧 L1-B |
536
+| **预期结果** | 右侧刷新为 L1-B 下二级列表;**不跳转** 新页 |
537
+
538
+### CGC-UI-004 A 页点二级进 C 独立页
539
+
540
+| 要素 | 内容 |
541
+|------|------|
542
+| **用例编号** | CGC-UI-004 |
543
+| **测试模块** | C 端商品分类 |
544
+| **测试项** | A1 跳转 |
545
+| **测试类型** | 界面测试 |
546
+| **测试工具** | Playwright(Chrome) |
547
+| **测试目的** | 验证 GC4、GC5a |
548
+| **前置条件** | A 页右侧有 L2-A1 |
549
+| **测试步骤** | 点击「抗生素」 |
550
+| **预期结果** | 进入 **C 独立列表页**;**无** Tab;可见路径「兽药 > 抗生素」 |
551
+
552
+### CGC-UI-005 B 页默认选中首 Tab
553
+
554
+| 要素 | 内容 |
555
+|------|------|
556
+| **用例编号** | CGC-UI-005 |
557
+| **测试模块** | C 端商品分类 |
558
+| **测试项** | 默认 Tab |
559
+| **测试类型** | 界面测试 |
560
+| **测试工具** | Playwright(Chrome) |
561
+| **测试目的** | 验证 §6.1 |
562
+| **前置条件** | 从首页进入 L1-A |
563
+| **测试步骤** | 观察 Tab 与列表 |
564
+| **预期结果** | **排序最靠前** 的二级 Tab 高亮;列表对应该 Tab |
565
+
566
+### CGC-UI-006 B 页切换 Tab 列表回顶
567
+
568
+| 要素 | 内容 |
569
+|------|------|
570
+| **用例编号** | CGC-UI-006 |
571
+| **测试模块** | C 端商品分类 |
572
+| **测试项** | C1 回顶 |
573
+| **测试类型** | 界面测试 |
574
+| **测试工具** | Playwright(Chrome) |
575
+| **测试目的** | 验证 GC5、C1 |
576
+| **前置条件** | B 页列表已下滚 |
577
+| **测试步骤** | 点击另一二级 Tab |
578
+| **预期结果** | 列表 **回顶**;展示新 Tab 商品 |
579
+
580
+### CGC-UI-007 搜索栏占位文案
581
+
582
+| 要素 | 内容 |
583
+|------|------|
584
+| **用例编号** | CGC-UI-007 |
585
+| **测试模块** | C 端商品分类 |
586
+| **测试项** | 搜索占位 |
587
+| **测试类型** | 界面测试 |
588
+| **测试工具** | Playwright(Chrome) |
589
+| **测试目的** | 验证 GC3 |
590
+| **前置条件** | 打开 A 或 B 或 C 页 |
591
+| **测试步骤** | 读取搜索框 placeholder |
592
+| **预期结果** | **「搜索兽药、饲料、店铺」** |
593
+
594
+### CGC-UI-008 点击搜索栏进搜索页
595
+
596
+| 要素 | 内容 |
597
+|------|------|
598
+| **用例编号** | CGC-UI-008 |
599
+| **测试模块** | C 端商品分类 |
600
+| **测试项** | 搜索入口 |
601
+| **测试类型** | 界面测试 |
602
+| **测试工具** | Playwright(Chrome) |
603
+| **测试目的** | 验证 GC-S1 |
604
+| **前置条件** | 分类页已加载 |
605
+| **测试步骤** | 点击搜索栏 |
606
+| **预期结果** | 路由至搜索输入页 |
607
+
608
+### CGC-UI-009 C 页含搜索栏无 Tab
609
+
610
+| 要素 | 内容 |
611
+|------|------|
612
+| **用例编号** | CGC-UI-009 |
613
+| **测试模块** | C 端商品分类 |
614
+| **测试项** | C 页结构 |
615
+| **测试类型** | 界面测试 |
616
+| **测试工具** | Playwright(Chrome) |
617
+| **测试目的** | 验证 GC5a |
618
+| **前置条件** | 从 A 进入 C 页 |
619
+| **测试步骤** | 检查页顶结构 |
620
+| **预期结果** | 有搜索栏+路径标题;**无** 二级 Tab |
621
+
622
+### CGC-UI-010 商品卡片字段展示
623
+
624
+| 要素 | 内容 |
625
+|------|------|
626
+| **用例编号** | CGC-UI-010 |
627
+| **测试模块** | C 端商品分类 |
628
+| **测试项** | 列表卡片 |
629
+| **测试类型** | 界面测试 |
630
+| **测试工具** | Playwright(Chrome) |
631
+| **测试目的** | 验证 GC10 |
632
+| **前置条件** | 列表有 G-A1 |
633
+| **测试步骤** | 检查首卡 |
634
+| **预期结果** | 可见主图、名称、售价、店铺名;**不** 展示销量数字 |
635
+
636
+### CGC-UI-011 切换销量价格排序
637
+
638
+| 要素 | 内容 |
639
+|------|------|
640
+| **用例编号** | CGC-UI-011 |
641
+| **测试模块** | C 端商品分类 |
642
+| **测试项** | 排序 UI |
643
+| **测试类型** | 界面测试 |
644
+| **测试工具** | Playwright(Chrome) |
645
+| **测试目的** | 验证 GC9、§7.3 |
646
+| **前置条件** | 列表 ≥2 条不同价/销量 |
647
+| **测试步骤** | ① 选「价格升序」② 选「销量降序」 |
648
+| **预期结果** | 列表顺序 **随之变化** |
649
+
650
+### CGC-UI-012 点击商品进详情
651
+
652
+| 要素 | 内容 |
653
+|------|------|
654
+| **用例编号** | CGC-UI-012 |
655
+| **测试模块** | C 端商品分类 |
656
+| **测试项** | 进详情 |
657
+| **测试类型** | 界面测试 |
658
+| **测试工具** | Playwright(Chrome) |
659
+| **测试目的** | 验证 GC11 |
660
+| **前置条件** | 列表有商品 |
661
+| **测试步骤** | 点击商品卡片 |
662
+| **预期结果** | 进入商品详情;URL 含 `goodsId` |
663
+
664
+### CGC-UI-013 访客与会员均可浏览
665
+
666
+| 要素 | 内容 |
667
+|------|------|
668
+| **用例编号** | CGC-UI-013 |
669
+| **测试模块** | C 端商品分类 |
670
+| **测试项** | 访客/会员 |
671
+| **测试类型** | 界面测试 |
672
+| **测试工具** | Playwright(Chrome) |
673
+| **测试目的** | 验证 GC2 |
674
+| **前置条件** | ① 未登录 ② M1 登录 |
675
+| **测试步骤** | 分别打开 A/B 页 |
676
+| **预期结果** | 均可见分类与列表 |
677
+
678
+### CGC-UI-014 零商品空态
679
+
680
+| 要素 | 内容 |
681
+|------|------|
682
+| **用例编号** | CGC-UI-014 |
683
+| **测试模块** | C 端商品分类 |
684
+| **测试项** | 列表空态 |
685
+| **测试类型** | 界面测试 |
686
+| **测试工具** | Playwright(Chrome) |
687
+| **测试目的** | 验证 §7.4 |
688
+| **前置条件** | 某二级无出售中商品 |
689
+| **测试步骤** | 打开该二级列表 |
690
+| **预期结果** | 空态文案(如「该分类下暂无商品」) |
691
+
692
+### CGC-UI-015 分类树失败不白屏
693
+
694
+| 要素 | 内容 |
695
+|------|------|
696
+| **用例编号** | CGC-UI-015 |
697
+| **测试模块** | C 端商品分类 |
698
+| **测试项** | tree 失败 |
699
+| **测试类型** | 界面测试 |
700
+| **测试工具** | Playwright(Chrome) |
701
+| **测试目的** | 验证 GC14 |
702
+| **前置条件** | Mock `/tree` 500 |
703
+| **测试步骤** | 打开 A 页 |
704
+| **预期结果** | 空态/错误提示;**不** 整页白屏 |
705
+
706
+### CGC-UI-016 商品列表失败空态
707
+
708
+| 要素 | 内容 |
709
+|------|------|
710
+| **用例编号** | CGC-UI-016 |
711
+| **测试模块** | C 端商品分类 |
712
+| **测试项** | goods 失败 |
713
+| **测试类型** | 界面测试 |
714
+| **测试工具** | Playwright(Chrome) |
715
+| **测试目的** | 验证 GC14、§7.6 |
716
+| **前置条件** | Mock `/goods` 失败;Tab 仍可见 |
717
+| **测试步骤** | 打开 B 页 |
718
+| **预期结果** | 列表区空态;Tab 区 **仍展示** |
719
+
720
+---
721
+
722
+## 四、业务规则覆盖矩阵
723
+
724
+| 规则 | UT | API | UI |
725
+|------|:--:|:---:|:--:|
726
+| GC1 仅平台分类 | — | API-005 | — |
727
+| GC2 匿名可读 | — | API-001/019 | UI-013 |
728
+| GC3 搜索占位 | — | — | UI-007 |
729
+| GC4 A 页/A→C | UT-003 | API-002 | UI-002/003/004 |
730
+| GC5 B Tab+列表 | UT-003 | API-006/007 | UI-001/005/006 |
731
+| GC5a C 独立页 | — | — | UI-004/009 |
732
+| GC6 可见性 | UT-004/006 | API-003/004/008/009/013/020 | — |
733
+| GC7 排序 | — | API-007 | — |
734
+| GC8 出售中 | UT-010 | API-017 | — |
735
+| GC9 四档排序 | UT-007/008/009 | API-014/015/016 | UI-011 |
736
+| GC10 卡片字段 | — | API-011 | UI-010 |
737
+| GC11 进详情 | — | — | UI-012 |
738
+| GC12 展示≠可购 | — | — | — |
739
+| GC13 停业/零库存 | — | API-018 | — |
740
+| GC14 模块空态 | UT-011/012 | — | UI-015/016 |
741
+| GC15 无写接口 | — | — | — |
742
+
743
+---
744
+
745
+## 五、自动化映射
746
+
747
+| 用例 | 建议代码位置 |
748
+|------|--------------|
749
+| CGC-UT-001~012 | `CategoryAppServiceImplTest.java` |
750
+| CGC-API-001~020 | `CategoryAppControllerTest.java` |
751
+| CGC-UI-001~016 | `e2e/consumer/category.spec.ts`(Playwright `channel: 'chrome'`) |
752
+
753
+---
754
+
755
+## 六、用例统计
756
+
757
+| 类型 | 数量 |
758
+|------|------|
759
+| 单元测试 CGC-UT | 12 |
760
+| 接口测试 CGC-API | 20 |
761
+| 界面测试 CGC-UI | 16 |
762
+| **合计** | **48** |
763
+
764
+---
765
+
766
+## 七、修订记录
767
+
768
+| 版本 | 说明 |
769
+|------|------|
770
+| **v1.0** | 首版;48 用例;覆盖 GC1~GC15、A/B/C 三页、四档排序、HM7 入口 |
771
+
772
+---
773
+
774
+*文档版本:v1.0 · 关联《商品分类功能需求.md》v1.0.1、《商品分类技术方案.md》v1.0*