front/docs/oms系统设计-050801.md
2026-06-04 19:15:28 +08:00

6137 lines
204 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# OMS 业务系统 — 表结构与接口设计
## 1 导读
### 1.1 这份文档解决什么问题?
1. **订单**:创建、预报、支付、状态机、费用与版本对齐、模板/批量任务等。
2. **渠道**:销售产品、服务渠道、计价与发布、计重、分区、自动触发、标签等。
3. **操作**:入库、出库、面单打印等仓储作业。
4. **充值**:充值申请、审核及入账(**客户账户与余额权威以权限/客户域实现为准**)。
5. **系统主数据**:国家、字典、仓库地址、通知等 **`/system/*`** 能力。
### 1.2 文档定位
**OMS** 为 **业务系统**;认证、RBAC、内部用户、客户主数据等由 **`docs/rbac系统设计-050901.md`** 独立设计与迭代。业务侧以 **`CustomerId`(主客户)** 表达订单/充值等归属;与令牌主体(**`principalType` / `principalId`** 及子账号场景下的主客户标识)的对齐见 **1.4**。**本篇不展开客户域、用户域表与接口**。
### 1.3 业务模块一览
| 模块 | 章 | 说明 |
| --- | --- | --- |
| **订单** | **4** | 全生命周期、查价、模板与批量任务 |
| **渠道** | **5** | 销售产品、服务渠道、计价、计重、分区、标签等 |
| **操作** | **6** | 入库、出库、打印 |
| **充值** | **7** | 充值申请、审核、入账 |
| **系统主数据** | **8** | 国家、字典、仓库、通知(`/system/*`) |
### 1.4 与客户 / 用户数据的边界
**客户主数据、子账号、内部用户、认证与 RBAC** 均在 **`docs/rbac系统设计-050901.md`**;**本篇不写**上述领域的需求变更与表结构。
OMS 业务表中的 **`CustomerId`** 等外键表示业务归属,**口径上等于主客户** **`t_customer.Id`**。**子账号** 登录时,令牌与 **`get-current-user`** 等响应中的 **主客户标识**(如 **`main_customer_id` / `mainCustomerId`**)见 **`docs/rbac系统设计-050901.md`**;OMS 侧订单/充值等 **`CustomerId`** 须与该主客户对齐,**不得**把子账号业务 Id 误当主客户 Id。**存在性、状态、余额、子账号归主**等仍以 **权限/客户域服务** 与联调约定为准,**不得**仅凭请求体 Id 绕过鉴权。
**不在本篇设计的内容**:统一认证、RBAC、内部用户、客户主数据与子账号等 —— 见 **`docs/rbac系统设计-050901.md`**。
---
## 2 业务设计思路与主流程
### 2.1 职责边界:OMS 与 RBAC / 客户域
| 侧 | 职责 |
| --- | --- |
| **OMS(本文)** | 订单全生命周期、渠道配置与算价支撑、仓内作业、充值业务流水、`/system/*` 业务主数据(国家、字典、仓库地址、通知等)。 |
| **RBAC / 客户域(另文)** | 登录与令牌、内部用户、客户主数据与子账号、权限码、**账户余额的权威读写**、与客户/员工相关的字典语义。 |
| **协作约定** | OMS 用 **`CustomerId`** 表达归属;**客户是否存在、是否可用、子账号归主、余额是否够付** 等一律以 **客户域 / RBAC 服务** 为准,**禁止**仅凭请求体 Id 当作已鉴权事实。 |
### 2.2 主流程 A:配置发布 → 运行时算价(渠道支撑订单)
**设计意图**:高并发查价/下单不走「每次打 DB 拉全量规则」;以 **版本化快照 + Redis + 计费实例内存** 为主路径,DB 发布表为审计与降级。
1. 运营维护销售产品、卖价/买价、分区(KpiArea)、计重、排货、服务渠道等。
2. **发布** 产生 **不可变版本**;结构化快照写入 **Redis**,计费服务加载 **本地内存**。
3. **查价与正式下单共用**:按绑定时刻(如 `bindTime`)选取应对生效的 **rules/price 版本** → 规则匹配(分区、计重、黑名单等)→ 出费用明细;查价响应宜带 **`rulesVersion` / `priceVersion`** 便于与订单对齐。
### 2.3 主流程 B:订单(客户侧业务链)
**设计意图**:**先锁「计费契约版本」、再按履约阶段分批落费**;历史订单不因后台新发版被静默改价。
1. **查价**(可选)→ **创建 / 预报** → 校验 → 草稿与包裹 → **预报阶段**将 `ChargingTime = forecast`(或未配置视为 forecast)的费用项写入 **`t_order_fee`**。
2. **契约固化**:预报等关键节点写入 **`SalesProductRulesVersion`、`SalesProductPriceVersion`**(及渠道侧应付相关版本,若有);非显式「改单重算」流程 **不得** 因后台新发布而改写已存版本号。
3. **状态机**(概念顺序;枚举见 **`## 10 附录` → `### 10.3`**):如 草稿 → 已预报 → 已收货 → 待支付 → 待发货 → 运输中 → 派送成功/失败;另有拦截、问题件、删除等分支。
4. **支付**:OMS 侧订单状态与支付动作对接;**扣减余额 / 支付渠道** 的权威在 **客户域**(及支付集成约定)。
5. **分阶段计费**:卖价/买价主表 **`ChargingTime`** 决定该项费用 **何时** 计入 `t_order_fee`:**forecast(预报) / inbound(入库) / outbound(出库)**。每一阶段只计算本阶段命中的项;且算价时仍读取 **该订单已绑定版本** 的快照,**不**改用当前全局「最新发布」。
6. **批量下单**:Excel/模板 或 JSON 批量接口 → 异步任务;**单次最多受理 2000 条**;**单票**仍遵守版本选择与 `ChargingTime` 规则。
### 2.4 主流程 C:操作(仓库)
1. **入库扫描**:实重、尺寸等落库;可触发 **`inbound` 阶段**计费(配置为入库计费的项)。
2. **出库扫描**:可触发 **`outbound` 阶段**计费。
3. **标签打印**:按单号与模板输出,记录打印结果。
### 2.5 主流程 D:充值
1. 客户提交 → **`t_recharge_record` 待审核**。
2. 审核通过/驳回;**通过时增加余额** 由 **客户域** 执行;OMS 保留流水与审核状态,**不是**余额账本权威。
### 2.6 主流程 E:系统主数据
国家、字典、仓库地址、通知供全业务引用;**与客户/内部员工相关的 DictType** 定义以 **`docs/rbac系统设计-050901.md`** 为准。
### 2.7 设计不变量(推断与实现时须一致)
| 不变量 | 说明 |
| --- | --- |
| 版本锁定 | 订单一旦写入规则/卖价版本字段,后续后台发版 **不自动** 推翻该票货的计费契约。 |
| 版本与落费时机解耦 | **版本**在预报等节点锁定;**何时写入 `t_order_fee`** 由 `ChargingTime` 分阶段执行。 |
| 后阶段仍用旧版快照 | 入库/出库触发的算价,加载的是 **该订单已绑定 version** 的快照,不是「当前最新 version」。 |
| 客户与余额 | 校验客户、余额、子账号关系 → **客户域**;OMS 持 `CustomerId` 与业务流程。 |
| 审计字段 | 写操作落 `Create*`/`Update*`;操作者来自 **登录会话**,见 **`## 3 公共规范` → `### 3.2`**。 |
### 2.8 本文档章节索引(从流程跳转到细节)
| 上述块 | 正文位置 |
| --- | --- |
| 文档定位、模块一览、权限边界 | **`## 1 导读` → `### 1.3`~`### 1.5`** |
| 订单、查价、费用、版本策略 | **`## 4 订单模块`**(尤其 **`### 4.3` 下单计费与版本策略**) |
| 产品/渠道/发布/分区/计重/标签 | **`## 5 渠道模块`** |
| 入库/出库/打印 | **`## 6 操作模块`** |
| 充值 | **`## 7 充值管理`** |
| 国家/字典/仓库/通知 | **`## 8 系统主数据`** |
| 需求迭代与历史差异 | **`## 9 需求迭代与变更记录`** |
| 枚举与路径速查 | **`## 10 附录`** |
---
## 3 公共规范
### 3.1 表结构设计公共规范
#### 3.1.1 表的字段命名规范
| 规范 | 说明 | 示例 |
| --- | --- | --- |
| 驼峰命名 | 字段名开头大写+单词驼峰 | `ServiceProviderId` |
| 单数名词 | 表名用单数,表示实体 | `order` 而非 `orders` |
| 业务前缀 | 表名按业务模块加前缀 | `t_order`、`t_channel` |
#### 3.1.2 必备字段(所有表)
| 字段名 | 字段类型 | 说明 |
| --- | --- | --- |
| Id | BIGINT | 主键,自增,步长 1 |
| IsDeleted | TINYINT | 软删除:0-否,1-是,默认 0 |
| CreateUserId | INT | 创建人 |
| CreateTime | DATETIME | 创建时间 |
| UpdateUserId | INT | 更新人 |
| UpdateTime | DATETIME | 更新时间 |
#### 3.1.3 字段命名规范
| 规范 | 说明 | 示例 |
| --- | --- | --- |
| 布尔字段 | 用 `IsXxx` 或 `HasXxx` | `IsDeleted`、`IsActive` |
| 外键字段 | `关联表名Id` 或 `XxxId` | `CustomerId`、`WarehouseId` |
| 时间字段 | `XxxTime` 或 `XxxAt` | `CreateTime`、`PayTime` |
| 禁用保留字 | 不用 desc、range、match 等 | 用 `Description` 替代 `Desc` |
#### 3.1.4 数据类型规范
| 类型 | 说明 |
| --- | --- |
| 主键 | BIGINT,单表自增 |
| 金额/重量 | DECIMAL,禁止 float/double |
| 字符串 | varchar 长度 ≤ 5000,超出用 TEXT |
| 枚举 | 小数据量用 TINYINT,可读性要求高用 VARCHAR |
| 状态标志 | TINYINT |
### 3.2 接口设计公共规范
| 规范 | 说明 |
| --- | --- |
| 操作审计必填 | 任何新增、编辑、删除操作,必须记录操作者与操作时间 |
| 新增接口 | 创建成功后需落库 `CreateUserId`、`CreateTime` |
| 编辑接口 | 更新成功后需落库 `UpdateUserId`、`UpdateTime` |
| 删除接口 | 逻辑删除时需落库 `UpdateUserId`、`UpdateTime`,并更新 `IsDeleted=1` |
> 审计字段中的操作者信息优先从当前登录会话获取,不允许由前端直接信任覆盖。
---
## 4 订单模块
### 4.1 核心需求说明
- **我的订单**:支持订单全流程管理,包括分页查询、详情查看、支付、预报、拦截/取消拦截、删除、打印、导出;订单状态覆盖草稿→已预报→已收货→待支付→待发货→运输中→派送成功/失败等全生命周期。
- **我要查价**:根据目的国、运输方式、重量尺寸、货物类型等条件查价,返回可选渠道及费用明细;与正式下单共用「选版本 + 内存匹配」计费逻辑,响应宜携带 `rulesVersion` / `priceVersion` 便于与订单对齐。
- **我要下单**:支持下载下单模板、上传 Excel 批量下单、查看任务进度与详情;模板分为标准模板与自定义模板。**单次导入订单上限 2000 条**(Excel 有效数据行或 `/order/parcel/batch-create` 请求数组长度,超出拒绝)。
### 4.2 表结构设计
> **命名约定**:表名按业务模块加 t_ 前缀;字段名驼峰命名(开头大写+单词驼峰)。
#### 4.2.1 订单表 `t_order`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| ServiceNo | VARCHAR | 64 | - | 是 | 运单号 |
| ClientNo | VARCHAR | 64 | - | 否 | 客户单号 |
| TrackNo | VARCHAR | 64 | - | 否 | 跟踪号 |
| ProductCode | VARCHAR | 128 | - | 否 | 产品code |
| ChannelCode | VARCHAR | 128 | - | 否 | 渠道code |
| ConsigneeCountry | VARCHAR | 8 | - | 否 | 国家二字码 |
| CargoAttribute | VARCHAR | 32 | - | 否 | 货物属性:general-普货,electronics-3c&带电,sensitive-敏感货 |
| OrderStatus | VARCHAR | 32 | - | 是 | 订单状态 |
| WarehouseId | BIGINT | - | 外键 | 否 | 仓库ID |
| CustomerId | BIGINT | - | 外键 | 否 | 客户ID |
| SenderId | BIGINT | - | 外键 | 否 | 发件人ID → t_order_sender |
| RecipientId | BIGINT | - | 外键 | 否 | 收件人ID → t_order_recipient |
| DeclarationId | BIGINT | - | 外键 | 否 | 申报信息ID → t_order_declaration |
| ExportCustomsMode | VARCHAR | 16 | - | 否 | 出口报关方式,见 ExportCustomsMode 枚举 |
| ReceiveTime | DATETIME | - | - | 否 | 收货时间 |
| ShipTime | DATETIME | - | - | 否 | 发货时间 |
| PayTime | DATETIME | - | - | 否 | 支付时间 |
| SignTime | DATETIME | - | - | 否 | 签收时间 |
| IsCod | TINYINT | - | - | 否 | 是否COD:0-否,1-是 |
| CodCurrency | VARCHAR | 16 | - | 否 | COD币种 |
| CodSum | DECIMAL | 18,4 | - | 否 | COD金额 |
| IsIntercept | TINYINT | - | - | 否 | 是否拦截:0-否,1-是 |
| IsRemote | TINYINT | - | - | 否 | 是否偏远:0-否,1-是 |
| PredeclareFailReason | VARCHAR | 512 | - | 否 | 预报失败原因:草稿已落库后环节失败时记录;黑名单命中、快照未命中等不落库场景不写此字段 |
| SalesProductRulesVersion | VARCHAR | 32 | - | 否 | 下单绑定的规则版本号(与发布记录 `Version` 一致,`OperatorType=rules`) |
| SalesProductPriceVersion | VARCHAR | 32 | - | 否 | 下单绑定的价格版本号(与发布记录 `Version` 一致,`OperatorType=price`) |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
**OrderStatus 枚举**:draft, predeclared, received, pendingPayment, pendingShip, pendingOnline, shipping, deliverySuccess, deliveryFailed, problematic, deleted, claimed
**CargoAttribute 枚举**:general-普货,electronics-3c&带电,sensitive-敏感货
**ExportCustomsMode 枚举**(存库与下列 code 一致):`9610Simple`(9610简易申报),`9610Summary`(9610汇总申报),`9610Separate`(9610单独申报),`9710`,`9810`,`0110`
#### 4.2.2 下单模板表 `t_order_template`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| Name | VARCHAR | 64 | - | 是 | 模板名称 |
| Type | VARCHAR | 16 | - | 是 | standard/custom |
| TemplateKey | VARCHAR | 64 | - | 否 | 模板标识 |
| CustomerId | BIGINT | - | 外键 | 否 | 客户ID(自定义模板) |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 4.2.3 下单任务表 `t_order_task`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| TaskName | VARCHAR | 128 | - | 是 | 任务名称 |
| Quantity | INT | - | - | 否 | 数量 |
| Status | VARCHAR | 16 | - | 是 | 处理中/已完成/待处理 |
| TemplateId | BIGINT | - | 外键 | 否 | 模板ID |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
| CustomerId | BIGINT | - | 外键 | 否 | 客户ID |
#### 4.2.4 订单发件人表 `t_order_sender`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| OrderId | BIGINT | - | 外键 | 是 | 订单ID |
| SenderName | VARCHAR | 50 | - | 否 | 姓名 |
| Company | VARCHAR | 255 | - | 否 | 公司 |
| Country | INT | - | - | 否 | 国家 |
| Province | VARCHAR | 50 | - | 否 | 州省 |
| City | VARCHAR | 50 | - | 否 | 城市 |
| ZipCode | VARCHAR | 50 | - | 否 | 邮编 |
| StreetAddress1 | VARCHAR | 500 | - | 否 | 地址1 |
| StreetAddress2 | VARCHAR | 500 | - | 否 | 地址2 |
| StreetAddress3 | VARCHAR | 500 | - | 否 | 地址3 |
| HouseNumber | VARCHAR | 50 | - | 否 | 门牌号 |
| Phone | VARCHAR | 50 | - | 否 | 手机 |
| TelePhone | VARCHAR | 50 | - | 否 | 电话 |
| Email | VARCHAR | 60 | - | 否 | 邮箱 |
| IsDefault | TINYINT | - | - | 否 | 是否默认:0-否,1-是 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 4.2.5 订单收件人表 `t_order_recipient`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| OrderId | BIGINT | - | 外键 | 是 | 订单ID |
| RecipientsName | VARCHAR | 30 | - | 否 | 收件人名称 |
| CompanyName | VARCHAR | 60 | - | 否 | 收件人公司名称 |
| Country | INT | - | - | 否 | 国家 |
| Phone | VARCHAR | 20 | - | 否 | 收件人手机 |
| TelePhone | VARCHAR | 20 | - | 否 | 收件人电话 |
| ZipCode | VARCHAR | 15 | - | 否 | 收件人邮编 |
| Province | VARCHAR | 25 | - | 否 | 收件人省/州 |
| City | VARCHAR | 25 | - | 否 | 收件人城市 |
| StreetNumber | VARCHAR | 20 | - | 否 | 收件人门牌号 |
| StreetAddress1 | VARCHAR | 255 | - | 否 | 收件人地址1 |
| StreetAddress2 | VARCHAR | 255 | - | 否 | 收件人地址2 |
| StreetAddress3 | VARCHAR | 255 | - | 否 | 收件人地址3 |
| Email | VARCHAR | 60 | - | 否 | 收件人邮箱 |
| TaxType | INT | - | - | 否 | 税号类型 |
| TaxNo | VARCHAR | 35 | - | 否 | 税号 |
| CertificateType | INT | - | - | 否 | 证件类型 |
| CertificateNo | VARCHAR | 30 | - | 否 | 证件号码 |
| Eori | VARCHAR | 255 | - | 否 | EORI |
| Vat | VARCHAR | 255 | - | 否 | Vat |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 4.2.6 订单申报信息表 `t_order_declaration`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| OrderId | BIGINT | - | 外键 | 是 | 订单ID |
| CnName | VARCHAR | 500 | - | 否 | 品名--中 |
| EnName | VARCHAR | 500 | - | 否 | 品名--英 |
| Sku | VARCHAR | 255 | - | 否 | SKU |
| Unit | VARCHAR | 5 | - | 否 | 单位 |
| UnitPrice | DECIMAL | 10,2 | - | 否 | 单价 |
| UnitWeight | DECIMAL | 11,3 | - | 否 | 单重 |
| Quantity | INT | - | - | 否 | 数量 |
| Amount | DECIMAL | 11,2 | - | 否 | 总金额 |
| Weight | DECIMAL | 11,3 | - | 否 | 总重量 |
| HsCode | VARCHAR | 50 | - | 否 | HsCode |
| SkuImage | VARCHAR | 255 | - | 否 | 图片 |
| Category | VARCHAR | 20 | - | 否 | 类别 |
| Brand | VARCHAR | 50 | - | 否 | 品牌 |
| BrandType | VARCHAR | 50 | - | 否 | 品牌类型 |
| IsMagnetic | BIT | - | - | 否 | 是否带磁带电 |
| Distribution | VARCHAR | 255 | - | 否 | 配货信息 |
| Material | VARCHAR | 50 | - | 否 | 材质 |
| EnMaterial | VARCHAR | 64 | - | 否 | 英文材质 |
| Purpose | VARCHAR | 50 | - | 否 | 用途 |
| Attributes | VARCHAR | 50 | - | 否 | 属性 |
| OriginCountry | VARCHAR | 50 | - | 否 | 原产国 |
| SalesLink | VARCHAR | 255 | - | 否 | 销售链接 |
| Model | VARCHAR | 64 | - | 否 | 型号 |
| Specifications | VARCHAR | 50 | - | 否 | 规格 |
| Comment | VARCHAR | 500 | - | 否 | 备注 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 4.2.7 订单件重尺表 `t_order_weight`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| OrderId | BIGINT | - | 外键 | 是 | 订单ID |
| Type | VARCHAR | 16 | - | 是 | 类型:forecast/receive/ship/downstream |
| Length | DECIMAL | 10,2 | - | 否 | 长(cm) |
| Width | DECIMAL | 10,2 | - | 否 | 宽(cm) |
| Height | DECIMAL | 10,2 | - | 否 | 高(cm) |
| ActualWeight | DECIMAL | 10,3 | - | 否 | 实重(kg) |
| VolumetricWeight | DECIMAL | 10,3 | - | 否 | 体积重(kg) |
| ChargeableWeight | DECIMAL | 10,3 | - | 否 | 计费重(kg) |
| Cubic | DECIMAL | 12,6 | - | 否 | 立方(m³) |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
**Type 枚举**:forecast(预报)、receive(收货)、ship(发货)、downstream(下游)
**履约更新**:**入库**后须用收货测量 **覆盖并重算** `receive` 行,并 **默认**将 **`receive` 行当前件重尺与计重结果**(`Length`/`Width`/`Height`/`ActualWeight`/`Cubic`/`VolumetricWeight`/`ChargeableWeight`)**同步覆盖** **`ship` 与 `downstream`**(§6.3.1.1、§4.3.4)。**出库**后须 **覆盖并重算** `ship` 行,并 **默认**将 **`ship` 行上述字段同步覆盖 `downstream`**(§6.3.2.1、§4.3.4)。`forecast` 一般保留下单预报口径;若渠道/下游回传与默认快照不一致,可再 **单独覆盖 `downstream`**。
#### 4.2.8 订单费用表 `t_order_fee`(子表,关联 t_order)
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| OrderId | BIGINT | - | 外键 | 是 | 订单ID |
| Type | VARCHAR | 16 | - | 是 | 类型:receivable(应收)/payable(应付) |
| CostItem | VARCHAR | 64 | - | 是 | 费用项 |
| Amount | DECIMAL | 18,4 | - | 是 | 金额 |
| Currency | VARCHAR | 16 | - | 是 | 币种:RMB/USD/EUR |
| DownstreamProvider | VARCHAR | 128 | - | 否 | 下游服务商 |
| PriceNo | VARCHAR | 64 | - | 否 | 价格编号(建议写入订单绑定的 `SalesProductPriceVersion` 或渠道侧价格版本) |
| PriceEffectiveTime | DATETIME | - | - | 否 | 价格版本生效时间(对应该次计费所用快照的生效时刻) |
| ChargingStage | VARCHAR | 16 | - | 否 | 本行费用是在哪一阶段计入:`forecast`(预报)/`inbound`(入库)/`outbound`(出库),与卖价/买价项 `ChargingTime` 一致,便于对账 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
**Type 枚举**:receivable(应收)、payable(应付)
### 4.3 下单计费与版本策略(配置 → 运行时 → 下单)
> 本节约定**销售产品**维度的规则/卖价如何进入线上算价链路,以及与 `t_order` 版本字段的对应关系;与渠道模块中「运营发布 → 生成版本」接口衔接。
#### 4.3.1 总体流程
```text
运营新建版本 → 录入/维护规则与卖价 → 发布并设为生效
↓
系统组装结构化 JSON(规则快照 + 价格快照,均绑定 salesProductId 与 version)
↓
写入 Redis(按产品/版本可寻址)→ 计费服务实例订阅或定时拉取 → 载入本地内存(热数据)
↓
用户下单 / 预估(高 QPS)
↓
内存匹配:按 bindTime 选定生效版本 → 规则(黑名单/偏远/计重/进位/价格分区等)→ 价格明细 → 输出费用
↓
订单落库:固化 SalesProductRulesVersion、SalesProductPriceVersion(必要时可另存发布记录 Id 便于追溯,非本表字段)
↓
后台上新发布、调价 → 仅刷新 Redis 与各实例内存中的「当前生效」视图
↓
新订单:bindTime 落在 new version 生效之后 → 绑定新版本算价
老订单:已写入的版本号不变 → 复核/对账/理赔仍以当时快照为准,不因后台最新发布而改历史费用
```
#### 4.3.2 设计要点
| 要点 | 说明 |
| --- | --- |
| 版本不可变 | 订单创建/预报成功时写入 `SalesProductRulesVersion`、`SalesProductPriceVersion`,作为该票货的**计费契约**;非「改单重算」类流程不得静默改写。 |
| 生效与历史 | 同一 `salesProductId` 在同一时刻仅一条「对外生效」规则+价格组合;历史 version 保留在 **Redis(多版本 key)** 与 **`t_sales_product_publish_record`**,便于审计与回放。 |
| Redis | 建议按 `salesProductId` + `version` 存完整结构化快照 JSON;可选维护 `current` 指针 key 指向当前生效 version,便于实例 O(1) 发现最新配置。 |
| 本地内存 | 实例启动或收到变更通知后,将当前需服务的 JSON **反序列化并预热**为内存对象(或编译为匹配结构),下单/预估路径**禁止**主链路同步穿透远程 DB。 |
| 下单与预估 | `/order/parcel/create` 与「我要查价」类预估接口**共用**同一套选版本与匹配逻辑;预估不落库,但响应中建议返回 `rulesVersion` / `priceVersion` 便于与正式单对齐展示。 |
| 降级 | Redis/内存未命中时,允许回退查询 **`t_sales_product_publish_record`**(按 `bindTime` 取当时生效的 rules/price 各一条,读 `Content`),命中后**异步回写 Redis**;并打点告警。 |
#### 4.3.3 与运营侧发布的衔接
- 运营在 Admin 完成卖价、排货规则等配置后,通过 **发布** 生成新版本(参见渠道模块 `/channel/sales-product/publish` 等);发布成功后由**配置同步任务**将结构化结果写入 Redis 并通知计费服务刷新内存。
- 「设为生效」与版本号 `version`、生效时间 `effectiveStartAt`(或等价字段)应对齐,以便 bindTime 维度解析「当前应绑哪一版」。
#### 4.3.4 版本确认时机 vs 费用计算时机(`ChargingTime`)
> **版本(契约)**:在**下单/预报**流程中(如 `/order/parcel/create` 绑定快照时)即确定 `SalesProductRulesVersion`、`SalesProductPriceVersion`(及渠道侧与应付相关的版本,若有)。后续只要未发生「改单重算」等显式流程,**不得**因后台发布新版本而改写订单已绑定版本。
> **金额(执行)**:应收、应付各费用项**何时真正跑公式、写 `t_order_fee`**,由卖价主表/买价主表上的 **`ChargingTime`(收费时机)** 决定,与版本确认时机解耦。
| `ChargingTime`(建议值) | 典型触发节点 | 应收(`Type=receivable`) | 应付(`Type=payable`) |
| --- | --- | --- | --- |
| `forecast` / 预报 | 下单预报成功(如 `/order/parcel/create` 走完全部校验) | **§4.3.4.1 应收重算**:卖价项 `chargingTime=forecast`(或未配置视为 forecast)参与;**重量**取 **`t_order_weight.Type=forecast`** | **§4.3.4.2 应付重算**:买价项按 `chargingTime` 筛选;**重量/件重尺仅取 `Type=downstream`** |
| `inbound` / 入库 | 仓库**收货**确认:**覆盖** `t_order_weight` 中 **`Type=receive`** 的件重尺后,**按 §4.4.3.1 步骤 7、8 同口径对该行重算** `VolumetricWeight`、`ChargeableWeight`;**默认**将 **`receive` 行计重结果同步覆盖 `ship` 与 `downstream`**,再触发本阶段算价(详见 §6.3.1.1) | **§4.3.4.1**:`chargingTime=inbound` 的卖价项;**重量**取 **`Type=receive`** | **§4.3.4.2**:`chargingTime=inbound` 的买价项;**仅 `downstream`** |
| `outbound` / 出库 | 仓库**出库/发货**扫描:**覆盖** `t_order_weight` 中 **`Type=ship`** 的件重尺后,**按 §4.4.3.1 步骤 7、8 同口径对该行重算**计费重;**默认**将 **`ship` 行计重结果同步覆盖 `downstream`**,再触发本阶段算价(详见 §6.3.2.1) | **§4.3.4.1**:`chargingTime=outbound` 的卖价项;**重量**取 **`Type=ship`** | **§4.3.4.2**:`chargingTime=outbound` 的买价项;**仅 `downstream`** |
**规则小结**
1. **先锁版本、再分阶段算钱**:版本在预报阶段锁定;各阶段只计算「本阶段 `ChargingTime` 命中的费用项」,避免一次性把所有费用项都算在预报里(除非配置均为预报)。
2. **后阶段算价仍读旧版本快照**:入库、出库触发算价时,仍从 Redis/内存(或降级 DB)加载该订单**已绑定 version** 对应的价格/规则内容,**不**使用当前全局「最新发布」。
3. **计费重数据源与阶段口径**:**预报**阶段仅对 **`Type=forecast`** 行执行 §4.4.3.1 步骤 7、8,算价取该行 **`ChargeableWeight`**。**入库**(§6.3.1.1)完成后须用收货实测 **覆盖 `Type=receive`** 的 `Length`、`Width`、`Height`、`ActualWeight`(并更新 `Cubic`),再 **对该行**按步骤 7、8 **重算** `VolumetricWeight`、`ChargeableWeight`;**默认**将 **`receive` 行**上述件重尺与计重字段 **同步覆盖 `Type=ship` 与 `Type=downstream`**(与收货口径对齐,直至出库再分化 `ship`)。`ChargingTime=inbound` 的算价 **以 `receive` 行计费重为准**。**出库**(§6.3.2.1)完成后须 **覆盖 `Type=ship`**:请求若带实测件重尺则写入;**未带时可将当前 `receive` 行件重尺拷贝覆盖 `ship` 行**(业务可约定固定复用收货测量),再 **对该行**按步骤 7、8 **重算**;**默认**将 **`ship` 行**计重结果 **同步覆盖 `Type=downstream`**。`ChargingTime=outbound` 的算价 **以 `ship` 行计费重为准**。**`forecast` 行保留预报口径**,入库/出库重算 **不强制回写** `forecast`。若渠道/下游回传与默认 **`downstream`** 快照不一致,可再单独覆盖该行。
3a. **计重规则 / 进位 ID 选取**:计算计费重时 **`effectiveWeightRuleId`**、**`effectiveWeightRoundingId`** **仅**取本单绑定 **`t_sales_product`** 的 **`WeightRuleId` / `WeightRoundingId`**(**不在**客户表配置)。明细来自 **`t_weight_rule_info` / `t_weight_rounding_info`**,或与 **`rules` 快照**内嵌片段一致且与上述 ID 匹配(实现可二选一)。详见 §4.4.3.1 步骤 7、8。
4. **缺省**:若配置未填 `ChargingTime`,实现上按 **`forecast`** 处理,与历史「预报即全算」行为兼容。
5. **快照字段对齐**:发布写入价格快照的 `priceItems[*].chargingTime` 应与主表 `ChargingTime` 一致;允许存中文(如「预报」「入库」「出库」)或与 DB 相同的英文编码,**计费服务应在入口处归一化**为 `forecast` / `inbound` / `outbound` 再参与阶段筛选。
6. **链路复用**:**预报成功、入库完成、出库完成** 各须调用 **§4.3.4.1** 更新/重算 **应收**;**`downstream` 行**被写入或更新后(含预报/入库/出库的默认同步、或渠道下游回传覆盖),须调用 **§4.3.4.2** 更新/重算 **应付**。运营批量纠偏见 **§4.4.1.10**。
##### 4.3.4.1 应收费用重算(`Type=receivable`)
1. **输入**:订单 Id;订单已绑定 **`SalesProductPriceVersion`** 对应卖价快照;本单 **`t_order_weight`** 四行当前值。
2. **筛选卖价项**:`priceItems` 中 `status=1`,按各卖价项 **`chargingTime`**(归一化后)参与对应阶段;批量重算时可对 **全部已配置阶段** 的应收项一次性重算,或仅重算与当前触发阶段相关的项(实现须文档化,**同一订单内幂等**)。
3. **重量(及依赖件重尺的因子)**:严格按卖价项 **`ChargingStage` / `chargingTime`** 映射 **`t_order_weight` 行**:**`forecast` → `Type=forecast`**;**`inbound` → `Type=receive`**;**`outbound` → `Type=ship`**。分区等其他因子仍按既有规则。
4. **输出**:计算金额后 **写入或更新** `t_order_fee`(`Type=receivable`,`ChargingStage` 与卖价项一致);建议 **`OrderId` + `CostItem` + `ChargingStage`(+ 可选 PriceNo)** 幂等 upsert,避免重复行。
5. **调用方**:`/order/parcel/create` 预报成功、**§6.3.1.1 入库**、**§6.3.2.1 出库** 流程尾部;以及 **§4.4.1.10** 批量接口中 `scopes` 含 `receivable` 时。
##### 4.3.4.2 应付费用重算(`Type=payable`)
1. **输入**:订单 Id;本单已确定服务渠道及绑定 **买价** 快照(版本与订单契约一致);本单 **`t_order_weight.Type=downstream`** 当前行(**仅此行**参与件重尺/计费重类因子)。
2. **筛选买价项**:`chargingTime` 与当前触发阶段匹配(批量重算时可对全部应付项重算,实现须幂等)。
3. **重量/体积/实重**:**一律**取自 **`downstream`** 行(如 `ChargeableWeight`、`Length`、`Width`、`Height`、`ActualWeight` 等,以买价因子配置为准),**禁止**使用 `forecast` / `receive` / `ship` 行替代。
4. **输出**:**写入或更新** `t_order_fee`(`Type=payable`,`ChargingStage` 与买价项一致)。
5. **调用方**:预报/入库/出库流程在 **`downstream` 行已更新** 之后;**渠道下游回传**更新 `downstream` 后;以及 **§4.4.1.10** 中 `scopes` 含 `payable` 时。
### 4.4 核心接口设计
#### 4.4.1 我的订单
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 分页查询订单列表 | POST | /order/list | 支持 status、keyword、createTimeStart/End 筛选 |
| 订单详情 | GET | /order/detail | 根据订单ID获取详情 |
| 支付 | POST | /order/pay | 订单支付 |
| 预报 | POST | /order/pre-declare | 订单预报 |
| 拦截 | POST | /order/intercept | 订单拦截 |
| 取消拦截 | POST | /order/cancel-intercept | 取消订单拦截 |
| 删除 | POST | /order/delete | 删除订单 |
| 打印 | POST | /order/print | 打印订单 |
| 导出 | POST | /order/export | 导出订单 |
| 批量重算费用 | POST | /order/fee/batch-recalculate | 入参订单 Id 列表 + `scopes`,分别触发 §4.3.4.1 应收 / §4.3.4.2 应付 |
##### 4.4.1.1 分页查询订单列表(POST /order/list)
###### 1 接口请求示例
**请求 /order/list**(POST,分页参数放 Body)
```json
{
"page": 1, // 页码,从1开始
"pageSize": 20, // 每页条数
"status": "all|draft|predeclared|received|...", // 订单状态筛选
"keyword": "", // 关键词(运单号/客户单号/跟踪号)
"createTimeStart": "", // 创建时间起
"createTimeEnd": "" // 创建时间止
}
```
**响应**(用于 order/myOrders 列表及订单详情抽屉,字段与 parcel/create 对应,创建时传啥返回啥 + weightList)
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"clientNo": "CUST...",
"trackNo": "TRK...",
"serviceNo": "LP...",
"productCode": "US_AIR",
"channelCode": "CH001",
"cargoAttribute": "general",
"exportCustomsMode": "9610Simple",
"warehouseId": 1,
"warehouse": "深圳仓",
"customerId": 1,
"isCod": false,
"codCurrency": "",
"codSum": "",
"orderStatus": "待支付",
"status": "pendingPayment",
"receiveTime": "",
"shipTime": "",
"payTime": "",
"signTime": "",
"isIntercept": false,
"isRemote": false,
"createTime": "2024-01-01 12:00:00",
"createBy": "",
"updateTime": "",
"updateBy": "",
"sender": {
"senderName": "发件人",
"company": "示例公司",
"phone": "13800138000",
"telePhone": "",
"email": "sender@example.com",
"countryCode": 0,
"province": "广东省",
"city": "深圳市",
"zipCode": "518000",
"streetAddress1": "示例地址1",
"streetAddress2": "",
"streetAddress3": "",
"houseNumber": ""
},
"receiver": {
"recipientsName": "收件人1",
"companyName": "",
"phone": "13800138000",
"telePhone": "",
"email": "",
"countryCode": 0,
"province": "California",
"city": "Los Angeles",
"streetNumber": "",
"streetAddress1": "123 Main St",
"streetAddress2": "",
"streetAddress3": "",
"zipCode": "90001",
"taxType": 0,
"taxNo": "",
"certificateType": 0,
"certificateNo": "",
"eori": "",
"vat": ""
},
"items": [
{
"cnName": "示例商品",
"enName": "Sample Product",
"sku": "SKU001",
"unit": "件",
"unitPrice": 10,
"unitWeight": 0.5,
"quantity": 1,
"amount": 10,
"weight": 0.5,
"hsCode": "1234567890",
"skuImage": "",
"category": "普货",
"brand": "示例品牌",
"brandType": "自有",
"isMagnetic": false,
"distribution": "",
"material": "塑料",
"enMaterial": "Plastic",
"purpose": "日用",
"attributes": "",
"originCountry": "中国",
"salesLink": "",
"model": "",
"specifications": "",
"comment": ""
}
],
"weightList": [
{
"type": "forecast",
"length": "20",
"width": "15",
"height": "10",
"actualWeight": "2.50",
"volumetricWeight": "2.00",
"chargeableWeight": "2.50",
"cubic": "0.003"
}
]
}
],
"total": 100
}
}
```
###### 2 接口业务逻辑
###### 核心流程
###### 状态与失败处理
###### 校验与异常
##### 4.4.1.2 订单详情(GET /order/detail)
###### 1 接口请求示例
**请求 /order/detail**(GET,Query 参数)
```
?id=1
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"clientNo": "CUST...",
"trackNo": "TRK...",
"serviceNo": "LP...",
"productCode": "US_AIR",
"channelCode": "CH001",
"cargoAttribute": "general",
"exportCustomsMode": "9610Simple",
"warehouseId": 1,
"warehouse": "深圳仓",
"customerId": 1,
"sender": {},
"receiver": {},
"items": [],
"weightList": []
}
}
```
###### 2 接口业务逻辑
###### 核心流程
-
###### 校验与异常
-
##### 4.4.1.3 支付(POST /order/pay)
###### 1 接口请求示例
**请求 /order/pay**(POST)
```json
{
"id": 1,
"requestId": ""
}
```
> requestId 幂等,可选。
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
##### 4.4.1.4 预报(POST /order/pre-declare)
###### 1 接口请求示例
**请求 /order/pre-declare**(POST)
```json
{
"id": 1,
"requestId": ""
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
##### 4.4.1.5 拦截(POST /order/intercept)
###### 1 接口请求示例
**请求 /order/intercept**(POST)
```json
{
"id": 1,
"requestId": ""
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
##### 4.4.1.6 取消拦截(POST /order/cancel-intercept)
###### 1 接口请求示例
**请求 /order/cancel-intercept**(POST)
```json
{
"id": 1,
"requestId": ""
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
##### 4.4.1.7 删除(POST /order/delete)
###### 1 接口请求示例
**请求 /order/delete**(POST)
```json
{
"id": 1,
"requestId": ""
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
##### 4.4.1.8 打印(POST /order/print)
###### 1 接口请求示例
**请求 /order/print**(POST)
```json
{
"id": 1,
"requestId": ""
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"printUrl": ""
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
##### 4.4.1.9 导出(POST /order/export)
###### 1 接口请求示例
**请求 /order/export**(POST)
```json
{
"status": "all|draft|...",
"keyword": "",
"createTimeStart": "",
"createTimeEnd": ""
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"downloadUrl": "",
"expireAt": ""
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
##### 4.4.1.10 批量重算费用(POST /order/fee/batch-recalculate)
用于「我的订单」运营/对账场景:按订单 Id 列表 **批量** 重算并回写 **`t_order_fee`**,算法拆分为 **应收** 与 **应付** 两段(**§4.3.4.1**、**§4.3.4.2**),与预报、入库、出库、下游链路 **共用同一套计算单元**。
###### 1 接口请求示例
**请求 /order/fee/batch-recalculate**(POST)
```json
{
"orderIds": [1001, 1002, 1003],
"scopes": ["receivable", "payable"]
}
```
> 参数说明:`orderIds` **必填**,非空数组,元素为 **`t_order.Id`**。`scopes` **必填**,取值为 `receivable`、`payable` 的子集(可只传其一或两者都传)。**`receivable`**:对列表内订单执行 **§4.3.4.1**。**`payable`**:对列表内订单执行 **§4.3.4.2**(依赖 **`downstream`** 行已存在且有效)。
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"total": 3,
"successCount": 2,
"failCount": 1,
"failures": [{ "orderId": 1003, "reason": "缺少 t_order_weight.Type=downstream" }]
}
}
```
###### 2 接口业务逻辑
###### 核心流程
1. **校验**:`orderIds` 去重、长度上限(如 ≤500,可配置)、订单存在且 **`IsDeleted=0`**;权限(如仅管理员/财务/指定角色)。
2. **当 `scopes` 含 `receivable`(重算应收)**
- 逐单加载:绑定 **`SalesProductPriceVersion`** 卖价快照、`t_order_weight`、`t_order_fee` 既有应收行(若策略为全量重算可先逻辑清理再写)。
- 按 **§4.3.4.1**:卖价项按 `chargingTime` 取 **`forecast` / `receive` / `ship`** 对应 **`ChargeableWeight`**(及分区等),重算金额并 **upsert** `Type=receivable`。
3. **当 `scopes` 含 `payable`(重算应付)**
- 逐单校验 **`t_order_weight` 存在 `Type=downstream`** 且可用于买价匹配;加载绑定 **买价** 快照。
- 按 **§4.3.4.2**:**仅 `downstream`** 行参与重量/件重尺因子,重算并 **upsert** `Type=payable`。
4. **事务**:建议 **按订单** 单事务提交,避免一批中一单失败影响其他单;`failures` 逐单记录原因。
5. **与履约链路的关系**:**预报成功、入库完成、出库完成** 应在各自流程内调用 **§4.3.4.1**;**`downstream` 更新后**(含三段流程同步写入、或下游回传)调用 **§4.3.4.2**。本接口为 **显式批量补算**,不改变订单已绑定版本契约。
###### 校验与异常
- `orderIds` 为空、`scopes` 为空或不合法、订单无绑定卖价/买价版本、缺 `downstream`(在请求 `payable` 时)等返回业务错误;部分失败时仍返回 `200` + `data.failures`(或与全局错误码策略一致)。
#### 4.4.2 我要查价
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 查价 | POST | /order/price-check | 根据目的国、重量尺寸等查价 |
##### 4.4.2.1 查价(POST /order/price-check)
###### 1 接口请求示例
**请求 /order/price-check**(POST)
```json
{
"destinationCountry": "US", // 目的国二字码
"shippingMethod": "", // 运输方式
"cargoWeight": "2", // 货物重量(kg)
"length": "20", // 长(cm)
"width": "15", // 宽(cm)
"height": "10", // 高(cm)
"cargoType": "普货", // 货物类型
"destinationProvince": "", // 目的省州
"postalCode": "" // 邮编
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"shippingMethod": "美国空运专线(普货)",
"warehouse": "一代深圳仓",
"deliveryTime": "3-7",
"volumetricWeight": "2",
"chargeableWeight": "2",
"costFormula": "(62*2) +5+7",
"freight": "62",
"otherFees": "5",
"totalCost": "136",
"maxDeliveryDays": 7,
"totalCostNum": 136
}
]
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
#### 4.4.3 我要下单
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 模板列表 | GET | /order/template/list | 获取下单模板列表 |
| 下载模板 | GET | /order/template/download | 下载 Excel 模板 |
| 上传下单 | POST | /order/upload | multipart/form-data,file + templateId |
| 任务进度列表 | GET | /order/task/list | 批量下单任务列表 |
| 任务详情 | GET | /order/task/detail | 任务详情 |
| 创建小包 | POST | /order/parcel/create | 创建小包订单 |
| 批量创建小包 | POST | /order/parcel/batch-create | 批量创建小包订单 |
##### 4.4.3.1 创建小包(POST /order/parcel/create)
###### 1 接口请求示例
**请求 /order/parcel/create**(POST)
> 字段与 t_order、t_order_sender、t_order_recipient、t_order_declaration、t_order_weight 表对应,采用 camelCase。
```json
{
"clientNo": "string", // 客户单号 t_order.ClientNo//必填
"trackNo": "string", // 跟踪号 t_order.TrackNo
"serviceNo": "string", // 运单号 t_order.ServiceNo
"productCode": "string", // 产品代码 t_order.ProductCode//必填
"channelCode": "string", // 渠道代码 t_order.ChannelCode
"cargoAttribute": "string", // 货物属性 t_order.CargoAttribute:general-普货,electronics-3c&带电,sensitive-敏感货//必填
"exportCustomsMode": "string", // 出口报关方式 t_order.ExportCustomsMode:9610Simple/9610Summary/9610Separate/9710/9810/0110,含义见 4.2.1 ExportCustomsMode 枚举//可选
"warehouseId": 0, // 仓库ID t_order.WarehouseId//必填
"length": 3, // 长(cm) t_order_weight.Length
"width": 3, // 宽(cm) t_order_weight.Width
"height": 3, // 高(cm) t_order_weight.Height
"actualWeight": 3, // 实重(kg) t_order_weight.ActualWeight//必填
"isCod": false, // 是否COD
"codSum": "string", // COD金额
"codCurrency": "string", // COD币种
"declaredCurrency": "string", // 申报币种//必填
"declaredSum": "string", // 申报总金额
"quantity": "string", // 包裹数量//必填
"receiver": { // 收件人 t_order_recipient
"recipientsName": "string", // 收件人名称//必填
"companyName": "string", // 收件人公司名称
"phone": "string", // 收件人手机//必填
"telePhone": "string", // 收件人电话
"email": "string", // 收件人邮箱
"countryCode": 0, // 国家//必填
"province": "string", // 收件人省/州
"city": "string", // 收件人城市
"streetNumber": "string", // 收件人门牌号
"streetAddress1": "string", // 收件人地址1//必填
"streetAddress2": "string", // 收件人地址2
"streetAddress3": "string", // 收件人地址3
"zipCode": "string", // 收件人邮编//必填
"taxType": 0, // 税号类型
"taxNo": "string", // 税号
"certificateType": 0, // 证件类型
"certificateNo": "string", // 证件号码
"eori": "string", // EORI
"vat": "string" // Vat
},
"sender": { // 发件人 t_order_sender
"senderName": "string", // 发件人姓名
"company": "string", // 发件人公司
"phone": "string", // 发件人手机
"telePhone": "string", // 发件人电话
"email": "string", // 发件人邮箱
"countryCode": 0, // 国家
"province": "string", // 州省
"city": "string", // 城市
"zipCode": "string", // 邮编
"streetAddress1": "string", // 地址1
"streetAddress2": "string", // 地址2
"streetAddress3": "string", // 地址3
"houseNumber": "string" // 门牌号
},
"items": [ // 申报明细 t_order_declaration
{
"cnName": "string", // 品名-中
"enName": "string", // 品名-英//必填
"sku": "string", // SKU
"unit": "string", // 单位
"unitPrice": 0, // 单价//必填
"unitWeight": 0, // 单重//必填
"quantity": 0, // 数量//必填
"amount": 0, // 总金额
"weight": 0, // 总重量
"imHsCode": "string", // 进口HsCode
"exHsCode": "string", // 出口HsCode
"skuImage": "string", // 图片
"category": "string", // 类别
"brand": "string", // 品牌
"brandType": "string", // 品牌类型
"isMagnetic": false, // 是否带磁带电
"distribution": "string", // 配货信息
"material": "string", // 材质
"enMaterial": "string", // 英文材质
"purpose": "string", // 用途
"attributes": "string", // 属性
"originCountry": "string", // 原产国
"salesLink": "string", // 销售链接
"model": "string", // 型号
"specifications": "string", // 规格
"comment": "string" // 备注
}
]
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"clientNo": "",
"serviceNo": "",
"trackNo": "",
"labelUrl": ""
}
}
```
> 幂等时可选传 requestId。
###### 2 接口业务逻辑
###### 核心流程
- 本接口用于创建小包订单预报数据,执行顺序固定为:参数校验 -> 查询销售产品与规则/价格快照绑定 -> **黑名单校验(命中则中止,不落库)** -> 创建订单草稿 -> 偏远校验 -> 计费重计算 -> **仅「预报」时机费用计算与落库** -> 更新订单状态 -> 返回结果。(入库/出库时机费用见 §4.3.4,由仓储收货、出库流程触发。)
- 参数校验失败,或销售产品/快照绑定失败、黑名单校验命中时,均不创建任何订单数据,直接返回业务错误。
- 进入草稿创建后,先将订单主数据、发件人、收件人、申报明细、预报件重尺数据落库,订单状态固定写入 `draft`。
- 后续偏远、计费重、费用等流程全部基于已创建的草稿订单继续处理;如任一业务环节失败,订单状态不扩展新枚举,统一保留 `draft`,并在 `t_order.PredeclareFailReason` 记录失败原因。
###### 详细业务逻辑
1. 参数解析与基础数据校验
- 校验请求体必填字段:`productCode`、`warehouseId`、`customerId`、`length`、`width`、`height`、`actualWeight`、`receiver.recipientsName`、`receiver.countryCode`、`receiver.zipCode`、`sender.senderName`、`items`。
- 校验数值合法性:`length/width/height/actualWeight` 必须大于 0。
- 校验枚举合法性:`cargoAttribute` 仅允许 `general`、`electronics`、`sensitive`。
- 校验枚举合法性:`exportCustomsMode` **可选**;若传入则须为 §4.2.1 **ExportCustomsMode** 枚举 code(`9610Simple`、`9610Summary`、`9610Separate`、`9710`、`9810`、`0110`),否则返回参数错误。
- **客户校验**:按 `customerId` 确认客户存在且业务状态可用(客户主数据与状态字段以 **RBAC/客户域服务** 为准;实现上可为远程调用或只读副本查询);不可用则返回错误。
- 查询 `t_warehouse_address`,条件:`Id = warehouseId and IsDeleted = 0`,返回字段:`Id`、`WarehouseName`;仓库不存在时返回错误。
- 查询 `t_country`,仅校验收件人国家,条件:`Id = receiver.countryCode and IsDeleted = 0`,返回字段:`Id`、`CountryCode`、`ChineseName`、`EnglishName`;国家不存在时返回错误。
2. 查询销售产品配置(用于定位发布记录)
- 查询 `t_sales_product`,条件:`ProductCode = productCode and IsDeleted = 0 and IsActive = 1`
- 返回字段:`Id`、`ProductCode`、`ProductName`
- 如不存在,**不创建订单**,直接返回失败(错误信息需明确「销售产品不存在或未启用」)。
3. 绑定销售产品计费快照(强关联:以创建订单当刻绑定的价格与规则为准;**运行时优先 Redis + 本地内存**)
- 绑定时点:以本次请求处理时间作为 `bindTime`(草稿落库后,`t_order.CreateTime` 应与该时点一致或可由该时点推导)。
- **主路径(推荐)**:按 `salesProductId = t_sales_product.Id` 与 `bindTime`,从 **Redis / 计费服务本地内存** 解析「当前应对外生效」的 **规则快照** 与 **价格快照**(结构化 JSON,与运营发布组装的版本一致),得到:
- `rulesSnapshot`:含 `version`、`effectiveStartAt`(或等价字段)、`content`
- `priceSnapshot`:含 `version`、`effectiveStartAt`、`content`
- **降级路径**:当 Redis/内存未命中或版本指针异常时,从 **`t_sales_product_publish_record`** 读取:
- 规则:条件 `SalesProductId = t_sales_product.Id and OperatorType = 'rules' and Status = '生效' and PublishTime <= bindTime`,按 `PublishTime desc` 取 1 条,`Content` 即规则快照正文(与下文 `rulesSnapshot.content` 同构)。
- 价格:条件 `OperatorType = 'price'`,其余同上,`Content` 即价格快照正文。
- 命中后应**异步回写 Redis**,并打点监控。
- 绑定失败处理(**尚未落库订单**):
- 若未找到 rulesSnapshot:**不创建订单**,直接返回失败(错误信息需明确「未命中规则快照版本(内存/Redis/DB)」)。
- 若未找到 priceSnapshot:**不创建订单**,直接返回失败(错误信息需明确「未命中价格快照版本(内存/Redis/DB)」)。
- 版本字段落库:在下一步「创建订单草稿」插入 `t_order` 时写入(强关联字段,**新单新价、老单不改**):
- `SalesProductRulesVersion = rulesSnapshot.version`(与发布记录 `Version` 一致)
- `SalesProductPriceVersion = priceSnapshot.version`
- 性能要求:
- 下单/预估主链路应在**进程内内存**完成快照读取与匹配;降级读 DB 仅作兜底,应限流并尽快恢复 Redis。
4. 黑名单校验(拦截型,来自已绑定的 rules 快照 `content`)
- 读取 `rulesSnapshot.content.areas.blacklist.rules`(国家/省市/邮编区间规则集合)。
- 匹配规则:
- 国家必须匹配参数校验阶段已解析的收件国家编码(与后续写入 `t_order.ConsigneeCountry` 的值一致,来源于 `t_country.CountryCode`)
- 若规则配置了省/市,则请求体收件人 `receiver` 的省/市也必须匹配
- 邮编落在任一 `postcodeRanges.left ~ postcodeRanges.right` 区间内即视为命中
- 命中处理:**不创建订单**,直接返回失败(错误信息需明确「收件地址命中黑名单区域」),不进入草稿落库及后续流程。
5. 创建订单草稿
- 插入 `t_order`:
- 写入字段:`ServiceNo`、`ClientNo`、`TrackNo`、`ProductCode`、`ChannelCode`、`ConsigneeCountry`、`CargoAttribute`、`ExportCustomsMode`、`OrderStatus`、`WarehouseId`、`CustomerId`、`IsCod`、`CodCurrency`、`CodSum`、`IsIntercept`、`IsRemote`、`PredeclareFailReason`、`SalesProductRulesVersion`、`SalesProductPriceVersion`、审计字段。
- 处理规则:
- `OrderStatus` 固定写 `draft`
- `ConsigneeCountry` 取收件国家对应的 `t_country.CountryCode`
- `IsIntercept` 初始写 `0`
- `IsRemote` 初始写 `0`
- `PredeclareFailReason` 初始写空
- `SalesProductRulesVersion`、`SalesProductPriceVersion` 取步骤 3 已解析的快照版本
- `ExportCustomsMode` 取请求体 `exportCustomsMode`(须为 §4.2.1 枚举 code),未传则 `NULL`
- 插入 `t_order_sender`:
- 条件:使用新生成的 `OrderId`
- 写入字段:`OrderId`、`SenderName`、`Company`、`Country`、`Province`、`City`、`ZipCode`、`StreetAddress1`、`StreetAddress2`、`StreetAddress3`、`HouseNumber`、`Phone`、`TelePhone`、`Email`
- 插入 `t_order_recipient`:
- 写入字段:`OrderId`、`RecipientsName`、`CompanyName`、`Country`、`Phone`、`TelePhone`、`ZipCode`、`Province`、`City`、`StreetNumber`、`StreetAddress1`、`StreetAddress2`、`StreetAddress3`、`Email`、`TaxType`、`TaxNo`、`CertificateType`、`CertificateNo`、`Eori`、`Vat`
- 插入 `t_order_declaration`:
- 对 `items` 数组逐条入库
- 写入字段:`OrderId`、`CnName`、`EnName`、`Sku`、`Unit`、`UnitPrice`、`UnitWeight`、`Quantity`、`Amount`、`Weight`、`HsCode`、`SkuImage`、`Category`、`Brand`、`BrandType`、`IsMagnetic`、`Distribution`、`Material`、`EnMaterial`、`Purpose`、`Attributes`、`OriginCountry`、`SalesLink`、`Model`、`Specifications`、`Comment`
- 插入 `t_order_weight`:
- 一次性写入 **4** 条记录,`Type` 依次为 **`forecast`**、**`receive`**、**`ship`**、**`downstream`**(与 §4.2.7 `Type` 枚举一致)
- 写入字段:`OrderId`、`Type`、`Length`、`Width`、`Height`、`ActualWeight`、`VolumetricWeight`、`ChargeableWeight`、`Cubic`
- **先**构造 **`Type = forecast`** 行的初始值:
- `ActualWeight` 取请求值
- `VolumetricWeight`、`ChargeableWeight` 可先写请求体值或 `0`,后续步骤 7、8 按规则重算并覆盖
- `Cubic = Length × Width × Height / 1000000`
- **`Type = receive` / `ship` / `downstream` 三行**:`Length`、`Width`、`Height`、`ActualWeight`、`VolumetricWeight`、`ChargeableWeight`、`Cubic` **均与 `forecast` 行相同**(创建时从 `forecast` 拷贝;后续收货、发货、下游等环节再按各自 `Type` 更新差异化测量)
6. 偏远区域校验(标记型,来自已绑定的 rules 快照)
- 读取 `rulesSnapshot.content.areas.remote.rules`(国家/省市/邮编区间规则集合)。
- 匹配规则与黑名单一致。
- 命中则更新 `t_order.IsRemote = 1`;未命中则更新 `t_order.IsRemote = 0`
- 偏远校验不拦截订单,只影响后续费用匹配。
7. 计费重计算(**有效规则 ID:仅销售产品**)
- **输入**:仅使用 `t_order_weight` 中 **`Type = forecast`** 的件重尺:`Length`、`Width`、`Height`、`ActualWeight`(单位与库表约定一致,一般为 cm / kg)。
- **解析 `effectiveWeightRuleId`**:取本单绑定 **`t_sales_product.WeightRuleId`**;须**非空**且对应 **`t_weight_rule.IsDeleted=0`**。否则更新 `PredeclareFailReason`(如「未配置计重规则」),保持 `draft`,失败返回。
- **加载明细集合 `rules`**:`t_weight_rule_info` 中 **`RuleId = effectiveWeightRuleId and IsDeleted=0`**,按 **`Id` 升序**排序;或从 **`rulesSnapshot.content.weightRule`** 读取与 **`effectiveWeightRuleId`** 一致的 **`clientRules`**(结构无 `client` 字段,与表 §5.2.16 对齐)。集合为空则同上失败。
- **算法(未进位计费重 `rawW`)**:
1. `gw = ActualWeight`(实重,kg)。
2. 依次遍历 `rules` 中每一行(已按 `Id` 升序):对该行解析 **`volumetricFactor`** 为正数,计算 **`vw = (Length × Width × Height) / volumetricFactor`**(与产品历史口径一致:体积 cm³ ÷ 系数 → 体积重 kg;若实现使用 m³,须在服务内统一换算)。
3. 将该行的 **`gw`、`vw`** 代入 **`condition`**(支持 `>`、`>=`、`<`、`<=`、`==` 等,运算对象可为 `gw`、`vw` 及常量;**不支持**跨行交叉引用)。
4. **第一条**使 `condition` 为**真**的行:**命中行**;若无一命中,失败(`PredeclareFailReason` 建议「计重规则条件均未命中」)。
5. 命中行的 **`billableWeight`** 取值仅为 **`gw` 或 `vw`**(大小写不敏感):`rawW = (billableWeight == 'gw') ? gw : vw`。
6. 写回 **`t_order_weight.VolumetricWeight`**:建议取**命中行**对应的 **`vw`**(便于对账;若业务约定取「展示用」体积重可另行说明)。**本接口(预报草稿)建议将同一 `vw` 同步写入 `forecast`、`receive`、`ship`、`downstream` 四行**,与创建时四行初始拷贝策略一致。**入库**在 **`receive` 行**重算后,**默认**将 `VolumetricWeight`(及同次流程写回的件重尺字段)**同步至 `ship`、`downstream`**;**出库**在 **`ship` 行**重算后,**默认同步至 `downstream`**;均**不再与 `forecast` 强制一致**。
8. 计费重进位(**有效进位 ID:仅销售产品**)
- **解析 `effectiveWeightRoundingId`**:取本单绑定 **`t_sales_product.WeightRoundingId`**;须**非空**且 **`t_weight_rounding.IsDeleted=0`**。无效则失败(`PredeclareFailReason` 如「未配置计重进位」)。
- **加载区间集合 `ranges`**:`t_weight_rounding_info` 中 **`RoundingId = effectiveWeightRoundingId and IsDeleted=0`**;或快照 **`rulesSnapshot.content.weightRounding.ranges`**(仅含 **`start`/`end`/`public`**,与 §5.2.18 对齐)。
- **匹配区间**:在 `ranges` 中找到满足 **`start <= rawW <= end`** 的区间(多区间重叠时按 **`Id` 升序**取第一条,或产品约定唯一覆盖);**无匹配**则失败。
- **算法(最终计费重)**:设命中区间 **`public = u`**(正数,进位值 kg)。推荐统一为**按进位值向上取整**:
- `ChargeableWeight = ceil(rawW / u) × u`
- `ceil` 为数学向上取整;实现须固定小数位数(如 3 位)避免浮点误差。
- 更新 **`t_order_weight.ChargeableWeight`**:**预报阶段建议四行与上一步 `VolumetricWeight` 策略一致,同步为同一计费重**。**入库**:在 **`receive` 行**进位完成后,**默认**将 `ChargeableWeight` **同步至 `ship`、`downstream`**。**出库**:在 **`ship` 行**进位完成后,**默认同步至 `downstream`**(§4.3.4)。若 **`public` 为空或非法**,失败。
9. 价格分区匹配(来自已绑定的 rules 快照)
- 读取 `rulesSnapshot.content.areas.price.rules`,按收件国家/省市/邮编区间匹配得到 `zoneName`(如“美国西部”)。
- 若未匹配到价格分区,更新 `t_order.PredeclareFailReason = '价格分区未匹配(规则快照)'`,订单状态保持 `draft`,返回失败。
10. 费用计算与费用落库(**拆分为应收 / 应付**,与 §4.3.4.1、§4.3.4.2 一致;本接口处于**预报**节点)
- **1)重算应收(§4.3.4.1)**:对 `chargingTime` 为 **`forecast`(或未配置视为 forecast)** 的卖价项执行算价;**重量**等件重尺因子取 **`Type=forecast`**;**分区**取步骤 9 的 `zoneName`;结果 **upsert** `t_order_fee`(`Type=receivable`,`ChargingStage=forecast`)。**`inbound` / `outbound` 卖价项本接口跳过**,留仓储流程触发后再跑 §4.3.4.1。
- **2)重算应付(§4.3.4.2)**:在 **`downstream` 行已随步骤 5~8 写入**(与 `forecast` 等一致或已有值)后,对 **`chargingTime=forecast`(或未配置)** 的买价项(若本单已确定服务渠道且存在绑定买价快照)算价;**重量/件重尺仅允许 `Type=downstream`**;结果 **upsert** `t_order_fee`(`Type=payable`,`ChargingStage=forecast`)。无渠道或无买价快照时可跳过应付。
- 写入字段(各行):`OrderId`、`Type`、`CostItem`、`Amount`、`Currency`、`PriceNo`、`PriceEffectiveTime`、`ChargingStage`;`PriceNo` 应收写 `SalesProductPriceVersion`,应付写渠道买价版本(若有)。
- **强校验建议**:若订单存在至少一条启用的卖价项,且**所有**启用项的 `chargingTime` 均**不是**预报,则本步骤可产生 0 条应收;是否允许「预报成功但尚无应收行」由产品策略决定。
- 若应收应算项未匹配到卖价明细,更新 `t_order.PredeclareFailReason = '产品卖价快照未配置或未匹配到有效价格'`,订单状态保持 `draft`,返回失败。
11. 固定报价示例规则(某产品,来自 price 快照的 continuePricing)
- 示例规则:
- 首重:1kg 以内,首重价 18 元
- 续重:超出 1kg 部分,每 0.5kg 为一个续重单位,续重单价 6 元 / 0.5kg
- 进位规则:不足 0.5kg 按 0.5kg 进位
- 适用前提:
- 该产品已匹配到对应价格分区与卖价配置
- `t_order_weight.ChargeableWeight` 已完成最终进位
- price 快照明细为 `calculationFormula = 续单价` 且包含 `continuePricing`
- 计算口径:
- 当 `ChargeableWeight <= 1` 时,总费用 = 18
- 当 `ChargeableWeight > 1` 时:
- 先收首重价 18
- 超出部分 = `ChargeableWeight - 1`
- 超出部分按 0.5kg 向上取整到续重单位数
- 续重费用 = 续重单位数 * 6
- 总费用 = 18 + 续重费用
- 示例:
- `ChargeableWeight = 0.8kg`,进位到 `1.0kg`,总费用 = 18
- `ChargeableWeight = 1.2kg`,超出 `0.2kg`,按 0.5kg 进 1 个续重单位,总费用 = 18 + 6 = 24
- `ChargeableWeight = 1.6kg`,超出 `0.6kg`,按 0.5kg 进 2 个续重单位,总费用 = 18 + 12 = 30
- 该示例说明:计费重来源仅为 `t_order_weight`,不参与 `t_order_declaration` 的商品重量汇总逻辑。
12. 成功完成预报
- 当偏远标记完成、计费重计算完成、**本阶段(预报)应计费用**写入完成后(黑名单已在草稿落库前完成校验):
- 更新 `t_order.OrderStatus = 'predeclared'`
- 更新 `t_order.PredeclareFailReason = ''`
- 更新 `t_order.UpdateUserId`、`t_order.UpdateTime`
- 返回字段:
- `clientNo`:取 `t_order.ClientNo`
- `serviceNo`:取 `t_order.ServiceNo`
- `trackNo`:取 `t_order.TrackNo`
- `labelUrl`:若当前流程未生成面单则返回空字符串
###### 状态与失败处理
- 参数校验失败:不创建订单,直接返回错误。
- 销售产品不存在或未启用、规则/价格快照未命中、黑名单命中:均**不创建订单**,直接返回错误(与参数校验一致,不落库,不写 `PredeclareFailReason`)。
- 草稿创建成功但后续业务失败:订单状态统一保留 `draft`。
- 偏远命中:仅更新 `IsRemote = 1`,不拦截流程。
- 计重规则、进位规则、价格分区、卖价配置缺失或未匹配:统一写入 `PredeclareFailReason`,订单状态保持 `draft`。
###### 校验与异常
- 对必填参数、枚举值、数值范围、客户/仓库/国家/销售产品存在性进行校验,异常场景返回统一错误码与错误信息。
- 草稿创建后的异常场景需要保留订单数据,并将失败原因写入 `t_order.PredeclareFailReason`,供订单列表和详情页展示。
##### 4.4.3.2 批量创建小包(POST /order/parcel/batch-create)
与 **§4.4.3.1 创建小包** 使用同一套单票业务规则(参数校验 → 销售产品与快照绑定 → 黑名单 → 草稿 → 偏远/计费重/预报费用 → 状态更新)。数组元素字段与单票请求体一致,含可选字段 **`exportCustomsMode`(出口报关方式)**,枚举与落库规则见 §4.2.1、§4.4.3.1。**差异**:本接口先将批次内订单**全部受理入库(或入队)**,HTTP **仅同步返回批量任务标识与受理概况**,**不返回**每票的 `serviceNo` / `labelUrl` 等最终结果;逐票处理在**后台异步**执行,处理进度与失败明细通过 **§4.4.3.7 任务详情**(`GET /order/task/detail`)查询。
###### 1 接口请求示例
**请求 /order/parcel/batch-create**(POST)
> 批量请求体为数组,数组内每一项与 `/order/parcel/create` 的单票请求结构完全一致。**单次导入条数上限为 2000**:`array.length > 2000` 时整批拒绝,不入队、不创建任务。幂等时可选传与单票一致的 `requestId`(若实现批次级幂等,则以该字段或服务端生成的批次维度键为准)。
```json
[
{
"clientNo": "string", // 客户单号 t_order.ClientNo
"trackNo": "string", // 跟踪号 t_order.TrackNo
"serviceNo": "string", // 运单号 t_order.ServiceNo
"productCode": "string", // 产品代码 t_order.ProductCode
"channelCode": "string", // 渠道代码 t_order.ChannelCode
"cargoAttribute": "string", // 货物属性 t_order.CargoAttribute:general-普货,electronics-3c&带电,sensitive-敏感货
"exportCustomsMode": "string", // 出口报关方式 t_order.ExportCustomsMode:9610Simple/9610Summary/9610Separate/9710/9810/0110,含义见 §4.2.1 ExportCustomsMode 枚举//可选,与单票 create 一致
"warehouseId": 0, // 仓库ID t_order.WarehouseId
"customerId": 0, // 客户ID t_order.CustomerId
"length": 3, // 长(cm) t_order_weight.Length
"width": 3, // 宽(cm) t_order_weight.Width
"height": 3, // 高(cm) t_order_weight.Height
"actualWeight": 3, // 实重(kg) t_order_weight.ActualWeight
"volumetricWeight": 3, // 体积重(kg) t_order_weight.VolumetricWeight
"chargeableWeight": 3, // 计费重(kg) t_order_weight.ChargeableWeight
"cubic": 0, // 立方(m³) t_order_weight.Cubic
"isCod": false, // 是否COD
"codCurrency": "string", // COD币种
"codSum": "string", // COD金额
"declaredCurrency": "string", // 申报币种
"declaredSum": "string", // 申报金额
"quantity": "string", // 包裹数量
"receiver": { // 收件人 t_order_recipient
"recipientsName": "string", // 收件人名称
"companyName": "string", // 收件人公司名称
"phone": "string", // 收件人手机
"telePhone": "string", // 收件人电话
"email": "string", // 收件人邮箱
"countryCode": 0, // 国家
"province": "string", // 收件人省/州
"city": "string", // 收件人城市
"streetNumber": "string", // 收件人门牌号
"streetAddress1": "string", // 收件人地址1
"streetAddress2": "string", // 收件人地址2
"streetAddress3": "string", // 收件人地址3
"zipCode": "string", // 收件人邮编
"taxType": 0, // 税号类型
"taxNo": "string", // 税号
"certificateType": 0, // 证件类型
"certificateNo": "string", // 证件号码
"eori": "string", // EORI
"vat": "string" // Vat
},
"sender": { // 发件人 t_order_sender
"senderName": "string", // 发件人姓名
"company": "string", // 发件人公司
"phone": "string", // 发件人手机
"telePhone": "string", // 发件人电话
"email": "string", // 发件人邮箱
"countryCode": 0, // 国家
"province": "string", // 州省
"city": "string", // 城市
"zipCode": "string", // 邮编
"streetAddress1": "string", // 地址1
"streetAddress2": "string", // 地址2
"streetAddress3": "string", // 地址3
"houseNumber": "string" // 门牌号
},
"items": [ // 申报明细 t_order_declaration
{
"cnName": "string", // 品名-中
"enName": "string", // 品名-英
"sku": "string", // SKU
"unit": "string", // 单位
"unitPrice": 0, // 单价
"unitWeight": 0, // 单重
"quantity": 0, // 数量
"amount": 0, // 总金额
"weight": 0, // 总重量
"imHsCode": "string", // 进口HsCode
"exHsCode": "string", // 出口HsCode
"skuImage": "string", // 图片
"category": "string", // 类别
"brand": "string", // 品牌
"brandType": "string", // 品牌类型
"isMagnetic": false, // 是否带磁带电
"distribution": "string", // 配货信息
"material": "string", // 材质
"enMaterial": "string", // 英文材质
"purpose": "string", // 用途
"attributes": "string", // 属性
"originCountry": "string", // 原产国
"salesLink": "string", // 销售链接
"model": "string", // 型号
"specifications": "string", // 规格
"comment": "string" // 备注
}
]
}
]
```
**响应**
> 成功受理时仅返回**任务维度**信息;单票业务结果(成功预报、草稿失败原因等)不在本响应中体现。
```json
{
"code": 0,
"msg": "success",
"data": {
"taskId": 1,
"taskName": "批量创建小包_20240509120000",
"totalCount": 100,
"acceptedCount": 98,
"rejectedItems": [
{
"index": 3,
"clientNo": "optional-echo",
"reason": "同步拒收原因说明(如 JSON 结构非法、缺少整段 receiver 等)"
}
]
}
}
```
- `totalCount`:请求数组长度。
- `acceptedCount`:已写入待异步处理队列(或等价持久化)的条数,`acceptedCount + rejectedItems.length = totalCount`(实现上也可将同步拒收记为 0 条入队,由 `rejectedItems` 说明)。
- `rejectedItems`:仅在**同步阶段**即确定无法受理的明细(例如某元素非对象、缺少整块必填结构);**不在此列表中的条目**将按单票逻辑在异步阶段处理,失败时记入任务详情中的失败列表或对应订单 `PredeclareFailReason`(与 §4.4.3.1 一致)。
###### 2 接口业务逻辑
###### 核心流程
1. **同步(请求线程)**
- 校验批次 envelope:数组非空、单次条数 **≤ 2000**、调用方权限/客户维度与单票接口一致。
- 对数组逐条做**轻量同步校验**(仅保证可进入异步流水线所需的最小结构;策略可选:过严则在同步期填入 `rejectedItems`,其余入队)。
- 创建 **`t_order_task`** 主记录(或等价批量任务实体):`TaskName` 建议带业务前缀与时间戳,`Quantity = acceptedCount`,`Status` 初始为待处理/处理中;关联 `CustomerId` 等与上下文一致。
- 将 `acceptedCount` 条明细持久化到**待处理存储**(任务明细表、消息队列、或带 `TaskId` 的缓冲表,实现选型由研发确定),每条携带原始 JSON 与行号 `index`,供消费者拉取。
- 立即返回 `taskId` 等响应字段,**不等待**任一票走完 §4.4.3.1 全链路。
2. **异步(工作线程 / 消费者)**
- 按 FIFO 或任务内顺序逐条取出明细,**完全复用 §4.4.3.1「详细业务逻辑」各步骤**(含:销售产品/快照失败与黑名单不落库;草稿后失败写 `draft` + `PredeclareFailReason`;成功则 `predeclared` 等)。
- 更新任务聚合进度:`successCount` / `failCount`、`Status`(处理中 → 已完成);失败条目写入任务详情中的 **`failList`**(建议含 `index`、`clientNo`、错误码/原因,便于与上传文件行号对齐)。
- 与单票一致:幂等、并发、快照读取性能要求仍适用。
###### 校验与异常
- **整批拒绝**:数组为空、超过 **2000** 条、未授权等 → HTTP 业务错误,**不创建**任务、不入队。
- **同步 per-row 拒收**:仅将明确无法入队的行写入 `rejectedItems`,其余正常入队;不要求在同步期校验仓库/国家/黑名单等业务规则(与单票异步阶段统一)。
- **`exportCustomsMode`**:若某元素携带该字段,异步执行 §4.4.3.1 时按单票规则校验(须为 §4.2.1 枚举 code);非法则该票失败并写 `PredeclareFailReason`(或任务详情 `failList`),与 `cargoAttribute` 等枚举错误处理一致。
- **异步失败**:与 §4.4.3.1「状态与失败处理」一致;用户通过 **§4.4.3.7** 看批次汇总,通过订单列表/详情看单票 `OrderStatus` 与 `PredeclareFailReason`。
##### 4.4.3.3 模板列表(GET /order/template/list)
###### 1 接口请求示例
**请求 /order/template/list**(GET,Query 参数)
```
?page=1&pageSize=20&type=standard|custom&keyword=
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"name": "标准模板",
"type": "standard",
"templateKey": "standard_v1",
"customerId": 0,
"createTime": "2024-01-01 12:00:00",
"updateTime": "2024-01-01 12:00:00"
}
],
"total": 10
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
##### 4.4.3.4 下载模板(GET /order/template/download)
###### 1 接口请求示例
**请求 /order/template/download**(GET,Query 参数)
```
?templateId=1
```
**响应**
> 直接返回 Excel 文件流,Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
###### 2 接口业务逻辑
###### 核心流程
- 标准模板 Excel 表头建议包含列 **「出口报关方式」**(与 `exportCustomsMode` / `t_order.ExportCustomsMode` 对应),单元格填写 §4.2.1 **ExportCustomsMode** 枚举 code;列缺失或空单元格按 **未传** 处理,落库 `NULL`。
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
- 若模板中提供「出口报关方式」列,非空值须为合法枚举 code,否则该行在解析或异步处理阶段失败(错误信息需可定位行号)。
##### 4.4.3.5 上传下单(POST /order/upload)
###### 1 接口请求示例
**请求 /order/upload**(POST,multipart/form-data)
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| file | File | Excel 文件 |
| templateId | number | 模板ID |
> **导入条数限制**:按模板解析得到的有效订单数据行(不含表头)**最多 2000 行**;超出则拒绝本次上传并返回错误,不创建任务。
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"taskId": 1
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- Excel 解析为与 **§4.4.3.2 批量创建** 单条元素等价的结构后入队;其中 **出口报关方式** 列(若模板配置)映射为 `exportCustomsMode`,规则与 **GET /order/template/download** 标准模板说明、§4.2.1 枚举一致。
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
- 文件格式、模板匹配、**有效订单行数 ≤ 2000** 等约束不满足时拒绝受理。
- **出口报关方式**:列存在且单元格非空时,值须为 §4.2.1 枚举 code,否则拒绝该行或标记失败(与批量 JSON 单条校验一致)。
##### 4.4.3.6 任务进度列表(GET /order/task/list)
###### 1 接口请求示例
**请求 /order/task/list**(GET,Query 参数)
```
?page=1&pageSize=20&status=&keyword=
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"taskName": "批量下单_20240101",
"quantity": 100,
"status": "处理中",
"templateId": 1,
"createTime": "2024-01-01 12:00:00",
"updateTime": "2024-01-01 12:00:00"
}
],
"total": 10
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
##### 4.4.3.7 任务详情(GET /order/task/detail)
###### 1 接口请求示例
**请求 /order/task/detail**(GET,Query 参数)
```
?taskId=1
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"taskName": "批量下单_20240101",
"quantity": 100,
"successCount": 80,
"failCount": 20,
"status": "已完成",
"templateId": 1,
"createTime": "2024-01-01 12:00:00",
"updateTime": "2024-01-01 12:00:00",
"failList": []
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
## 5 渠道模块
### 5.1 核心需求说明
- **销售产品**:管理对外销售的产品(专线/快递/邮政),配置时效、黑名单区、偏远分区、价格分区、计重规则、计重进位等;支持价格配置。
- **产品配置**:产品基础信息(编码、名称、分组、时效、面单、黑名单区、偏远分区、价格分区、计重规则、计重进位等)。
- **产品卖价**:为每个产品配置多条价格项(运费、保险费、燃油附加费等);每条价格项通过弹窗配置:
- **基础信息**:费用项、费用单位、币种、收费时机、收费条件、计费因素(因素+因素值,如货物类型、分区、重量、仓库、客户等)。
- **价格表**:根据计费因素动态生成价格表,支持分区/货物类型/重量等组合、计算公式(总价/单价/续单价)、计算值、客户组。
- **排货规则**:选择排货方案(成本最低/时效最快/综合最优),配置服务商列表(服务商名称、服务渠道、权重、指定客户)。
- **发布记录**:价格与规则的发布历史,支持查看版本内容、回滚到指定版本。
- **服务渠道**:管理下游服务渠道(支持渠道组+渠道层级),配置预报单号类型、时效、买价等。
- **渠道配置**:渠道基础信息(渠道代码、名称、时效、预报配置、描述、预报前脚本等)。
- **渠道买价**:为每个渠道配置多条买价项(运费、保险费、燃油附加费等);每条买价项通过弹窗配置:
- **基础信息**:费用项、费用单位、币种、收费时机、收费条件、计费因素(因素+因素值)。
- **价格表**:根据计费因素动态生成价格表,支持分区/货物类型/重量等组合、计算公式、计算值、客户组。
- **发布记录**:买价与规则的发布历史,支持查看版本内容、回滚到指定版本。
- **黑名单区/偏远分区/价格分区**:共用 KpiArea 表,仅通过 `AreaType` 区分;支持分区及明细(国家/省/市/邮编区间)的增删改查。
- **计重规则/计重进位**:配置计费重量规则与进位方式,明细结构与分区类似。
- **自动触发**:配置下单/入库/出库时的自动拦截、警告、问题件等触发规则。
- **标签设计**:保存 vg-print 导出的标签模板 JSON,支持尾程/客户/入库/出库/sku 等类型。
### 5.2 表结构设计
#### 5.2.1 销售产品表 `t_sales_product`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| ProductCode | VARCHAR | 64 | - | 是 | 产品编码 |
| ProductName | VARCHAR | 128 | - | 是 | 产品名称 |
| ProductGroup | VARCHAR | 32 | - | 否 | 分组(专线/快递/邮政) |
| MinTimeliness | INT | - | - | 否 | 最小时效(天) |
| MaxTimeliness | INT | - | - | 否 | 最大时效(天) |
| CustomerWaybill | VARCHAR | 32 | - | 否 | 系统面单/客户面单 |
| BlacklistAreaId | BIGINT | - | 外键 | 否 | 黑名单区ID |
| RemoteAreaId | BIGINT | - | 外键 | 否 | 偏远分区ID |
| PriceAreaId | BIGINT | - | 外键 | 否 | 价格分区ID(`t_kpi_area`,`AreaType=price`) |
| WeightRuleId | BIGINT | - | 外键 | 否 | 计重规则ID |
| WeightRoundingId | BIGINT | - | 外键 | 否 | 计重进位ID |
| ProductDescription | TEXT | - | - | 否 | 产品描述 |
| PreStorageScript | TEXT | - | - | 否 | 入库前脚本 |
| IsActive | TINYINT | - | - | 是 | 是否启用 |
| Scheme | VARCHAR | 32 | - | 是 | 排货方案:costLowest/timeFastest/overallBest |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.2 产品卖价表 `t_sales_product_price`(子表,关联 t_sales_product)
> 产品卖价主表,每个产品可配置多条价格项(运费、保险费、燃油附加费等)。
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| SalesProductId | BIGINT | - | 外键 | 是 | 销售产品ID |
| SerialNumber | INT | - | - | 是 | 序号 |
| Status | TINYINT | - | - | 是 | 状态:0-禁用,1-启用 |
| PriceName | VARCHAR | 64 | - | 是 | 价格名称(如运费、保险费) |
| CostItem | VARCHAR | 64 | - | 是 | 费用项 |
| CostUnit | VARCHAR | 16 | - | 是 | 费用单位:kg/票/件 |
| Currency | VARCHAR | 16 | - | 是 | 币种:RMB/USD/EUR |
| ChargingTime | VARCHAR | 32 | - | 否 | 收费时机:决定本费用项在订单哪一履约阶段写入 `t_order_fee`(见 **§4.3.4**)。建议存编码:`forecast`(预报)、`inbound`(入库)、`outbound`(出库);展示可对应中文「预报/入库/出库」。**缺省按 `forecast`**。 |
| ChargingCondition | VARCHAR | 64 | - | 否 | 收费条件 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.3 产品卖价-计费因素表 `t_sales_product_price_factor`(子表,关联 t_sales_product_price)
> 弹窗「基础信息」中的计费因素配置,支持货物类型、分区、重量、仓库、客户等。
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| PriceId | BIGINT | - | 外键 | 是 | 产品卖价ID |
| SerialNumber | INT | - | - | 是 | 序号(决定因素排序) |
| Factor | VARCHAR | 64 | - | 是 | 因素:货物类型/分区/重量/仓库/客户 |
| FactorValues | JSON | - | - | 否 | 因素值数组,如 ["普货","敏感货"] |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.4 产品卖价-价格表明细表 `t_sales_product_price_detail`(子表,关联 t_sales_product_price)
> 弹窗「价格表」中的价格明细,表头由计费因素动态生成,存储各组合的价格值。
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| PriceId | BIGINT | - | 外键 | 是 | 产品卖价ID |
| FactorValuesJson | JSON | - | - | 否 | 非客户因素组合,如 {"分区":"欧洲","货物类型":"普货","重量":"0-0.5"} |
| CalculationFormula | VARCHAR | 32 | - | 是 | 计算公式:总价/单价/续单价 |
| CalculatedValue | DECIMAL | 18,4 | - | 否 | 计算值 |
| CustomerGroupValues | JSON | - | - | 否 | 客户组对应价格,如 {"客户组1":10,"客户组2":12} |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.5 排货规则-服务商表 `t_sales_product_allocation_provider`(子表,关联 t_sales_product_allocation_rule)
> 服务商列表:服务商名称、服务渠道、权重、指定客户。
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| SalesProductId | BIGINT | - | 外键 | 是 | 销售产品ID |
| SerialNumber | INT | - | - | 是 | 序号 |
| ProviderName | VARCHAR | 128 | - | 是 | 服务商名称 |
| ServiceChannelId | BIGINT | - | 外键 | 否 | 服务渠道ID |
| Weight | INT | - | - | 是 | 权重(评分用) |
| CustomerIds | JSON | - | - | 否 | 指定客户ID数组,空表示全部 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
**Scheme 枚举**:costLowest(成本最低)、timeFastest(时效最快)、overallBest(综合最优)
#### 5.2.6 发布记录表 `t_sales_product_publish_record`(子表,关联 t_sales_product)
> 价格与规则的发布历史,支持查看版本内容、回滚。
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| SalesProductId | BIGINT | - | 外键 | 是 | 销售产品ID |
| Version | VARCHAR | 32 | - | 是 | 版本号,如 20231227112339 |
| OperatorType | VARCHAR | 16 | - | 是 | 操作类型:price(价格)/rules(规则) |
| Status | VARCHAR | 16 | - | 是 | 状态:生效/回滚/无效 |
| PublishUser | VARCHAR | 64 | - | 是 | 发布人 |
| PublishTime | DATETIME | - | - | 是 | 发布时间 |
| Description | VARCHAR | 512 | - | 否 | 版本备注 |
| Content | JSON | - | - | 否 | 发布内容(价格或规则配置的JSON快照) |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
##### 5.2.6.1 发布内容 `Content`(JSON快照)结构规范(用于限制校验与计费)
> 设计目标:
> 1) **强绑定有效期**:订单创建时按当时的生效版本(PublishTime)绑定发布记录;后续计算以绑定版本为准。
> 2) **计算快**:下单/计费/校验不再联查 `t_kpi_area*`、`t_weight_rule*`、`t_weight_rounding*`、`t_sales_product_price*` 等多表;只读取一次发布记录 `Content`(可缓存)。
> 3) **可回溯**:任何时候都能根据订单绑定的发布记录ID拿到当时的规则/价格快照。
>
> **性能实现要求(强制)**:
> - 发布后的规则/价格快照以 **`t_sales_product_publish_record` 为权威落库**;同步任务将标准化后的 `Content` 写入 **Redis**(按 `salesProductId` + `OperatorType` + `Version` 或 `current` 指针),并通知计费服务刷新**本地内存**。
> - **在线下单/预估只读 Redis + 本地内存**;仅在 Redis/内存异常时**降级**直读发布记录表(见订单模块 `/order/parcel/create` 步骤 4),并应异步回填 Redis。
##### 5.2.6.2 Redis 与发布同步(在线计费)
- **Key 示例**(可按团队规范调整前缀):
- `oms:pricing:sp:{salesProductId}:rules:{version}` / `...:price:{version}`:存放与 `Content` 同构的 JSON(或已序列化的二进制)。
- `oms:pricing:sp:{salesProductId}:rules:current` / `...:price:current`:指向当前对外生效的 `version` 字符串,便于 O(1) 定位。
- **在线匹配数据来源**:计费服务进程内已解析的 `rulesSnapshot` / `priceSnapshot`(来自 Redis 加载或启动预热)。
**发布同步流程(必须)**:
1. 业务侧完成一次规则或价格发布,写入 `t_sales_product_publish_record`(含 `Version`、`Status`、`PublishTime`、`Content`)。
2. 将 `Content` 标准化后写入 Redis(对应 `OperatorType` 的 key);更新 `current` 指针指向新版本(与「生效」语义一致)。
3. 通知各计费实例刷新内存;实例冷启动可从 Redis 全量加载,必要时可从发布表补齐缺失版本。
4. 若 Redis 写入失败:应**阻断或重试发布**,直至线上可见一致快照,避免「库里有版本但线上算价仍用旧缓存」。
###### 1) 通用字段(rules / price 共用)
- `schemaVersion`:快照结构版本(从 1 开始)
- `operatorType`:`rules` 或 `price`(与发布记录 `OperatorType` 保持一致)
- `salesProduct`:发布时的产品基础信息快照(用于展示与校验,不参与联查)
- `effective`:
- `startAt`:生效时间(一般等于 `PublishTime`)
- `endAt`:失效时间(可为空,表示至今有效;当有新版本发布时可由系统补齐旧版本 `endAt`)
- `checksum`:可选,用于校验内容是否被篡改(如 sha256)
###### 2) `rules` 快照内容(限制 + 计重 + 进位)
`Content` 示例(OperatorType=rules):
```json
{
"schemaVersion": 1,
"operatorType": "rules",
"salesProduct": {
"salesProductId": 1,
"productCode": "US_AIR",
"productName": "美国空运专线(普货)",
"cargoAttributes": ["general", "electronics", "sensitive"],
"scheme": "overallBest"
},
"effective": { "startAt": "2026-04-25 14:30:00", "endAt": null },
"areas": {
"blacklist": {
"areaName": "黑名单区1",
"rules": [
{
"countryCode": "US",
"province": null,
"city": null,
"postcodeRanges": [
{ "left": "90000", "right": "90010" },
{ "left": "91000", "right": "91099" }
]
}
]
},
"remote": {
"areaName": "偏远分区1",
"rules": [
{
"countryCode": "US",
"province": "California",
"city": null,
"postcodeRanges": [{ "left": "90001", "right": "90099" }]
}
]
},
"price": {
"areaName": "价格分区(美国)",
"rules": [
{
"zoneCode": "US_WEST",
"zoneName": "美国西部",
"countryCode": "US",
"province": null,
"city": null,
"postcodeRanges": [{ "left": "90000", "right": "96999" }]
}
]
}
},
"weightRule": {
"weightRuleId": 1,
"name": "标准计重规则",
"clientRules": [
{ "volumetricFactor": 6000, "condition": "gw>vw", "billableWeight": "gw" },
{ "volumetricFactor": 6000, "condition": "gw<=vw", "billableWeight": "vw" }
]
},
"weightRounding": {
"weightRoundingId": 1,
"name": "标准进位",
"ranges": [{ "start": 0, "end": 30, "public": "0.5" }]
},
"checksum": ""
}
```
说明:
- `areas.blacklist/remote/price` 将 `t_kpi_area_info` 的明细**扁平化内嵌**到快照里,避免下单时联查分区表。
- `areas.price.rules[*].zoneCode/zoneName` 用于后续计价因子“分区”匹配(对应价格快照的 `factorValuesJson.分区`)。
- `weightRule.clientRules` 将 `t_weight_rule_info` 直接内嵌(**无 `client` 列**);运行时以 §4.4.3.1 步骤 7 解析的 **`effectiveWeightRuleId`** 为准,与快照中 **`weightRuleId`** 对齐或回表查询。
- `weightRounding.ranges` 将 `t_weight_rounding_info` 内嵌(**仅 `public` 进位值**);运行时以步骤 8 的 **`effectiveWeightRoundingId`** 为准。
###### 3) `price` 快照内容(卖价配置 + 价格表)
`Content` 示例(OperatorType=price):
```json
{
"schemaVersion": 1,
"operatorType": "price",
"salesProduct": {
"salesProductId": 1,
"productCode": "US_AIR",
"productName": "美国空运专线(普货)"
},
"effective": { "startAt": "2026-04-25 14:30:00", "endAt": null },
"priceItems": [
{
"serialNumber": 1,
"status": 1,
"priceName": "运费",
"costItem": "freight",
"costUnit": "kg",
"currency": "RMB",
"chargingTime": "预报",
"chargingCondition": "",
"factors": [
{ "serialNumber": 1, "factor": "分区" },
{ "serialNumber": 2, "factor": "重量" }
],
"details": [
{
"factorValuesJson": { "分区": "美国西部", "重量": "0-9999" },
"calculationFormula": "续单价",
"calculatedValue": 0,
"customerGroupValues": null,
"continuePricing": {
"firstWeightKg": 1,
"firstPrice": 18,
"continueUnitKg": 0.5,
"continueUnitPrice": 6,
"roundingUnitKg": 0.5
}
}
]
}
],
"checksum": ""
}
```
说明:
- 为了支持“**首重/续重/进位**”的可落地配置,当 `calculationFormula = 续单价` 时,使用 `continuePricing` 描述完整计费参数(不再依赖额外表字段)。
- `roundingUnitKg` 表示计价侧进位值(如 0.5kg)。若同时存在规则侧进位(`rules` 快照里的 `weightRounding`),**计费重进位以规则侧为准**,`roundingUnitKg` 用于价格项自身的续重单位计算兜底(两者一般配置一致)。
#### 5.2.7 服务渠道组表 `t_service_channel_group`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| Name | VARCHAR | 128 | - | 是 | 渠道组名称 |
| AppId | BIGINT | - | - | 否 | 应用ID(页面新增/编辑服务商时保存) |
| ApiServiceProviderId | BIGINT | - | - | 否 | API服务商ID(页面新增/编辑服务商时保存) |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.8 服务渠道表 `t_service_channel`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| GroupId | BIGINT | - | 外键 | 否 | 渠道组ID(可选) |
| ChannelCode | VARCHAR | 64 | - | 是 | 渠道编码 |
| ChannelName | VARCHAR | 128 | - | 是 | 渠道名称 |
| MinTime | INT | - | - | 否 | 最小时效(天) |
| MaxTime | INT | - | - | 否 | 最大时效(天) |
| ForecastType | VARCHAR | 32 | - | 否 | 预报单号类型(customer/waybill/tracking) |
| ForecastCode | VARCHAR | 128 | - | 否 | 预报单号 |
| Description | TEXT | - | - | 否 | 描述 |
| PreScript | TEXT | - | - | 否 | 入库前脚本 |
| IsActive | TINYINT | - | - | 是 | 是否启用 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.9 渠道买价表 `t_service_channel_price`(子表,关联 t_service_channel)
> 渠道买价主表,每个渠道可配置多条买价项(运费、保险费、燃油附加费等)。
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| ServiceChannelId | BIGINT | - | 外键 | 是 | 服务渠道ID |
| SerialNumber | INT | - | - | 是 | 序号 |
| Status | TINYINT | - | - | 是 | 状态:0-禁用,1-启用 |
| PriceName | VARCHAR | 64 | - | 是 | 价格名称 |
| CostItem | VARCHAR | 64 | - | 是 | 费用项 |
| CostUnit | VARCHAR | 16 | - | 是 | 费用单位:kg/票/件 |
| Currency | VARCHAR | 16 | - | 是 | 币种:RMB/USD/EUR |
| ChargingTime | VARCHAR | 32 | - | 否 | 收费时机:与卖价表 **`ChargingTime` 语义一致**(§4.3.4),决定应付费用项在预报/入库/出库哪一阶段写入 `t_order_fee`(`Type=payable`)。建议编码:`forecast` / `inbound` / `outbound`,缺省 `forecast`。 |
| ChargingCondition | VARCHAR | 64 | - | 否 | 收费条件 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.10 渠道买价-计费因素表 `t_service_channel_price_factor`(子表,关联 t_service_channel_price)
> 弹窗「基础信息」中的计费因素配置。
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| PriceId | BIGINT | - | 外键 | 是 | 渠道买价ID |
| SerialNumber | INT | - | - | 是 | 序号 |
| Factor | VARCHAR | 64 | - | 是 | 因素:货物类型/分区/重量/仓库/客户 |
| FactorValues | JSON | - | - | 否 | 因素值数组 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.11 渠道买价-价格表明细表 `t_service_channel_price_detail`(子表,关联 t_service_channel_price)
> 弹窗「价格表」中的价格明细。
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| PriceId | BIGINT | - | 外键 | 是 | 渠道买价ID |
| FactorValuesJson | JSON | - | - | 否 | 非客户因素组合 |
| CalculationFormula | VARCHAR | 32 | - | 是 | 计算公式:总价/单价/续单价 |
| CalculatedValue | DECIMAL | 18,4 | - | 否 | 计算值 |
| CustomerGroupValues | JSON | - | - | 否 | 客户组对应价格 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.12 服务渠道发布记录表 `t_service_channel_publish_record`(子表,关联 t_service_channel)
> 买价与规则的发布历史。
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| ServiceChannelId | BIGINT | - | 外键 | 是 | 服务渠道ID |
| Version | VARCHAR | 32 | - | 是 | 版本号 |
| OperatorType | VARCHAR | 16 | - | 是 | 操作类型:price(价格)/rules(规则) |
| Status | VARCHAR | 16 | - | 是 | 状态:生效/回滚/无效 |
| PublishUser | VARCHAR | 64 | - | 是 | 发布人 |
| PublishTime | DATETIME | - | - | 是 | 发布时间 |
| Description | VARCHAR | 512 | - | 否 | 版本备注 |
| Content | JSON | - | - | 否 | 发布内容JSON快照 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.13 分区表(黑名单区/偏远分区/价格分区共用)`t_kpi_area`
> 三类分区共用本表,仅通过 `AreaType` 区分业务含义。
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| AreaType | VARCHAR | 32 | - | 是 | 类型: blacklist/remote/price |
| Name | VARCHAR | 128 | - | 是 | 分区名称 |
| SystemName | VARCHAR | 128 | - | 否 | 系统名称 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
**AreaType 枚举**:blacklist(黑名单区)、remote(偏远分区)、price(价格分区)
#### 5.2.14 分区明细表 `t_kpi_area_info`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| AreaId | BIGINT | - | 外键 | 是 | 分区ID |
| CountryCode | VARCHAR | 8 | - | 否 | 国家二字码 |
| CountryName | VARCHAR | 64 | - | 否 | 国家名称 |
| Province | VARCHAR | 128 | - | 否 | 省份 |
| City | VARCHAR | 128 | - | 否 | 城市 |
| LeftPostcode | VARCHAR | 32 | - | 否 | 左侧邮编 |
| RightPostcode | VARCHAR | 32 | - | 否 | 右侧邮编 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.15 计重规则表 `t_weight_rule`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| Name | VARCHAR | 128 | - | 是 | 规则名称 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.16 计重规则明细表 `t_weight_rule_info`(子表,关联 t_weight_rule)
> 计重规则子页面,仅配置**条件表达式**、**计费重取值**、**材积系数**;不再使用「客户/客户组」列。同一 `RuleId` 下多行按 **`Id` 升序**逐条尝试,**第一条**使条件为真的明细生效(算法见 §4.4.3.1 步骤 7)。运行时通过本单绑定 **`t_sales_product.WeightRuleId`** 选用对应 `t_weight_rule`(不同销售产品可绑定不同规则)。
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| RuleId | BIGINT | - | 外键 | 是 | 计重规则ID |
| Condition | VARCHAR | 256 | - | 是 | 条件表达式(如:gw>vw) |
| BillableWeight | VARCHAR | 64 | - | 是 | 计费重取值(如:vw、gw) |
| VolumetricFactor | VARCHAR | 32 | - | 是 | 材积系数(如:6000) |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.17 计重进位表 `t_weight_rounding`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| Name | VARCHAR | 128 | - | 是 | 进位名称 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.18 计重进位明细表 `t_weight_rounding_info`(子表,关联 t_weight_rounding)
> 计重进位子页面,按重量区间配置**单一进位值** `Public`(不再按客户组分列)。运行时通过本单绑定 **`t_sales_product.WeightRoundingId`** 选用对应 `t_weight_rounding`(算法见 §4.4.3.1 步骤 8)。
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| RoundingId | BIGINT | - | 外键 | 是 | 计重进位ID |
| StartValue | DECIMAL | 18,4 | - | 是 | 起始值(kg) |
| EndValue | DECIMAL | 18,4 | - | 是 | 结束值(kg) |
| Public | VARCHAR | 32 | - | 否 | 进位值(kg,如 0.5 表示按 0.5kg 向上取整) |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.19 自动触发表 `t_auto_trigger`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| Status | TINYINT | - | - | 是 | 是否启用 |
| Operation | VARCHAR | 32 | - | 否 | 操作: 拦截/警告/问题件 |
| TriggerTiming | VARCHAR | 32 | - | 否 | 触发时机: 下单/入库/出库 |
| TriggerCondition | TEXT | - | - | 否 | 触发条件 |
| PromptMessage | VARCHAR | 512 | - | 否 | 提示信息 |
| Notifier | VARCHAR | 128 | - | 否 | 通知人 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 5.2.20 标签模板表 `t_label_template`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| Name | VARCHAR | 128 | - | 是 | 模板名称 |
| TemplateJson | JSON | - | - | 否 | vg-print 模板 JSON |
| TemplateType | VARCHAR | 32 | - | 否 | 尾程/客户/入库/出库/sku |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
### 5.3 核心接口设计
#### 5.3.1 销售产品
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 产品列表 | GET | /channel/sales-product/list | 产品列表 |
| 产品详情 | GET | /channel/sales-product/detail | 产品详情 |
| 新增产品 | POST | /channel/sales-product/create | 新增产品 |
| 更新产品 | POST | /channel/sales-product/update | 更新产品(包含产品配置 tab 保存) |
| 删除产品 | POST | /channel/sales-product/delete | 删除产品 |
##### 5.3.1.1 产品列表(GET /channel/sales-product/list)
###### 1 接口请求示例
**请求 /channel/sales-product/list**(GET,Query 参数)
```
?page=1&pageSize=20&productGroup=专线&keyword=&isActive=1
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"productCode": "US-AIR-P",
"productName": "美国空运专线(普货)",
"productGroup": "专线",
"minTimeliness": 3,
"maxTimeliness": 7,
"customerWaybill": "系统面单",
"blacklistAreaId": 1,
"remoteAreaId": 2,
"priceAreaId": 3,
"weightRuleId": 1,
"weightRoundingId": 1,
"productDescription": "美国空运普货专线",
"isActive": 1,
"scheme": "costLowest",
"createTime": "2024-01-01 12:00:00",
"updateTime": "2024-01-01 12:00:00"
}
],
"total": 10
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 接收分页与筛选参数后构建查询条件(productGroup、keyword、isActive)。
###### 校验与异常
- 按分页参数查询销售产品列表,返回 `items` 与 `total`。
##### 5.3.1.2 产品详情(GET /channel/sales-product/detail)
###### 1 接口请求示例
**请求 /channel/sales-product/detail**(GET,Query 参数)
```
?id=1
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"productCode": "US-AIR-P",
"productName": "美国空运专线(普货)",
"productGroup": "专线",
"minTimeliness": 3,
"maxTimeliness": 7,
"customerWaybill": "系统面单",
"blacklistAreaId": 1,
"remoteAreaId": 2,
"priceAreaId": 3,
"weightRuleId": 1,
"weightRoundingId": 1,
"productDescription": "美国空运普货专线",
"preStorageScript": "",
"isActive": 1,
"scheme": "costLowest",
"createTime": "2024-01-01 12:00:00",
"createBy": "admin",
"updateTime": "2024-01-01 12:00:00",
"updateBy": "admin"
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据 `id` 查询产品主数据及扩展配置,组装详情对象返回。
###### 校验与异常
- 若产品不存在或不可访问,返回业务错误码。
##### 5.3.1.3 新增产品(POST /channel/sales-product/create)
###### 1 接口请求示例
**请求 /channel/sales-product/create**(POST)
```json
{
"productCode": "US-AIR-P",
"productName": "美国空运专线(普货)",
"productGroup": "专线",
"minTimeliness": 3,
"maxTimeliness": 7,
"customerWaybill": "系统面单",
"blacklistAreaId": 1,
"remoteAreaId": 2,
"priceAreaId": 3,
"weightRuleId": 1,
"weightRoundingId": 1,
"productDescription": "美国空运普货专线",
"preStorageScript": "",
"isActive": 1,
"scheme": "costLowest"
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 校验产品编码唯一性与必填字段后新增产品记录。
###### 校验与异常
- 初始化产品配置与默认状态,写入审计字段。
##### 5.3.1.4 更新产品(POST /channel/sales-product/update)
###### 1 接口请求示例
**请求 /channel/sales-product/update**(POST)
```json
{
"id": 1,
"productCode": "US-AIR-P",
"productName": "美国空运专线(普货)",
"productGroup": "专线",
"minTimeliness": 3,
"maxTimeliness": 7,
"customerWaybill": "系统面单",
"blacklistAreaId": 1,
"remoteAreaId": 2,
"priceAreaId": 3,
"weightRuleId": 1,
"weightRoundingId": 1,
"productDescription": "美国空运普货专线",
"preStorageScript": "",
"isActive": 1,
"scheme": "costLowest"
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据 `id` 校验产品存在性后更新可变字段(包含产品配置 tab 的保存内容)。
###### 校验与异常
- 记录操作人和更新时间,并触发配置缓存刷新。
##### 5.3.1.5 删除产品(POST /channel/sales-product/delete)
###### 1 接口请求示例
**请求 /channel/sales-product/delete**(POST)
```json
{
"id": 1
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 校验产品是否可删除(无有效关联配置/任务)后执行逻辑删除。
###### 校验与异常
- 更新删除标识及审计字段,避免物理删除带来的历史数据丢失。
#### 5.3.2 产品卖价(子表接口)
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 价格项列表 | GET | /channel/sales-product/price/list | 按产品ID获取价格项列表 |
| 新增价格项 | POST | /channel/sales-product/price/create | 新增价格项(含基础信息+计费因素+价格表) |
| 更新价格项 | POST | /channel/sales-product/price/update | 更新价格项 |
| 删除价格项 | POST | /channel/sales-product/price/delete | 删除价格项 |
| 切换价格项状态 | POST | /channel/sales-product/price/toggleStatus | 启用/禁用价格项 |
##### 5.3.2.1 价格项列表(GET /channel/sales-product/price/list)
###### 1 接口请求示例
**请求 /channel/sales-product/price/list**(GET,Query 参数)
```
?salesProductId=1
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"salesProductId": 1,
"serialNumber": 1,
"status": 1,
"priceName": "运费",
"costItem": "freight",
"costUnit": "kg",
"currency": "RMB",
"chargingTime": "出库",
"chargingCondition": "",
"factors": [
{
"id": 1,
"priceId": 1,
"serialNumber": 1,
"factor": "货物类型",
"factorValues": ["普货", "敏感货"]
}
],
"details": [
{
"id": 1,
"priceId": 1,
"factorValuesJson": {"分区": "欧洲", "货物类型": "普货", "重量": "0-0.5"},
"calculationFormula": "单价",
"calculatedValue": 62,
"customerGroupValues": {"客户组1": 60, "客户组2": 65}
}
],
"version": "20260425143000",
"publishTime": "2026-04-25 14:30:00",
"createTime": "2024-01-01 12:00:00",
"updateTime": "2024-01-01 12:00:00"
}
]
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按 `salesProductId` 查询价格项主表,并聚合返回计费因素与价格明细子数据。
- 结果按 `serialNumber` 升序组织,便于前端按配置顺序渲染。
###### 校验与异常
- 校验 `salesProductId` 必填且产品存在;不存在时返回业务错误。
- 仅返回未删除记录,查询异常返回统一错误码。
##### 5.3.2.2 新增价格项(POST /channel/sales-product/price/create)
###### 1 接口请求示例
**请求 /channel/sales-product/price/create**(POST)
```json
{
"salesProductId": 1,
"serialNumber": 1,
"status": 1,
"priceName": "运费",
"costItem": "freight",
"costUnit": "kg",
"currency": "RMB",
"chargingTime": "出库",
"chargingCondition": "",
"factors": [
{
"serialNumber": 1,
"factor": "货物类型",
"factorValues": ["普货", "敏感货"]
},
{
"serialNumber": 2,
"factor": "分区",
"factorValues": ["欧洲", "美国"]
}
],
"details": [
{
"factorValuesJson": {"分区": "欧洲", "货物类型": "普货", "重量": "0-0.5"},
"calculationFormula": "单价",
"calculatedValue": 62,
"customerGroupValues": {"客户组1": 60, "客户组2": 65}
}
]
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 新增价格项主记录,并写入对应 `factors` 与 `details` 子记录。
- 同步写入审计字段,保持主从数据一致性。
###### 校验与异常
- 校验 `salesProductId`、`priceName`、`costItem`、`costUnit` 等必填字段。
- 校验同产品下 `serialNumber` 不冲突;冲突时返回业务错误。
##### 5.3.2.3 更新价格项(POST /channel/sales-product/price/update)
###### 1 接口请求示例
**请求 /channel/sales-product/price/update**(POST)
```json
{
"id": 1,
"salesProductId": 1,
"serialNumber": 1,
"status": 1,
"priceName": "运费",
"costItem": "freight",
"costUnit": "kg",
"currency": "RMB",
"chargingTime": "出库",
"chargingCondition": "",
"factors": [],
"details": []
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按 `id` 更新价格项主数据,并按最新请求重建/同步因素与明细子数据。
- 保留版本追溯能力,更新后可参与后续发布流程。
###### 校验与异常
- 校验 `id` 存在且归属 `salesProductId`。
- 校验更新后配置合法(计算公式、阶梯区间、客户组取值等),不合法时返回业务错误。
##### 5.3.2.4 删除价格项(POST /channel/sales-product/price/delete)
###### 1 接口请求示例
**请求 /channel/sales-product/price/delete**(POST)
```json
{
"id": 1
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按价格项 `id` 执行逻辑删除,并处理关联子表数据的可见性。
###### 校验与异常
- 校验 `id` 存在且允许删除(如未处于不可删状态)。
- 删除失败返回统一业务错误码。
##### 5.3.2.5 切换价格项状态(POST /channel/sales-product/price/toggleStatus)
###### 1 接口请求示例
**请求 /channel/sales-product/price/toggleStatus**(POST)
```json
{
"id": 1,
"status": 0
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按 `id` 更新价格项启用状态(`status`),并刷新相关缓存。
###### 校验与异常
- 校验 `status` 仅允许合法枚举值(启用/禁用)。
- 校验 `id` 存在,不存在时返回业务错误。
#### 5.3.3 排货规则(子表接口)
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 获取排货规则 | GET | /channel/sales-product/allocation/get | 按产品ID获取排货方案及服务商列表 |
| 保存排货规则 | POST | /channel/sales-product/allocation/save | 保存排货方案及服务商(保存并可选发布) |
##### 5.3.3.1 获取排货规则(GET /channel/sales-product/allocation/get)
###### 1 接口请求示例
**请求 /channel/sales-product/allocation/get**(GET,Query 参数)
```
?salesProductId=1
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"salesProductId": 1,
"scheme": "costLowest",
"providers": [
{
"id": 1,
"salesProductId": 1,
"serialNumber": 1,
"providerName": "一代物流",
"serviceChannelId": 10,
"serviceChannelName": "DHL美国专线",
"weight": 100,
"customerIds": [1, 2],
"createTime": "2024-01-01 12:00:00",
"updateTime": "2024-01-01 12:00:00"
}
]
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据 `salesProductId` 查询排货方案与服务商列表并按顺序返回。
###### 校验与异常
- 校验 `salesProductId` 必填且产品存在;不存在时返回业务错误。
##### 5.3.3.2 保存排货规则(POST /channel/sales-product/allocation/save)
###### 1 接口请求示例
**请求 /channel/sales-product/allocation/save**(POST)
```json
{
"salesProductId": 1,
"publish": false,
"scheme": "costLowest",
"providers": [
{
"id": 0,
"serialNumber": 1,
"providerName": "一代物流",
"serviceChannelId": 10,
"weight": 100,
"customerIds": [1, 2]
}
]
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 保存排货策略与服务商配置,按 `id` 判定新增或更新。
- 当 `publish=true` 时触发规则发布并生成版本记录。
###### 校验与异常
- 校验 `salesProductId`、`scheme` 与 `providers` 合法性。
- 保存失败回滚事务并返回统一错误信息。
#### 5.3.4 发布记录(子表接口)
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 价格发布记录列表 | GET | /channel/sales-product/publish/price/list | 按产品ID获取价格发布历史 |
| 规则发布记录列表 | GET | /channel/sales-product/publish/rules/list | 按产品ID获取规则发布历史 |
| 发布 | POST | /channel/sales-product/publish | 发布当前配置(价格或规则) |
| 回滚到版本 | POST | /channel/sales-product/publish/rollback | 回滚到指定版本 |
##### 5.3.4.1 价格发布记录列表(GET /channel/sales-product/publish/price/list)
###### 1 接口请求示例
**请求 /channel/sales-product/publish/price/list**(GET,Query 参数)
```
?salesProductId=1&page=1&pageSize=20
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"salesProductId": 1,
"version": "20231227112339",
"operatorType": "price",
"status": "生效",
"publishUser": "admin",
"publishTime": "2023-12-27 11:23:39",
"description": "价格调整",
"content": {},
"createTime": "2023-12-27 11:23:39"
}
],
"total": 5
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按产品ID分页查询价格类发布历史,返回版本与发布人信息。
###### 校验与异常
- 校验 `salesProductId` 与分页参数合法性,异常时返回统一错误码。
##### 5.3.4.2 规则发布记录列表(GET /channel/sales-product/publish/rules/list)
###### 1 接口请求示例
**请求 /channel/sales-product/publish/rules/list**(GET,Query 参数)
```
?salesProductId=1&page=1&pageSize=20
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 2,
"salesProductId": 1,
"version": "20231228103000",
"operatorType": "rules",
"status": "生效",
"publishUser": "admin",
"publishTime": "2023-12-28 10:30:00",
"description": "规则调整",
"content": {},
"createTime": "2023-12-28 10:30:00"
}
],
"total": 3
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按产品ID分页查询规则类发布历史,`operatorType` 固定为 `rules`。
###### 校验与异常
- 校验 `salesProductId` 与分页参数合法性,异常时返回统一错误码。
##### 5.3.4.3 发布(POST /channel/sales-product/publish)
###### 1 接口请求示例
**请求 /channel/sales-product/publish**(POST)
```json
{
"salesProductId": 1,
"operatorType": "price",
"description": "价格调整"
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 将当前配置发布为新版本并写入发布记录。
###### 校验与异常
- 校验 `salesProductId`、`operatorType` 必填且可发布。
##### 5.3.4.4 回滚到版本(POST /channel/sales-product/publish/rollback)
###### 1 接口请求示例
**请求 /channel/sales-product/publish/rollback**(POST)
```json
{
"salesProductId": 1,
"recordId": 1,
"operatorType": "price"
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 将配置回滚到指定发布版本并生成回滚记录。
###### 校验与异常
- 校验 `recordId` 版本存在且与产品、类型一致,不一致时返回业务错误。
#### 5.3.5 服务渠道
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 渠道列表 | GET | /channel/service-channel/list | 渠道列表(含渠道组层级) |
| 渠道详情 | GET | /channel/service-channel/detail | 渠道详情 |
| 新增渠道 | POST | /channel/service-channel/create | 新增渠道 |
| 更新渠道 | POST | /channel/service-channel/update | 更新渠道(包含渠道配置 tab 保存) |
| 删除渠道 | POST | /channel/service-channel/delete | 删除渠道 |
| 服务商分组列表 | GET | /channel/service-channel/group/list | 获取服务商分组列表(下拉/筛选) |
| 新增服务商 | POST | /channel/service-channel/group/create | 新增服务商分组(写入 `t_service_channel_group`,保存应用ID与API服务商ID) |
| 编辑服务商 | POST | /channel/service-channel/group/update | 编辑服务商分组(更新名称/应用ID/API服务商ID) |
| 删除服务商 | POST | /channel/service-channel/group/delete | 删除服务商分组(若分组下存在渠道需禁止删除) |
##### 5.3.5.1 渠道列表(GET /channel/service-channel/list)
###### 1 接口请求示例
**请求 /channel/service-channel/list**(GET,Query 参数)
```
?page=1&pageSize=20&groupId=&keyword=&isActive=1
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"groupId": 1,
"groupName": "DHL渠道组",
"channelCode": "DHL-US",
"channelName": "DHL美国专线",
"minTime": 3,
"maxTime": 7,
"forecastType": "waybill",
"forecastCode": "",
"description": "DHL美国专线渠道",
"isActive": 1,
"createTime": "2024-01-01 12:00:00",
"updateTime": "2024-01-01 12:00:00"
}
],
"total": 10
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按分页与筛选条件查询渠道列表,返回渠道及所属分组信息。
###### 校验与异常
- 校验分页参数合法性,异常时返回统一错误码。
##### 5.3.5.2 渠道详情(GET /channel/service-channel/detail)
###### 1 接口请求示例
**请求 /channel/service-channel/detail**(GET,Query 参数)
```
?id=1
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"groupId": 1,
"groupName": "DHL渠道组",
"channelCode": "DHL-US",
"channelName": "DHL美国专线",
"minTime": 3,
"maxTime": 7,
"forecastType": "waybill",
"forecastCode": "",
"description": "DHL美国专线渠道",
"preScript": "",
"isActive": 1,
"createTime": "2024-01-01 12:00:00",
"createBy": "admin",
"updateTime": "2024-01-01 12:00:00",
"updateBy": "admin"
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据渠道 `id` 查询渠道详情与配置信息。
###### 校验与异常
- 校验 `id` 必填且存在,不存在时返回业务错误。
##### 5.3.5.3 新增渠道(POST /channel/service-channel/create)
###### 1 接口请求示例
**请求 /channel/service-channel/create**(POST)
```json
{
"groupId": 1,
"channelCode": "DHL-US",
"channelName": "DHL美国专线",
"minTime": 3,
"maxTime": 7,
"forecastType": "waybill",
"forecastCode": "",
"description": "DHL美国专线渠道",
"preScript": "",
"isActive": 1
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 新增渠道基础配置并写入审计字段。
###### 校验与异常
- 校验 `groupId`、`channelCode`、`channelName` 等必填字段,编码冲突时返回业务错误。
##### 5.3.5.4 服务商分组列表(GET /channel/service-channel/group/list)
###### 1 接口请求示例
**请求 /channel/service-channel/group/list**(GET,Query 参数)
```
?keyword=&appId=&apiServiceProviderId=
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": [
{
"id": 1,
"name": "壹号",
"appId": 8,
"apiServiceProviderId": 41,
"createTime": "2026-05-04 14:15:45",
"updateTime": "1900-01-01 00:00:00"
}
]
}
```
###### 2 接口业务逻辑
###### 核心流程
- 查询 `t_service_channel_group` 未删除分组,可按 `keyword`、`appId`、`apiServiceProviderId` 过滤,按 `id` 倒序返回。
###### 校验与异常
- 过滤参数格式非法时返回业务错误;无数据时返回空数组。
##### 5.3.5.5 新增服务商(POST /channel/service-channel/group/create)
###### 1 接口请求示例
**请求 /channel/service-channel/group/create**(POST)
```json
{
"name": "DHL服务商",
"appId": 1,
"apiServiceProviderId": 12
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 在 `t_service_channel_group` 新增服务商分组并写入应用ID与API服务商ID。
###### 校验与异常
- 校验 `name`、`appId`、`apiServiceProviderId` 必填且有效。
##### 5.3.5.6 编辑服务商(POST /channel/service-channel/group/update)
###### 1 接口请求示例
**请求 /channel/service-channel/group/update**(POST)
```json
{
"id": 1,
"name": "DHL服务商(更新)",
"appId": 1,
"apiServiceProviderId": 12
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据分组 `id` 更新服务商名称、应用ID与API服务商ID。
###### 校验与异常
- 校验 `id` 存在且可编辑,不存在时返回业务错误。
##### 5.3.5.7 删除服务商(POST /channel/service-channel/group/delete)
###### 1 接口请求示例
**请求 /channel/service-channel/group/delete**(POST)
```json
{
"id": 1
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据分组 `id` 执行逻辑删除。
###### 校验与异常
- 若分组下存在渠道则禁止删除并返回业务错误。
##### 5.3.5.8 更新渠道(POST /channel/service-channel/update)
###### 1 接口请求示例
**请求 /channel/service-channel/update**(POST)
```json
{
"id": 1,
"groupId": 1,
"channelCode": "DHL-US",
"channelName": "DHL美国专线",
"minTime": 3,
"maxTime": 7,
"forecastType": "waybill",
"forecastCode": "",
"description": "DHL美国专线渠道",
"preScript": "",
"isActive": 1
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据 `id` 更新渠道基础信息与渠道配置 tab 数据。
###### 校验与异常
- 校验 `id` 存在及字段合法性,更新失败返回业务错误。
##### 5.3.5.9 删除渠道(POST /channel/service-channel/delete)
###### 1 接口请求示例
**请求 /channel/service-channel/delete**(POST)
```json
{
"id": 1
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 校验渠道是否可删除后执行逻辑删除并保留历史审计信息。
###### 校验与异常
- 存在关联配置或不可删除状态时返回业务错误。
#### 5.3.6 渠道买价(子表接口)
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 买价项列表 | GET | /channel/service-channel/price/list | 按渠道ID获取买价项列表 |
| 新增买价项 | POST | /channel/service-channel/price/create | 新增买价项(含基础信息+计费因素+价格表) |
| 更新买价项 | POST | /channel/service-channel/price/update | 更新买价项 |
| 删除买价项 | POST | /channel/service-channel/price/delete | 删除买价项 |
| 切换买价项状态 | POST | /channel/service-channel/price/toggleStatus | 启用/禁用买价项 |
##### 5.3.6.1 买价项列表(GET /channel/service-channel/price/list)
###### 1 接口请求示例
**请求 /channel/service-channel/price/list**(GET,Query 参数)
```
?serviceChannelId=1
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"serviceChannelId": 1,
"serialNumber": 1,
"status": 1,
"priceName": "运费",
"costItem": "freight",
"costUnit": "kg",
"currency": "RMB",
"chargingTime": "出库",
"chargingCondition": "",
"factors": [],
"details": [],
"createTime": "2024-01-01 12:00:00",
"updateTime": "2024-01-01 12:00:00"
}
]
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按 `serviceChannelId` 查询买价项及其因素、明细子项。
###### 校验与异常
- 校验 `serviceChannelId` 必填且渠道存在;异常返回统一错误码。
##### 5.3.6.2 新增买价项(POST /channel/service-channel/price/create)
###### 1 接口请求示例
**请求 /channel/service-channel/price/create**(POST)
```json
{
"serviceChannelId": 1,
"serialNumber": 1,
"status": 1,
"priceName": "运费",
"costItem": "freight",
"costUnit": "kg",
"currency": "RMB",
"chargingTime": "出库",
"chargingCondition": "",
"factors": [
{
"serialNumber": 1,
"factor": "货物类型",
"factorValues": ["普货", "敏感货"]
}
],
"details": [
{
"factorValuesJson": {"分区": "欧洲", "货物类型": "普货", "重量": "0-0.5"},
"calculationFormula": "单价",
"calculatedValue": 45,
"customerGroupValues": {}
}
]
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 新增买价主记录并写入因素、明细子表。
###### 校验与异常
- 校验必填参数与序号冲突规则,校验失败返回业务错误。
##### 5.3.6.3 更新买价项(POST /channel/service-channel/price/update)
###### 1 接口请求示例
**请求 /channel/service-channel/price/update**(POST)
```json
{
"id": 1,
"serviceChannelId": 1,
"serialNumber": 1,
"status": 1,
"priceName": "运费",
"costItem": "freight",
"costUnit": "kg",
"currency": "RMB",
"chargingTime": "出库",
"chargingCondition": "",
"factors": [],
"details": []
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按 `id` 更新买价主数据并同步子项明细。
###### 校验与异常
- 校验 `id` 存在且归属渠道一致,校验失败返回业务错误。
##### 5.3.6.4 删除买价项(POST /channel/service-channel/price/delete)
###### 1 接口请求示例
**请求 /channel/service-channel/price/delete**(POST)
```json
{
"id": 1
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据 `id` 逻辑删除买价项并处理关联子项可见性。
###### 校验与异常
- 校验 `id` 存在且允许删除。
##### 5.3.6.5 切换买价项状态(POST /channel/service-channel/price/toggleStatus)
###### 1 接口请求示例
**请求 /channel/service-channel/price/toggleStatus**(POST)
```json
{
"id": 1,
"status": 0
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按 `id` 更新买价项启用状态并刷新缓存。
###### 校验与异常
- 校验 `status` 枚举与 `id` 存在性,不合法时返回业务错误。
#### 5.3.7 服务渠道发布记录(子表接口)
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 价格发布记录列表 | GET | /channel/service-channel/publish/price/list | 按渠道ID获取买价发布历史 |
| 规则发布记录列表 | GET | /channel/service-channel/publish/rules/list | 按渠道ID获取规则发布历史 |
| 发布 | POST | /channel/service-channel/publish | 发布当前配置(买价或规则) |
| 回滚到版本 | POST | /channel/service-channel/publish/rollback | 回滚到指定版本 |
##### 5.3.7.1 价格发布记录列表(GET /channel/service-channel/publish/price/list)
###### 1 接口请求示例
**请求 /channel/service-channel/publish/price/list**(GET,Query 参数)
```
?serviceChannelId=1&page=1&pageSize=20
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"serviceChannelId": 1,
"version": "20231227112339",
"operatorType": "price",
"status": "生效",
"publishUser": "admin",
"publishTime": "2023-12-27 11:23:39",
"description": "买价调整",
"content": {},
"createTime": "2023-12-27 11:23:39"
}
],
"total": 5
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按渠道ID分页查询买价发布记录并返回版本信息。
###### 校验与异常
- 校验 `serviceChannelId` 与分页参数合法性。
##### 5.3.7.2 规则发布记录列表(GET /channel/service-channel/publish/rules/list)
###### 1 接口请求示例
**请求 /channel/service-channel/publish/rules/list**(GET,Query 参数)
```
?serviceChannelId=1&page=1&pageSize=20
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 2,
"serviceChannelId": 1,
"version": "20231228103000",
"operatorType": "rules",
"status": "生效",
"publishUser": "admin",
"publishTime": "2023-12-28 10:30:00",
"description": "规则调整",
"content": {},
"createTime": "2023-12-28 10:30:00"
}
],
"total": 3
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按渠道ID分页查询规则发布记录,`operatorType` 固定为 `rules`。
###### 校验与异常
- 校验 `serviceChannelId` 与分页参数合法性。
##### 5.3.7.3 发布(POST /channel/service-channel/publish)
###### 1 接口请求示例
**请求 /channel/service-channel/publish**(POST)
```json
{
"serviceChannelId": 1,
"operatorType": "price",
"description": "买价调整"
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 发布当前渠道配置并生成新的发布版本记录。
###### 校验与异常
- 校验 `serviceChannelId`、`operatorType` 必填且可发布。
##### 5.3.7.4 回滚到版本(POST /channel/service-channel/publish/rollback)
###### 1 接口请求示例
**请求 /channel/service-channel/publish/rollback**(POST)
```json
{
"serviceChannelId": 1,
"recordId": 1,
"operatorType": "price"
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 回滚到指定版本并生成回滚操作记录。
###### 校验与异常
- 校验 `recordId` 与渠道、类型匹配关系,异常时返回业务错误。
#### 5.3.8 黑名单区 / 偏远分区 / 价格分区(共用 KpiArea)
> 共用 `/channel/kpi-area/*` 接口,仅通过请求/响应中的 `areaType`(对应表字段 `AreaType`)区分分区类型。
| 类型 | areaType |
| --- | --- |
| 黑名单区 | blacklist |
| 偏远分区 | remote |
| 价格分区 | price |
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 创建分区 | POST | /channel/kpi-area/create | 创建分区 |
| 修改分区 | POST | /channel/kpi-area/update | 修改分区 |
| 删除分区 | POST | /channel/kpi-area/delete | 删除分区 |
| 分区列表 | POST | /channel/kpi-area/list | 分区列表 |
| 新增分区明细 | POST | /channel/kpi-area/info/add | 新增分区明细 |
| 修改分区明细 | POST | /channel/kpi-area/info/update | 修改分区明细 |
| 删除分区明细 | POST | /channel/kpi-area/info/delete | 删除分区明细 |
| 分区明细列表 | POST | /channel/kpi-area/info/list | 分区明细列表 |
##### 5.3.8.1 创建分区(POST /channel/kpi-area/create)
###### 1 接口请求示例
**请求 /channel/kpi-area/create**(POST)
```json
{
"areaType": "blacklist",
"name": "黑名单区1",
"systemName": "系统黑名单"
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据 `areaType` 创建分区主记录。
###### 校验与异常
- 校验 `areaType`、`name` 必填且合法。
##### 5.3.8.2 修改分区(POST /channel/kpi-area/update)
###### 1 接口请求示例
**请求 /channel/kpi-area/update**(POST)
```json
{
"id": 1,
"areaType": "blacklist",
"name": "黑名单区1",
"systemName": "系统黑名单"
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据 `id` 更新分区名称与系统名称。
###### 校验与异常
- 校验 `id` 存在且与 `areaType` 匹配。
##### 5.3.8.3 删除分区(POST /channel/kpi-area/delete)
###### 1 接口请求示例
**请求 /channel/kpi-area/delete**(POST)
```json
{
"id": 1
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据 `id` 执行分区逻辑删除。
###### 校验与异常
- 分区存在明细时按业务规则拦截或级联处理。
##### 5.3.8.4 分区列表(POST /channel/kpi-area/list)
###### 1 接口请求示例
**请求 /channel/kpi-area/list**(POST)
```json
{
"areaType": "blacklist",
"page": 1,
"pageSize": 20
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"areaType": "blacklist",
"name": "黑名单区1",
"systemName": "系统黑名单",
"createTime": "2024-01-01 12:00:00",
"updateTime": "2024-01-01 12:00:00"
}
],
"total": 5
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按 `areaType` 分页查询分区列表。
###### 校验与异常
- 校验分页参数与 `areaType` 合法性。
##### 5.3.8.5 新增分区明细(POST /channel/kpi-area/info/add)
###### 1 接口请求示例
**请求 /channel/kpi-area/info/add**(POST)
```json
{
"areaId": 1,
"countryCode": "US",
"countryName": "美国",
"province": "加州",
"city": "洛杉矶",
"leftPostcode": "90001",
"rightPostcode": "90099"
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 向指定分区新增明细记录(国家/省市/邮编区间)。
###### 校验与异常
- 校验 `areaId` 存在且区间数据合法。
##### 5.3.8.6 修改分区明细(POST /channel/kpi-area/info/update)
###### 1 接口请求示例
**请求 /channel/kpi-area/info/update**(POST)
```json
{
"id": 1,
"areaId": 1,
"countryCode": "US",
"countryName": "美国",
"province": "加州",
"city": "洛杉矶",
"leftPostcode": "90001",
"rightPostcode": "90099"
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据明细 `id` 更新分区明细字段。
###### 校验与异常
- 校验 `id` 存在,更新内容合法。
##### 5.3.8.7 删除分区明细(POST /channel/kpi-area/info/delete)
###### 1 接口请求示例
**请求 /channel/kpi-area/info/delete**(POST)
```json
{
"id": 1
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据明细 `id` 执行逻辑删除。
###### 校验与异常
- 校验 `id` 存在,不存在时返回业务错误。
##### 5.3.8.8 分区明细列表(POST /channel/kpi-area/info/list)
###### 1 接口请求示例
**请求 /channel/kpi-area/info/list**(POST)
```json
{
"areaId": 1,
"page": 1,
"pageSize": 20
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"areaId": 1,
"countryCode": "US",
"countryName": "美国",
"province": "加州",
"city": "洛杉矶",
"leftPostcode": "90001",
"rightPostcode": "90099",
"createTime": "2024-01-01 12:00:00",
"updateTime": "2024-01-01 12:00:00"
}
],
"total": 10
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按 `areaId` 分页查询分区明细列表。
###### 校验与异常
- 校验 `areaId` 与分页参数合法性。
#### 5.3.9 计重规则 / 计重进位
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 规则列表 | POST | /channel/weight-rule/list | 规则列表 |
| 创建规则 | POST | /channel/weight-rule/create | 创建规则 |
| 更新规则 | POST | /channel/weight-rule/update | 更新规则 |
| 删除规则 | POST | /channel/weight-rule/delete | 删除规则 |
| 规则明细列表 | POST | /channel/weight-rule/info/list | 规则明细列表 |
| 新增明细 | POST | /channel/weight-rule/info/add | 新增明细 |
| 更新明细 | POST | /channel/weight-rule/info/update | 更新明细 |
| 删除明细 | POST | /channel/weight-rule/info/delete | 删除明细 |
| 进位列表 | POST | /channel/weight-rounding/list | 进位列表 |
| 创建进位 | POST | /channel/weight-rounding/create | 创建进位 |
| 更新进位 | POST | /channel/weight-rounding/update | 更新进位 |
| 删除进位 | POST | /channel/weight-rounding/delete | 删除进位 |
| 进位明细列表 | POST | /channel/weight-rounding/info/list | 进位明细列表 |
| 新增进位明细 | POST | /channel/weight-rounding/info/add | 新增进位明细 |
| 更新进位明细 | POST | /channel/weight-rounding/info/update | 更新进位明细 |
| 删除进位明细 | POST | /channel/weight-rounding/info/delete | 删除进位明细 |
##### 5.3.9.1 规则列表(POST /channel/weight-rule/list)
###### 1 接口请求示例
**请求 /channel/weight-rule/list**(POST)
```json
{
"page": 1,
"pageSize": 20
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": { "items": [{ "id": 1, "name": "标准计重规则" }], "total": 5 }
}
```
###### 2 接口业务逻辑
###### 核心流程
- 分页查询计重规则主表。
###### 校验与异常
- 校验分页参数合法性。
##### 5.3.9.2 创建规则(POST /channel/weight-rule/create)
###### 1 接口请求示例
**请求 /channel/weight-rule/create**(POST)
```json
{ "name": "标准计重规则" }
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 新增计重规则并写入审计信息。
###### 校验与异常
- 校验 `name` 必填且不重复。
##### 5.3.9.3 更新规则(POST /channel/weight-rule/update)
###### 1 接口请求示例
**请求 /channel/weight-rule/update**(POST)
```json
{ "id": 1, "name": "标准计重规则" }
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 根据 `id` 更新规则信息。
###### 校验与异常
- 校验 `id` 存在。
##### 5.3.9.4 删除规则(POST /channel/weight-rule/delete)
###### 1 接口请求示例
**请求 /channel/weight-rule/delete**(POST)
```json
{ "id": 1 }
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 对规则执行逻辑删除。
###### 校验与异常
- 校验是否存在关联明细。
##### 5.3.9.5 规则明细列表(POST /channel/weight-rule/info/list)
###### 1 接口请求示例
**请求 /channel/weight-rule/info/list**(POST)
```json
{ "ruleId": 1, "page": 1, "pageSize": 20 }
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": { "items": [{ "id": 1, "ruleId": 1, "condition": "gw>vw", "billableWeight": "gw", "volumetricFactor": "6000" }], "total": 3 }
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按 `ruleId` 分页查询规则明细。
###### 校验与异常
- 校验 `ruleId` 必填且有效。
##### 5.3.9.6 新增明细(POST /channel/weight-rule/info/add)
###### 1 接口请求示例
**请求 /channel/weight-rule/info/add**(POST)
```json
{ "ruleId": 1, "condition": "gw>vw", "billableWeight": "vw", "volumetricFactor": "6000" }
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 新增规则明细。
###### 校验与异常
- 校验 `ruleId` 与字段合法性。
##### 5.3.9.7 更新明细(POST /channel/weight-rule/info/update)
###### 1 接口请求示例
**请求 /channel/weight-rule/info/update**(POST)
```json
{ "id": 1, "ruleId": 1, "condition": "gw>vw", "billableWeight": "vw", "volumetricFactor": "6000" }
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 根据 `id` 更新规则明细。
###### 校验与异常
- 校验 `id` 存在且归属正确。
##### 5.3.9.8 删除明细(POST /channel/weight-rule/info/delete)
###### 1 接口请求示例
**请求 /channel/weight-rule/info/delete**(POST)
```json
{ "id": 1 }
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 删除规则明细(逻辑删除)。
###### 校验与异常
- 校验 `id` 存在。
##### 5.3.9.9 进位列表(POST /channel/weight-rounding/list)
###### 1 接口请求示例
**请求 /channel/weight-rounding/list**(POST)
```json
{ "page": 1, "pageSize": 20 }
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": { "items": [{ "id": 1, "name": "标准进位" }], "total": 5 }
}
```
###### 2 接口业务逻辑
###### 核心流程
- 分页查询计重进位主表。
###### 校验与异常
- 校验分页参数。
##### 5.3.9.10 创建进位(POST /channel/weight-rounding/create)
###### 1 接口请求示例
**请求 /channel/weight-rounding/create**(POST)
```json
{ "name": "标准进位" }
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 新增进位规则。
###### 校验与异常
- 校验 `name` 必填。
##### 5.3.9.11 更新进位(POST /channel/weight-rounding/update)
###### 1 接口请求示例
**请求 /channel/weight-rounding/update**(POST)
```json
{ "id": 1, "name": "标准进位" }
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 根据 `id` 更新进位规则。
###### 校验与异常
- 校验 `id` 存在。
##### 5.3.9.12 删除进位(POST /channel/weight-rounding/delete)
###### 1 接口请求示例
**请求 /channel/weight-rounding/delete**(POST)
```json
{ "id": 1 }
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 逻辑删除进位规则。
###### 校验与异常
- 校验是否可删除。
##### 5.3.9.13 进位明细列表(POST /channel/weight-rounding/info/list)
###### 1 接口请求示例
**请求 /channel/weight-rounding/info/list**(POST)
```json
{ "roundingId": 1, "page": 1, "pageSize": 20 }
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": { "items": [{ "id": 1, "roundingId": 1, "startValue": 0, "endValue": 1, "public": "0.5" }], "total": 5 }
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按 `roundingId` 分页查询进位明细。
###### 校验与异常
- 校验 `roundingId` 存在。
##### 5.3.9.14 新增进位明细(POST /channel/weight-rounding/info/add)
###### 1 接口请求示例
**请求 /channel/weight-rounding/info/add**(POST)
```json
{ "roundingId": 1, "startValue": 0, "endValue": 1, "public": "0.5" }
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 新增进位明细。
###### 校验与异常
- 校验区间与 `public` 进位值合法性。
##### 5.3.9.15 更新进位明细(POST /channel/weight-rounding/info/update)
###### 1 接口请求示例
**请求 /channel/weight-rounding/info/update**(POST)
```json
{ "id": 1, "roundingId": 1, "startValue": 0, "endValue": 1, "public": "0.5" }
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 根据 `id` 更新进位明细。
###### 校验与异常
- 校验 `id` 存在且归属正确。
##### 5.3.9.16 删除进位明细(POST /channel/weight-rounding/info/delete)
###### 1 接口请求示例
**请求 /channel/weight-rounding/info/delete**(POST)
```json
{ "id": 1 }
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 删除进位明细(逻辑删除)。
###### 校验与异常
- 校验 `id` 存在。
#### 5.3.10 自动触发
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 列表 | GET | /channel/auto-trigger/list | 列表 |
| 新增 | POST | /channel/auto-trigger/create | 新增 |
| 更新 | POST | /channel/auto-trigger/update | 更新 |
| 删除 | POST | /channel/auto-trigger/delete | 删除 |
| 切换启用状态 | POST | /channel/auto-trigger/toggleStatus | 切换启用状态 |
##### 5.3.10.1 列表(GET /channel/auto-trigger/list)
###### 1 接口请求示例
**请求 /channel/auto-trigger/list**(GET,Query 参数)
```
?page=1&pageSize=20&operation=&triggerTiming=
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": { "items": [{ "id": 1, "operation": "拦截", "triggerTiming": "下单" }], "total": 5 }
}
```
###### 2 接口业务逻辑
###### 核心流程
- 分页查询自动触发规则列表。
###### 校验与异常
- 校验分页与筛选参数合法。
##### 5.3.10.2 新增(POST /channel/auto-trigger/create)
###### 1 接口请求示例
**请求 /channel/auto-trigger/create**(POST)
```json
{
"status": 1,
"operation": "拦截",
"triggerTiming": "下单",
"triggerCondition": "weight>30",
"promptMessage": "重量超过30kg,请确认",
"notifier": "admin@example.com"
}
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 新增自动触发规则。
###### 校验与异常
- 校验关键字段必填。
##### 5.3.10.3 更新(POST /channel/auto-trigger/update)
###### 1 接口请求示例
**请求 /channel/auto-trigger/update**(POST)
```json
{
"id": 1,
"status": 1,
"operation": "拦截",
"triggerTiming": "下单",
"triggerCondition": "weight>30",
"promptMessage": "重量超过30kg,请确认",
"notifier": "admin@example.com"
}
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 根据 `id` 更新自动触发规则。
###### 校验与异常
- 校验 `id` 存在且可编辑。
##### 5.3.10.4 删除(POST /channel/auto-trigger/delete)
###### 1 接口请求示例
**请求 /channel/auto-trigger/delete**(POST)
```json
{ "id": 1 }
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 逻辑删除自动触发规则。
###### 校验与异常
- 校验 `id` 存在且可删除。
##### 5.3.10.5 切换启用状态(POST /channel/auto-trigger/toggleStatus)
###### 1 接口请求示例
**请求 /channel/auto-trigger/toggleStatus**(POST)
```json
{ "id": 1, "status": 0 }
```
**响应**
```json
{ "code": 0, "msg": "success", "data": null }
```
###### 2 接口业务逻辑
###### 核心流程
- 按 `id` 切换规则启用状态。
###### 校验与异常
- 校验 `status` 枚举合法性。
#### 5.3.11 标签设计
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 保存标签模板 | POST | /channel/label-template/save | 保存标签模板 |
###### 1 接口请求示例
**请求 /channel/label-template/save**(POST)
```json
{
"id": 0,
"name": "尾程标签模板1",
"templateType": "lastMile",
"templateJson": {}
}
```
> 参数说明:`id` 模板ID,0为新增;`templateType` 类型:lastMile(尾程)/customer(客户)/inbound(入库)/outbound(出库)/sku;`templateJson` vg-print 导出的模板 JSON。
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"name": "尾程标签模板1",
"templateType": "lastMile",
"templateJson": {},
"createTime": "2024-01-01 12:00:00",
"updateTime": "2024-01-01 12:00:00"
}
}
```
----------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------
###### 2 接口业务逻辑
###### 核心流程
- 按本节接口定义执行业务处理流程,完成参数解析、数据查询/写入与结果返回。
###### 校验与异常
- 对必填参数、状态流转与数据存在性进行校验;异常场景返回统一错误码与错误信息。
## 6 操作模块
### 6.1 核心需求说明
- **入库扫描**:扫描运单号完成入库,记录实重、尺寸、销售产品、目的国、仓库、操作人、操作时间;**须覆盖 `Type=receive` 行并重算计费重**,并 **默认将计重结果同步覆盖 `ship` 与 `downstream`**(规则见 §4.3.4、§4.4.3.1),再按配置调用 **§4.3.4.1**(应收,`receive`)与 **§4.3.4.2**(应付,`downstream`,`ChargingStage=inbound`);支持查看最近入库记录。
- **出库扫描**:扫描运单号完成出库,**记录实重、尺寸、销售产品、目的国、服务渠道ID**、仓库、操作人、操作时间(**`t_outbound_record` 在入库记录字段基础上另存 `ServiceChannelId`**,见 §6.2.2);**须覆盖 `Type=ship` 行并重算计费重**,并 **默认将计重结果同步覆盖 `downstream`**(实测尺重见请求体,未传则可用 `receive` 行拷贝到 `ship`,见 §6.3.2.1),再按配置调用 **§4.3.4.1**(应收,`ship`)与 **§4.3.4.2**(应付,`downstream`,`ChargingStage=outbound`);支持查看最近出库记录。
- **标签打印**:按运单号/客户单号/跟踪号/其他单号选择标签模板打印;记录打印结果与操作人。
### 6.2 表结构设计
#### 6.2.1 入库记录表 `t_inbound_record`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| WaybillNo | VARCHAR | 64 | - | 是 | 运单号 |
| Weight | DECIMAL | 10,2 | - | 否 | 实重(kg) |
| Length | DECIMAL | 10,2 | - | 否 | 长(cm) |
| Width | DECIMAL | 10,2 | - | 否 | 宽(cm) |
| Height | DECIMAL | 10,2 | - | 否 | 高(cm) |
| SalesProductId | BIGINT | - | 外键 | 否 | 销售产品ID |
| DestinationCountry | VARCHAR | 8 | - | 否 | 目的国 |
| WarehouseId | BIGINT | - | 外键 | 否 | 仓库ID |
| OperateTime | DATETIME | - | - | 是 | 操作时间 |
| OperatorId | BIGINT | - | 外键 | 否 | 操作人ID |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 6.2.2 出库记录表 `t_outbound_record`
> **与 `t_inbound_record` 对齐**:除业务含义为「出库」外,件重尺与业务维度字段与入库记录表一致;**另存 `ServiceChannelId`**,关联 **`t_service_channel.Id`**,记录本单出库时所选/所绑定的服务渠道,便于对账与列表筛选。
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| WaybillNo | VARCHAR | 64 | - | 是 | 运单号 |
| Weight | DECIMAL | 10,2 | - | 否 | 实重(kg),与本次出库写入 `t_order_weight.Type=ship` 的 `ActualWeight` 来源一致(复量或拷贝自收货) |
| Length | DECIMAL | 10,2 | - | 否 | 长(cm) |
| Width | DECIMAL | 10,2 | - | 否 | 宽(cm) |
| Height | DECIMAL | 10,2 | - | 否 | 高(cm) |
| SalesProductId | BIGINT | - | 外键 | 否 | 销售产品ID |
| DestinationCountry | VARCHAR | 8 | - | 否 | 目的国 |
| ServiceChannelId | BIGINT | - | 外键(**`t_service_channel.Id`**) | 否 | **服务渠道ID**;请求可选传入,未传时建议服务端按订单已绑定服务渠道或排货结果兜底 |
| WarehouseId | BIGINT | - | 外键 | 否 | 仓库ID |
| OperateTime | DATETIME | - | - | 是 | 操作时间 |
| OperatorId | BIGINT | - | 外键 | 否 | 操作人ID |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 6.2.3 标签打印记录表 `t_label_print_record`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| WaybillNo | VARCHAR | 64 | - | 是 | 运单号 |
| CustomerOrderNo | VARCHAR | 64 | - | 否 | 客户单号 |
| TrackingNo | VARCHAR | 64 | - | 否 | 跟踪号 |
| OtherNumber | VARCHAR | 64 | - | 否 | 其他单号 |
| LabelTemplateId | BIGINT | - | 外键 | 否 | 标签模板ID |
| PrintSuccess | TINYINT | - | - | 是 | 是否打印成功 |
| PrintTime | DATETIME | - | - | 是 | 打印时间 |
| OperatorId | BIGINT | - | 外键 | 否 | 操作人ID |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
### 6.3 核心接口设计
#### 6.3.1 入库扫描
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 入库 | POST | /operation/inbound/create | 入库 |
| 最近入库记录 | GET | /operation/inbound/recent-list | 最近入库记录 |
##### 6.3.1.1 入库(POST /operation/inbound/create)
###### 1 接口请求示例
**请求 /operation/inbound/create**(POST)
```json
{
"waybillNo": "LP1234567890",
"weight": 2.5,
"length": 20,
"width": 15,
"height": 10,
"salesProductId": 1,
"destinationCountry": "US",
"warehouseId": 1
}
```
> 参数说明:`waybillNo` 运单号;`weight` 实重(kg);`length/width/height` 长宽高(cm);`salesProductId` 销售产品ID;`destinationCountry` 目的国二字码;`warehouseId` 仓库ID。操作时间、操作人由服务端从当前会话获取。
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"waybillNo": "LP1234567890",
"weight": 2.5,
"length": 20,
"width": 15,
"height": 10,
"salesProductId": 1,
"destinationCountry": "US",
"warehouseId": 1,
"operateTime": "2024-01-01 12:00:00",
"operatorId": 1,
"createTime": "2024-01-01 12:00:00"
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 解析 `waybillNo` 等参数,定位订单(及绑定销售产品、已落库的 `t_order_weight` 四行)。
- 写入 **`t_inbound_record`**(实重、尺寸、销售产品、目的国、仓库、操作人、操作时间等)。
- **覆盖 `t_order_weight` 中本单 `Type=receive` 行**:`Length`、`Width`、`Height`、`ActualWeight` 以本次入库实测为准(与请求体 `weight`、`length`、`width`、`height` 对齐),`Cubic = Length × Width × Height / 1000000`。
- **对该 `receive` 行**按本单绑定 **`WeightRuleId` / `WeightRoundingId`**(及 rules 快照或子表明细)执行 **§4.4.3.1 步骤 7、8 相同算法**,重算 **`VolumetricWeight`、`ChargeableWeight`**(**不修改 `forecast` 行**,除非另有「改单重算」流程)。
- **默认**:将 **`receive` 行**当前 **`Length`、`Width`、`Height`、`ActualWeight`、`Cubic`、`VolumetricWeight`、`ChargeableWeight`** **拷贝覆盖** 本单 **`Type=ship` 与 `Type=downstream`** 两行(与收货计重对齐;出库时再按 §6.3.2.1 分化 `ship`)。
- 若存在 **`ChargingTime=inbound`** 的费用项:**先**执行 **§4.3.4.1**(卖价,重量/计重因子取 **`Type=receive`**);**再**执行 **§4.3.4.2**(买价,重量/件重尺**仅 `Type=downstream`**)。两行 **`ChargingStage` 均为 `inbound`**(与买价项 `chargingTime` 一致)。
###### 校验与异常
- 对必填参数、订单状态、单号与订单关联、仓库权限等进行校验;件重尺非法、计重规则未命中、进位无匹配等按业务错误返回。
##### 6.3.1.2 最近入库记录(GET /operation/inbound/recent-list)
###### 1 接口请求示例
**请求 /operation/inbound/recent-list**(GET,Query 参数)
```
?limit=20&warehouseId=
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"waybillNo": "LP1234567890",
"weight": 2.5,
"length": 20,
"width": 15,
"height": 10,
"salesProductId": 1,
"salesProductName": "美国空运专线(普货)",
"destinationCountry": "US",
"warehouseId": 1,
"warehouseName": "深圳仓",
"operateTime": "2024-01-01 12:00:00",
"operatorId": 1,
"operatorName": "张三",
"createTime": "2024-01-01 12:00:00"
}
]
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
#### 6.3.2 出库扫描
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 出库 | POST | /operation/outbound/create | 出库 |
| 最近出库记录 | GET | /operation/outbound/recent-list | 最近出库记录 |
##### 6.3.2.1 出库(POST /operation/outbound/create)
###### 1 接口请求示例
**请求 /operation/outbound/create**(POST)
```json
{
"waybillNo": "LP1234567890",
"weight": 2.5,
"length": 20,
"width": 15,
"height": 10,
"salesProductId": 1,
"destinationCountry": "US",
"serviceChannelId": 100,
"warehouseId": 1
}
```
> 参数说明:`waybillNo` 运单号;`weight` 实重(kg);`length`/`width`/`height` 长宽高(cm);`salesProductId` 销售产品ID;`destinationCountry` 目的国二字码;**`serviceChannelId` 服务渠道ID**(对应 **`t_service_channel.Id`**,可选,未传时服务端按订单绑定/排货兜底并写入 `t_outbound_record`);`warehouseId` 仓库ID(可选,不传则从订单或默认仓库获取)。**`weight`、`length`、`width`、`height` 可选**:若出库前需以发货复量覆盖 `ship` 行则传入;**均未传时,先将本单 `Type=receive` 行当前件重尺拷贝覆盖 `ship` 行**,再进入计重。**`salesProductId`、`destinationCountry` 可选**,未传时建议服务端用订单绑定/收件国兜底。操作时间、操作人由服务端从当前会话获取。
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"waybillNo": "LP1234567890",
"weight": 2.5,
"length": 20,
"width": 15,
"height": 10,
"salesProductId": 1,
"destinationCountry": "US",
"serviceChannelId": 100,
"warehouseId": 1,
"operateTime": "2024-01-01 12:00:00",
"operatorId": 1,
"createTime": "2024-01-01 12:00:00"
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 解析 `waybillNo`、定位订单与 `t_order_weight`。
- 写入 **`t_outbound_record`**(**实重、尺寸、销售产品、目的国、服务渠道ID**、仓库、操作人、操作时间;件重尺与业务维度与 **`t_inbound_record`** 对齐,**并含 `ServiceChannelId`**,取值以请求、订单绑定及出库时 `ship` 行最终件重尺为准)。
- **覆盖 `t_order_weight` 中本单 `Type=ship` 行**:若请求携带 **`weight` / `length` / `width` / `height`**,则以之为准写入该行并更新 `Cubic`;**否则**将 **`Type=receive` 行**当前件重尺 **拷贝覆盖** 至 **`ship` 行**(含 `Cubic`),再进入计重。
- **对该 `ship` 行**按 **`WeightRuleId` / `WeightRoundingId`** 执行 **§4.4.3.1 步骤 7、8 相同算法**,重算 **`VolumetricWeight`、`ChargeableWeight`**。
- **默认**:将 **`ship` 行**当前 **`Length`、`Width`、`Height`、`ActualWeight`、`Cubic`、`VolumetricWeight`、`ChargeableWeight`** **拷贝覆盖** 本单 **`Type=downstream`** 行。
- 若存在 **`ChargingTime=outbound`** 的费用项:**先**执行 **§4.3.4.1**(卖价,重量/计重因子取 **`Type=ship`**);**再**执行 **§4.3.4.2**(买价,**仅 `downstream`**)。**`ChargingStage=outbound`**。
###### 校验与异常
- 对必填参数、订单状态、出库前置条件(如已入库)等进行校验;计重失败时返回明确错误信息。
##### 6.3.2.2 最近出库记录(GET /operation/outbound/recent-list)
###### 1 接口请求示例
**请求 /operation/outbound/recent-list**(GET,Query 参数)
```
?limit=20&warehouseId=
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"waybillNo": "LP1234567890",
"weight": 2.5,
"length": 20,
"width": 15,
"height": 10,
"salesProductId": 1,
"salesProductName": "美国空运专线(普货)",
"destinationCountry": "US",
"serviceChannelId": 100,
"serviceChannelName": "XX组-USPS",
"warehouseId": 1,
"warehouseName": "深圳仓",
"operateTime": "2024-01-01 12:00:00",
"operatorId": 1,
"operatorName": "张三",
"createTime": "2024-01-01 12:00:00"
}
]
}
}
```
> 响应字段在 **§6.3.1.2 最近入库记录** 列表基础上增加 **`serviceChannelId` / `serviceChannelName`**(联查 **`t_service_channel`** 及展示名,命名可与实现一致),仅业务来源为出库。
###### 2 接口业务逻辑
###### 核心流程
- 分页/条数限制与 **`GET /operation/inbound/recent-list`** 一致;返回 **`t_outbound_record`** 行及联查名称字段。
###### 校验与异常
- 对 `limit`、`warehouseId` 等查询参数及数据权限校验;异常返回统一错误码与错误信息。
#### 6.3.3 标签打印
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 打印标签 | POST | /operation/print-label/print | 打印标签 |
| 最近打印记录 | GET | /operation/print-label/recent-list | 最近打印记录 |
| 标签模板列表 | GET | /operation/print-label/template-list | 标签模板列表 |
##### 6.3.3.1 打印标签(POST /operation/print-label/print)
###### 1 接口请求示例
**请求 /operation/print-label/print**(POST)
```json
{
"number": "LP1234567890",
"numberType": "waybill",
"labelTemplateId": 1
}
```
> 参数说明:`number` 运单号/客户单号/跟踪号/其他单号;`numberType` 单号类型:waybill(运单号)/customerOrder(客户单号)/tracking(跟踪号)/other(其他单号);`labelTemplateId` 标签模板ID,可选,不传则按 templateType 取默认模板。也可传 `templateType`:lastMile(尾程)/customer(客户)/inbound(入库)/outbound(出库)/sku。
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"waybillNo": "LP1234567890",
"customerOrderNo": "",
"trackingNo": "",
"otherNumber": "",
"labelTemplateId": 1,
"labelTemplateName": "尾程标签模板1",
"printSuccess": 1,
"printTime": "2024-01-01 12:00:00",
"operatorId": 1,
"createTime": "2024-01-01 12:00:00"
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
##### 6.3.3.2 最近打印记录(GET /operation/print-label/recent-list)
###### 1 接口请求示例
**请求 /operation/print-label/recent-list**(GET,Query 参数)
```
?limit=20&labelTemplateId=
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"waybillNo": "LP1234567890",
"customerOrderNo": "CUST001",
"trackingNo": "TRK001",
"otherNumber": "",
"labelTemplateId": 1,
"labelTemplateName": "尾程标签模板1",
"printSuccess": 1,
"printTime": "2024-01-01 12:00:00",
"operatorId": 1,
"operatorName": "张三",
"createTime": "2024-01-01 12:00:00"
}
]
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
##### 6.3.3.3 标签模板列表(GET /operation/print-label/template-list)
###### 1 接口请求示例
**请求 /operation/print-label/template-list**(GET,Query 参数)
```
?templateType=lastMile
```
> 参数说明:`templateType` 可选,筛选模板类型:lastMile/customer/inbound/outbound/sku。不传则返回全部可用模板。
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"name": "尾程标签模板1",
"templateType": "lastMile",
"templateJson": {},
"createTime": "2024-01-01 12:00:00",
"updateTime": "2024-01-01 12:00:00"
}
]
}
}
```
----------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------
###### 2 接口业务逻辑
###### 核心流程
- 按接口定义执行业务流程,完成参数解析、数据处理与结果返回。
###### 校验与异常
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
----------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------
## 7 充值管理
### 7.1 核心需求说明
- **充值流程**:客户提交充值金额 → 选择支付方式(微信/支付宝)→ 上传充值截图 → 提交审核 → 管理员审核(通过/驳回)→ 审核通过后更新客户余额。
- **充值记录**:完整留存充值全流程信息,支持查询、导出;充值状态包含:待审核、审核通过、审核驳回。
### 7.2 表结构设计
#### 7.2.1 充值记录表 `t_recharge_record`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| CustomerId | BIGINT | - | 外键(客户主键,权威定义见 RBAC/客户域) | 是 | 充值归属客户;主账号/子账号口径以权限系统约定为准 |
| SerialNumber | VARCHAR | 64 | - | 是 | 流水号,规则:RECHARGE+日期+随机6位数字 |
| RechargeTime | DATETIME | - | - | 是 | 充值提交时间,默认当前时间 |
| PayType | TINYINT | - | - | 是 | 支付方式:1-微信,2-支付宝 |
| RechargeAmount | DECIMAL | 18,2 | - | 是 | 充值金额,≥0.01 |
| RechargeScreenshot | VARCHAR | 255 | - | 否 | 充值截图URL(存储文件服务器地址) |
| RechargeStatus | TINYINT | - | - | 是 | 充值状态:0-待审核,1-审核通过,2-审核驳回 |
| Remark | VARCHAR | 512 | - | 否 | 备注(客户/审核人填写) |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
| AuditTime | DATETIME | - | - | 否 | 审核时间 |
| AuditBy | VARCHAR | 32 | - | 否 | 审核人(管理员账号) |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
### 7.3 核心接口设计
| 接口名称 | 请求方式 | 请求路径 | 请求参数 | 响应参数 | 接口说明 |
| --- | --- | --- | --- | --- | --- |
| 提交充值申请 | POST | /recharge/recharge/submit | customerId, payType, rechargeAmount, screenshotUrl, remark | code, msg, data.serialNumber | 生成流水号,保存充值记录,状态为待审核 |
| 审核充值申请 | POST | /recharge/recharge-record/approve 或 reject | id, auditStatus, auditBy, remark | code, msg | 审核通过则更新客户余额,更新审核时间、状态、审核人 |
| 查询充值记录 | GET | /recharge/recharge-record/list | customerId, serialNumber, startTime, endTime, status | list, total | 多条件分页查询充值记录 |
| 导出充值记录 | GET | /recharge/export | customerId, startTime, endTime | 文件流 | 导出充值记录为Excel文件 |
##### 6.3.1.1 提交充值申请(POST /recharge/recharge/submit)
###### 1 接口请求示例
**请求 /recharge/recharge/submit**(POST)
```json
{
"customerId": 1,
"rechargeAmount": 500,
"payType": 1,
"rechargeScreenshot": "https://example.com/upload/screenshot.jpg",
"remark": "充值500元"
}
```
> 参数说明:`customerId` 客户ID(客户端提交时可不传,取当前登录客户);`rechargeAmount` 充值金额,≥0.01;`payType` 支付方式:1-微信,2-支付宝;`rechargeScreenshot` 充值截图URL;`remark` 备注。
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"serialNumber": "RECHARGE2024010100123456",
"customerId": 1,
"rechargeAmount": 500.00,
"payType": 1,
"rechargeScreenshot": "https://example.com/upload/screenshot.jpg",
"rechargeStatus": 0,
"remark": "充值500元",
"rechargeTime": "2024-01-01 12:00:00",
"createTime": "2024-01-01 12:00:00"
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 创建充值记录并生成流水号,初始状态为待审核。
###### 校验与异常
- 校验金额、支付方式与截图信息合法性,不合法时返回业务错误。
##### 6.3.1.2 审核充值申请-通过(POST /recharge/recharge-record/approve)
###### 1 接口请求示例
**请求 /recharge/recharge-record/approve**(POST)
```json
{
"id": 1,
"remark": "审核通过"
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 审核通过后更新充值状态与审核信息,并增加客户余额。
###### 校验与异常
- 校验记录状态必须为待审核;重复审核或状态冲突时返回业务错误。
##### 6.3.1.3 审核充值申请-驳回(POST /recharge/recharge-record/reject)
###### 1 接口请求示例
**请求 /recharge/recharge-record/reject**(POST)
```json
{
"id": 1,
"remark": "截图不清晰,请重新上传"
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 审核驳回后更新充值状态与审核备注,不变更客户余额。
###### 校验与异常
- 校验记录状态与审核权限,非法操作时返回业务错误。
##### 6.3.1.4 查询充值记录(GET /recharge/recharge-record/list)
###### 1 接口请求示例
**请求 /recharge/recharge-record/list**(GET,Query 参数)
```
?page=1&pageSize=20&customerId=1&serialNumber=&rechargeStatus=0&startTime=2024-01-01&endTime=2024-01-31
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"customerId": 1,
"customerCode": "W2501",
"customerPhone": "13800138000",
"serialNumber": "RECHARGE2024010100123456",
"rechargeTime": "2024-01-01 12:00:00",
"payType": 1,
"payTypeName": "微信",
"rechargeAmount": 500.00,
"rechargeScreenshot": "https://example.com/upload/screenshot.jpg",
"rechargeStatus": 0,
"rechargeStatusName": "待审核",
"remark": "充值500元",
"auditTime": null,
"auditBy": null,
"createTime": "2024-01-01 12:00:00",
"updateTime": "2024-01-01 12:00:00"
}
],
"total": 20
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按客户、流水号、状态和时间区间进行分页查询,返回记录列表与总数。
###### 校验与异常
- 校验时间区间与分页参数合法性,查询异常时返回统一错误码。
##### 6.3.1.5 导出充值记录(GET /recharge/export)
###### 1 接口请求示例
**请求 /recharge/export**(GET,Query 参数)
```
?customerId=1&startTime=2024-01-01&endTime=2024-01-31
```
**响应**
> 返回 Excel 文件流,Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
###### 2 接口业务逻辑
###### 核心流程
- 根据筛选条件导出充值记录为 Excel 文件流。
###### 校验与异常
- 校验导出条件与数据权限,导出失败时返回统一错误信息。
----------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------
## 8 系统主数据
### 8.1 核心需求说明
- **仓库地址**:管理仓库收货地址信息,包括主仓/中转仓/退货仓/其他类型;支持收件人、联系电话、收货地址、客服电话、客服微信及二维码等配置。
- **国家管理**:维护国家基础资料(中文名称、英文名称、二字代码),供渠道分区、下单地址等模块复用。
- **字典管理**:维护 **`t_dict`**,供全平台下拉与状态映射复用。**与客户、内部员工相关的 `DictType` 及编码含义**在 **`docs/rbac系统设计-050901.md`** 定义与迭代;OMS 仅保证业务侧按 **`DictType` + `DictCode`** 引用时与实现一致。本篇表结构中 **`DictType` 示例**以 **仓库、通知等纯业务枚举** 为主。
### 8.2 表结构设计
#### 8.2.1 与 RBAC 文档的职责划分
**权限系统**(认证、内部用户、客户主数据、角色权限、菜单、组织等)**全部由 `docs/rbac系统设计-050901.md` 承担**;OMS **「8 系统主数据」** 只描述 **业务侧主数据**:**国家、字典、通知、仓库地址** 等(见下)。
#### 8.2.2 国家表 `t_country`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| ChineseName | VARCHAR | 128 | - | 是 | 中文名称 |
| EnglishName | VARCHAR | 128 | - | 是 | 英文名称 |
| CountryCode | VARCHAR | 2 | - | 是 | 国家二字代码(ISO 3166-1 alpha-2) |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 8.2.3 字典表 `t_dict`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| DictType | VARCHAR | 64 | - | 是 | 字典类型(如 **`notice_type`**、**`warehouse_type`** 等 OMS 业务枚举;与客户/员工相关的 `DictType` 由 **RBAC 文档** 统一约定) |
| DictSort | INT | - | - | 是 | 字典排序,值越小越靠前 |
| DictCode | VARCHAR | 64 | - | 是 | 字典编码(同类型下唯一) |
| DictName | VARCHAR | 128 | - | 是 | 字典名称 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 8.2.4 公告通知表 `t_notice`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| NoticeType | VARCHAR | 16 | - | 是 | 通知类型:notice-公告,message-站内信 |
| Title | VARCHAR | 200 | - | 是 | 标题 |
| Content | TEXT | - | - | 是 | 内容 |
| IsPopup | TINYINT | - | - | 是 | 是否弹窗:0-否,1-是 |
| ReceiverType | VARCHAR | 16 | - | 是 | 接收对象:user-用户,customer-客户 |
| ReceiverIds | JSON | - | - | 是 | 接收人 ID 列表 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
#### 8.2.5 通知已读明细表 `t_notice_read`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| NoticeId | BIGINT | - | 外键(关联 `t_notice.Id`) | 是 | 通知 ID |
| ReceiverType | VARCHAR | 16 | - | 是 | 接收对象:user-用户,customer-客户 |
| ReceiverId | BIGINT | - | - | 是 | 已读主体 ID(用户 ID/客户 ID) |
| ReadTime | DATETIME | - | - | 是 | 已读时间 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
#### 8.2.6 仓库地址表 `t_warehouse_address`
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
| --- | --- | --- | --- | --- | --- |
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
| WarehouseName | VARCHAR | 128 | - | 是 | 仓库名称 |
| WarehouseType | VARCHAR | 32 | - | 是 | main/transit/return/other |
| Recipient | VARCHAR | 64 | - | 否 | 收件人 |
| ContactPhone | VARCHAR | 32 | - | 否 | 联系电话 |
| ShippingAddress | TEXT | - | - | 否 | 收货地址 |
| ServicePhone | VARCHAR | 32 | - | 否 | 客服电话 |
| ServiceWechat | VARCHAR | 64 | - | 否 | 客服微信 |
| ServiceWechatFiles | JSON | - | - | 否 | 微信二维码图片 |
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
| CreateUserId | INT | - | - | 是 | 创建人 |
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
| UpdateUserId | INT | - | - | 否 | 更新人 |
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
### 8.3 核心接口设计
> **登录、刷新、登出、当前主体(含 `/auth/*` 或 `/rbac/auth/*` 等)**:路径与契约见 **`docs/rbac系统设计-050901.md`** 与线上 Swagger;**不在本篇逐条列出**。
| 接口名称 | 请求方式 | 请求路径 | 说明 |
| --- | --- | --- | --- |
| 国家列表 | GET | /system/country/list | 查询国家列表 |
| 字典列表 | GET | /system/dict/list | 按类型分页查询字典 |
| 新增字典 | POST | /system/dict/create | 新增字典项 |
| 更新字典 | POST | /system/dict/update | 更新字典项 |
| 删除字典 | POST | /system/dict/delete | 删除字典项 |
| 仓库列表 | GET | /system/warehouse/list | 仓库列表 |
| 仓库详情 | GET | /system/warehouse/detail | 仓库详情 |
| 新增仓库 | POST | /system/warehouse/create | 新增 |
| 更新仓库 | POST | /system/warehouse/update | 更新 |
| 删除仓库 | POST | /system/warehouse/delete | 删除 |
| 通知列表 | GET | /system/notice/list | 分页查询通知 |
| 新增通知 | POST | /system/notice/create | 新增公告/站内信 |
| 编辑通知 | POST | /system/notice/update | 编辑公告/站内信 |
| 删除通知 | POST | /system/notice/delete | 删除公告/站内信 |
| 确认已读 | POST | /system/notice/read-confirm | 当前用户/客户确认已读 |
### 8.4 系统主数据接口详细设计(`/system/*` 等)
> **范围**:本章 **`/system/*`** 及 **`t_country` / `t_dict` / `t_notice` / `t_warehouse_address`**;**认证、令牌、权限码、客户域/用户域 HTTP** 见 **`docs/rbac系统设计-050901.md`** 与 **OpenAPI/Swagger**。
##### 8.4.1.1 国家列表(GET /system/country/list)
###### 1 接口请求示例
**请求 /system/country/list**(GET)
> 无请求参数,返回全部国家字段。
**响应**
```json
{
"code": 0,
"msg": "success",
"data": [
{
"id": 1,
"chineseName": "中国",
"englishName": "China",
"countryCode": "CN"
},
{
"id": 2,
"chineseName": "美国",
"englishName": "United States",
"countryCode": "US"
}
]
}
```
###### 2 接口业务逻辑
###### 核心流程
- 查询 `t_country` 全量有效数据,按 `countryCode` 或 `englishName` 排序返回。
###### 校验与异常
- 仅返回未删除记录;无数据时返回空数组。
##### 8.4.1.2 字典列表(GET /system/dict/list)
###### 1 接口请求示例
**请求 /system/dict/list**(GET,Query 参数)
```
?page=1&pageSize=20&dictType=warehouse_type&keyword=主仓
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"dictType": "warehouse_type",
"dictSort": 1,
"dictCode": "main",
"dictName": "主仓",
"createTime": "2026-04-27 10:00:00",
"updateTime": "2026-04-27 10:00:00"
}
],
"total": 1
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按 `dictType`、关键词分页查询 `t_dict`,按 `dictSort` 升序、`id` 降序返回。
###### 校验与异常
- 校验分页参数合法性;仅返回未删除记录,查询异常时返回统一错误码。
##### 8.4.1.3 新增字典(POST /system/dict/create)
###### 1 接口请求示例
**请求 /system/dict/create**(POST)
```json
{
"dictType": "warehouse_type",
"dictSort": 1,
"dictCode": "main",
"dictName": "主仓"
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 新增字典项并写入创建审计信息。
###### 校验与异常
- 校验 `dictType`、`dictSort`、`dictCode`、`dictName` 必填及格式合法性。
- 校验同一 `dictType` 下 `dictCode` 唯一,重复时返回业务错误。
##### 8.4.1.4 更新字典(POST /system/dict/update)
###### 1 接口请求示例
**请求 /system/dict/update**(POST)
```json
{
"id": 1,
"dictType": "warehouse_type",
"dictSort": 2,
"dictCode": "transit",
"dictName": "中转仓"
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据字典 ID 更新字典类型、排序、编码、名称及审计信息。
###### 校验与异常
- 校验 `id` 存在且未删除,更新字段合法。
- 若更新后 `dictType + dictCode` 与其他记录冲突,返回业务错误。
##### 8.4.1.5 删除字典(POST /system/dict/delete)
###### 1 接口请求示例
**请求 /system/dict/delete**(POST)
```json
{
"id": 1
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据字典 ID 执行逻辑删除并保留历史审计信息。
###### 校验与异常
- 校验 `id` 存在与删除前置条件,不存在或已删除时返回业务错误。
##### 8.4.1.6 仓库列表(GET /system/warehouse/list)
###### 1 接口请求示例
**请求 /system/warehouse/list**(GET,Query 参数)
```
?page=1&pageSize=20&warehouseType=main&keyword=
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 1,
"warehouseName": "深圳主仓",
"warehouseType": "main",
"warehouseTypeName": "主仓",
"recipient": "张三",
"contactPhone": "0755-12345678",
"shippingAddress": "广东省深圳市南山区xxx路xxx号",
"servicePhone": "400-xxx-xxxx",
"serviceWechat": "itfx_service",
"serviceWechatFiles": ["https://example.com/qrcode1.jpg"],
"createTime": "2024-01-01 12:00:00",
"updateTime": "2024-01-01 12:00:00"
}
],
"total": 5
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按仓库类型与关键词分页查询仓库地址信息列表。
###### 校验与异常
- 校验分页参数与筛选条件合法性,异常时返回统一错误码。
##### 8.4.1.7 仓库详情(GET /system/warehouse/detail)
###### 1 接口请求示例
**请求 /system/warehouse/detail**(GET,Query 参数)
```
?id=1
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"id": 1,
"warehouseName": "深圳主仓",
"warehouseType": "main",
"recipient": "张三",
"contactPhone": "0755-12345678",
"shippingAddress": "广东省深圳市南山区xxx路xxx号",
"servicePhone": "400-xxx-xxxx",
"serviceWechat": "itfx_service",
"serviceWechatFiles": ["https://example.com/qrcode1.jpg"],
"createTime": "2024-01-01 12:00:00",
"createBy": "admin",
"updateTime": "2024-01-01 12:00:00",
"updateBy": "admin"
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据仓库 ID 查询仓库详情,返回完整地址与客服信息。
###### 校验与异常
- 校验 `id` 必填且存在,不存在时返回业务错误。
##### 8.4.1.8 新增仓库(POST /system/warehouse/create)
###### 1 接口请求示例
**请求 /system/warehouse/create**(POST)
```json
{
"warehouseName": "深圳主仓",
"warehouseType": "main",
"recipient": "张三",
"contactPhone": "0755-12345678",
"shippingAddress": "广东省深圳市南山区xxx路xxx号",
"servicePhone": "400-xxx-xxxx",
"serviceWechat": "itfx_service",
"serviceWechatFiles": ["https://example.com/qrcode1.jpg"]
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 新增仓库地址配置并写入创建审计信息。
###### 校验与异常
- 校验仓库名称、类型及地址字段完整性,不合法时返回业务错误。
##### 8.4.1.9 更新仓库(POST /system/warehouse/update)
###### 1 接口请求示例
**请求 /system/warehouse/update**(POST)
```json
{
"id": 1,
"warehouseName": "深圳主仓",
"warehouseType": "main",
"recipient": "张三",
"contactPhone": "0755-12345678",
"shippingAddress": "广东省深圳市南山区xxx路xxx号",
"servicePhone": "400-xxx-xxxx",
"serviceWechat": "itfx_service",
"serviceWechatFiles": ["https://example.com/qrcode1.jpg"]
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据仓库 ID 更新仓库基础信息与联系方式。
###### 校验与异常
- 校验 `id` 存在与更新字段合法性,更新失败时返回业务错误。
##### 8.4.1.10 删除仓库(POST /system/warehouse/delete)
###### 1 接口请求示例
**请求 /system/warehouse/delete**(POST)
```json
{
"id": 1
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据仓库 ID 执行逻辑删除并保留历史记录。
###### 校验与异常
- 校验 `id` 存在与删除前置条件,冲突时返回业务错误。
##### 8.4.1.11 通知列表(GET /system/notice/list)
###### 1 接口请求示例
**请求 /system/notice/list**(GET,Query 参数)
```
?page=1&pageSize=20&noticeType=notice&receiverType=user&keyword=活动
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": {
"items": [
{
"id": 101,
"noticeType": "notice",
"noticeTypeName": "公告",
"title": "五一放假通知",
"content": "仓库 5 月 1 日-5 月 3 日暂停收货。",
"isPopup": 1,
"receiverType": "user",
"receiverTypeName": "用户",
"receiverIds": [1001, 1002, 1003],
"readCount": 2,
"receiverCount": 3,
"createTime": "2026-04-25 12:00:00",
"updateTime": "2026-04-25 12:00:00"
}
],
"total": 1
}
}
```
###### 2 接口业务逻辑
###### 核心流程
- 按通知类型、接收对象、关键词分页查询 `t_notice`。
- 已读数通过聚合 `t_notice_read`(按 `NoticeId` + `ReceiverId` 去重)计算,接收人总数按 `receiverIds` 统计。
###### 校验与异常
- 校验分页参数合法性,仅返回未删除记录;异常时返回统一错误码。
##### 8.4.1.12 新增通知(POST /system/notice/create)
###### 1 接口请求示例
**请求 /system/notice/create**(POST)
```json
{
"noticeType": "notice",
"title": "五一放假通知",
"content": "仓库 5 月 1 日-5 月 3 日暂停收货。",
"isPopup": 1,
"receiverType": "user",
"receiverIds": [1001, 1002, 1003]
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 新增公告/站内信并写入接收对象与接收人列表。
###### 校验与异常
- 校验 `noticeType`、`title`、`content`、`isPopup`、`receiverType`、`receiverIds` 必填及枚举合法性。
- `receiverIds` 为空或包含无效用户/客户时返回业务错误。
##### 8.4.1.13 编辑通知(POST /system/notice/update)
###### 1 接口请求示例
**请求 /system/notice/update**(POST)
```json
{
"id": 101,
"noticeType": "message",
"title": "账单补充说明",
"content": "请于本周内完成 4 月账单核对。",
"isPopup": 0,
"receiverType": "customer",
"receiverIds": [2001, 2002]
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据通知 ID 更新通知主体内容与接收范围。
- 如接收人发生变化,清理 `t_notice_read` 中不在新接收范围内的已读明细。
###### 校验与异常
- 校验 `id` 存在且未删除,更新字段合法。
- 已发布通知可编辑但必须保留至少 1 个接收人。
##### 8.4.1.14 删除通知(POST /system/notice/delete)
###### 1 接口请求示例
**请求 /system/notice/delete**(POST)
```json
{
"id": 101
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 根据通知 ID 执行逻辑删除,保留历史审计信息。
###### 校验与异常
- 校验 `id` 必填且存在,不存在或已删除时返回业务错误。
##### 8.4.1.15 确认已读(POST /system/notice/read-confirm)
###### 1 接口请求示例
**请求 /system/notice/read-confirm**(POST)
```json
{
"id": 101
}
```
**响应**
```json
{
"code": 0,
"msg": "success",
"data": null
}
```
###### 2 接口业务逻辑
###### 核心流程
- 当前登录用户/客户调用后,在 `t_notice_read` 中按 `NoticeId + ReceiverType + ReceiverId` 幂等写入已读记录,并记录 `readTime`。
###### 校验与异常
- 校验通知存在且未删除。
- 校验当前主体在通知接收范围内,不在范围内时返回无权限错误。
----------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------
## 9 需求迭代与变更记录
### 9.1 需求迭代目录(按日期管理)
> 迭代编号建议使用 `YYYYMMDD-序号`,按日期倒序维护;不同功能迭代可使用 `-01`、`-02` 递增。
| 迭代日期 | 迭代编号 | 迭代功能 | 变更模块 | 备注 |
| --- | --- | --- | --- | --- |
| 2026-04-30 | 20260430-03 | 获取渠道分组 | 渠道模块 | 之前漏写该接口 |
| — | — | **与客户、内部用户、认证、RBAC 相关的迭代** | — | **一律在 `docs/rbac系统设计-050901.md` 维护**;OMS 本篇仅记录业务域迭代 |
### 9.2 20260427-03 差异明细(按表结构与接口)
> 对比范围:`oms表结构与接口设计20260427.md` vs `oms表结构与接口设计20260430.md`。**与认证、客户、内部用户、RBAC 相关的历史差异**不再在此归档,见 **`docs/rbac系统设计-050901.md`**。
#### 9.2.1 表结构差异
**新增表(0)**
- 无
**更新表(0)**
- 无
**删除表(0)**
- 无
#### 9.2.2 接口差异
**新增接口(1)**
- `GET | /channel/service-channel/group/list`(获取渠道分组列表)
**更新接口(0)**
- 无
**删除接口(0)**
- 无
---
## 10 附录
### 10.1 接口路径规范(kebab-case 汇总)
| 模块 | 路径前缀 | 示例 |
| --- | --- | --- |
| 订单 | /order/ | list, detail, pay, pre-declare, cancel-intercept, delete, price-check, fee/batch-recalculate |
| 渠道-销售产品 | /channel/sales-product/ | list, detail, create, update, delete, price/list, allocation/get |
| 渠道-服务渠道 | /channel/service-channel/ | list, detail, create, update, delete, group/create, group/update, group/delete, price/list |
| 渠道-计重 | /channel/weight-rule/, weight-rounding/ | list, create, update, delete, info/list |
| 渠道-自动触发 | /channel/auto-trigger/ | list, create, update, delete, toggle-status |
| 渠道-分区(KpiArea) | /channel/kpi-area/ | create, update, delete, list, info/add, info/update, info/delete, info/list |
| 渠道-标签 | /channel/label-template/save | |
| 操作 | /operation/ | inbound/create, inbound/recent-list, outbound/create, print-label/print |
| 权限与客户域(非 OMS) | — | **`docs/rbac系统设计-050901.md`**(`/customer/*`、`/rbac/*` 等) |
| 充值 | /recharge/ | recharge/submit, recharge-record/list, approve, reject |
| 系统 | /system/ | country/list, warehouse/list, warehouse/detail, warehouse/create, warehouse/update, warehouse/delete, notice/list, notice/create, notice/update, notice/delete, notice/read-confirm |
### 10.2 分区类型说明(黑名单/偏远/价格)
| 页面 | areaType | 说明 |
| --- | --- | --- |
| 黑名单区 | blacklist | 黑名单分区 |
| 偏远分区 | remote | 偏远分区 |
| 价格分区 | price | 用于计价/价格表关联的分区 |
调用 KpiArea 接口时,通过 `areaType` 区分不同业务分区,无需新建表或接口。
### 10.3 状态码与枚举
#### 10.3.1 订单状态
| status | 中文 |
| --- | --- |
| draft | 草稿 |
| predeclared | 已预报 |
| received | 已收货 |
| pendingPayment | 待支付 |
| pendingShip | 待发货 |
| pendingOnline | 待上网 |
| shipping | 运输中 |
| deliverySuccess | 派送成功 |
| deliveryFailed | 派送失败 |
| problematic | 问题件 |
| deleted | 已删除 |
| claimed | 已理赔 |
#### 10.3.2 充值状态
| payStatus | 中文 |
| --- | --- |
| 0 | 待审核 |
| 1 | 成功 |
| 2 | 失败 |
#### 10.3.3 仓库类型
| warehouseType | 中文 |
| --- | --- |
| main | 主仓 |
| transit | 中转仓 |
| return | 退货仓 |
| other | 其他 |
#### 10.3.4 通知类型
| noticeType | 中文 |
| --- | --- |
| notice | 公告 |
| message | 站内信 |
#### 10.3.5 接收对象类型
| receiverType | 中文 |
| --- | --- |
| user | 内部登录主体(取值语义与主体类型定义见 RBAC 文档) |
| customer | 客户登录主体(同上) |
**与 RBAC 三类主体**:权限规范另有 **`customer_sub`(子账号)** 登录主体(见 **`docs/rbac系统设计-050901.md`**)。**`t_notice` / 已读表** 当前 **`ReceiverType`** 仅列 **`user` / `customer`** 时:**`ReceiverIds` 是否包含子账号 Id、抑或一律按主客户 Id 广播**,由产品与 **`/system/notice/*`** 实现约定;若需显式区分 **`customer_sub`**,应同步改 OMS OpenAPI 与本文 **`8.2.4`/`8.2.5`** 字段说明。
**说明**:**客户分组、客户状态等客户主数据枚举** 不在 OMS 附录维护,见 **`docs/rbac系统设计-050901.md`**。
----------------------------------------------------------------------------------------
*文档版本:v1.1 | 最后更新:2026-04-27*