docs: 更新商品模块API设计

This commit is contained in:
2026-01-23 14:23:50 +08:00
parent 3385674490
commit 9e571f13cf

View 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 | 类目 IDstring |
| parentId | 父级类目 ID |
| name | 类目名称 |
| sortOrder | 排序 |
| isEnabled | 是否启用 |
| availableScenes | 生效场景 |
| availableTimeRanges | 生效时段 |
| createdAt | 创建时间 |
### 9.2 ProductDtoSPU
| 字段 | 说明 |
| --- | --- |
| id | 商品 IDstring |
| 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 IDstring |
| 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. UserApiC 端用户)接口清单
- `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 是否强制门店确认。