feat(store): 门店套餐 v3.4.10 全端实现

新增 StorePackage 与变更审核流,覆盖总部直存、合伙/门店提审、C 端展示与套餐异议工单;同步 PRD/开发文档与 REQ 索引。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-03 19:30:59 +08:00
parent 7e3dc131ad
commit 3c19b7dd97
35 changed files with 2128 additions and 28 deletions
@@ -0,0 +1,372 @@
# 杜康好客 · 门店套餐功能开发文档
> **版本**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 | 门店 IDFK → `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` CRUDadmin)、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 | 用户 | 门店详情展示生效套餐;套餐异议申诉入口 |