epic-006-parent-child-goods-multi-spec-support.md 23 KB

史诗006:父子商品多规格支持 - 棕地增强

史诗状态

进度: 7/8 故事完成 (87.5%) 最近更新: 2025-12-13 (故事8:购物车页面规格切换功能已添加) 当前状态: 故事1-7已完成,故事8待开始

完成概览

  • 故事1: 管理后台父子商品配置功能 (已完成)
  • 故事2: 父子商品管理UI体验优化 (已完成)
  • 故事3: 子商品行内编辑功能 (已完成)
  • 故事4: 商品API父子商品支持优化 (已完成)
  • 故事5: 父子商品多规格选择组件开发 (已完成)
  • 故事6: 商品详情页规格选择集成 (已完成)
  • 故事7: 购物车和订单规格支持 (已完成)
  • 故事8: 购物车页面规格切换功能 (待开始)

史诗目标

新增父子商品多规格支持功能,在商品添加购物车或立即购买时,能同时支持单规格和多规格选择,以子商品作为多规格选项,并支持手动指定子商品。

史诗描述

现有系统上下文

  • 数据库支持:商品表已有父子商品关系字段(spuId/spuName)
  • Schema支持:所有商品Schema(Admin/User/Public)都包含spuId/spuName字段
  • UI实现(故事2已完成):
    • 商品管理UI已集成统一的父子商品管理面板(GoodsParentChildPanel.tsx
    • 支持创建模式和编辑模式的不同行为
    • 包含子商品列表管理(ChildGoodsList.tsx)和批量创建(BatchSpecCreatorInline.tsx)组件
  • 前端组件:已有GoodsSpecSelector组件但被注释,购物车支持spec字段但无规格选择逻辑
  • 技术栈:TypeORM + Hono + React + Taro小程序 + 多租户架构
  • API路由
    • 小程序:使用publicGoodsRoutesMt(公共商品路由)
    • 管理后台:使用adminGoodsRoutesMt(已聚合父子商品管理API)
  • 多租户特性:商品实体有tenantId字段,API路由支持租户隔离和数据权限
  • 已完成功能(故事1-2):
    • 管理员父子商品配置功能
    • 父子商品管理UI体验优化
    • 父子商品管理API(获取子商品列表、设为父商品、解除关系、批量创建)

增强详情

  • 管理后台(故事1-2已完成):新增父子商品配置界面,支持手动关联已有商品和批量创建子商品
  • 前端:父子商品的多规格选择界面和逻辑,支持多租户环境
  • API调整
    • 故事2已完成:管理员父子商品管理API(获取子商品列表、设为父商品、解除关系、批量创建)
    • 故事4部分已实现:公共商品列表API已默认只返回父商品(spuId=0)
    • 故事4待实现:商品详情API父子商品支持优化、查询性能优化
    • API策略:公共API默认过滤只显示父商品,管理员API保持完整视图但支持spuId参数过滤
  • 集成点:多租户商品模块、商品管理UI、商品详情页、购物车系统、订单提交流程
  • 成功标准
    1. ✅ 管理员能配置父子商品关系(故事1-2已完成)
    2. ✅ 管理员能直接在父子商品管理面板中编辑子商品信息(故事3已完成)
    3. ✅ 用户能在商品详情页选择子商品作为规格(故事5-6已完成)
    4. ✅ 购物车和订单正确记录规格信息(故事7已完成)
    5. ✅ 商品列表页保持整洁(只显示父商品)(故事4已完成)
    6. ✅ 多租户隔离机制保持完整(故事1-7已实现)
    7. ⏳ 用户能在购物车页面切换规格(故事8待实现)

设计决策

1. 规格概念澄清

  • 规格 = 子商品的名称:子商品的name字段作为规格名称
  • 规格选择 = 选择子商品:选择规格时实际选择对应的子商品
  • 购物车逻辑简化
    • 如果选择规格:使用子商品的idnamepricestock
    • 如果不选择规格:使用父商品的idnamepricestock
    • 关键洞察name字段已经包含完整的规格信息,spec字段可能暂时不需要
  • 核心优势:购物车和订单系统几乎不需要修改

2. 商品列表展示策略

  • 商品列表页(首页、分类页、搜索页):只显示父商品(spuId=0)
  • 商品详情页:显示父商品详情 + 规格选择器(子商品作为选项)
  • 单规格商品:保持现有行为不变(spuId=0且无子商品)

3. API设计

  • 公共商品列表API/api/v1/goods):默认只返回父商品(spuId=0)
  • 商品详情API/api/v1/goods/:id):
    • 父商品:返回商品详情 + 子商品列表(作为规格选项)
    • 子商品:返回子商品详情 + 父商品基本信息
  • 新增API(故事2已实现):
    • GET /api/v1/goods/:id/children - 获取指定父商品的子商品列表
    • POST /api/v1/goods/:id/set-as-parent - 将普通商品设为父商品
    • DELETE /api/v1/goods/:id/parent - 解除子商品的父子关系
    • POST /api/v1/goods/batch-create-children - 批量创建子商品(支持事务)
  • API聚合策略:通过admin-goods-aggregated.mt.ts聚合基础CRUD和父子商品管理路由,保持adminGoodsRoutesMt名称不变,前端代码无需修改
  • 管理员商品API策略
    • 默认显示所有商品(包括父子商品),保持完整管理视图
    • 支持spuId查询参数过滤,管理员可通过spuId=0只查看父商品
    • 在管理员商品管理列表页面添加父子商品关系展示和筛选功能
    • 添加"只显示父商品"筛选选项,提升管理体验

4. 父子商品配置方式

  1. 手动关联:在创建/编辑父商品时,选择已有商品作为子商品
  2. 批量创建:创建父商品时,同时创建多个子商品规格(如不同颜色、尺寸等)

故事

  1. 故事1:管理后台父子商品配置功能已完成 (2025-12-07)

    • 在商品管理UI中添加spuId/spuName字段表单控件
    • 新增子商品关联选择器,支持选择已有商品作为子商品
    • 新增批量子商品创建功能,支持统一创建多个子商品规格
    • 父子商品关系展示和编辑界面
    • 验收标准:管理员能成功配置父子商品关系
    • 完成状态
      • ✅ 功能实现完成
      • ✅ 单元测试通过(31个测试用例)
      • ✅ 集成测试通过(后端6个 + 前端5个)
      • ✅ 代码已提交并推送到远程仓库
  2. 故事2:父子商品管理UI体验优化已完成 (2025-12-10)

    • 在商品创建和编辑页面都添加统一的父子商品管理面板
    • 面板智能支持创建模式和编辑模式的不同行为
    • 创建模式:支持设为父商品、选择父商品、批量创建子商品规格模板
    • 编辑模式:支持父子关系列表展示、子商品管理、关系解除
    • 将批量创建子商品功能整合到面板中,支持创建时批量创建
    • 面板与表单数据实时同步,确保提交数据一致性
    • API实现:在现有adminGoodsRoutesMt中聚合父子商品管理API(获取子商品列表、设为父商品、解除关系、批量创建)
    • 验收标准:管理员能在创建和编辑时一次性完成商品和规格配置,提高工作效率;前端代码无需修改即可使用新API
    • 完成状态
      • ✅ 父子商品管理API实现完成(17个集成测试全部通过)
      • GoodsParentChildPanel.tsx组件已创建并支持创建/编辑模式
      • ChildGoodsList.tsxBatchSpecCreatorInline.tsx组件已创建并集成到面板中
      • GoodsManagement.tsx已集成新面板到创建和编辑表单
      • ✅ 表单数据同步和提交逻辑已实现
      • ✅ 保持与现有功能的兼容性,平滑迁移用户体验
    • 技术实现细节
      • API聚合:通过admin-goods-aggregated.mt.ts聚合基础CRUD和父子商品管理路由,保持adminGoodsRoutesMt名称不变
      • 面板设计:采用标签页设计(关系视图、批量创建、管理子商品),支持不同模式下的差异化行为
      • 数据同步:通过onDataChange回调实现面板与表单数据的实时同步
      • 批量创建:支持事务处理,子商品继承父商品的分类、供应商、商户等信息
  3. 故事3:子商品行内编辑功能已完成 (2025-12-11)

    • 问题背景:当前在商品编辑对话框中,管理子商品时,点击编辑按钮直接调用更新API,并没有切换成更新表单
    • 解决方案:在子商品列表中实现行内编辑功能,点击编辑时将当前行切换为可编辑状态
    • 功能需求
      • ChildGoodsList.tsx组件中添加行内编辑模式
      • 点击编辑按钮时,当前行切换为表单输入模式
      • 支持编辑子商品的基本信息:名称、价格、成本价、库存、排序、状态
      • 提供保存和取消按钮,保存时调用更新API
      • 编辑完成后自动刷新子商品列表
    • 技术实现
      • 扩展ChildGoodsList组件,支持editingChildId状态管理
      • 添加行内编辑表单,复用现有商品表单验证逻辑
      • 集成商品更新API调用
      • 保持与现有父子商品管理面板的集成
    • 验收标准:管理员能在父子商品管理面板中直接编辑子商品信息,无需跳转到其他页面,编辑体验流畅自然
    • 完成状态
      • ✅ 成功扩展了ChildGoodsList组件,添加了行内编辑功能
      • ✅ 实现了ChildGoodsInlineEditForm组件,支持所有必需字段的编辑
      • ✅ 集成了商品更新API调用,包含完整的错误处理和加载状态
      • ✅ 实现了编辑完成后的自动刷新逻辑
      • ✅ 修改了handleEdit函数逻辑:行内编辑现在优先于onEditChild回调
      • ✅ 添加了enableInlineEdit配置选项,支持灵活控制行内编辑行为
      • ✅ 添加了完整的单元测试,覆盖编辑模式切换、表单验证、API调用等场景
      • ✅ 更新了现有测试文件,确保向后兼容性
    • 文件变更
      • 新建文件
      • packages/goods-management-ui-mt/src/components/ChildGoodsInlineEditForm.tsx - 行内编辑表单组件
      • packages/goods-management-ui-mt/tests/unit/ChildGoodsInlineEditForm.test.tsx - 行内编辑表单测试
      • 修改文件
      • packages/goods-management-ui-mt/src/components/ChildGoodsList.tsx - 扩展行内编辑功能
      • packages/goods-management-ui-mt/tests/unit/ChildGoodsList.test.tsx - 更新测试,添加行内编辑功能测试
      • docs/stories/006.003.child-goods-inline-edit.story.md - 更新任务状态和开发记录
  4. 故事4:商品API父子商品支持优化已完成 (2025-12-12)

    • 已完成功能
      • ✅ 商品详情API:根据商品类型返回相应数据(父商品+子商品列表或子商品+父商品信息)
      • ✅ 管理员商品API:增强父子商品关系展示和查询优化,不默认过滤父商品(管理员需要完整视图)
      • ✅ 商品列表查询性能优化:添加spuId字段数据库索引
      • ✅ 添加spuId查询参数支持,管理员可通过spuId=0过滤只显示父商品
      • API架构增强:扩展shared-crud支持自定义服务工厂和独立的listFilters/detailFilters配置
      • 商品模块路由优化
      • 公共商品路由:listFilters: { state: 1, spuId: 0 }, detailFilters: { state: 1 }
      • 管理员商品路由:listFilters: {}, detailFilters: {}(无过滤)
      • 向后兼容性:修复详情查询过滤逻辑,当没有提供detailFilters时使用defaultFilters
      • Schema扩展:更新商品Schema支持children和parent字段
      • 测试完善:添加完整的集成测试,修复现有测试
    • 技术实现细节
      • shared-crud扩展:添加serviceFactory、listFilters、detailFilters选项,支持自定义商品服务
      • 过滤逻辑优化:列表查询使用listFilters(优先)或defaultFilters,详情查询使用detailFilters(优先)或defaultFilters
      • 数据库索引:添加spuId字段索引和复合索引@Index(['tenantId', 'spuId'])优化查询性能
      • 自定义商品服务:GoodsServiceMt支持父子商品详情返回,包含租户ID过滤确保数据安全
      • API测试:添加shared-crud集成测试验证listFilters和detailFilters功能
    • 验收标准:API变更保持向后兼容,商品详情包含完整的父子商品信息,商品列表查询性能良好,管理员可灵活过滤父子商品,所有测试通过
    • 完成状态
      • ✅ 功能实现完成
      • ✅ 扩展shared-crud支持自定义服务工厂和独立过滤配置
      • ✅ 更新商品模块路由使用新的过滤选项
      • ✅ 添加spuId字段数据库索引优化查询性能
      • ✅ 修复详情查询过滤逻辑,保持向后兼容性
      • ✅ 更新商品Schema支持children和parent字段
      • ✅ 修复测试用例中的权限错误期望值
      • ✅ 添加完整的集成测试验证父子商品功能
      • ✅ 代码已提交并推送到远程仓库
  5. 故事5:父子商品多规格选择组件开发已实现 (2025-12-12)

    • 激活并增强现有的GoodsSpecSelector组件
    • 支持父子商品关系,以子商品名称作为规格选项显示
    • 规格选择实际选择对应的子商品ID
    • 适配多租户商品数据查询
    • 验收标准:规格选择器能正确显示子商品名称作为规格,并能选择对应的子商品
    • 完成状态
      • ✅ 修改组件props:将goodsId改为parentGoodsId,添加API调用获取子商品列表
      • ✅ 组件功能完整:支持加载、错误、空状态显示,规格选择和数量调整
      • ✅ 集成到商品详情页:取消组件导入注释,添加规格选择状态管理
      • ✅ 更新"加入购物车"和"立即购买"功能,支持规格选择逻辑
      • ✅ 修复多租户路由暴露问题:创建public-goods-aggregated.mt.ts聚合路由,确保子商品API正确暴露
      • ✅ 移除类型断言:组件完全类型安全,无需any类型断言
      • ✅ 添加单元测试:创建mini/tests/components/goods-spec-selector.test.tsx,8个测试通过
      • ✅ 保持向后兼容性:无规格商品时使用父商品信息
  6. 故事6:商品详情页规格选择集成已完成

    • 在商品详情页集成规格选择组件
    • "立即购买"和"加入购物车"支持规格选择
    • 规格选择后使用子商品的价格和库存信息
    • 多租户环境下的商品规格数据获取
    • 验收标准:用户能在商品详情页成功选择规格,系统使用正确的子商品价格和库存
    • 完成状态
      • ✅ 验证并清理商品详情页面中的规格选择集成(移除"规格选择功能暂时移除"注释)
      • ✅ 确认GoodsSpecSelector组件props传递正确,状态管理正常
      • ✅ 验证"立即购买"和"加入购物车"函数正确处理规格选择逻辑
      • ✅ 修复库存限制逻辑,使其基于规格库存而非父商品库存
      • ✅ 验证多租户API路由包含正确的租户过滤(父子商品在同一租户下)
      • ✅ 创建商品详情页集成测试文件,修复测试文件路径结构
      • ✅ 创建E2E测试占位文件
      • ✅ 所有任务和子任务标记为完成
    • 文件变更
      • 修改的文件
      • mini/src/pages/goods-detail/index.tsx - 移除过时注释,更新库存限制逻辑以支持规格库存
      • mini/tests/unit/components/goods-spec-selector/goods-spec-selector.test.tsx - 修复测试期望和关闭按钮选择器
      • mini/tests/unit/components/taro/Button.test.tsx - 移动Taro原生Button测试到标准位置
      • 新建的文件
      • mini/tests/unit/pages/goods-detail/goods-detail.test.tsx - 商品详情页集成测试(参照OrderButtonBar.test.tsx模式重写)
      • mini/tests/e2e/goods-detail-spec.e2e.test.ts - E2E测试占位文件
      • 移动的文件(遵循项目测试目录结构标准):
      • mini/tests/pages/goods-detail.test.tsxmini/tests/unit/pages/goods-detail/goods-detail.test.tsx
      • mini/tests/components/goods-spec-selector.test.tsxmini/tests/unit/components/goods-spec-selector/goods-spec-selector.test.tsx
      • mini/tests/components/Button.test.tsxmini/tests/unit/components/taro/Button.test.tsx
  7. 故事7:购物车和订单规格支持已完成 (2025-12-13)

    • 购物车最小化修改:适配addToCart逻辑,支持添加子商品(使用子商品信息填充CartItem)
    • 规格信息显示:购物车和订单中通过name字段显示完整规格信息
    • 订单系统兼容:订单创建使用商品ID(可能是子商品ID),保持现有逻辑
    • 多租户兼容性:确保父子商品在同一租户下
    • 验收标准:购物车能正确添加子商品,订单显示完整商品名称,现有单规格商品不受影响
    • 完成状态
      • ✅ 购物车上下文 (CartContext.tsx) 已更新注释,明确支持子商品ID,购物车逻辑天然支持父子商品
      • ✅ 商品详情页现有逻辑已正确处理规格选择,使用子商品ID、价格、库存信息
      • ✅ 订单创建API (CreateOrderRequestDto) 已支持子商品ID,OrderGoodsMt实体正确存储子商品ID
      • ✅ 订单显示通过子商品name字段(包含规格信息)实现完整规格显示
      • ✅ 创建购物车规格支持测试文件 (CartContext.test.tsx),5个测试全部通过
      • ✅ 多租户兼容性由API层保证,前端购物车为本地存储,无需租户验证
    • 修复的测试问题:测试"应该支持添加同一子商品多次(数量累加)"中商品数量显示为库存值(10)而非购买数量(1)的问题已修复,原因为TestComponent的useEffect依赖了cart对象导致无限循环,现已移除该依赖避免循环
    • 文件变更
      • 修改的文件
      • mini/src/contexts/CartContext.tsx - 更新接口注释和函数说明
      • mini/tests/unit/contexts/CartContext.test.tsx - 修复测试中的useEffect依赖循环问题
      • docs/stories/006.007.story.md - 更新任务状态和开发记录
      • 创建的文件
      • mini/tests/unit/contexts/CartContext.test.tsx - 购物车规格支持测试文件
  8. 故事8:购物车页面规格切换功能待开始

    • 问题背景:当前购物车页面显示已添加的商品,但如果用户想切换同一父商品下的不同规格(子商品),需要删除现有商品,重新到商品详情页选择规格再添加,用户体验不够流畅。
    • 解决方案:在购物车页面增加规格切换功能,允许用户直接切换同一父商品下的不同子商品规格。
    • 功能需求
      • 购物车项显示规格选择器(类似商品详情页的GoodsSpecSelector组件)
      • 切换规格时自动更新购物车项的商品ID、名称、价格、库存等信息
      • 保持购物车项数量不变,只切换规格
      • 规格切换后重新计算小计和总计
      • 支持库存验证:切换到的规格必须有足够库存
    • 技术实现
      • 扩展CartContext或购物车组件,支持规格切换逻辑
      • 在购物车项组件中集成GoodsSpecSelector组件
      • 复用故事5中已开发的规格选择器组件
      • 添加规格切换API调用或本地状态更新
      • 确保多租户兼容性:父子商品在同一租户下
    • 验收标准
      • 用户能在购物车页面成功切换商品规格
      • 切换后商品名称、价格、库存信息正确更新
      • 购物车总价正确重新计算
      • 库存不足的规格无法选择或给出提示
      • 现有单规格商品购物车体验不受影响
    • 完成状态
      • ⏳ 功能待实现
      • ⏳ 技术方案待设计
      • ⏳ 测试待编写
    • 文件变更
      • 待修改的文件
      • mini/src/contexts/CartContext.tsx - 扩展规格切换功能
      • mini/src/components/CartItem.tsx(或类似组件)- 集成规格选择器
      • mini/tests/unit/contexts/CartContext.test.tsx - 添加规格切换测试
      • 可能新建的文件
      • 购物车规格切换组件(如果需要)

兼容性要求

  • 现有API保持向后兼容,新增端点不影响现有功能(故事2、4、7已确保)
  • 数据库schema向后兼容,利用现有spuId字段(故事1-4已实现)
  • UI变更遵循现有设计模式(故事2、3、5、6已实现)
  • 性能影响最小化,特别是商品列表查询(故事4添加数据库索引优化)
  • 多租户隔离机制保持完整(故事1-7已实现)

风险缓解

  • 主要风险:API变更影响现有客户端,规格选择逻辑影响购物车功能
  • 缓解措施:逐步集成,保持向后兼容,现有无规格商品继续正常工作
  • 回滚计划:移除新增API端点,恢复原有逻辑,保持多租户完整性

完成定义

  • 所有故事完成,验收标准满足(7/8完成,故事8待实现)
  • 现有功能通过测试验证(故事1-7测试通过)
  • API变更经过兼容性测试(故事2-7 API测试通过)
  • 多租户隔离机制保持完整(故事1-7已实现)
  • 性能测试通过,无明显性能下降(故事4添加数据库索引优化)
  • 文档适当更新(史诗文档已更新)
  • 现有功能无回归(故事1-7验证通过)

技术要点

数据库层面

  • 利用现有spuId字段:0表示父商品或单规格商品,>0表示子商品
  • spuName字段存储父商品名称,便于展示

多租户支持

  • 所有操作必须包含tenantId过滤
  • 父子商品必须在同一租户下
  • 数据权限机制保持完整

性能考虑

  • 商品列表查询添加spuId=0条件
  • 子商品列表查询使用分页
  • 规格选择器数据懒加载

前端适配

  • 规格选择器:显示子商品名称作为规格选项,选择时使用子商品信息
  • 购物车逻辑极致简化
    • 如果选择规格:CartItem使用子商品的idnamepricestock
    • 如果不选择规格:CartItem使用父商品的idnamepricestock
    • 关键name字段已经包含完整规格信息,spec字段可暂时忽略或设置为相同值
  • 商品详情页:父商品信息展示,规格选择后使用选中商品的信息
  • 最大优势:购物车和订单逻辑几乎不需要修改,只需正确选择商品
  • 购物车页面规格切换(故事8):用户可在购物车页面直接切换同一父商品下的不同规格,无需删除重选,提升用户体验

史诗创建时间:2025-12-06 创建人:John (Product Manager) 技术栈:TypeORM + Hono + React + Taro小程序 + 多租户架构 优先级:高(支持电商核心功能)