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

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

史诗状态

进度: 10/13 故事完成 (76.9%) 最近更新: 2025-12-15 (故事10:购物车商品名称显示优化完成) 当前状态: 故事1-10已完成,故事11-13待开始

完成概览

  • 故事1: 管理后台父子商品配置功能 (已完成)
  • 故事2: 父子商品管理UI体验优化 (已完成)
  • 故事3: 子商品行内编辑功能 (已完成)
  • 故事4: 商品API父子商品支持优化 (已完成)
  • 故事5: 父子商品多规格选择组件开发 (已完成)
  • 故事6: 商品详情页规格选择集成 (已完成)
  • 故事7: 购物车和订单规格支持 (已完成)
  • 故事8: 购物车页面规格切换功能 (已完成)
  • 故事9: 父子商品名称关联查询优化(为购物车显示做准备) (已完成)
  • 故事10: 购物车商品名称显示优化 (已完成)
  • 故事11: 子商品删除功能实现 (待开始)
  • 故事12: 商品详情页规格选择流程优化 (待开始)
  • 故事13: 父子商品列表缓存自动刷新优化 (待开始)

史诗目标

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

史诗描述

现有系统上下文

  • 数据库支持:商品表已有父子商品关系字段(spuId/spuName)
  • Schema支持:所有商品Schema(Admin/User/Public)都包含spuId字段,spuName字段将在故事9中移除,改用parent对象关联查询
  • 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已实现)
    8. ✅ 父子商品名称通过关联查询获取,为购物车显示提供准确父商品名称(故事9已实现)
    9. ✅ 购物车中父子商品显示完整的组合名称(父商品名称 + 子商品规格名称)(故事10已实现)
    10. ⏳ 管理员能删除不需要的子商品规格(故事11待实现)
    11. ⏳ 用户在商品详情页能一键完成规格选择和购物车/购买操作(故事12待实现)

设计决策

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:购物车页面规格切换功能已完成 (2025-12-13)

    • 问题背景:当前购物车页面显示已添加的商品,但如果用户想切换同一父商品下的不同规格(子商品),需要删除现有商品,重新到商品详情页选择规格再添加,用户体验不够流畅。
    • 解决方案:在购物车页面增加规格切换功能,允许用户直接切换同一父商品下的不同子商品规格。
    • 功能需求
      • 购物车项显示规格选择器(类似商品详情页的GoodsSpecSelector组件)
      • 切换规格时自动更新购物车项的商品ID、名称、价格、库存等信息
      • 保持购物车项数量不变,只切换规格
      • 规格切换后重新计算小计和总计
      • 支持库存验证:切换到的规格必须有足够库存
    • 技术实现
      • 扩展CartContext或购物车组件,支持规格切换逻辑
      • 在购物车项组件中集成GoodsSpecSelector组件
      • 复用故事5中已开发的规格选择器组件
      • 添加规格切换API调用或本地状态更新
      • 确保多租户兼容性:父子商品在同一租户下
    • 验收标准
      • 用户能在购物车页面成功切换商品规格
      • 切换后商品名称、价格、库存信息正确更新
      • 购物车总价正确重新计算
      • 库存不足的规格无法选择或给出提示
      • 现有单规格商品购物车体验不受影响
    • 完成状态
    • ✅ 功能实现完成:扩展CartContext支持规格切换逻辑,集成GoodsSpecSelector组件,添加库存验证和错误处理
    • ✅ 测试通过:为CartContext添加switchSpec单元测试,为购物车页面添加规格切换组件测试,23个测试全部通过
    • ✅ 文件变更完成:更新CartContext.tsx、购物车页面、测试文件等(详见下方文件变更列表)
    • ✅ 多租户兼容性和向后兼容性已验证
    • 文件变更
    • 修改的文件
      • mini/src/contexts/CartContext.tsx - 扩展CartContext,添加switchSpec函数,更新CartItem接口,修复数据迁移逻辑(parentGoodsId默认值从item.id改为0)
      • mini/src/pages/cart/index.tsx - 集成GoodsSpecSelector组件,添加规格切换功能,清理多余的调试信息
      • mini/tests/unit/contexts/CartContext.test.tsx - 添加switchSpec函数单元测试
      • mini/tests/unit/pages/cart/index.test.tsx - 添加购物车页面规格切换组件测试,修复测试结构使用真实CartContext,解决空购物车状态测试问题,修复库存不足提示测试,添加7个完整的规格切换集成测试
      • mini/tests/__mocks__/taroMock.ts - 扩展Taro API mock,添加request方法支持
      • mini/src/components/goods-spec-selector/index.tsx - 改进错误处理,解析API响应体获取具体错误消息
      • mini/src/pages/goods-detail/index.tsx - 修复parentGoodsId计算逻辑,正确处理父子商品关系
      • mini/tests/unit/pages/goods-detail/goods-detail.test.tsx - 更新测试期望,添加parentGoodsId字段验证
      • docs/stories/006.008.cart-spec-switching.story.md - 更新任务状态和开发记录
  9. 故事9:父子商品名称关联查询优化(为购物车显示做准备)已完成

    • 问题背景:故事10"购物车商品名称显示优化"需要在购物车中分开显示父子商品:商品名称显示父商品名称,规格名称显示子商品规格名称。当前系统使用spuName字段冗余存储父商品名称,但存在数据一致性问题:当父商品名称更新时,不会自动同步更新子商品的spuName字段。这导致购物车等场景显示的商品名称可能不一致。故事9的目标是为故事10提供基础支持,建立可靠的父子商品名称关联查询机制。
    • 解决方案:采用关联实体查询方案,通过parent对象关联查询获取父商品信息,为购物车提供准确、实时的父商品名称,解决spuName字段的数据一致性问题。
    • 功能需求
      • 核心目标:为故事10的小程序购物车名称显示提供数据基础
      • 完善商品详情API:确保返回的parent对象包含完整的父商品基本信息(至少包含idname字段)
      • 更新商品Schema:将parent字段的类型从z.any()改为具体的父商品Schema,确保类型安全
      • 小程序购物车适配:确保购物车页面能通过goods.parent?.name获取父商品名称,用于故事10的商品名称显示优化
      • API不再返回spuName字段,前端使用parent.name获取父商品名称
      • 优先关注小程序端:管理后台的更新可以后续进行,故事9主要服务于小程序购物车需求
    • 技术实现
      • 后端:完善GoodsServiceMt.getById方法,确保parent对象包含必要字段;更新商品Schema类型定义,并从商品Schema中移除spuName字段
      • 小程序端适配:购物车页面直接使用goods.parent?.name获取父商品名称,不再依赖spuName字段,无需专门的工具函数
      • 购物车数据准备:确保购物车中使用的商品数据包含完整的parent对象信息
      • 测试:添加关联查询的单元测试和集成测试,确保数据一致性和购物车可用性
    • 验收标准
      • 商品详情API返回的parent对象包含完整的父商品基本信息(id、name等)
      • 购物车页面能正确通过goods.parent?.name获取父商品名称(为故事10做准备)
      • API不再返回spuName字段,前端代码直接使用parent.name获取父商品名称
      • 父子商品名称显示准确,无数据不一致问题
      • 所有测试通过,无回归问题
      • 故事10能顺利基于故事9的实现完成购物车商品名称显示优化
    • 完成状态
      • ✅ 功能已实现:创建ParentGoodsSchema,更新所有商品Schema类型,完善GoodsServiceMt.getById方法,添加租户过滤,移除spuName字段
      • ✅ 技术方案已设计:采用关联查询方案,通过parent对象获取父商品信息,保持数据库实体向后兼容
      • ✅ 测试已编写:更新集成测试验证parent对象完整性和API不再返回spuName字段,所有测试通过
    • 文件变更
      • 新增文件:
      • packages/goods-module-mt/src/schemas/parent-goods.schema.mt.ts - 父商品精简Schema定义
      • 已修改的文件:
      • packages/goods-module-mt/src/schemas/public-goods.schema.mt.ts - 更新parent和children字段类型,移除spuName
      • packages/goods-module-mt/src/schemas/admin-goods.schema.mt.ts - 更新parent和children字段类型,移除spuName
      • packages/goods-module-mt/src/schemas/user-goods.schema.mt.ts - 移除spuName字段
      • packages/goods-module-mt/src/schemas/goods.schema.mt.ts - 更新UpdateGoodsDto,移除spuName字段
      • packages/goods-module-mt/src/schemas/index.mt.ts - 导出ParentGoodsSchema
      • packages/goods-module-mt/src/services/goods.service.mt.ts - 完善getById方法,添加租户过滤和完整字段选择
      • packages/goods-module-mt/tests/integration/admin-goods-parent-child.integration.test.ts - 更新测试验证parent对象
      • packages/goods-module-mt/tests/integration/admin-goods-routes.integration.test.ts - 更新测试
      • packages/goods-module-mt/tests/integration/public-goods-children.integration.test.ts - 更新测试验证spuName移除
      • packages/goods-module-mt/tests/integration/public-goods-parent-filter.integration.test.ts - 更新测试验证parent对象完整性
      • 验证文件:
      • mini/src/pages/cart/index.tsx - 购物车页面,验证数据基础可用性
      • packages/goods-module-mt/src/entities/goods.entity.mt.ts - 验证spuName字段保留在实体中
  10. 故事10:购物车商品名称显示优化已完成

    • 问题背景:父子商品在管理后台配置时,父商品使用完整商品名称(如"连衣裙"),子商品使用规格名称(如"红色 大码"、"蓝色 中码")。在当前实现中,购物车页面(mini/src/pages/cart/index.tsx:253)使用goodsName = latestGoods?.name || item.name显示商品名称,对于子商品只显示规格名称,而没有显示父商品名称。购物车页面已经将商品名称和规格名称分开显示(goods-title显示商品名称,specs-text显示规格名称),但子商品的商品名称显示的是规格名称,而不是父商品名称,导致商品信息显示不完整。
    • 解决方案:优化购物车中父子商品的显示方式,利用商品详情API返回的parent对象获取父商品名称,商品名称显示父商品名称,规格名称显示子商品规格名称,提供清晰完整的商品信息。
    • 功能需求
      • 购物车中显示父子商品时,商品名称显示父商品名称,规格名称显示子商品规格名称
      • 对于单规格商品(非父子商品,parentGoodsId === 0),保持现有显示方式不变,直接显示商品名称
      • 确保订单提交页面(mini/src/pages/order-submit/index.tsx:277)等所有显示商品名称的地方都遵循同样的显示逻辑
      • 保持购物车中商品名称显示的一致性和清晰性
    • 技术实现
      • 修改购物车页面(mini/src/pages/cart/index.tsx)的商品名称显示逻辑,在goodsName计算时判断是否为子商品(通过parentGoodsId !== 0spuId > 0
      • 如果是子商品,商品名称直接使用parent.name获取父商品名称(通过故事9实现的关联查询),不再使用spuName字段
      • 规格名称使用latestGoods?.name || '选择规格'显示子商品规格名称(子商品的name字段就是规格名称)
      • 修改订单提交页面(mini/src/pages/order-submit/index.tsx)的商品名称显示逻辑,遵循同样的显示原则
      • 直接使用parent.name获取父商品名称,不再依赖spuName字段,统一处理父子商品名称获取逻辑
      • 确保多租户兼容性:父子商品在同一租户下,商品详情API返回完整的parent对象
      • 优化:逐步移除CartItem接口中的spec字段,避免数据冗余(子商品的name字段已经包含规格信息)
    • 验收标准
      • 购物车中父子商品显示时,商品名称显示父商品名称,规格名称显示子商品规格名称
      • 单规格商品显示保持不变
      • 订单提交页面、订单详情页等所有相关页面显示一致
      • 现有功能不受影响,无回归问题
      • 父子商品信息显示清晰完整,用户能直观了解商品全貌
    • 完成状态
      • ⏳ 功能待实现
      • ⏳ 技术方案待设计
      • ⏳ 测试待编写
    • 文件变更
      • 待修改的文件
      • mini/src/pages/cart/index.tsx - 修改商品名称显示逻辑(第253行goodsName计算),移除对item.spec的依赖
      • mini/src/pages/order-submit/index.tsx - 修改商品名称显示逻辑(第277行item.name显示)
      • mini/src/pages/goods-detail/index.tsx - 移除添加购物车时设置spec字段的逻辑
      • mini/src/contexts/CartContext.tsx - 移除CartItem接口中的spec字段,更新switchSpec函数
      • 其他可能显示商品名称的订单相关组件
      • 可能新建的文件:无(无需新建工具函数,直接使用parent.name
  11. 故事11:子商品删除功能实现待开始

    • 问题背景:当前在管理后台商品管理对话框的父子商品管理面板中,子商品列表(ChildGoodsList组件)提供了删除按钮,但该按钮没有实际作用。点击删除按钮时,handleDelete函数仅检查onDeleteChild回调是否存在,而父组件GoodsParentChildPanel并未传递此回调,导致删除操作无效。管理员无法在管理界面中直接删除子商品规格。
    • 解决方案:实现子商品删除功能,在父子商品管理面板中为子商品列表添加有效的删除操作,允许管理员删除不需要的子商品规格。
    • 功能需求
      • GoodsParentChildPanel组件中为ChildGoodsList组件传递onDeleteChild回调函数
      • 实现删除确认对话框,防止误操作
      • 调用商品删除API(或解除父子关系API,根据业务逻辑决定)实际删除子商品
      • 删除成功后刷新子商品列表,更新UI状态
      • 确保多租户兼容性:只能删除当前租户下的子商品
    • 技术实现
      • GoodsParentChildPanel组件中添加onDeleteChild回调函数,处理子商品删除逻辑
      • 使用商品删除API(DELETE /api/v1/goods/:id)删除子商品实体,或使用解除父子关系API(DELETE /api/v1/goods/:id/parent)仅解除关系但保留商品(根据业务需求选择)
      • 添加删除确认对话框,使用现有Dialog组件
      • 删除成功后调用refetch刷新子商品列表数据
      • 错误处理:显示友好的错误提示
      • 保持与现有父子商品管理功能的集成一致性
    • 验收标准
      • 管理员能在父子商品管理面板中成功删除子商品
      • 删除前有确认提示,防止误操作
      • 删除后子商品列表实时更新
      • 删除操作仅影响当前租户的数据,多租户隔离保持完整
      • 现有功能不受影响,无回归问题
    • 完成状态
      • ⏳ 功能待实现
      • ⏳ 技术方案待设计
      • ⏳ 测试待编写
    • 文件变更
      • 待修改的文件
      • packages/goods-management-ui-mt/src/components/GoodsParentChildPanel.tsx - 添加onDeleteChild回调函数和删除确认对话框
      • packages/goods-management-ui-mt/src/components/ChildGoodsList.tsx - 可能需优化删除按钮的视觉反馈
      • 可能添加删除确认对话框组件或复用现有Dialog
      • 可能新建的文件:无(复用现有组件和API)
  12. 故事12:商品详情页规格选择流程优化待开始

    • 问题背景:当前小程序商品详情页的规格选择流程不够流畅。页面中有一个独立的"选择规格"按钮,用户需要先点击该按钮选择规格,关闭规格选择弹窗,然后再点击"加入购物车"或"立即购买"按钮。这种两步操作给用户带来不便,特别是对于多规格商品,用户需要记住已选择的规格再进行购买操作,体验不够直观。
    • 解决方案:优化商品详情页的规格选择流程,将规格选择与购物车/购买操作合并。当用户点击"加入购物车"或"立即购买"按钮时,如果商品有多规格选项且用户未选择规格,自动弹出规格选择器。用户选择规格和数量后,直接执行对应的购物车添加或购买操作,实现一键完成规格选择和购买。
      • 完整流程
      • 用户点击"加入购物车"或"立即购买"按钮
      • 系统判断:如果商品有多规格选项且用户未选择规格 → 弹出规格选择器
      • 用户在规格选择器中选择规格和数量,点击确定
      • 直接执行对应的购物车添加或购买操作
      • 如果用户没有完成操作(如取消或返回),选择的规格状态保持在页面中
      • 用户再次点击操作按钮 → 再次弹出规格选择器,自动选中上次选择的规格
      • 用户可以快速确认原有选择,或修改规格/数量后继续操作
    • 功能需求
      • 移除商品详情页中独立的"选择规格"按钮,将规格选择与操作按钮深度集成
      • 点击"加入购物车"或"立即购买"时,自动判断是否需要弹出规格选择器
      • 规格选择器弹出后,用户选择规格和数量,点击确定直接执行对应操作
      • 用户在页面上已选择的规格状态可以保持,下次弹出规格选择器时自动选中之前选择的规格,方便用户快速确认或修改选择
      • 保持向后兼容性:单规格商品(无子商品)的操作流程不变
      • 优化用户界面,在按钮上显示当前选择的规格信息(如有)
    • 技术实现
      • 修改mini/src/pages/goods-detail/index.tsx中的handleAddToCarthandleBuyNow函数,添加规格选择判断逻辑
      • 重构规格选择状态管理,将showSpecModal状态与操作流程关联
      • 扩展GoodsSpecSelector组件的onConfirm回调,支持直接执行购物车添加或购买操作
      • 添加规格选择上下文,记录用户选择规格后的目标操作(加入购物车或立即购买)
      • 优化按钮禁用状态逻辑,基于规格选择状态动态更新
      • 添加规格信息显示组件,在操作按钮区域显示当前选择的规格和价格
    • 验收标准
      • 用户点击"加入购物车"或"立即购买"时,如果需要选择规格,自动弹出规格选择器
      • 用户在规格选择器中选择规格和数量后,直接执行对应的购物车添加或购买操作
      • 用户在页面上已选择的规格状态可以保持,下次弹出规格选择器时自动选中之前选择的规格,方便用户快速确认或修改选择
      • 单规格商品的操作流程保持不变,不受影响
      • 用户界面清晰显示当前选择的规格信息(如有)
      • 操作流程流畅,无多余的弹窗关闭和重新点击步骤
    • 完成状态
      • ⏳ 功能待实现
      • ⏳ 技术方案待设计
      • ⏳ 测试待编写
    • 文件变更
      • 待修改的文件
      • mini/src/pages/goods-detail/index.tsx - 重构规格选择流程,合并操作逻辑
      • mini/src/components/goods-spec-selector/index.tsx - 扩展组件支持直接操作执行
      • 可能修改mini/src/contexts/CartContext.tsx中的购物车添加逻辑
      • 可能新建的文件
      • mini/src/components/goods-spec-action-context.tsx - 规格选择操作上下文组件(可选)
      • mini/src/components/selected-spec-display.tsx - 已选规格信息显示组件(可选)
  13. 故事13:父子商品列表缓存自动刷新优化待开始

    • 问题背景:在管理后台商品对话框中,使用批量创建子商品规格功能后,父子关系列表没有自动更新。管理员需要手动刷新页面或切换到其他标签页再返回才能看到新创建的子商品,影响操作体验。
    • 解决方案:优化 React Query 缓存刷新逻辑,在批量创建子商品成功后自动使相关查询失效,触发子商品列表自动刷新。
    • 功能需求
      • 批量创建子商品规格成功后,父子关系视图中的子商品列表立即自动更新
      • 批量创建子商品规格成功后,管理子商品标签页中的列表立即自动更新
      • 其他父子商品操作(设为父商品、解除父子关系、行内编辑子商品)的缓存刷新逻辑保持一致
      • 缓存刷新逻辑高效,不会造成不必要的网络请求
    • 技术实现
      • GoodsParentChildPanel 组件中添加 useQueryClient
      • 修改 batchCreateChildrenMutationonSuccess 回调,使用 queryClient.invalidateQueries 使相关查询失效
      • 需要失效的查询键:['goods-children', goodsId, tenantId]['goods', 'children', 'list', parentGoodsId, tenantId]
      • 确保其他 mutation(设为父商品、解除关系)也有适当的缓存刷新逻辑
    • 验收标准
      • 批量创建子商品后,父子关系视图列表自动更新
      • 批量创建子商品后,管理子商品标签页列表自动更新
      • 其他父子商品操作后的缓存刷新正常
      • 现有功能不受影响,无回归问题
    • 完成状态
      • ⏳ 功能待实现
      • ⏳ 技术方案待设计
      • ⏳ 测试待编写
    • 文件变更
      • 待修改的文件
      • packages/goods-management-ui-mt/src/components/GoodsParentChildPanel.tsx - 添加 useQueryClient,修改 mutation 的 onSuccess 回调
      • 可能修改 packages/goods-management-ui-mt/src/components/ChildGoodsList.tsx - 确保行内编辑后的 refetch 逻辑正确
      • 测试文件
      • packages/goods-management-ui-mt/tests/unit/GoodsParentChildPanel.test.tsx - 添加缓存刷新测试
      • packages/goods-management-ui-mt/tests/unit/ChildGoodsList.test.tsx - 更新测试验证缓存刷新

兼容性要求

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

风险缓解

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

完成定义

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

技术要点

数据库层面

  • 利用现有spuId字段:0表示父商品或单规格商品,>0表示子商品
  • spuName字段存储父商品名称(冗余字段,故事9中移除,改用关联查询)

多租户支持

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

性能考虑

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

前端适配

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

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