# 商品模块 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`,与现有 `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 是否强制门店确认。