Merge branch 'dev' of github.com:msumshk/TakeoutAPI into dev
This commit is contained in:
426
商品模块_API设计_v1.md
Normal file
426
商品模块_API设计_v1.md
Normal file
@@ -0,0 +1,426 @@
|
||||
# 商品模块 API 设计(v1)
|
||||
> 企业级/混合模式版
|
||||
|
||||
## 0. 目标与范围
|
||||
- 面向商业化外卖 SaaS,覆盖“总部主库 + 门店私库 + 多维度经营 + 可配置开关 + 可扩展集成”。
|
||||
- 本文仅讨论 API 设计与契约,不涉及实现细节。
|
||||
- 默认对齐项目现有约束:多租户、CQRS、统一响应、Snowflake ID、JWT/RBAC。
|
||||
|
||||
## 1. 通用规范
|
||||
### 1.1 路由与版本
|
||||
- AdminApi:`/api/admin/v1/...`
|
||||
- MiniApi:`/api/mini/v1/...`
|
||||
- UserApi:`/api/user/v1/...`
|
||||
|
||||
### 1.2 鉴权与租户
|
||||
- Header:`Authorization: Bearer {token}`
|
||||
- 租户:`X-Tenant-Id` 或 `X-Tenant-Code`(必填,除白名单路径)
|
||||
- 角色权限:AdminApi 必须绑定 `PermissionAuthorize` 权限码
|
||||
|
||||
### 1.3 响应格式
|
||||
统一使用 `ApiResponse<T>`,与现有 `Shared.Web` 约定一致。
|
||||
|
||||
### 1.4 ID 与并发
|
||||
- 所有 `long` 类型 ID 在 API 中 **序列化为 string**。
|
||||
- 更新类接口需携带 `rowVersion`(Base64)或 `If-Match` 以并发控制。
|
||||
|
||||
### 1.5 幂等与限流
|
||||
- 创建、批量变更、导入等写接口支持 `Idempotency-Key`。
|
||||
- 面向公网端启用限流策略,读接口优先缓存。
|
||||
|
||||
### 1.6 分页与排序
|
||||
统一参数:`page`、`pageSize`、`sortBy`、`sortOrder`(`asc|desc`)。
|
||||
|
||||
## 2. 产品原则(10 年外卖 SaaS 视角)
|
||||
- 品牌一致性优先:总部主库保证品牌统一,门店仅允许“可控范围内的微调”。
|
||||
- 经营灵活性必备:门店私有商品与局部覆盖是应对“城市、商圈、人力”的关键。
|
||||
- C 端效率优先:类目不超过 2 级,菜单渲染优先走聚合与缓存。
|
||||
- 扩展优先:渠道/场景/时段/三方同步都必须可开关,避免“为少数租户拖累成本”。
|
||||
|
||||
## 3. 功能域拆分(可开关)
|
||||
### 3.1 核心域
|
||||
- 公共商品库(Master Library,总部商品)
|
||||
- 门店私有库(Store Library,本地特色)
|
||||
- 引用/下发机制(Push & Pull)
|
||||
- 类目管理(2 级以内 + 时段可见)
|
||||
- 商品(Product/SPU)与规格 SKU
|
||||
- 场景/渠道/时段维度可见性
|
||||
- 计价与打包费策略
|
||||
- 库存视图与沽清(含每日重置)
|
||||
|
||||
### 3.2 可选域
|
||||
- 加料/口味(Addon/Modifier)
|
||||
- 套餐/组合(Bundle/N 选 M)
|
||||
- 称重计价与时价
|
||||
- 后厨生产(KDS/打印标签/台位)
|
||||
- 三方平台同步(美团/饿了么/抖音)
|
||||
- 审核流与定时上架
|
||||
- 多语言(I18n)
|
||||
- 评分/销量统计视图(Stats)
|
||||
|
||||
## 4. 核心架构:公共商品库 + 门店私有库 + Push/Pull
|
||||
### 4.1 公共商品库(Tenant/Master Library)
|
||||
- 定义:总部创建的标准化商品。
|
||||
- 作用:维护品牌统一形象(名称、图、描述、营养、后厨分类)。
|
||||
- 管控:总部可锁定核心字段,门店仅可引用,不可篡改。
|
||||
|
||||
### 4.2 门店私有库(Store Private Library)
|
||||
- 定义:门店为本地市场创建的特色商品。
|
||||
- 作用:一店一策(开业活动、地域限定等)。
|
||||
- 权限:仅本门店可见,总部可审计但默认不干预。
|
||||
|
||||
### 4.3 引用与下发(Push & Pull)
|
||||
- 总部推送(Push):支持“静默上架”或“待门店确认”。
|
||||
- 门店拉取(Pull):门店经理从公共库勾选引入到本店经营列表。
|
||||
- 门店引用后允许“局部覆盖”,但不破坏主库锁定字段。
|
||||
|
||||
### 4.4 混合视图标识
|
||||
- API 输出 `libraryType` + `masterProductId`,便于后台列表用标签区分来源。
|
||||
- `lockedFields` 返回总部锁定字段,避免门店误操作。
|
||||
|
||||
## 5. 维度管理:类目、场景、渠道与时段
|
||||
### 5.1 类目(2 级以内)
|
||||
- 类目支持“生效时段”,如早餐类目 10:00 后隐藏。
|
||||
- 类目可绑定“场景”,如堂食专属类目。
|
||||
|
||||
### 5.2 场景(履约场景)
|
||||
- 堂食(DineIn)、外卖(Delivery)、自提(Pickup)。
|
||||
- 外卖场景强制打包费规则;堂食可免打包费。
|
||||
|
||||
### 5.3 渠道(流量入口)
|
||||
- 微信小程序、POS 点餐、美团、饿了么、抖音等。
|
||||
- 支持“渠道隔离”:显示顺序、价格、上下架状态可独立配置。
|
||||
|
||||
### 5.4 维度优先级(建议)
|
||||
门店覆盖 > 渠道配置 > 时段配置 > 商品基础配置。
|
||||
|
||||
## 6. 核心业务规则
|
||||
### 6.1 覆盖机制(Override Rule)
|
||||
- 门店可对价格、场景、上架/沽清做覆盖。
|
||||
- 被锁定字段不允许覆盖;如需调整须总部解锁或走审核。
|
||||
|
||||
### 6.2 计价与打包费
|
||||
- 计价模式:固定单价、按克计价(称重菜)、时价(随行就市)。
|
||||
- 打包费支持按 SKU 设置,且可按场景配置(堂食可为 0)。
|
||||
- 打包费支持单单封顶(不超过 X 元)。
|
||||
|
||||
### 6.3 库存与自动重置
|
||||
- 支持门店级“每日自动恢复初始库存”(默认凌晨执行)。
|
||||
- 沽清为临时状态,不影响主库与其他门店。
|
||||
|
||||
### 6.4 规格、加料与套餐
|
||||
- SKU 影响价格与库存。
|
||||
- 加料支持“收费加料 + 免费属性”,可配置选配上限/下限。
|
||||
- 动态套餐支持“N 选 M”,并要求库存穿透:
|
||||
- 套餐内关键单品沽清时,套餐自动联动下架。
|
||||
|
||||
### 6.5 生产与后厨(KDS/打印)
|
||||
- 商品可绑定“打印标签 + 后厨台位”。
|
||||
- 订单下发需标示场景,以区分堂食/外卖/自提出餐逻辑。
|
||||
|
||||
### 6.6 三方平台同步
|
||||
- 内置 Mapping 机制,支持“商品—平台商品”映射。
|
||||
- 价格/沽清变动触发事件总线,异步同步到平台接口。
|
||||
|
||||
## 7. 权限与审计
|
||||
- 总部运营:管理主库、类目、全局规则、价格上限、审核流。
|
||||
- 门店经理:门店私有商品、门店覆盖、今日沽清。
|
||||
- 字段级审计日志:记录“谁在何时修改了哪个门店商品的价格/状态”。
|
||||
- 推送审计:记录主库变更的下发范围与结果。
|
||||
|
||||
## 8. 功能开关(租户级)
|
||||
用于“商业化套餐可选启用”。建议 AdminApi 提供读取能力,写入由套餐/配置管理模块控制。
|
||||
|
||||
示例结构:
|
||||
```json
|
||||
{
|
||||
"enableMasterLibrary": true,
|
||||
"enableStoreLibrary": true,
|
||||
"enablePushPull": true,
|
||||
"enableVariant": true,
|
||||
"enableAddon": true,
|
||||
"enableBundle": true,
|
||||
"enableSceneFilter": true,
|
||||
"enableChannelIsolation": true,
|
||||
"enableChannelPrice": true,
|
||||
"enableTimePrice": true,
|
||||
"enableStoreOverride": true,
|
||||
"enablePricingWeight": true,
|
||||
"enablePricingMarket": true,
|
||||
"enablePackagingFee": true,
|
||||
"enablePackagingFeeCap": true,
|
||||
"enableDailyStockReset": true,
|
||||
"enableInventory": true,
|
||||
"enableApproval": false,
|
||||
"enableScheduledPublish": true,
|
||||
"enableKds": true,
|
||||
"enableThirdPartySync": true,
|
||||
"enableMultiLanguage": false,
|
||||
"enableNutritionInfo": false
|
||||
}
|
||||
```
|
||||
|
||||
## 9. 关键 DTO(摘要)
|
||||
> 字段命名遵循现有规范,布尔值使用 `Is/Has` 前缀。
|
||||
|
||||
### 9.1 CategoryDto
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| id | 类目 ID(string) |
|
||||
| parentId | 父级类目 ID |
|
||||
| name | 类目名称 |
|
||||
| sortOrder | 排序 |
|
||||
| isEnabled | 是否启用 |
|
||||
| availableScenes | 生效场景 |
|
||||
| availableTimeRanges | 生效时段 |
|
||||
| createdAt | 创建时间 |
|
||||
|
||||
### 9.2 ProductDto(SPU)
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| id | 商品 ID(string) |
|
||||
| libraryType | Master/Store |
|
||||
| masterProductId | 引用的主库商品 ID |
|
||||
| storeId | 归属门店 |
|
||||
| name | 商品名称 |
|
||||
| categoryId | 类目 ID |
|
||||
| unit | 单位 |
|
||||
| tags | 标签 |
|
||||
| coverImageUrl | 封面图 |
|
||||
| imageUrls | 轮播图 |
|
||||
| isEnabled | 是否启用 |
|
||||
| isPublished | 是否上架 |
|
||||
| hasSku | 是否包含 SKU |
|
||||
| hasAddon | 是否包含加料 |
|
||||
| pricingMode | Fixed/Weight/Market |
|
||||
| basePrice | 基础价格 |
|
||||
| packagingFee | 打包费(基础) |
|
||||
| packagingFeeCap | 打包费封顶 |
|
||||
| availableScenes | 生效场景 |
|
||||
| availableChannels | 生效渠道 |
|
||||
| lockedFields | 被总部锁定字段 |
|
||||
| rowVersion | 并发字段 |
|
||||
|
||||
### 9.3 SkuDto
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| id | SKU ID(string) |
|
||||
| productId | 商品 ID |
|
||||
| specValues | 规格值列表 |
|
||||
| price | 价格 |
|
||||
| stock | 库存 |
|
||||
| isEnabled | 是否启用 |
|
||||
| rowVersion | 并发字段 |
|
||||
|
||||
### 9.4 StoreProductOverrideDto
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| storeId | 门店 ID |
|
||||
| productId | 商品 ID |
|
||||
| overridePrice | 覆盖价格 |
|
||||
| overrideScenes | 覆盖场景 |
|
||||
| isSoldOut | 是否沽清 |
|
||||
| isApproved | 是否已审核 |
|
||||
| overrideReason | 覆盖原因 |
|
||||
|
||||
### 9.5 AddonGroupDto(可选)
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| id | 组 ID |
|
||||
| name | 组名 |
|
||||
| minSelected | 最少选择 |
|
||||
| maxSelected | 最多选择 |
|
||||
| isRequired | 是否必选 |
|
||||
| items | 加料项列表 |
|
||||
|
||||
### 9.6 ChannelSettingDto(可选)
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| channelCode | 渠道编码 |
|
||||
| price | 渠道价格 |
|
||||
| sortOrder | 渠道排序 |
|
||||
| isEnabled | 是否启用 |
|
||||
|
||||
### 9.7 ProductionProfileDto(可选)
|
||||
| 字段 | 说明 |
|
||||
| --- | --- |
|
||||
| kitchenStationId | 后厨台位 |
|
||||
| printTagId | 打印标签 |
|
||||
|
||||
## 10. AdminApi(管理端)接口清单
|
||||
### 10.1 公共商品库(总部)
|
||||
- `GET /api/admin/v1/master-products`
|
||||
- `GET /api/admin/v1/master-products/{id}`
|
||||
- `POST /api/admin/v1/master-products`
|
||||
- `PUT /api/admin/v1/master-products/{id}`
|
||||
- `PUT /api/admin/v1/master-products/{id}/lock-fields`
|
||||
- `PUT /api/admin/v1/master-products/{id}/publish`
|
||||
- `PUT /api/admin/v1/master-products/{id}/unpublish`
|
||||
|
||||
### 10.2 门店私有库与经营商品
|
||||
- `GET /api/admin/v1/stores/{storeId}/products`
|
||||
- `POST /api/admin/v1/stores/{storeId}/products`(创建门店私有商品)
|
||||
- `POST /api/admin/v1/stores/{storeId}/products/pull`(从主库拉取)
|
||||
- `PUT /api/admin/v1/stores/{storeId}/products/{id}`
|
||||
- `PUT /api/admin/v1/stores/{storeId}/products/{id}/override`
|
||||
- `PUT /api/admin/v1/stores/{storeId}/products/{id}/publish`
|
||||
- `PUT /api/admin/v1/stores/{storeId}/products/{id}/unpublish`
|
||||
- `PUT /api/admin/v1/stores/{storeId}/products/{id}/sold-out`
|
||||
|
||||
### 10.3 总部推送(Push)
|
||||
- `POST /api/admin/v1/master-products/{id}/push`(指定门店)
|
||||
- `GET /api/admin/v1/master-products/{id}/push-tasks`
|
||||
- `POST /api/admin/v1/push-tasks/{taskId}/retry`
|
||||
|
||||
### 10.4 类目
|
||||
- `GET /api/admin/v1/categories`
|
||||
- `POST /api/admin/v1/categories`
|
||||
- `PUT /api/admin/v1/categories/{id}`
|
||||
- `DELETE /api/admin/v1/categories/{id}`
|
||||
- `PUT /api/admin/v1/categories/{id}/enable`
|
||||
- `PUT /api/admin/v1/categories/{id}/disable`
|
||||
- `PUT /api/admin/v1/categories/sort`(批量排序)
|
||||
- `PUT /api/admin/v1/categories/{id}/schedule`(类目时段)
|
||||
- `PUT /api/admin/v1/categories/{id}/scenes`(类目场景)
|
||||
|
||||
### 10.5 规格与 SKU
|
||||
- `GET /api/admin/v1/products/{id}/spec-groups`
|
||||
- `PUT /api/admin/v1/products/{id}/spec-groups`
|
||||
- `GET /api/admin/v1/products/{id}/skus`
|
||||
- `POST /api/admin/v1/products/{id}/skus`
|
||||
- `PUT /api/admin/v1/skus/{id}`
|
||||
- `PUT /api/admin/v1/skus/{id}/enable`
|
||||
- `PUT /api/admin/v1/skus/{id}/disable`
|
||||
- `PUT /api/admin/v1/skus/{id}/price`
|
||||
- `PUT /api/admin/v1/skus/{id}/stock`
|
||||
- `PUT /api/admin/v1/skus/{id}/pricing-mode`
|
||||
- `PUT /api/admin/v1/skus/{id}/packaging-fee`
|
||||
- `PUT /api/admin/v1/skus/{id}/inventory-policy`
|
||||
|
||||
### 10.6 场景/渠道/时段
|
||||
- `PUT /api/admin/v1/products/{id}/scenes`
|
||||
- `PUT /api/admin/v1/products/{id}/channels`
|
||||
- `PUT /api/admin/v1/products/{id}/time-slots`
|
||||
- `PUT /api/admin/v1/products/{id}/channel-mappings`
|
||||
- `POST /api/admin/v1/products/{id}/channel-sync`
|
||||
|
||||
### 10.7 加料/口味(可选)
|
||||
- `GET /api/admin/v1/products/{id}/addon-groups`
|
||||
- `PUT /api/admin/v1/products/{id}/addon-groups`
|
||||
- `PUT /api/admin/v1/addon-groups/{id}/items`
|
||||
- `PUT /api/admin/v1/addon-groups/{id}/enable`
|
||||
- `PUT /api/admin/v1/addon-groups/{id}/disable`
|
||||
|
||||
### 10.8 套餐/组合(可选)
|
||||
- `GET /api/admin/v1/bundles`
|
||||
- `POST /api/admin/v1/bundles`
|
||||
- `PUT /api/admin/v1/bundles/{id}`
|
||||
- `PUT /api/admin/v1/bundles/{id}/publish`
|
||||
- `PUT /api/admin/v1/bundles/{id}/unpublish`
|
||||
- `PUT /api/admin/v1/bundles/{id}/items`
|
||||
- `PUT /api/admin/v1/bundles/{id}/rules`(N 选 M)
|
||||
|
||||
### 10.9 后厨生产(可选)
|
||||
- `PUT /api/admin/v1/products/{id}/production-profile`
|
||||
- `PUT /api/admin/v1/skus/{id}/production-profile`
|
||||
|
||||
### 10.10 导入导出与索引
|
||||
- `POST /api/admin/v1/products/import`
|
||||
- `GET /api/admin/v1/products/import/{taskId}`
|
||||
- `GET /api/admin/v1/products/export`
|
||||
- `POST /api/admin/v1/products/reindex`
|
||||
|
||||
### 10.11 审计与日志
|
||||
- `GET /api/admin/v1/products/{id}/audit-logs`
|
||||
- `GET /api/admin/v1/stores/{storeId}/products/{id}/override-logs`
|
||||
- `GET /api/admin/v1/master-products/{id}/push-logs`
|
||||
|
||||
### 10.12 功能开关读取
|
||||
- `GET /api/admin/v1/products/features`
|
||||
|
||||
## 11. MiniApi(小程序端)接口清单
|
||||
- `GET /api/mini/v1/categories?scene=Delivery&channel=WeChatMiniProgram`
|
||||
- `GET /api/mini/v1/menus/{storeId}?scene=Delivery&channel=WeChatMiniProgram`
|
||||
- `GET /api/mini/v1/products?storeId=...&scene=Delivery&channel=WeChatMiniProgram`
|
||||
- `GET /api/mini/v1/products/{id}?scene=Delivery&channel=WeChatMiniProgram`
|
||||
- `GET /api/mini/v1/products/hot?storeId=...`
|
||||
- `GET /api/mini/v1/products/recommended?storeId=...`
|
||||
- `POST /api/mini/v1/products/price-estimate`
|
||||
- `POST /api/mini/v1/products/checkout-validate`
|
||||
- `POST /api/mini/v1/products/snapshots`(订单服务调用)
|
||||
|
||||
## 12. UserApi(C 端用户)接口清单
|
||||
- `GET /api/user/v1/categories?scene=Delivery&channel=H5`
|
||||
- `GET /api/user/v1/menus/{storeId}?scene=Delivery&channel=H5`
|
||||
- `GET /api/user/v1/products?storeId=...&scene=Delivery&channel=H5`
|
||||
- `GET /api/user/v1/products/{id}?scene=Delivery&channel=H5`
|
||||
- `GET /api/user/v1/products/hot?storeId=...`
|
||||
- `GET /api/user/v1/products/recommended?storeId=...`
|
||||
- `POST /api/user/v1/products/price-estimate`
|
||||
- `POST /api/user/v1/products/checkout-validate`
|
||||
|
||||
## 13. 事件与扩展点
|
||||
采用 Outbox 模式输出领域事件,便于搜索索引、缓存失效、推荐计算与三方同步。
|
||||
- `MasterProductCreated`
|
||||
- `MasterProductUpdated`
|
||||
- `MasterProductPushed`
|
||||
- `StoreProductPulled`
|
||||
- `StoreProductOverridden`
|
||||
- `ProductPriceChanged`
|
||||
- `ProductAvailabilityChanged`
|
||||
- `ProductSoldOutChanged`
|
||||
- `SkuStockChanged`
|
||||
- `ProductChannelSyncRequested`
|
||||
|
||||
## 14. 示例(关键请求)
|
||||
### 14.1 商品创建(总部主库)
|
||||
```json
|
||||
{
|
||||
"name": "黄金鸡排饭",
|
||||
"categoryId": "1782328933492367360",
|
||||
"unit": "份",
|
||||
"coverImageUrl": "https://cdn/xxx.jpg",
|
||||
"imageUrls": ["https://cdn/xxx1.jpg", "https://cdn/xxx2.jpg"],
|
||||
"pricingMode": "Fixed",
|
||||
"basePrice": 19.9,
|
||||
"isEnabled": true
|
||||
}
|
||||
```
|
||||
|
||||
### 14.2 门店覆盖(价格 + 场景)
|
||||
```json
|
||||
{
|
||||
"overridePrice": 21.9,
|
||||
"overrideScenes": ["Delivery", "Pickup"],
|
||||
"isSoldOut": false,
|
||||
"overrideReason": "外卖平台佣金调整"
|
||||
}
|
||||
```
|
||||
|
||||
### 14.3 结算校验(Mini/User)
|
||||
```json
|
||||
{
|
||||
"storeId": "1782328933492367000",
|
||||
"scene": "Delivery",
|
||||
"channel": "WeChatMiniProgram",
|
||||
"items": [
|
||||
{
|
||||
"productId": "1782328933492367360",
|
||||
"skuId": "1782328933492367400",
|
||||
"quantity": 2,
|
||||
"addonItemIds": ["1782328933492367501", "1782328933492367502"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 15. 依赖说明
|
||||
- 文件上传:复用 Storage 模块(FilesController)获取 URL。
|
||||
- 库存:优先对接 Inventory 模块,商品侧仅提供视图与校验。
|
||||
- 订单:下单时生成商品快照,避免历史价格漂移。
|
||||
- 后厨:KDS/打印由生产模块承接,商品仅配置绑定信息。
|
||||
- 三方同步:由集成服务监听事件并进行异步同步与重试。
|
||||
- 权限码:`product.read`、`product.write`、`product.publish`、`product.import` 等(待统一权限表配置)。
|
||||
|
||||
---
|
||||
**待确认**:渠道编码标准、称重计价精度与四舍五入规则、库存每日重置默认时间、Push 是否强制门店确认。
|
||||
Reference in New Issue
Block a user