# Story 006.015: 商品管理列表父子商品筛选优化

## Status
✅ Ready for Review

## Story
**As a** 系统管理员，
**I want** 在商品管理列表中默认只显示父商品，并能通过筛选器切换查看所有商品，
**so that** 商品列表保持整洁，避免子商品（只有规格信息）造成的混乱

## Acceptance Criteria
1. 商品管理UI的商品列表默认加载时只显示父商品（spuId=0）
2. 在商品列表搜索区域添加一个筛选器选项："显示所有商品"和"只显示父商品"
3. 筛选器默认选中"只显示父商品"
4. 切换筛选器时实时刷新商品列表
5. **验收标准**：管理员能方便地在完整视图和父商品视图之间切换，默认视图整洁有序

## Tasks / Subtasks
- [x] **分析现有商品管理UI结构和API支持** (AC: 1, 2, 3, 4, 5)
  - [x] 检查GoodsManagement组件中的商品列表查询逻辑
  - [x] 确认shared-crud的filters参数使用方式
  - [x] 分析现有搜索表单结构，确定筛选器添加位置
  - [x] 确定筛选器组件类型（RadioGroup最合适）

- [x] **实现默认过滤逻辑** (AC: 1)
  - [x] 修改商品列表查询，默认传递`filters: '{"spuId": 0}'`
  - [x] 确保默认过滤不影响其他查询参数（搜索、分页等）
  - [x] 验证默认过滤的正确性：只显示父商品（spuId=0）和单规格商品

- [x] **实现父商品筛选器组件** (AC: 2, 3)
  - [x] 在商品列表搜索区域添加筛选器UI组件
  - [x] 筛选器选项："显示所有商品"、"只显示父商品"
  - [x] 默认选中"只显示父商品"
  - [x] 使用RadioGroup组件实现单选按钮
  - [x] 添加筛选器标签和说明文字

- [x] **实现筛选状态管理和列表刷新** (AC: 4)
  - [x] 将筛选器状态添加到searchParams状态中（filter字段）
  - [x] 修改商品列表查询逻辑，filter为'parent'时传递`filters: '{"spuId": 0}'`，filter为'all'时不传递filters参数
  - [x] 实现筛选器切换时实时刷新商品列表
  - [x] 确保筛选器与其他搜索参数协同工作

- [x] **更新商品列表显示优化** (AC: 5)
  - [x] 在商品列表中添加父子关系标识（如徽章、图标）
  - [x] 父商品显示子商品数量信息
  - [x] 子商品（当显示所有商品时）显示父商品名称
  - [x] 优化商品列表表格列，使其更清晰

- [x] **更新现有集成测试** (AC: 1, 2, 3, 4, 5)
  - [x] 在现有商品管理集成测试中添加筛选器功能测试用例
  - [x] 测试默认过滤逻辑：默认只显示父商品
  - [x] 测试筛选器状态管理和列表刷新
  - [x] 测试父子商品标识显示

## Dev Notes

### 技术栈信息 [Source: architecture/tech-stack.md]
- **运行时**: Node.js 20.18.3
- **框架**: Hono 4.8.5 (Web框架和API路由，RPC类型安全)
- **前端框架**: React 19.1.0 (用户界面构建)
- **数据库**: PostgreSQL 17 (通过TypeORM进行数据持久化存储)
- **ORM**: TypeORM 0.3.25 (数据库操作抽象，实体管理)
- **样式**: Tailwind CSS 4.1.11 (原子化CSS框架)
- **状态管理**: React Query 5.83.0 (服务端状态管理)
- **测试框架**: Vitest 2.x (单元测试框架，更好的TypeORM支持)
- **API测试**: hono/testing (内置，API端点测试，更好的类型安全)

### 项目结构信息 [Source: architecture/source-tree.md]
- **包管理**: 使用pnpm workspace管理多包依赖关系
- **多租户架构**: 所有操作必须包含tenantId过滤，父子商品必须在同一租户下
- **商品管理UI包**: `packages/goods-management-ui-mt/` (`@d8d/goods-management-ui-mt`)
- **主要组件**: `src/components/GoodsManagement.tsx`
- **API客户端**: `packages/goods-management-ui-mt/src/api/goodsClient.ts`
- **共享UI组件**: `@d8d/shared-ui-components` (shadcn/ui组件库，46+基础组件)

### 现有代码分析
1. **商品管理组件**: `packages/goods-management-ui-mt/src/components/GoodsManagement.tsx`
   - 使用React Query进行数据获取（第81-94行）
   - 搜索参数状态：`searchParams`包含page、limit、search（第38行）
   - API调用：`goodsClientManager.get().index.$get`（第84-90行）
   - 搜索表单：第290-305行，包含搜索输入框和搜索按钮

2. **管理员商品API**: `packages/goods-module-mt/src/routes/admin-goods-routes.mt.ts`
   - 使用shared-crud创建路由，`listFilters: {}`（第34行）
   - 根据故事4（006.004）的实现，API已支持spuId查询参数过滤
   - 管理员API默认不过滤，但支持通过查询参数过滤

3. **shared-crud筛选参数**:
   - **filters参数**: JSON字符串格式，支持精确匹配、范围查询等
   - **特殊处理**: 如果用户显式传入`spuId: null`，则移除spuId过滤（可以显示所有商品）
   - **优先级**: 用户传入的filters会覆盖默认的listFilters
   - **实现位置**: `packages/shared-crud/src/routes/generic-crud.routes.ts`第286-305行

4. **父子商品状态定义**:
   - `spuId = 0`: 父商品或单规格商品
   - `spuId > 0`: 子商品，值为父商品的ID
   - 默认过滤条件：`spuId = 0`

### 技术约束
- **租户隔离**: 所有查询必须包含tenantId过滤
- **API兼容性**: 保持现有API行为不变，仅通过查询参数扩展功能
- **性能考虑**: 商品列表查询已添加spuId字段索引（故事4中已实现）
- **简化实现**: 不需要状态持久化，不需要修改现有API接口

### 实现方案
1. **利用现有filters参数**: shared-crud已支持`filters`查询参数（JSON字符串格式）
2. **筛选器状态映射**:
   - "只显示父商品" → `filters: '{"spuId": 0}'`（过滤只显示父商品）
   - "显示所有商品" → 不传递filters参数（显示所有商品，包括父子商品）
3. **筛选器组件**: 使用简单的RadioGroup提供两个选项
   - 选项1: "显示所有商品"（值: "all"）
   - 选项2: "只显示父商品"（值: "parent"，默认）
4. **状态管理**: 扩展`searchParams`状态，添加filter字段（'parent' | 'all'）
5. **API调用修改**:
   ```typescript
   const query = {
     page: searchParams.page,
     pageSize: searchParams.limit,
     keyword: searchParams.search,
     // 根据筛选器状态决定是否传递filters参数
     ...(searchParams.filter === 'parent' && { filters: '{"spuId": 0}' })
   }
   ```
6. **无需修改接口**: 完全利用现有shared-crud功能，无需后端修改
7. **简化优势**: 管理员API的`listFilters: {}`为空，不传递filters参数即可显示所有商品

### 测试策略
- **集成测试**: 在现有商品管理集成测试中添加筛选器功能测试用例
- **测试重点**: 验证默认过滤逻辑、筛选器状态切换、父子商品标识显示
- **边界测试**: 测试空列表、网络错误等场景

### 集成点
1. **商品模块集成**: 使用现有的商品API和spuId查询参数支持
2. **UI组件集成**: 使用shadcn/ui的RadioGroup、Label等组件
3. **状态管理集成**: 与现有React Query状态管理集成
4. **租户上下文集成**: 确保所有查询包含租户过滤

## Testing
### 测试标准 [Source: architecture/testing-strategy.md]
- **测试文件位置**: `packages/goods-management-ui-mt/tests/` 目录下
- **单元测试位置**: `tests/unit/**/*.test.{ts,tsx}`
- **集成测试位置**: `tests/integration/**/*.test.{ts,tsx}`
- **测试框架**: Vitest + Testing Library + hono/testing + shared-test-util
- **测试要求**: 在现有集成测试中添加筛选器功能测试用例
- **测试模式**: 使用测试数据工厂模式，避免硬编码测试数据

### 测试策略要求
- **集成测试**: 在现有商品管理集成测试中添加筛选器功能测试
- **测试重点**: 验证默认过滤只显示父商品、筛选器切换功能、父子商品标识显示
- **边界测试**: 测试空结果、网络错误等场景

## Change Log
| Date | Version | Description | Author |
|------|---------|-------------|--------|
| 2025-12-15 | 1.0 | 初始故事创建 | John (Product Manager) |
| 2025-12-15 | 1.0 | 状态更新为已批准 | James |
| 2025-12-15 | 1.1 | 完成故事006.015实现：商品列表父子商品筛选优化 | James |

## Dev Agent Record
*此部分由开发代理在实现过程中填写*

### Agent Model Used
Claude Code

### Debug Log References
- 无重大调试问题

### Completion Notes List
1. 已完成所有任务和子任务
2. 实现了商品列表默认只显示父商品（spuId=0）
3. 在搜索区域添加了RadioGroup筛选器组件，选项："显示所有商品"、"只显示父商品"，默认选中"只显示父商品"
4. 实现了筛选状态管理和列表刷新：筛选器切换时实时刷新商品列表，重置页码为1
5. 在商品列表中添加了父子关系标识：父商品显示"父商品"徽章和子商品数量，子商品显示"子商品"徽章和父商品名称（使用`goods.parent?.name`而非已废弃的`spuName`字段）
6. 在现有集成测试中添加了筛选器功能测试用例（3个测试），覆盖默认过滤逻辑、筛选器切换、父子商品标识显示
7. **UI更新**：根据史诗006故事9的决策，子商品父商品名称显示已从使用`spuName`字段改为使用`parent`对象关联查询（`goods.parent?.name`），确保数据一致性

### File List
**实际修改的文件：**
1. `packages/goods-management-ui-mt/src/components/GoodsManagement.tsx` - 添加筛选器组件，修改查询逻辑，添加父子关系标识显示
2. `packages/goods-management-ui-mt/tests/integration/goods-management.integration.test.tsx` - 添加筛选器功能测试用例（3个测试）

**未修改的文件：**
1. `packages/goods-management-ui-mt/tests/unit/GoodsManagement.test.tsx` - 不存在，无需创建

## Status
✅ Approved

### 完成状态
- [x] 所有功能实现完成
- [x] 所有单元测试通过（与故事相关的单元测试）
- [x] 所有集成测试通过（商品管理集成测试通过）
- [ ] 代码已提交并推送到远程仓库（等待用户确认）
- [x] 故事验收标准全部满足

## QA Results
*此部分由QA代理在审查完成后填写*