# 杜康好客 · 门店套餐功能开发文档 > **版本**:3.4.10 > **日期**:2026-08-03 > **状态**:需求已定 / 待实现 > **关联**:[`杜康好客-v3-PRD.md`](./杜康好客-v3-PRD.md) §3.9 · REQ-P-027 / REQ-S-021 / REQ-H-025 / REQ-U-027 ## 变更记录 | 版本 | 日期 | 说明 | |------|------|------| | **3.4.10** | 2026-08-03 | 新增门店套餐需求与开发设计 | --- ## 目录 1. [背景与目标](#1-背景与目标) 2. [数据模型](#2-数据模型) 3. [API 草案](#3-api-草案) 4. [四端 UI](#4-四端-ui) 5. [审核与展示状态机](#5-审核与展示状态机) 6. [客服「套餐异议」](#6-客服套餐异议) 7. [验收清单(ACC)](#7-验收清单acc) 8. [实现分期建议](#8-实现分期建议) ### 流程概览 ```mermaid flowchart LR partnerEdit[PartnerOrShopEdit] draft[PackageChangeRequest] hqReview[HqApproveOrReject] live[LiveStorePackages] mini[MiniStoreDetail] dispute[PackageDisputeTicket] partnerEdit --> draft draft --> hqReview hqReview -->|APPROVED| live live --> mini mini --> dispute hqDirect[HqDirectSave] --> live ``` --- ## 1. 背景与目标 门店可向 C 端用户展示餐饮套餐信息,帮助用户在核销前了解可核销内容。套餐与酒水 SKU 无关,需独立数据模型与审核流。 ### 1.1 需求原文(10 条) 1. **合伙人新建门店**:在现有拓店流程(基本信息 / 照片 / 结算资质)后增加「套餐」页;可不填、可跳过。 2. **套餐字段**:套餐名称、价格(元)、菜品、使用时间、其他说明。 3. **C 端门店详情**:在「门店详情」区块附近,以纵向列表展示套餐;每条为「标题 + 内容」形式。 4. **数量约束**:可不填;可多条;**同一门店最多 10 条**。 5. **门店端**:新增套餐列表页,可编辑并**提交审核**。 6. **合伙人端**:选择门店 → 套餐列表 → 编辑 → **提交审核**。 7. **总部端**:门店详情新增「套餐」Tab,**直接保存生效、无需审核**。 8. **审核流**:合伙/门店提交 → 总部通过/驳回;**通过后自动替换生效套餐**。 9. **C 端展示**:仅展示**已审核通过(生效中)**的套餐。 10. **客服异议**:用户对核销中套餐有异议 → 客服申诉类型新增 **套餐异议**。 ### 1.2 现状对齐 | 现状 | 影响 | |------|------| | 无门店套餐实体;`Store.tags` Json 未承载套餐 | 需新建表,不复用酒水 SKU | | 合伙人拓店三步:基本信息 / 照片 / 结算资质 | 新增「套餐」页(第 4 步或独立页) | | 门店端仅改营业状态,**无资料编辑 API** | 需新增门店端套餐 CRUD + 提审 API | | 门店审核仅 `NEW`/`RESUBMIT` 整店审,**无「套餐变更」独立审核** | 需独立「套餐变更审核」模型 | | 售后工单四类型无「套餐异议」 | 扩展工单/客服类型 | | C 端详情已有 intro / 权益规则 / 环境图 | 在「门店详情」区块附近增加套餐纵向列表 | ### 1.3 明确不做(本文档阶段) - 不改 Prisma / 不写 API / 不改四端业务代码(本文档仅设计) - 不把套餐挂到商品目录 `CommonProductItem` - 不合并进整店 `Store.auditStatus`(套餐变更独立提审) --- ## 2. 数据模型 ### 2.1 生效数据 `StorePackage`(表名 `store_package`) | 字段 | 类型 | 说明 | |------|------|------| | `id` | String (cuid) | 主键 | | `storeId` | String | 门店 ID,FK → `Store` | | `name` | String | 套餐名称,如「套餐A」 | | `price` | Decimal | 价格(元) | | `dishes` | Text | 菜品文案;前端按顿号/换行展示;或 Json 字符串数组 | | `usableTime` | String? | 使用时间,如「节假日除外」 | | `otherNotes` | String? | 其他说明,如「不可叠加」 | | `sortOrder` | Int | 展示序 0~9 | | `createdAt` | DateTime | | | `updatedAt` | DateTime | | **约束** - 同一 `storeId` 下生效套餐 **≤ 10** 条 - 删除/替换由审核通过或总部直存事务完成 ### 2.2 变更提审 `StorePackageChangeRequest`(表名 `store_package_change_request`) | 字段 | 类型 | 说明 | |------|------|------| | `id` | String (cuid) | 主键 | | `storeId` | String | 门店 ID | | `status` | Enum | `PENDING` / `APPROVED` / `REJECTED` | | `packagesJson` | Json | 提审快照(完整套餐数组,最多 10 条) | | `submitterType` | Enum | `PARTNER` / `SHOP` | | `submitterId` | String | 提交人 ID | | `rejectReason` | String? | 驳回原因 | | `reviewedAt` | DateTime? | | | `reviewerId` | String? | 总部审核人 | | `createdAt` | DateTime | | **规则** - 同门店同时仅允许 **一条 `PENDING`** - 审核 **通过**:事务内删除该店全部 `StorePackage`,按 `packagesJson` 重建 - 审核 **驳回**:不改动生效表;提审方可修改后再提 ### 2.3 提审快照 JSON 结构(`packagesJson` 数组元素) ```json { "name": "套餐A", "price": "198.00", "dishes": "红烧肉、红烧鱼、油焖茄子", "usableTime": "节假日除外", "otherNotes": "不可叠加", "sortOrder": 0 } ``` ### 2.4 总部直存 - 写 `StorePackage`,**不写** `StorePackageChangeRequest` - 可选记 `CommonEvent`(如 `STORE_PACKAGE_UPDATE`)留痕 ### 2.5 枚举(建议写入 `packages/shared-types`) ```typescript enum StorePackageChangeStatus { PENDING = 'PENDING', APPROVED = 'APPROVED', REJECTED = 'REJECTED', } enum StorePackageSubmitterType { PARTNER = 'PARTNER', SHOP = 'SHOP', } // 扩展工单类型 enum TicketType { // ...existing PACKAGE_DISPUTE = 'PACKAGE_DISPUTE', } ``` --- ## 3. API 草案 > 路径前缀均为 `/api/v1`;响应 `{ code, message, data }`。 ### 3.1 合伙人端 | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/partner/stores/:storeId/packages` | 读草稿/生效套餐(见实现约定) | 管辖门店 | | PUT | `/partner/stores/:storeId/packages` | 提交套餐变更审核(body=套餐数组) | 管辖门店 | | GET | `/partner/stores/:storeId/package-change-requests` | 历史提审记录 | 管辖门店 | **拓店草稿**:新建流程中套餐可先写入 `storeDraft.packages`,门店创建成功后随首次提审或总部审核入库。 ### 3.2 门店端(新增能力) | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/shop/store/packages` | 当前门店套餐(草稿+生效) | 门店主账号/店员 | | PUT | `/shop/store/packages` | 提交套餐变更审核 | 门店主账号 | > 现状:门店端无资料编辑 API,本模块为 **新增** Shop Store API。 ### 3.3 总部端 | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/admin/stores/:storeId/packages` | 读生效套餐 | HQ | | PUT | `/admin/stores/:storeId/packages` | **直存**生效(无需审核) | HQ | | GET | `/admin/store-package-audits` | 待审/历史列表 | HQ | | PUT | `/admin/store-package-audits/:requestId/audit` | 通过/驳回 | HQ | **审核 body 示例** ```json { "action": "APPROVE" } ``` ```json { "action": "REJECT", "rejectReason": "价格描述不清晰" } ``` ### 3.4 C 端(公开) | 方法 | 路径 | 说明 | |------|------|------| | GET | `/stores/:storeId` | 门店详情 **嵌入** `packages[]`(仅生效) | | GET | `/stores/:storeId/packages` | 可选独立接口,仅返回生效套餐 | **响应字段(单条)** ```json { "name": "套餐A", "price": "198.00", "dishes": "红烧肉、红烧鱼、油焖茄子", "usableTime": "节假日除外", "otherNotes": "不可叠加", "sortOrder": 0 } ``` ### 3.5 客服异议 扩展创建售后/客服工单: | 字段 | 说明 | |------|------| | `ticketType` | `PACKAGE_DISPUTE` | | `storeId` | 必填 | | `redeemRecordId` | 可选,关联核销单 | --- ## 4. 四端 UI ### 4.1 合伙人端(H5) | 页面 | 路径建议 | 说明 | |------|----------|------| | 拓店·套餐 | `/stores/create/packages` | 第 4 步;可跳过;数据进 `storeDraft` | | 门店详情·套餐入口 | 门店详情页 | 跳转套餐列表 | | 套餐列表 | `/stores/:id/packages` | 展示当前生效 + 待审状态 | | 套餐编辑 | `/stores/:id/packages/edit` | 表单:名称/价格/菜品/时间/说明;最多 10 条 | | 提交审核 | 编辑页底部 | PUT 提审;同店有 PENDING 时禁用 | 参考:`apps/h5-partner/src/pages/StoreCreatePage.tsx` ### 4.2 门店端(H5) | 页面 | 路径建议 | 说明 | |------|----------|------| | 套餐列表 | `/packages` | 新入口(门店信息 Tab 或独立菜单) | | 套餐编辑 | `/packages/edit` | 同合伙人表单 | | 提交审核 | 编辑页底部 | PUT `/shop/store/packages` | ### 4.3 总部端(admin-web) | 页面 | 说明 | |------|------| | 门店详情 Drawer · Tab「套餐」 | 在 `StoresPage.tsx` Drawer 增加 Tab;表单直存 | | 套餐变更审核列表 | 独立页或门店内待审提示;通过/驳回 | 参考:`apps/admin-web/src/pages/StoresPage.tsx` ### 4.4 用户端(mini-user) | 位置 | 说明 | |------|------| | 门店详情 · 「门店详情」区块上方或下方 | 纵向卡片列表 | | 单条样式 | 标题 = 套餐名;内容 = 价格 / 菜品 / 使用时间 / 其他说明 | | 空态 | 无生效套餐时不展示该区块 | 参考:`apps/mini-user/src/pages/store-detail/index.tsx` ### 4.5 C 端展示示例 ``` 套餐A 198 元 · 红烧肉、红烧鱼、油焖茄子 使用时间:节假日除外 说明:不可叠加 ``` --- ## 5. 审核与展示状态机 ```mermaid stateDiagram-v2 [*] --> NoLive: 从未通过 NoLive --> Live: 首次审核通过 / 总部直存 Live --> Pending: 合伙/门店提审 Pending --> Live: 审核通过(覆盖) Pending --> Live: 审核驳回(保留旧 Live) Live --> Live: 总部直存(覆盖) ``` | 状态 | C 端展示 | 编辑方可见 | |------|----------|------------| | 无生效套餐 | 不展示套餐区块 | 可编辑、可提审 | | 有生效 + 无待审 | 展示生效版 | 可编辑、可提审 | | 有生效 + 待审中 | **仍展示上一版生效** | 显示「审核中」;不可重复提审 | | 审核通过 | 展示新版 | 可再次编辑提审 | | 审核驳回 | 仍展示旧版 | 可修改后再提;展示驳回原因 | --- ## 6. 客服「套餐异议」 | 项 | 说明 | |----|------| | 类型文案 | 套餐异议 | | 枚举 | `PACKAGE_DISPUTE` | | 入口 | C 端核销相关页 / 门店详情 / 客服页可选该类型 | | 关联 | `storeId` 必填;可选 `redeemRecordId` | | 总部 | 客服工单列表可按类型筛选处理 | 与现有四类型工单并列,不替换原有类型。 --- ## 7. 验收清单(ACC) | ID | 对应需求 | 验收项 | |----|----------|--------| | ACC-PKG-01 | #1 | 合伙人拓店流程有「套餐」页,可跳过;跳过后门店可无套餐 | | ACC-PKG-02 | #2 | 单条套餐含名称、价格、菜品、使用时间、其他说明五字段 | | ACC-PKG-03 | #3 | C 端门店详情以纵向标题+内容展示套餐 | | ACC-PKG-04 | #4 | 同一门店生效套餐 ≤10;第 11 条保存/提审被拒绝 | | ACC-PKG-05 | #5 | 门店端可列表、编辑、提交审核 | | ACC-PKG-06 | #6 | 合伙人可选门店、编辑套餐、提交审核 | | ACC-PKG-07 | #7 | 总部门店详情「套餐」Tab 直存后 C 端立即可见(无需审核) | | ACC-PKG-08 | #8 | 合伙/门店提审 → 总部通过后生效表被完整替换;驳回后生效表不变 | | ACC-PKG-09 | #9 | C 端 never 展示未通过/待审套餐;提审中仍展示旧版 | | ACC-PKG-10 | #10 | 用户可创建「套餐异议」工单;总部可按类型筛选 | | ACC-PKG-11 | — | 同门店仅一条 PENDING;重复提审返回业务错误 | | ACC-PKG-12 | — | 空套餐数组提审合法;通过后 C 端不展示套餐区块 | | ACC-PKG-13 | — | 总部直存记事件或审计日志(若启用 CommonEvent) | --- ## 8. 实现分期建议 | 阶段 | 范围 | 交付 | |------|------|------| | **M1** | 表结构 + 总部直存 + C 端展示 | Prisma 迁移、`StorePackage` CRUD(admin)、mini-user 展示 | | **M2** | 合伙人新建/编辑提审 + 总部审核 | `StorePackageChangeRequest`、partner 页面、admin 审核 | | **M3** | 门店端提审 | Shop API + h5-shop 页面 | | **M4** | 套餐异议工单类型 | `PACKAGE_DISPUTE` 枚举、C 端入口、admin 筛选 | --- ## 附录:REQ 映射 | REQ | 端 | 内容 | |-----|----|------| | REQ-P-027 | 合伙人 | 拓店套餐页 + 门店套餐列表/编辑/提审 | | REQ-S-021 | 门店 | 套餐列表/编辑/提审 | | REQ-H-025 | 总部 | 门店详情套餐 Tab 直存;套餐变更审核通过/驳回 | | REQ-U-027 | 用户 | 门店详情展示生效套餐;套餐异议申诉入口 |