新增页面按钮目录与菜单同步三级按钮;增强我的订单(问题件/理赔件、费用、详情抽屉)、Nana 助手、联系客服、渠道问题模板及登录/RBAC 等相关功能。 Co-authored-by: Cursor <cursoragent@cursor.com>
7895 lines
295 KiB
Markdown
7895 lines
295 KiB
Markdown
# OMS 业务系统 — 表结构与接口设计
|
||
|
||
## 1 导读
|
||
|
||
### 1.1 这份文档解决什么问题?
|
||
|
||
1. **订单**:创建、预报、支付、状态机、费用与版本对齐、模板/批量任务等。
|
||
2. **渠道**:销售产品、服务渠道、计价与发布、计重、分区、自动触发、标签等。
|
||
3. **操作**:入库、出库、面单打印等仓储作业。
|
||
4. **充值**:充值申请、审核及入账(**客户余额权威在 `t_customer.Balance`**;**余额变动统一写入 `t_customer_account_flow` 资金流水**,审核通过后由 OMS 客户服务更新余额)。
|
||
5. **系统主数据**:国家、字典、仓库地址、**其他配置(含小包轨迹服务)**、通知等 **`/system/*`** 能力。
|
||
|
||
|
||
### 1.2 文档定位
|
||
|
||
**OMS** 为 **业务系统**;认证、RBAC、内部用户、客户主数据等由 **`docs/rbac系统设计-060801.md`** 描述(与线上一致)。业务侧以 **`CustomerId`(`t_customer.Id`)** 表达订单/充值等归属;与 JWT 会话(**`mapId`**、**`loginType`**)的对齐见 **1.4**。**本篇不展开 RBAC 表与 `/rbac/*`、`/customer/*` 接口明细**。
|
||
|
||
### 1.3 业务模块一览
|
||
|
||
| 模块 | 章 | 说明 |
|
||
| --- | --- | --- |
|
||
| **订单** | **4** | 全生命周期、查价、模板与批量任务 |
|
||
| **渠道** | **5** | 销售产品、服务渠道、计价、计重、分区、标签等 |
|
||
| **操作** | **6** | 入库、出库、打印 |
|
||
| **充值** | **7** | 充值申请、审核、入账;**客户账户资金流水** |
|
||
| **系统主数据** | **8** | 国家、字典、仓库、通知(`/system/*`) |
|
||
|
||
### 1.4 与客户 / 用户数据的边界
|
||
|
||
**客户主数据、子账号、内部用户、认证与 RBAC** 均在 **`docs/rbac系统设计-060801.md`**;**本篇不写**上述领域的表结构与 `/rbac/*`、`/customer/*` 接口明细。
|
||
|
||
#### 双 ID 与业务归属(与 RBAC 文档一致)
|
||
|
||
| 标识 | 来源 | OMS 用途 |
|
||
| --- | --- | --- |
|
||
| `user.Id` | RBAC GUID | JWT 主体;`/rbac/*` 管理接口 |
|
||
| `user.userid` | RBAC 自增 INT | JWT Claim **`mapId`**;`CurrentUser.GetId<int>()` |
|
||
| `t_customer.UserId` | OMS 客户表 | **等于** `user.userid`,桥接 RBAC 与 OMS |
|
||
| `t_customer.Id` | OMS 客户主键 | 业务表 **`CustomerId`** 外键 |
|
||
|
||
OMS 业务表中的 **`CustomerId`** 指向 **`t_customer.Id`**(客户业务主键),**不是** `user.userid`。
|
||
|
||
#### 登录主体与业务 CustomerId 解析
|
||
|
||
| `loginType`(JWT Claim) | 值 | 登录校验 | OMS 业务上下文 |
|
||
| --- | --- | --- | --- |
|
||
| 内部用户 | 0 | RBAC `user` 账号密码 | `mapId` 可作审计 `CreateUserId`;一般不直接作为 `CustomerId` |
|
||
| 客户(含主/子) | 1 | `t_customer` 手机/邮箱/编号 + MD5 密码 | 先用 **`mapId` 查 `t_customer.UserId`**,再取该行 **`t_customer.Id`** 作为业务 `CustomerId` |
|
||
|
||
**子客户**:`t_customer.ParentCustomerId` 非空表示子客户;登录 Token 的 `mapId` 仍是**该子客户自己的** `user.userid`。订单/充值等若按「主客户归属」落库,须将子客户解析为其 **`ParentCustomerId` 对应的主客户 `t_customer.Id`**(具体接口在实现时二选一:按登录客户自身 Id,或归并到主客户 Id,须在接口章节写死;**禁止**把 `mapId` 直接当作 `t_customer.Id`)。
|
||
|
||
**存在性、状态、余额、子客户权限** 以 **`/customer/*`** 与 RBAC 服务为准;**不得**仅凭请求体 Id 绕过鉴权。**订单模块**(`/admin/order/*`)另须遵守 **§4.4.0** 客户数据隔离。
|
||
|
||
**不在本篇设计的内容**:统一认证、RBAC 管理、内部用户、客户 CRUD、角色权限分配等 —— 见 **`docs/rbac系统设计-060801.md`**。
|
||
|
||
### 1.5 后端实现对照(oms0414-back)
|
||
|
||
> 对照仓库:`C:\Users\wilso\Desktop\oms0414-back`(`SimpleAdmin.sln`)。HTTP 由 **Fake 框架** 将 `ApplicationService` 上的 `[HttpGet]` / `[HttpPost]` 自动暴露为 API,**无手写 Controller**。结构说明见仓库内 **`系统结构说明.md`**。
|
||
|
||
#### 1.5.1 技术栈与路由约定
|
||
|
||
| 项 | 说明 |
|
||
| --- | --- |
|
||
| 运行时 | .NET 8、SqlSugar + MySQL、JWT(Claim **`mapId`** = `user.userid`)、Swagger |
|
||
| OMS 业务路由 | `/order/*`、`/channel/*`、`/operation/*`、`/recharge/*`、`/system/*`、`/customer/*` |
|
||
| 认证 / RBAC | `/rbac/auth/*`(宿主 `AuthService` 覆盖)、`/rbac/*`(NuGet `Fake.Rbac.Application`) |
|
||
| 微信 | `/wechat/mp/*`、`/wechat/customer/*`、`/wechat/oauth/*` |
|
||
| 分页响应 | `OmsPageResult<T>` → `{ items, total }`;无分页列表 → `OmsItemsResult<T>` → `{ items }` |
|
||
| 分页默认值 | `OmsDocHelper.NormalizePage` / `NormalizePageSize`(默认 pageSize **30**) |
|
||
|
||
#### 1.5.2 ApplicationService 与模块映射
|
||
|
||
| 业务域 | Service 类(`Oms.Application/Services/`) | 路由前缀 |
|
||
| --- | --- | --- |
|
||
| 订单 | `OrderService`(+ `ParcelBilling.cs`、`ParcelQuote.cs`、`Helpers.cs`) | `/order/*` |
|
||
| 算价引擎 | `ParcelBillingCalculator`(静态,无 HTTP) | — |
|
||
| 销售产品 | `SalesProductService` | `/channel/sales-product/*` |
|
||
| 服务渠道 | `ServiceChannelService` | `/channel/service-channel/*` |
|
||
| KPI 分区 | `KpiAreaService` | `/channel/kpi-area/*` |
|
||
| 计重 / 进位 | `WeightRuleService`、`WeightRoundingService` | `/channel/weight-rule/*`、`/channel/weight-rounding/*` |
|
||
| 自动触发 / 标签 | `AutoTriggerService`、`LabelTemplateService` | `/channel/auto-trigger/*`、`/channel/label-template/save` |
|
||
| 问题模板 | `IssueTemplateService` | `/channel/issue-template/*` |
|
||
| 仓内作业 | `InboundRecordService`、`OutboundRecordService`、`LabelPrintService` | `/operation/*` |
|
||
| 充值 | `RechargeService` | `/recharge/*` |
|
||
| 客户账户流水 | `CustomerAccountFlowService`、`CustomerBalanceMutator` | `/customer/account-flow/*`(列表);余额变动由充值审核、订单支付触发 |
|
||
| 系统主数据 | `CountryService`、`DictService`、`NoticeService`、`WarehouseService` | `/system/*` |
|
||
| 客户 | `CustomerService` | `/customer/*`(契约见 RBAC 文档) |
|
||
|
||
#### 1.5.3 会话与客户隔离(`OmsSessionHelper`)
|
||
|
||
| 方法 | 行为 |
|
||
| --- | --- |
|
||
| `ResolveSessionCustomerAsync` | **`loginType=1`**:按 `mapId` 查 `t_customer.UserId`,得到 **`t_customer.Id`**;请求体 `customerId` 若传则须一致,否则 **「客户ID非法」**。**管理员**:须传 `customerId`(或 fallback `mapId` 作 Id 查客户),否则 **「客户ID不能为空」** |
|
||
| `ResolveListCustomerIdAsync` | **客户登录**:强制返回 `t_customer.Id` 用于列表过滤。**管理员**:返回 `null`,**不过滤** `CustomerId`(可查看全部客户订单/任务/充值) |
|
||
| `GetOrderOrThrowAsync` | 存在性校验后,客户登录时 **`order.CustomerId` 须等于会话 Id**,否则 **「订单不存在」** |
|
||
|
||
> **与 §4.4.0 的差异**:设计文档要求客户门户专用;**当前实现允许管理员(`loginType=0`)跨客户查询**,列表接口不注入 `CustomerId` 条件。
|
||
|
||
#### 1.5.4 设计 vs 实现:关键差异速查
|
||
|
||
| 能力 | 设计目标 | 当前实现(oms0414-back) |
|
||
| --- | --- | --- |
|
||
| 快照读取 | Redis + 进程内存为主,DB 降级 | **直接读** `t_sales_product_publish_record`;无发布记录时 **Live 组装**(`BuildLiveRulesSnapshotAsync` / `BuildLivePriceSnapshotAsync`),版本号写 **`LIVE-{salesProductId}`** |
|
||
| 预报应付费用 | §4.3.4.2 排货确认渠道 + 渠道买价 | **已实现**:`/order/parcel/create` 预报成功后 **`ConfirmServiceChannelAndPayableAsync`**(`ChargingStage=forecast`,重量取 **`downstream`** 行) |
|
||
| 入库触发应收/应付 | §6.3.1.1 收货计重后重算 | **已实现**:`/operation/inbound/create` → `ApplyReceiveBillingByWaybillAsync`(应收 `ChargingStage=inbound` + **重新排货确认渠道与应付**) |
|
||
| 出库更新状态与发货件重尺 | §6.3.2.1 覆盖 `Type=ship` 并重算;**`OrderStatus=pendingOnline`** | **未实现**:`/operation/outbound/create` 当前仅写 `t_outbound_record`,不更新 `t_order` / `t_order_weight` |
|
||
| 批量 JSON 下单 | 异步任务 + `taskId` | **同步逐条**调用 `CreateParcelCoreAsync`,返回 **`batchNo` + `items[]`** |
|
||
| Excel 导入 | 解析后等同批量创建 / 异步 | **同步**解析 Excel,**简化落单**(`OrderStatus=pendingPayment`,仅 **1 条** `forecast` 重量,**不走** §4.4.3.1 全链路) |
|
||
| 批量重算费用 | `POST /order/fee/batch-recalculate` | **未实现**(§4.4.1.10 为设计稿) |
|
||
| 订单支付 | 扣减 `t_customer.Balance` 并写 `t_customer_account_flow` | **已实现**:仅 `pendingPayment` 可支付;应收合计扣款;流水 `BizType=orderPay` |
|
||
| 面单 URL | 可选空 | 预报成功时生成 **HTML** 至 `/uploads/prints/orders/`,响应 **`labelUrl`** |
|
||
| 规则快照下游元数据 | 排货 Provider 含渠道/itfx 对接字段 | **已实现**:规则发布时 **`AllocationProviderSnapshotEnricher`** enrich `Providers`;排货确认写入 **`t_order`** 四字段;**AppSecret 不落快照/订单** |
|
||
| 轨迹 / 下游预报 | 预报后异步轨迹;下游 createOrderOne | **部分**:预报成功异步 UpsertSmallBag;「我的订单」可手动 **`POST /order/track-upsert-small-bag`**;下游预报由前端调 itfx **`ForwardWithSubscribe`**,凭证优先读订单绑定字段 |
|
||
| 创建问题件 / 理赔件 | §4.4.1.13~§4.4.1.15 批量标记 + 问题模板消费 | **已落地**(`OrderService.Exception` + `OrderExceptionWorkflowService`) |
|
||
| 问题件通知 | 按模板 `NotifyMethod` 投递 | **部分**:`inApp`(站内信)、`email`(操作人 SMTP)已接入;`sms/feishu/wechat` 待通道对接 |
|
||
| 自动触发运行时 | 下单/入库/出库消费 `t_auto_trigger` | **部分**:`AutoTriggerRuntimeService` 已挂钩;**问题件**联动问题模板,**拦截/警告**已支持 |
|
||
|
||
完整接口清单与实现状态见 **§10.6**;订单各接口「当前实现」见 **§4.4**;**渠道各接口总览见 §5.3.0**。
|
||
|
||
---
|
||
|
||
## 2 业务设计思路与主流程
|
||
|
||
### 2.1 职责边界:OMS 与 RBAC / 客户域
|
||
|
||
| 侧 | 职责 |
|
||
| --- | --- |
|
||
| **OMS(本文)** | 订单全生命周期、渠道配置与算价支撑、仓内作业、充值业务流水、**客户账户资金流水(`t_customer_account_flow`)**、`/system/*` 业务主数据(国家、字典、仓库地址、**其他配置**、通知等)。 |
|
||
| **RBAC / 客户域(另文)** | JWT 认证(`/rbac/auth/*`)、RBAC 用户/角色/菜单/组织(`/rbac/*`)、客户资料与 RBAC 联动(`/customer/*`)、权限码目录与分配、子客户专属角色 `CustomerSub_{userId}`。 |
|
||
| **协作约定** | OMS 用 **`t_customer.Id`** 作业务 `CustomerId`;会话侧用 **`mapId`(`user.userid`)** 定位客户行;**客户是否存在、状态、子客户权限、余额** 以 **`t_customer` + RBAC 服务** 为准,**禁止**把 `mapId` 直接当 `t_customer.Id`,**禁止**仅凭请求体 Id 当作已鉴权事实。 |
|
||
|
||
### 2.2 主流程 A:配置发布 → 运行时算价(渠道支撑订单)
|
||
|
||
**设计意图**:高并发查价/下单不走「每次打 DB 拉全量规则」;以 **版本化快照 + Redis + 计费实例内存** 为主路径,DB 发布表为审计与降级。
|
||
|
||
1. 运营维护销售产品、卖价/买价、分区(KpiArea)、计重、排货、服务渠道等。
|
||
2. **发布** 产生 **不可变版本**;结构化快照写入 **Redis**,计费服务加载 **本地内存**。
|
||
3. **查价与正式下单共用**:按绑定时刻(如 `bindTime`)选取应对生效的 **rules/price 版本** → 规则匹配(分区、计重、黑名单等)→ 出费用明细;查价响应宜带 **`rulesVersion` / `priceVersion`** 便于与订单对齐。
|
||
|
||
> **当前实现(oms0414-back)**:步骤 2~3 的 Redis/内存路径 **尚未接入**;运行时直接读 **`t_sales_product_publish_record`**,无发布时 **Live 组装**(见 **§4.3.5**)。
|
||
|
||
### 2.3 主流程 B:订单(客户侧业务链)
|
||
|
||
**设计意图**:**先锁「计费契约版本」、再按履约阶段分批落费**;历史订单不因后台新发版被静默改价。
|
||
|
||
1. **查价**(可选)→ **创建 / 预报** → 校验 → 草稿与包裹 → **预报计重完成后**将全部启用卖价项写入 **`t_order_fee`**(`ChargingStage=forecast`)。
|
||
2. **契约固化**:预报等关键节点写入 **`SalesProductRulesVersion`、`SalesProductPriceVersion`**(及渠道侧应付相关版本,若有);非显式「改单重算」流程 **不得** 因后台新发布而改写已存版本号。
|
||
3. **状态机**(概念顺序;枚举见 **`## 10 附录` → `### 10.3`**):如 草稿 → 已预报 → 已收货 → 待支付 → 待发货 → **待上网** → 运输中 → 派送成功/失败;另有拦截、**问题件**(`problematic`)、**理赔件**(`claimed`)、删除等分支。问题件/理赔件支持 **处理子状态**(`HandleStatus`:待处理/已处理),详见 **§4.4.1.13~§4.4.1.15**。
|
||
4. **支付**:OMS 侧订单状态与支付动作对接;**扣减 `t_customer.Balance`** 并 **写入 `t_customer_account_flow`(`BizType=orderPay`,金额为负)**;支付金额取订单 **`t_order_fee` 应收合计**。
|
||
5. **分阶段计费(按件重尺类型)**:卖价/买价 **不再配置「收费时机」**;应收在 **`t_order_weight.Type=forecast` 或 `receive` 计重完成** 后触发,全部启用价格项参与;`t_order_fee.ChargingStage` 标记本次算价所用重量阶段(`forecast` / `inbound`)。出库阶段(`ship` 行)待 §6.3.2.1 接入。
|
||
6. **批量下单**:Excel/模板 或 JSON 批量接口 → 异步任务;**单次最多受理 2000 条**。
|
||
|
||
### 2.4 主流程 C:操作(仓库)
|
||
|
||
1. **入库扫描**:实重、尺寸等落库;**仅更新 `Type=receive` 件重尺**并重算;可触发 **`inbound` 阶段**应收计费。
|
||
2. **出库扫描**:**仅更新 `Type=ship` 发货件重尺**并重算计费重;订单 **待发货 → 待上网**,写入 **`ShipTime`**;可触发 **`outbound` 阶段**应收计费。
|
||
3. **标签打印**:按单号与模板输出,记录打印结果。
|
||
|
||
### 2.5 主流程 D:充值
|
||
|
||
1. 客户提交 → **`t_recharge_record` 待审核**。
|
||
2. 审核通过/驳回;**通过时** 经 **`CustomerBalanceMutator`** 增加 **`t_customer.Balance`** 并写入 **`t_customer_account_flow`(`BizType=recharge`,金额为正)**;**`t_recharge_record`** 保留充值申请与审核状态(**非**统一资金流水表)。
|
||
|
||
### 2.6 主流程 E:系统主数据
|
||
|
||
国家、字典、仓库地址、**其他配置(第三方集成参数,如小包轨迹 `track_service`)**、通知供全业务引用;**与客户/内部员工相关的 DictType** 定义以 **`docs/rbac系统设计-060801.md`** 为准。
|
||
|
||
### 2.7 设计不变量(推断与实现时须一致)
|
||
|
||
| 不变量 | 说明 |
|
||
| --- | --- |
|
||
| 版本锁定 | 订单一旦写入规则/卖价版本字段,后续后台发版 **不自动** 推翻该票货的计费契约。 |
|
||
| Live 快照 | 无发布记录时版本号为 **`LIVE-{salesProductId}`**,仍落库为订单契约(**§4.3.5**)。 |
|
||
| 版本与落费时机解耦 | **版本**在预报等节点锁定;**何时写入 `t_order_fee`** 由 **件重尺类型**(forecast/receive/ship)触发的算价流程决定,与卖价/买价配置项无关。 |
|
||
| 后阶段仍用旧版快照 | 入库/出库触发的算价,加载的是 **该订单已绑定 version** 的快照,不是「当前最新 version」。 |
|
||
| 客户与余额 | 校验客户、余额、子客户关系 → **`t_customer` + `/customer/*`**;OMS 持 `CustomerId`(`t_customer.Id`)与业务流程;**任意余额变动须同步写 `t_customer_account_flow`**,流水含变动前后余额。 |
|
||
| **订单客户隔离** | **`/admin/order/*`(服务内 `/order/*`)** 下全部增删改查须以会话解析的 **`effectiveCustomerId`** 过滤与鉴权;**禁止**跨客户读写订单及相关任务/模板。见 **§4.4.0**。 |
|
||
| 审计字段 | 写操作落 `Create*`/`Update*`;操作者来自 **JWT `mapId`**(`CurrentUser.GetId<int>()`),见 **`### 3.2`、`### 3.3`**。 |
|
||
|
||
### 2.8 本文档章节索引(从流程跳转到细节)
|
||
|
||
| 上述块 | 正文位置 |
|
||
| --- | --- |
|
||
| 文档定位、模块一览、权限边界 | **`## 1 导读` → `### 1.3`~`### 1.5`** |
|
||
| 订单、查价、费用、版本策略 | **`## 4 订单模块`**(**`### 4.3.5` 当前实现**、**`### 4.4.0` 客户隔离**、**`### 4.3` 下单计费与版本策略**) |
|
||
| 产品/渠道/发布/分区/计重/标签 | **`## 5 渠道模块`**(**`### 5.3.0` 当前实现总览**) |
|
||
| 入库/出库/打印 | **`## 6 操作模块`** |
|
||
| 充值与账户流水 | **`## 7 充值管理`**(含 **`§7.4` 资金流水**) |
|
||
| 国家/字典/仓库/通知 | **`## 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` |
|
||
|
||
> 审计字段中的操作者信息优先从当前登录会话的 **`mapId`**(`CurrentUser.GetId<int>()`,即 `user.userid`)获取,不允许由前端直接信任覆盖。
|
||
|
||
### 3.3 OMS 与 RBAC 会话协作
|
||
|
||
与 **`docs/rbac系统设计-060801.md`** 对齐的 OMS 侧约定如下。
|
||
|
||
#### 3.3.1 认证接口(不在本篇逐条列出)
|
||
|
||
| 能力 | 路径 | 说明 |
|
||
| --- | --- | --- |
|
||
| 登录 | `POST /rbac/auth/login` | `type=0` 内部用户,`type=1` 客户 |
|
||
| 刷新令牌 | `POST /rbac/auth/refresh-token` | |
|
||
| 当前用户 | `GET /rbac/auth/get-current-user` | 返回 `roles`、`permissions`、`menus` |
|
||
| 改密 | `POST /rbac/auth/change-password` | 客户改 `t_customer` MD5,内部用户改 RBAC 哈希 |
|
||
|
||
请求头:`Authorization: Bearer {accessToken}`。
|
||
|
||
#### 3.3.2 从会话解析 OMS 业务主体
|
||
|
||
```
|
||
JWT → mapId(user.userid INT)
|
||
→ 查 t_customer WHERE UserId = mapId AND IsDeleted = 0
|
||
→ 得到 t_customer.Id 作为业务 CustomerId
|
||
→ 若 ParentCustomerId 非空则为子客户,按接口约定决定是否归并到主客户 Id
|
||
```
|
||
|
||
#### 3.3.3 权限码与接口鉴权
|
||
|
||
- 权限码格式:`Rbac.Users.Query`、`Api.Interfaces.Query` 等(PascalCase 点分隔),定义见 RBAC 文档 **§5**。
|
||
- 当前后端对 OMS 业务接口主要校验 **已登录**(`[Authorize]`);细粒度 Permission 拦截待 RBAC 包完善。
|
||
- 前端路由 `meta.permission` 与 `userInfo.permissions` 对齐;实现细节见 RBAC 文档 **§9**。
|
||
|
||
#### 3.3.4 客户域 HTTP(OMS 宿主内)
|
||
|
||
客户资料 CRUD、子客户创建(含权限子集校验)见 **`/customer/*`**,契约见 **`docs/rbac系统设计-060801.md` §8.7**,不在本篇展开。
|
||
|
||
#### 3.3.5 订单模块客户数据隔离(`/admin/order/*`)
|
||
|
||
线上网关将 OMS 订单接口挂载为 **`https://api.nanafa.com/admin/order/*`**;服务内路由为 **`/order/*`**(网关前缀 `/admin` 由反向代理剥离或统一转发,二者指同一组接口)。
|
||
|
||
| 项 | 约定 |
|
||
| --- | --- |
|
||
| 隔离键 | 会话解析得到的 **`effectiveCustomerId`**(**`t_customer.Id`**,非 `mapId`) |
|
||
| 解析方式 | `loginType=1`:`mapId` → `t_customer.UserId` → `t_customer.Id`;子客户是否归并主客户 Id 与 **§1.4** 一致,**全订单模块统一** |
|
||
| 查询(R) | 列表/导出/任务列表等:**强制** `WHERE CustomerId = effectiveCustomerId`(及 `IsDeleted=0` 等既有条件);**忽略**请求体中用于越权的其它 `customerId` |
|
||
| 单条读 | 按 `id` 加载后校验 `t_order.CustomerId == effectiveCustomerId`,否则按 **「订单不存在」** 返回(**禁止**泄露他客订单存在性) |
|
||
| 新增(C) | 创建订单/任务时 **`CustomerId` 仅取自会话**,写入 `t_order.CustomerId` / `t_order_task.CustomerId`;请求体若带 `customerId` 须与 `effectiveCustomerId` **一致**,否则 **403** |
|
||
| 更新/删除(U/D) | 支付、预报、拦截、删除、打印、费用重算等:先 **归属校验** 再执行业务;批量接口逐单校验,任一不属于当前客户则该单失败或整批拒绝(实现二选一须在 **§4.4.0** 写死) |
|
||
| 关联表 | `t_order_sender`、`t_order_recipient`、`t_order_declaration`、`t_order_weight`、`t_order_fee` 等通过 **`OrderId`** 间接隔离,**禁止**仅凭子表 Id 绕过主表 `CustomerId` 校验 |
|
||
| 内部用户 | `loginType=0` **默认禁止**调用 `/admin/order/*` 客户订单接口;若运营后台需跨客户查单,须 **另设** 内部管理路径与 PermissionCode,**不得**复用客户侧 `/admin/order` 且不带客户隔离 |
|
||
|
||
> **安全原则**:`CustomerId` **不得**由前端单独作为可信事实;**不得**仅按订单 `Id` 操作而不校验归属。
|
||
|
||
## 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 |
|
||
| DownstreamSupplierCode | VARCHAR | 64 | - | 否 | 下游 API 服务商编码(itfx **`supplierCode`**);排货确认渠道时自规则快照 **`Providers`** 写入 |
|
||
| DownstreamAppKey | VARCHAR | 128 | - | 否 | 下游 itfx 应用 **AppKey**;与 **`DownstreamAppId`** 配套 |
|
||
| DownstreamAppId | INT | - | - | 否 | 下游 itfx 应用 Id;**AppSecret 不落库**,运行时按 Id 调 itfx **`/App/GetInfo`** |
|
||
| ChannelForecastCode | VARCHAR | 128 | - | 否 | 渠道预报编码(**ForecastCode** / 轨迹 **ApiCode**);排货确认时固化 |
|
||
| ConsigneeCountry | VARCHAR | 8 | - | 否 | 国家二字码 |
|
||
| CargoAttribute | VARCHAR | 32 | - | 否 | 货物属性:general-普货,electronics-3c&带电,sensitive-敏感货 |
|
||
| OrderStatus | VARCHAR | 32 | - | 是 | 订单状态 |
|
||
| HandleStatus | VARCHAR | 16 | - | 否 | 处理子状态:`pending` 待处理 / `processed` 已处理;仅 **问题件、理赔件** 有效 |
|
||
| IssueType | VARCHAR | 32 | - | 否 | 问题类型 code,见 **§10.3.6** / `IssueTypeCodes` |
|
||
| IssueTitle | VARCHAR | 128 | - | 否 | 问题标题(可来自问题模板) |
|
||
| IssueContent | TEXT | - | - | 否 | 问题内容 |
|
||
| IssueTemplateId | BIGINT | - | 外键 → `t_issue_template.Id` | 否 | 引用的问题模板 |
|
||
| IssueRemark | VARCHAR | 512 | - | 否 | 问题备注(人工补充) |
|
||
| IssueTime | DATETIME | - | - | 否 | 标记问题件时间 |
|
||
| ClaimType | VARCHAR | 32 | - | 否 | 理赔类型 code,见 **§10.3.7** |
|
||
| ClaimAmount | DECIMAL | 18,4 | - | 否 | 索赔金额 |
|
||
| ClaimCurrency | VARCHAR | 16 | - | 否 | 索赔币种 |
|
||
| ClaimReason | VARCHAR | 512 | - | 否 | 理赔原因说明 |
|
||
| ClaimTime | DATETIME | - | - | 否 | 创建理赔件时间 |
|
||
| PreviousOrderStatus | VARCHAR | 32 | - | 否 | 进入异常状态前的订单状态(便于追溯/回滚扩展) |
|
||
| WarehouseId | BIGINT | - | 外键 | 否 | 仓库ID |
|
||
| CustomerId | BIGINT | - | 外键 → `t_customer.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`
|
||
|
||
> **请求约定**:`exportCustomsMode` **可选**;若传入须为上表 **code 本身**。非法示例:`9610`、`1`(字典序号)、`export_customs_mode` 字典项编码等;非法时返回 **「出口报关方式不合法」**。
|
||
|
||
#### 4.2.2 下单模板表 `t_order_template`
|
||
|
||
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
|
||
| Name | VARCHAR | 64 | - | 是 | 模板名称 |
|
||
| Type | VARCHAR | 16 | - | 是 | standard/custom |
|
||
| TemplateKey | VARCHAR | 64 | - | 否 | 模板标识 |
|
||
| CustomerId | BIGINT | - | 外键 → `t_customer.Id` | 否 | 客户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 | - | 外键 → `t_customer.Id` | 否 | 订单归属客户业务主键 |
|
||
|
||
#### 4.2.4 订单发件人表 `t_order_sender`
|
||
|
||
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
|
||
| OrderId | BIGINT | - | 外键 | 是 | 订单ID |
|
||
| SenderName | VARCHAR | 50 | - | 否 | 姓名 |
|
||
| Company | VARCHAR | 255 | - | 否 | 公司 |
|
||
| Country | INT | - | - | 否 | 国家(存 **`t_country.Id`**;创建小包请求体见 §4.4.3.1,传 ISO 二字码) |
|
||
| 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 | - | - | 否 | 国家(存 **`t_country.Id`**;创建小包请求体见 §4.4.3.1,传 ISO 二字码) |
|
||
| 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(下游)
|
||
|
||
**履约更新**:四行 **`t_order_weight`** 各维护独立件重尺口径,**入库/出库仅覆盖并重算本环节对应 `Type` 行**,**不得**向其它 `Type` 拷贝或同步。**入库** → 仅 **`receive` 行**(§6.3.1.1);**出库** → 仅 **`ship` 行**(§6.3.2.1)。**`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`(出库),由触发算价时的件重尺类型决定,便于对账 |
|
||
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
|
||
| CreateUserId | INT | - | - | 是 | 创建人 |
|
||
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
|
||
| UpdateUserId | INT | - | - | 否 | 更新人 |
|
||
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
|
||
|
||
**Type 枚举**:receivable(应收)、payable(应付)
|
||
|
||
#### 4.2.9 订单异常事件表 `t_order_exception_event`
|
||
|
||
> **用途**:问题件/理赔件操作的 **审计流水**(一单可多次「更新处理子状态」;问题件与理赔件各记一条创建事件)。主表 `t_order` 存 **当前快照**,本表存 **历史**。
|
||
|
||
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| Id | BIGINT | - | 主键 | 是 | 自增 |
|
||
| OrderId | BIGINT | - | 外键 → `t_order.Id` | 是 | 订单 Id |
|
||
| EventType | VARCHAR | 32 | - | 是 | `mark_problematic` / `mark_claimed` / `handle_processed` |
|
||
| FromStatus | VARCHAR | 32 | - | 否 | 变更前 `OrderStatus` |
|
||
| ToStatus | VARCHAR | 32 | - | 否 | 变更后 `OrderStatus` |
|
||
| FromHandleStatus | VARCHAR | 16 | - | 否 | 变更前 `HandleStatus` |
|
||
| ToHandleStatus | VARCHAR | 16 | - | 否 | 变更后 `HandleStatus` |
|
||
| IssueType | VARCHAR | 32 | - | 否 | 问题类型(创建问题件时) |
|
||
| Title | VARCHAR | 128 | - | 否 | 标题快照 |
|
||
| Content | TEXT | - | - | 否 | 内容快照 |
|
||
| IssueTemplateId | BIGINT | - | - | 否 | 模板 Id |
|
||
| NotifyMethod | VARCHAR | 64 | - | 否 | 通知方式快照(逗号分隔) |
|
||
| ClaimType | VARCHAR | 32 | - | 否 | 理赔类型 |
|
||
| ClaimAmount | DECIMAL | 18,4 | - | 否 | 索赔金额 |
|
||
| ClaimCurrency | VARCHAR | 16 | - | 否 | 索赔币种 |
|
||
| ClaimReason | VARCHAR | 512 | - | 否 | 理赔原因 |
|
||
| Remark | VARCHAR | 512 | - | 否 | 操作备注 |
|
||
| CreateUserId | INT | - | - | 是 | 操作人 |
|
||
| CreateTime | DATETIME | - | - | 是 | 操作时间 |
|
||
|
||
**索引**:`idx_oee_order_time` (`OrderId`, `CreateTime`)。
|
||
|
||
### 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 费用计算时机(件重尺阶段)
|
||
|
||
> **版本(契约)**:在**下单/预报**流程中(如 `/order/parcel/create` 绑定快照时)即确定 `SalesProductRulesVersion`、`SalesProductPriceVersion`(及渠道侧与应付相关的版本,若有)。后续只要未发生「改单重算」等显式流程,**不得**因后台发布新版本而改写订单已绑定版本。
|
||
> **金额(执行)**:卖价/买价主表 **已移除 `ChargingTime`(收费时机)**;应收在 **`t_order_weight` 对应行计重完成** 后触发,**全部启用价格项**参与;`t_order_fee.ChargingStage` 记录本次重量阶段。
|
||
|
||
| 件重尺 `Type` | 典型触发节点 | 应收(`Type=receivable`) | 应付(`Type=payable`,设计) |
|
||
| --- | --- | --- | --- |
|
||
| `forecast` | 下单预报成功 | **§4.3.4.1**:全部启用卖价项;`ChargingStage=forecast` | **§4.3.4.2**:排货确认渠道 + 渠道买价;`ChargingStage=forecast`;重量取 **`downstream`** |
|
||
| `receive` | 仓库收货 `/operation/inbound/create` | **§4.3.4.1**:全部启用卖价项;`ChargingStage=inbound` | **§4.3.4.2**:**重新**排货确认渠道 + 渠道买价;`ChargingStage=inbound`;重量取 **`receive`** |
|
||
| `ship` | 仓库出库 §6.3.2.1 | **§4.3.4.1**(**未实现**) | **§4.3.4.2**(**未实现**) |
|
||
|
||
**规则小结**:先锁版本;按件重尺阶段触发算价;每次对 **全部启用** 价格项算价;后阶段仍读订单绑定快照。
|
||
|
||
##### 4.3.4.1 应收费用重算(`Type=receivable`)
|
||
|
||
1. **筛选**:`status=1` 的全部卖价项。
|
||
2. **重量**:按触发节点取 `forecast` / `receive` / `ship` 行计费重。
|
||
3. **输出**:upsert `t_order_fee`,幂等键 `OrderId + CostItem + ChargingStage`。
|
||
4. **已实现**:`/order/parcel/create`、`ApplyReceiveBillingByWaybillAsync`。
|
||
|
||
##### 4.3.4.2 应付费用重算(`Type=payable`)
|
||
|
||
1. **排货选渠**:读取订单绑定 **规则快照** 内 `Providers` 与 `Scheme`;按 **客户范围**(`CustomerIds` 空=全部)过滤候选服务商;对每个候选 **服务渠道** 试算买价总额,再按方案选型:
|
||
- **`costLowest`**:应付总额最低;
|
||
- **`timeFastest`**:`MinTime` 最小;
|
||
- **`overallBest`**:`Weight` 最大。
|
||
选中后回写 **`t_order.ChannelCode`**,并自选中 **`Providers`** 项固化下游对接字段:**`DownstreamSupplierCode`**、**`DownstreamAppKey`**、**`DownstreamAppId`**、**`ChannelForecastCode`**(实现:`ConfirmServiceChannelAndPayableAsync` → **`AllocationProviderSnapshotEnricher.ApplyDownstreamFieldsToOrder`**)。
|
||
2. **筛选**:`status=1` 的全部买价项(优先读渠道买价发布快照,无则 Live 组装)。
|
||
3. **重量**:预报阶段取 **`downstream`** 行计费重;入库阶段取 **`receive`** 行计费重(收货复量后重新排货)。
|
||
4. **输出**:upsert `t_order_fee`,幂等键 `OrderId + CostItem + ChargingStage`;`PriceNo` 写渠道买价版本。
|
||
5. **调用方**:`/order/parcel/create`(预报)、`ApplyReceiveBillingByWaybillAsync`(入库)、`/order/fee/recalculate-payable`。
|
||
6. **下游运行时读取**:轨迹 **UpsertSmallBag** 的 **`ApiCode`** 优先 **`t_order.ChannelForecastCode`**,缺失时回退 live 查 **`t_service_channel.ForecastCode`**;itfx 下游预报(前端 **`createOrderOne`**)优先读订单 **`DownstreamSupplierCode` / `DownstreamAppKey` / `DownstreamAppId`**,**AppSecret** 仍按 **`DownstreamAppId`** 调 itfx App API。
|
||
|
||
#### 4.3.5 当前实现:快照解析与算价(oms0414-back)
|
||
|
||
> 实现位置:`OrderService.ParcelBilling.cs` → `ResolveSnapshotBindingAsync`;`ParcelBillingCalculator.cs`。
|
||
|
||
**快照绑定(`ResolveSnapshotBindingAsync`)**
|
||
|
||
1. `bindTime = DateTime.Now`。
|
||
2. 分别查询 **`t_sales_product_publish_record`**:`OperatorType = 'rules' | 'price'`,`PublishTime <= bindTime`,按发布时间降序取生效记录。
|
||
3. **有发布记录**:反序列化 `Content` 为规则/价格快照 DTO;价格快照 **`Prices` 为空** → 失败 **「未命中价格快照版本(内存/Redis/DB)」**。
|
||
4. **无发布记录(Live 降级)**:
|
||
- **规则**:从 `t_sales_product` + `t_kpi_area_info` / `t_weight_rule_info` / `t_weight_rounding_info` 实时组装;若产品 **`WeightRuleId` 为空** → 失败 **「未命中规则快照版本(内存/Redis/DB)」**。
|
||
- **价格**:从 `t_sales_product_price`(及 factor、detail)实时组装;**无任何价格行** → 同上价格失败。
|
||
- 版本字段落库:**`LIVE-{salesProductId}`**;生效时间取 `bindTime`。
|
||
5. **Redis / 进程内存**:当前代码路径 **未接入**;错误文案保留「内存/Redis/DB」字样以兼容后续接入。
|
||
|
||
**分区 / 黑名单 / 偏远**
|
||
|
||
- 优先读快照内嵌的 **`BlacklistAreaInfos` / `RemoteAreaInfos` / `PriceAreaInfos`**;缺失时回退 **`t_kpi_area_info`**(按产品或快照上的 `*AreaId`)。
|
||
- 黑名单命中 → **不落库**,抛 **「收件地址命中黑名单区域」**。
|
||
- 偏远命中 → 更新 **`t_order.IsRemote = 1`**,不拦截。
|
||
|
||
**计重与进位(`ApplyChargeableWeightAsync`)**
|
||
|
||
- `effectiveWeightRuleId` / `effectiveWeightRoundingId`:优先 **`t_sales_product`**,其次规则快照上的 ID 字段。
|
||
- 明细:优先快照 **`WeightRuleInfos` / `WeightRoundingInfos`**,否则查 **`t_weight_rule_info` / `t_weight_rounding_info`**。
|
||
- 算法:`ParcelBillingCalculator.CalculateRawBillableWeight` → `ApplyRounding`(`ceil(raw/unit)×unit`,3 位小数)。
|
||
- 预报成功后 **四行** `t_order_weight`(`forecast/receive/ship/downstream`)**同步相同**件重尺与计费重。
|
||
|
||
**卖价算价**
|
||
|
||
| 场景 | 方法 | 说明 |
|
||
| --- | --- | --- |
|
||
| 小包创建 / 查价 | `CalculateReceivableFees` / `CalculateQuoteReceivableFees` | 全部启用卖价项;创建写 `ChargingStage=forecast` |
|
||
| 入库收货 | `ApplyReceiveBillingByWaybillAsync` | 仅覆盖 **`Type=receive`** 并重算应收;**重新排货**确认渠道与应付(`ChargingStage=inbound`);**`OrderStatus=pendingPayment`**,写入 **`ReceiveTime`** |
|
||
| 出库发货 | `ApplyShipBillingByWaybillAsync`(待新增) | 仅覆盖 **`Type=ship`** 并重算;写 `ChargingStage=outbound`(应收待接入);**`OrderStatus=pendingOnline`**,写入 **`ShipTime`** |
|
||
| 应付 | `CalculatePayableFees` + `ConfirmServiceChannelAndPayableAsync` | 预报 / 入库 / 手动重算应付 |
|
||
|
||
**查价额外逻辑**(`OrderService.ParcelQuote.cs`):遍历 **全部已启用** 销售产品(`shippingMethod` 模糊匹配 `ProductCode` / `ProductName`);失败写入 **`failures[]`**(含 `productId`、`step`、`reason`);成功项带 **`rulesVersion`、`priceVersion`、`isRemote`**。
|
||
|
||
### 4.4 核心接口设计
|
||
|
||
> **网关路径**:生产环境对外为 **`/admin/order/*`**(例:`POST https://api.nanafa.com/admin/order/list`);下文契约路径以 **`/order/*`** 表示服务内路由,与前端 `orderUrl('/order/...')` 拼接规则一致。
|
||
|
||
#### 4.4.0 客户数据隔离(强制)
|
||
|
||
**适用范围**:**§4.4.1 我的订单**、**§4.4.2 我要查价**、**§4.4.3 我要下单** 中列出的全部接口,以及同模块后续增补的订单相关接口。
|
||
|
||
**会话客户上下文**
|
||
|
||
1. 从 JWT 解析 **`mapId`**、**`loginType`**(见 **§3.3.2**)。
|
||
2. 仅 **`loginType=1`(客户登录)** 允许访问本组接口;否则返回 **401/403**。
|
||
3. 查询 `t_customer`:`UserId = mapId AND IsDeleted = 0`,得到 **`effectiveCustomerId = t_customer.Id`**。
|
||
4. 若 **`ParentCustomerId` 非空**(子客户):按 **§1.4** 约定,订单归属使用 **主客户 `t_customer.Id`** 或 **子客户自身 `Id`** 之一;**全模块、全接口统一**,实现与测试须与选定策略一致(推荐:**子客户订单落在主客户 `CustomerId` 下**,子账号仅能通过 RBAC 子客户权限访问主客户数据)。
|
||
|
||
**隔离规则(增删改查)**
|
||
|
||
| 操作类型 | 接口示例 | 强制规则 |
|
||
| --- | --- | --- |
|
||
| **查 · 列表** | `POST /order/list`、`GET /order/task/list`、`GET /order/template/list` | 查询条件 **必须包含** `CustomerId = effectiveCustomerId`;**不得**返回其它客户数据 |
|
||
| **查 · 详情** | `GET /order/detail`、`GET /order/task/detail` | 加载实体后校验 `CustomerId`;不匹配返回业务错误「订单不存在」或 **403**(**禁止**返回他客数据) |
|
||
| **增** | `POST /order/parcel/create`、`POST /order/parcel/batch-create`、`POST /order/upload` | 落库 `CustomerId` **仅**取 `effectiveCustomerId`;请求体 `customerId` 若存在须 **相等**,否则拒绝 |
|
||
| **改** | `POST /order/pay`、`POST /order/pre-declare`、`POST /order/intercept`、`POST /order/cancel-intercept`、`POST /order/fee/batch-recalculate` | 按 `id` / `orderIds` 加载订单后 **逐单**校验 `CustomerId`;不属于当前客户 **不得**变更状态或费用 |
|
||
| **删** | `POST /order/delete` | 同上,仅允许删除 **`CustomerId = effectiveCustomerId`** 的订单 |
|
||
| **其它写** | `POST /order/print`、`POST /order/export` | 打印/导出前校验归属;导出筛选范围 **限定**当前客户 |
|
||
|
||
**批量与关联资源**
|
||
|
||
- **`POST /order/fee/batch-recalculate`**:`orderIds` 中每一单均须归属 `effectiveCustomerId`;建议 **部分失败** 策略(`failures` 记录越权单,错误码如 `ORDER_FORBIDDEN`),与 **§4.4.1.10** 一致。
|
||
- **`t_order_template`**(自定义模板):`CustomerId` 隔离;标准模板(`Type=standard`)全员可见,**不可**通过模板 Id 间接操作他客订单。
|
||
- **`t_order_task`**:列表/详情 **仅**返回 `CustomerId = effectiveCustomerId` 的任务;任务下关联订单创建时 **继承**同一 `CustomerId`。
|
||
|
||
**查价(§4.4.2)**
|
||
|
||
- `POST /order/price-check` 不读写 `t_order`,但仍须 **已登录客户**;算价所用产品/仓库权限须与当前客户可用范围一致(若产品按客户组/客户 Id 限制,按配置过滤);**禁止**借查价接口探测他客专属价目。
|
||
|
||
**错误码建议**
|
||
|
||
| 场景 | 建议 |
|
||
| --- | --- |
|
||
| 未登录 / Token 无效 | 401 |
|
||
| 非客户身份调用 | 403 |
|
||
| 订单 Id 存在但 `CustomerId` 不匹配 | 404 或 403(文案统一「订单不存在」,避免信息泄露) |
|
||
| 请求体 `customerId` 与会话不一致 | 业务错误 **「客户ID非法」**(HTTP 403 亦可) |
|
||
|
||
**实现检查清单(后端,2026-06-24 已落地)**
|
||
|
||
1. 封装 **`ResolveSessionCustomerAsync` / `ResolveListCustomerIdAsync`**(`OmsSessionHelper`),创建类接口 **先**解析 `effectiveCustomerId` 再校验其它字段;列表/导出/任务列表 **强制** `WHERE CustomerId = effectiveCustomerId`(**禁止**仅用 `CreateUserId` 过滤)。
|
||
2. 列表类 Repository 查询 **统一注入** `CustomerId` 条件,**禁止**仅靠前端传参过滤。
|
||
3. **`GetOrderOrThrowAsync(id)`** 在存在性校验后增加 **`order.CustomerId == effectiveCustomerId`**,不匹配返回 **「订单不存在」**。
|
||
4. 创建类接口(`parcel/create`、`batch-create`、`upload`):**未传** `customerId` 时由会话补全;**已传**则须与会话一致;落库 **仅**写入 `effectiveCustomerId`。
|
||
5. 单测/集成测覆盖:**客户 A 的 Token 无法 list/detail/改/删 客户 B 的订单**。
|
||
|
||
#### 4.4.1 我的订单
|
||
|
||
| 接口名称 | 请求方式 | 请求路径 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| 分页查询订单列表 | POST | /order/list | 支持 status、keyword、createTimeStart/End 筛选;**强制按会话 `CustomerId` 隔离**(§4.4.0) |
|
||
| 订单详情 | 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`;**逐单校验 `CustomerId`**(§4.4.0) |
|
||
| 创建轨迹订单 | POST | /order/track-upsert-small-bag | 手动同步调用轨迹 **UpsertSmallBag**(联调/补推);**须校验订单归属** |
|
||
| 创建问题件 | POST | /order/mark-problematic | 批量将订单标记为 **问题件**;消费问题模板(§5.2.21);**须校验订单归属** |
|
||
| 创建理赔件 | POST | /order/mark-claimed | 批量将订单标记为 **理赔件**;**须校验订单归属** |
|
||
| 更新处理子状态 | POST | /order/handle-status/update | 问题件/理赔件 **待处理→已处理**;**须校验订单归属** |
|
||
| 预报下游订单 | —(前端) | itfx **`POST /InterfaceForward/ForwardWithSubscribe`** | 「我的订单」批量操作;`standardCode=createOrderOne`;凭证优先读订单下游绑定字段(§4.4.1.12) |
|
||
|
||
##### 4.4.1.1 分页查询订单列表(POST /order/list)
|
||
|
||
###### 1 接口请求示例
|
||
|
||
**请求 /order/list**(POST,分页参数放 Body)
|
||
|
||
```json
|
||
{
|
||
"page": 1, // 页码,从1开始
|
||
"pageSize": 20, // 每页条数
|
||
"status": "all|draft|predeclared|received|...", // 订单状态筛选
|
||
"handleStatus": "pending|processed", // 处理子状态;仅 status=problematic|claimed 时生效
|
||
"keyword": "", // 关键词(运单号/客户单号/跟踪号)
|
||
"createTimeStart": "", // 创建时间起
|
||
"createTimeEnd": "" // 创建时间止
|
||
}
|
||
```
|
||
|
||
**响应**(用于 order/myOrders 列表及订单详情抽屉,字段与 parcel/create 对应;**注意**:`sender.countryCode` / `receiver.countryCode` 响应中为 **`t_country.Id`(int)**,与创建请求体的 ISO 二字码不同)
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": {
|
||
"items": [
|
||
{
|
||
"id": 1,
|
||
"clientNo": "CUST...",
|
||
"trackNo": "TRK...",
|
||
"serviceNo": "LP...",
|
||
"productCode": "US_AIR",
|
||
"channelCode": "CH001",
|
||
"downstreamSupplierCode": "SP001",
|
||
"downstreamAppKey": "AKxxxx",
|
||
"downstreamAppId": 12,
|
||
"channelForecastCode": "FORECAST_01",
|
||
"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": 1,
|
||
"province": "广东省",
|
||
"city": "深圳市",
|
||
"zipCode": "518000",
|
||
"streetAddress1": "示例地址1",
|
||
"streetAddress2": "",
|
||
"streetAddress3": "",
|
||
"houseNumber": ""
|
||
},
|
||
"receiver": {
|
||
"recipientsName": "收件人1",
|
||
"companyName": "",
|
||
"phone": "13800138000",
|
||
"telePhone": "",
|
||
"email": "",
|
||
"countryCode": 1,
|
||
"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 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. 解析会话 **`effectiveCustomerId`**(§4.4.0)。
|
||
2. 构造分页查询条件时 **固定追加** `t_order.CustomerId = effectiveCustomerId`(及 `IsDeleted = 0`、状态/关键词/时间等前端筛选)。
|
||
3. **忽略**请求体中任意用于切换客户的 `customerId` 字段(若存在)。
|
||
|
||
###### 状态与失败处理
|
||
|
||
- 未登录或非客户身份:401/403。
|
||
|
||
###### 校验与异常
|
||
|
||
- 不得返回其它客户订单;不得通过 `keyword` 等条件绕过 `CustomerId` 过滤。
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- `OrderService.GetPaginatedListAsync`:`ResolveListCustomerIdAsync` — **客户**强制 `CustomerId` 过滤;**管理员** `customerId=null` **不过滤**。
|
||
- 关键词匹配 **`ServiceNo`、`ClientNo`、`TrackNo`**;`status=all` 或不传表示不过滤状态。
|
||
- 排序:`UpdateTime desc, Id desc`;分页默认见 §1.5.1。
|
||
|
||
##### 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 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. 按 `id` 查询订单;不存在或 `IsDeleted=1` 时返回「订单不存在」。
|
||
2. 校验 `t_order.CustomerId == effectiveCustomerId`(§4.4.0);不匹配时 **同样**返回「订单不存在」(禁止泄露他客订单)。
|
||
3. 映射详情并返回。
|
||
|
||
###### 校验与异常
|
||
|
||
- 禁止仅凭 `id` 跨客户读取。
|
||
|
||
##### 4.4.1.3 支付(POST /order/pay)
|
||
|
||
###### 1 接口请求示例
|
||
|
||
**请求 /order/pay**(POST)
|
||
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"requestId": ""
|
||
}
|
||
```
|
||
|
||
> requestId 幂等,可选。
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. `GetOrderOrThrowAsync(id)`:订单存在且客户隔离通过。
|
||
2. **状态校验**:若已 **`pendingShip` 且已有 `PayTime`** → **「订单已支付」**;若 **`OrderStatus` 非 `pendingPayment` 且非 `received`**(`received` 为入库后历史数据兼容)→ **「当前订单状态不可支付」**。
|
||
3. **应付金额**:汇总该订单 **`t_order_fee`** 中 **`Type=receivable`** 的 **`Amount`**;**≤ 0** → **「订单应收金额无效,无法支付」**。
|
||
4. **扣款**:加载 **`order.CustomerId`** 对应客户;调用 **`CustomerBalanceMutator.DebitOrderPayAsync`**:
|
||
- 校验 **`Balance ≥ payAmount`**,不足 → **「账户余额不足」**;
|
||
- **`t_customer.Balance -= payAmount`**;
|
||
- 插入 **`t_customer_account_flow`**:`BizType=orderPay`,`BizId=order.Id`,`BizNo=ServiceNo`,**`Amount` 为负数**,记录 **`BalanceBefore` / `BalanceAfter`**,流水号 **`FLOW{yyyyMMddHHmmss}{6位随机}`**。
|
||
5. 更新订单:**`PayTime = DateTime.Now`**,**`OrderStatus = pendingShip`**。
|
||
|
||
> 步骤 4~5 在同一 **`ApplicationService` 工作单元**内提交;任一步失败整体回滚。
|
||
|
||
###### 校验与异常
|
||
|
||
| 场景 | 提示 |
|
||
| --- | --- |
|
||
| 订单不存在 / 越权 | **「订单不存在」** |
|
||
| 已支付 | **「订单已支付」** |
|
||
| 状态非待支付 | **「当前订单状态不可支付」** |
|
||
| 无客户 | **「订单未关联客户,无法支付」** |
|
||
| 应收无效 | **「订单应收金额无效,无法支付」** |
|
||
| 客户不存在 | **「客户不存在」** |
|
||
| 余额不足 | **「账户余额不足」** |
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- 实现:`OrderService.PayAsync`(`OrderService.cs`)+ **`CustomerBalanceMutator`**(`CustomerBalanceMutator.cs`)。
|
||
- 与 §2.3 主流程 B「支付扣减余额」**已对齐**;**不修改** `t_order_fee`(费用在预报/入库阶段已写入)。
|
||
|
||
##### 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 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. `GetOrderOrThrowAsync(id)`。
|
||
2. 直接将 **`OrderStatus` 置为 `predeclared`** 并保存。
|
||
3. **不**重跑计重/算价/黑名单;**不**校验是否已有预报费用。
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- 实现:`OrderService.PreDeclareAsync` — 纯状态更新,供运营手工补状态。
|
||
|
||
##### 4.4.1.5 拦截(POST /order/intercept)
|
||
|
||
###### 1 接口请求示例
|
||
|
||
**请求 /order/intercept**(POST)
|
||
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"requestId": ""
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. `GetOrderOrThrowAsync(id)` → 设置 **`IsIntercept = 1`** 并保存。
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- `OrderService.InterceptAsync`。
|
||
|
||
##### 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 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. `GetOrderOrThrowAsync(id)` → 设置 **`IsIntercept = 0`** 并保存。
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- `OrderService.CancelInterceptAsync`。
|
||
|
||
##### 4.4.1.7 删除(POST /order/delete)
|
||
|
||
###### 1 接口请求示例
|
||
|
||
**请求 /order/delete**(POST)
|
||
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"requestId": ""
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. `GetOrderOrThrowAsync(id)`。
|
||
2. **`OrderStatus = deleted`**,调用仓储 **软删除**(`IsDeleted=1`)。
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- `OrderService.DeleteAsync` — 先更新状态再 `DeleteAsync` 软删。
|
||
|
||
##### 4.4.1.8 打印(POST /order/print)
|
||
|
||
###### 1 接口请求示例
|
||
|
||
**请求 /order/print**(POST)
|
||
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"requestId": ""
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": {
|
||
"printUrl": ""
|
||
}
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. `GetOrderOrThrowAsync(id)` → 映射订单详情。
|
||
2. `CreateOrderPrintFileAsync`:生成 **HTML** 面单至 `wwwroot/uploads/prints/orders/{serviceNo}-{timestamp}.html`。
|
||
3. 返回 **`printUrl`**(相对路径,如 `/uploads/prints/orders/...`)。
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- `OrderService.PrintAsync` + `OrderService.Helpers.CreateOrderPrintFileAsync`(简易 HTML,非渠道标签模板)。
|
||
|
||
##### 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 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. 客户登录时 **`ResolveListCustomerIdAsync`** 限定导出范围。
|
||
2. 按 `status`、`keyword`、`createTimeStart/End` 查询订单(与 list 同 predicate)。
|
||
3. MiniExcel 写入 `wwwroot/uploads/exports/orders/orders-{timestamp}.xlsx`。
|
||
4. 清理 **24 小时**前旧文件;返回 **`downloadUrl`**、`expireAt`(当前时间 + 24h)。
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- `OrderService.ExportAsync`。
|
||
|
||
##### 4.4.1.10 批量重算费用(POST /order/fee/batch-recalculate)
|
||
|
||
> **实现状态:未落地**(oms0414-back 无对应 `ApplicationService` 方法)。以下为设计目标,供后续迭代实现;**§10.6** 标记为「设计中」。
|
||
|
||
用于「我的订单」运营/对账场景:按订单 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`**;**逐单校验 `t_order.CustomerId = effectiveCustomerId`(§4.4.0)**,越权单记入 `failures` 或整批拒绝;调用方须已登录且为 **`loginType=1` 客户**。
|
||
2. **当 `scopes` 含 `receivable`(重算应收)**
|
||
- 逐单加载:绑定 **`SalesProductPriceVersion`** 卖价快照、`t_order_weight`、`t_order_fee` 既有应收行(若策略为全量重算可先逻辑清理再写)。
|
||
- 按 **§4.3.4.1**:全部启用卖价项;重量取对应 **`t_order_weight` 行** `ChargeableWeight`,**upsert** 应收。
|
||
3. **当 `scopes` 含 `payable`(重算应付)**
|
||
- 逐单校验 **`t_order_weight` 存在 `Type=downstream`** 且可用于买价匹配;加载绑定 **买价** 快照。
|
||
- 按 **§4.3.4.2**:**仅 `downstream`** 行参与重量/件重尺因子,重算并 **upsert** `Type=payable`。
|
||
4. **事务**:建议 **按订单** 单事务提交,避免一批中一单失败影响其他单;`failures` 逐单记录原因。
|
||
5. **与履约链路的关系**:**预报成功、入库完成、出库完成** 应在各自流程内调用 **§4.3.4.1**(重量分别取 **`forecast` / `receive` / `ship` 行**);**`downstream` 行更新后**(下游回传或独立流程)再调用 **§4.3.4.2**。本接口为 **显式批量补算**,不改变订单已绑定版本契约。
|
||
|
||
###### 校验与异常
|
||
|
||
- `orderIds` 为空、`scopes` 为空或不合法、订单无绑定卖价/买价版本、缺 `downstream`(在请求 `payable` 时)等返回业务错误;部分失败时仍返回 `200` + `data.failures`(或与全局错误码策略一致)。
|
||
|
||
##### 4.4.1.11 创建轨迹订单(POST /order/track-upsert-small-bag)
|
||
|
||
> **实现状态:已落地**(oms0414-back `OrderService.TrackUpsertSmallBagAsync`;前端「我的订单」批量按钮)。
|
||
|
||
手动触发小包轨迹 **`POST {TrackBaseUrl}/OrderV2/UpsertSmallBag`**(同步调用,返回完整请求/响应便于联调)。与预报成功后的 **异步** 轨迹推送(`SmallBagTrackOrderNotifier`)共用 **`BuildSmallBagTrackUpsertRequestAsync`** 组装逻辑。
|
||
|
||
###### 1 接口请求示例
|
||
|
||
**请求 /order/track-upsert-small-bag**(POST)
|
||
|
||
```json
|
||
{ "id": 1001 }
|
||
```
|
||
|
||
**响应**(`SmallBagTrackUpsertResult`)
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": {
|
||
"skipped": false,
|
||
"skipReason": null,
|
||
"requestUrl": "http://api.nanafa.com/track/OrderV2/UpsertSmallBag",
|
||
"requestBody": { "appKey": "...", "orderNo": "LP...", "apiCode": "FORECAST_01" },
|
||
"responseStatusCode": 200,
|
||
"responseBody": "..."
|
||
}
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
1. 校验订单存在、未删除、**归属当前客户**(§4.4.0);**`ServiceNo`** 非空。
|
||
2. 组装 **`SmallBagTrackUpsertRequest`**:客户编码、运单号、跟踪号、渠道、产品、收件人国家/省/邮编等。
|
||
3. **`ApiCode`**:优先 **`t_order.ChannelForecastCode`**;为空且 **`ChannelCode`** 有值时,回退查 **`t_service_channel.ForecastCode`**。
|
||
4. 轨迹凭证读 **`t_system_other_config`**(`ConfigCode=track_service`),缺省回退 **`Oms:TrackService`**(§8.2.7);未启用或参数不全时返回 **`skipped=true`** 及原因。
|
||
5. 联调入口:**`POST /order/debug/track-upsert-small-bag`**(Swagger,可自定义测试字段)。
|
||
|
||
##### 4.4.1.12 预报下游订单(前端 → itfx,非 OMS 后端接口)
|
||
|
||
> **实现状态:已落地**(前端 `order/myOrders` 批量操作 + `forwardCreateOrderOne`;凭证解析 `resolveOrderDownstreamCredentials`)。
|
||
|
||
运营在「我的订单」勾选订单后,浏览器直连 itfx 网关 **`POST /InterfaceForward/ForwardWithSubscribe`**,**`standardCode=createOrderOne`**,Body 为 **§4.4.3.1** 同构的 parcel/create JSON(`orderCreateOneBody.ts` 由订单详情组装)。
|
||
|
||
###### 1 凭证解析优先级
|
||
|
||
| 顺序 | 来源 | 字段 |
|
||
| --- | --- | --- |
|
||
| 1(优先) | **`t_order` 绑定字段**(排货确认时写入) | `downstreamSupplierCode`、`downstreamAppKey`、`downstreamAppId` |
|
||
| 2(回退) | 渠道树 live 查询 | `ChannelCode` → `t_service_channel` → 分组 → itfx App / ServiceProvider |
|
||
|
||
**AppSecret** 不落订单、不落规则快照;无论哪条路径,均按 **`downstreamAppId`(或分组 `AppId`)** 调 itfx **`GET /App/GetInfo`** 读取。
|
||
|
||
###### 2 请求要点
|
||
|
||
- Query:`standardCode=createOrderOne`,`supplierCode={DownstreamSupplierCode}`。
|
||
- Header:`appkey`、`appSecret`、`followName=0`(itfx 网关约定)。
|
||
- Body:camelCase,与 **§4.4.3.1** 创建小包请求体对齐(含 `receiver`、`items` 等)。
|
||
|
||
###### 3 与规则快照的关系
|
||
|
||
- 规则发布时 **`Providers[]`** 已 enrich 渠道/itfx 元数据(§5.3.0.2);排货确认渠道时固化至 **`t_order`**。
|
||
- **历史订单**(确认渠道时尚未写入四字段)仍走凭证解析 **回退路径**;重新发布销售产品规则后,**新排货确认** 的单据将携带完整绑定字段。
|
||
|
||
##### 4.4.1.13 创建问题件(POST /order/mark-problematic)
|
||
|
||
> **实现状态:设计中**(oms0414-back `OrderService` 待新增;前端 `order/myOrders` 批量弹窗)。
|
||
|
||
###### 1 业务目标
|
||
|
||
运营在「我的订单」勾选一条或多条订单,填写问题类型与话术(可引用 **问题模板** §5.2.21),将订单 **`OrderStatus` 置为 `problematic`**,**`HandleStatus` 置为 `pending`**,并写入问题快照字段;供 **问题件** Tab 与 **待处理/已处理** 子 Tab 筛选展示。
|
||
|
||
###### 2 状态准入规则
|
||
|
||
| 规则 | 说明 |
|
||
| --- | --- |
|
||
| **禁止源状态** | `draft`、`deleted`、`problematic`、`claimed` |
|
||
| **允许源状态** | `predeclared`、`received`、`pendingPayment`、`pendingShip`、`pendingOnline`、`shipping`、`deliverySuccess`、`deliveryFailed` |
|
||
| **互斥** | 已为问题件/理赔件/已删除的订单 **不可重复创建**;一期 **不支持** 问题件↔理赔件互转 |
|
||
| **拦截** | `IsIntercept=1` **不阻止** 标记问题件(与拦截标记独立) |
|
||
|
||
变更时写入 **`PreviousOrderStatus`** = 变更前 `OrderStatus`,便于二期回滚或报表。
|
||
|
||
###### 3 请求示例
|
||
|
||
```json
|
||
{
|
||
"orderIds": [1001, 1002],
|
||
"issueTemplateId": 12,
|
||
"issueType": "address",
|
||
"title": "收件地址无法派送",
|
||
"content": "运单 {serviceNo} 收件地址不完整,请补充门牌号与邮编。",
|
||
"remark": "客户已电话确认"
|
||
}
|
||
```
|
||
|
||
| 字段 | 必填 | 说明 |
|
||
| --- | --- | --- |
|
||
| `orderIds` | 是 | 订单 Id 列表;**单次最多 200 条** |
|
||
| `issueTemplateId` | 否 | 引用 `t_issue_template.Id`;传入时 **覆盖** `title`/`content`/`issueType`,并快照 `NotifyMethod` 至事件表 |
|
||
| `issueType` | 条件 | 未传 `issueTemplateId` 时 **必填**;枚举见 **§10.3.6** |
|
||
| `title` | 条件 | 未传模板时 **必填**;传模板时可覆盖模板标题 |
|
||
| `content` | 条件 | 未传模板时 **必填** |
|
||
| `remark` | 否 | 人工备注,写入 `IssueRemark` 与事件表 |
|
||
|
||
**模板解析**:按订单归属客户 **`OrganizationId`**(经 `t_customer` 关联)调用 **`GET /channel/issue-template/options?organizationId=&issueType=`** 同源逻辑;`issueTemplateId` 须为 **启用** 且未删除的模板。
|
||
|
||
**占位符(一期)**:`content` **原样落库**;二期在发通知前渲染 `{serviceNo}`、`{clientNo}`、`{trackNo}` 等。
|
||
|
||
###### 4 响应示例(部分失败)
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": {
|
||
"successCount": 1,
|
||
"failures": [
|
||
{ "orderId": 1002, "serviceNo": "LP002", "reason": "仅草稿或已删除订单不可标记问题件" }
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
###### 5 落库与副作用
|
||
|
||
1. **更新 `t_order`**:`OrderStatus=problematic`,`HandleStatus=pending`,问题字段、`IssueTime=now`,`PreviousOrderStatus`。
|
||
2. **插入 `t_order_exception_event`**:`EventType=mark_problematic`,快照标题/内容/模板/通知方式。
|
||
3. **通知(二期)**:引用问题模板时读取 `NotifyMethod` 并投递(**`inApp` 站内信、`email` 操作人 SMTP 已接入**;`sms/feishu/wechat` 待通道对接);通知失败 **不回滚** 问题件标记。
|
||
4. **客户隔离**:逐单 `GetOrderOrThrowAsync`;管理员须具备数据权限(§4.4.0、`ApplyInternalOrderDataScopeAsync`)。
|
||
|
||
###### 6 权限
|
||
|
||
| 权限码 | 说明 |
|
||
| --- | --- |
|
||
| `Order.Problematic.Create` | 工具栏「创建问题件」及接口调用 |
|
||
|
||
##### 4.4.1.14 创建理赔件(POST /order/mark-claimed)
|
||
|
||
> **实现状态:设计中**。
|
||
|
||
###### 1 业务目标
|
||
|
||
运营勾选订单,填写理赔类型、索赔金额与原因,将订单 **`OrderStatus` 置为 `claimed`(理赔件)**,**`HandleStatus` 置为 `pending`**。
|
||
|
||
###### 2 状态准入规则
|
||
|
||
与 **§4.4.1.13** 相同:**禁止** `draft`/`deleted`/`problematic`/`claimed`;**允许**其余在途/完结状态(见上表)。
|
||
|
||
> 业务上理赔多发生在运输/派送阶段;技术上与问题件共用同一准入集合,由运营自行判断。
|
||
|
||
###### 3 请求示例
|
||
|
||
```json
|
||
{
|
||
"orderIds": [2001],
|
||
"claimType": "lost",
|
||
"claimAmount": 128.5,
|
||
"claimCurrency": "USD",
|
||
"claimReason": "末端确认丢件,申请运费+货值赔偿",
|
||
"remark": ""
|
||
}
|
||
```
|
||
|
||
| 字段 | 必填 | 说明 |
|
||
| --- | --- | --- |
|
||
| `orderIds` | 是 | 同上,最多 200 条 |
|
||
| `claimType` | 是 | 理赔类型,见 **§10.3.7** |
|
||
| `claimAmount` | 是 | `> 0` |
|
||
| `claimCurrency` | 是 | `RMB` / `USD` / `EUR` 等 |
|
||
| `claimReason` | 是 | 最长 512 |
|
||
| `remark` | 否 | 写入事件表 |
|
||
|
||
###### 4 响应
|
||
|
||
与 **§4.4.1.13** 同构:`successCount` + `failures[]`。
|
||
|
||
###### 5 落库
|
||
|
||
1. **`t_order`**:`OrderStatus=claimed`,`HandleStatus=pending`,理赔字段、`ClaimTime=now`,`PreviousOrderStatus`;**清空**问题件专用字段(若曾有问题字段残留)。
|
||
2. **`t_order_exception_event`**:`EventType=mark_claimed`。
|
||
|
||
###### 6 权限
|
||
|
||
| 权限码 | 说明 |
|
||
| --- | --- |
|
||
| `Order.Claim.Create` | 工具栏「创建理赔件」 |
|
||
|
||
##### 4.4.1.15 更新处理子状态(POST /order/handle-status/update)
|
||
|
||
> **实现状态:设计中**(`HandleStatus` 列与列表筛选 **已落地**,见 `migrate_t_order_handle_status.sql`)。
|
||
|
||
###### 1 业务目标
|
||
|
||
在 **问题件 / 理赔件** Tab 下,将选中订单由 **待处理** 标记为 **已处理**(或一期仅支持 `pending → processed`)。
|
||
|
||
###### 2 准入
|
||
|
||
- 当前 `OrderStatus` 必须为 `problematic` 或 `claimed`。
|
||
- 当前 `HandleStatus` 为 `pending` 或 `NULL`(视为待处理)。
|
||
|
||
###### 3 请求示例
|
||
|
||
```json
|
||
{
|
||
"orderIds": [1001],
|
||
"handleStatus": "processed",
|
||
"remark": "已联系客户并结案"
|
||
}
|
||
```
|
||
|
||
###### 4 落库
|
||
|
||
- 更新 `t_order.HandleStatus`。
|
||
- 插入 `t_order_exception_event`:`EventType=handle_processed`,记录 `FromHandleStatus` / `ToHandleStatus`。
|
||
|
||
###### 5 权限
|
||
|
||
| 权限码 | 说明 |
|
||
| --- | --- |
|
||
| `Order.HandleStatus.Update` | 子 Tab「标记已处理」 |
|
||
|
||
##### 4.4.1.16 前端交互(`order/myOrders`)
|
||
|
||
| 场景 | UI |
|
||
| --- | --- |
|
||
| 非问题件/理赔件 Tab | 工具栏 **创建问题件**、**创建理赔件**(须勾选行;`hasPermission` 控制) |
|
||
| 问题件/理赔件 Tab | 子 Tab **待处理 / 已处理**(已落地);工具栏 **标记已处理**(`Order.HandleStatus.Update`) |
|
||
| 创建问题件弹窗 | `ModalPro` + `FormPro`:问题类型 → 联动 **`issue-template/options`** → 可选模板;标题、内容可编辑;备注 |
|
||
| 创建理赔件弹窗 | 理赔类型、金额、币种、原因 |
|
||
| 批量策略 | 优先调用 **批量接口**;失败行 `Message` 汇总展示 `failures` |
|
||
|
||
列表/详情响应补充字段(实现时):`handleStatus`、`handleStatusName`、`issueType`、`issueTitle`、`claimType`、`claimAmount` 等(与 **§4.2.1** 对齐)。
|
||
|
||
#### 4.4.2 我要查价
|
||
|
||
| 接口名称 | 请求方式 | 请求路径 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| 查价 | POST | /order/price-check | 按销售产品**最新生效卖价版本**试算运费并返回;**须客户登录**(§4.4.0) |
|
||
|
||
##### 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": "freight:11 + handling:20",
|
||
"freight": "11",
|
||
"otherFees": "20",
|
||
"totalCost": "31",
|
||
"maxDeliveryDays": 7,
|
||
"totalCostNum": 31,
|
||
"productCode": "test01",
|
||
"rulesVersion": "RULES-20260601-001",
|
||
"priceVersion": "PRICE-20260601-001",
|
||
"isRemote": false
|
||
}
|
||
],
|
||
"failures": [
|
||
{
|
||
"productId": 2,
|
||
"productCode": "OTHER",
|
||
"productName": "其他产品",
|
||
"step": "zone",
|
||
"reason": "目的国 US 邮编 00000 未匹配产品价格分区(请检查 PriceAreaId 及分区规则)"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. **校验**:`destinationCountry` 必填(ISO 二字码);`cargoWeight`、`length`、`width`、`height` 须 **> 0**。
|
||
2. **绑定时刻**:`bindTime = DateTime.Now`;解析卖价最新生效快照(`ResolveSnapshotBindingAsync`)。
|
||
3. **产品范围**:`shippingMethod` 按产品代码/名称过滤;未找到 → `failures[0].step=product`。
|
||
4. **逐产品试算**(按快照明细匹配):
|
||
- **分区**:用 **目的国 + 邮编**(及省州/城市若有)匹配产品 `PriceAreaId` 对应 `t_kpi_area_info`;未命中 → `step=zone`。
|
||
- **计重**:按规则快照计重得到 `chargeableWeight`;失败 → `step=weight`。
|
||
- **卖价明细**:在快照 `prices[].details` 中匹配 **分区 + 重量区间 + 货物属性**:
|
||
- 重量因子存 **左开右闭区间** `(start, end]`,如 `0,0.5` 表示 `0 < 重量 ≤ 0.5`;单值 `0.5` 兼容为 `(0, 0.5]`。
|
||
- `calculationFormula` 使用字典编码(如 `unit`、`total`、`continueUnit`),与渠道配置 `calculation_formula` 一致。
|
||
- **算价**:`CalculateQuoteReceivableFees`;无明细命中 → `step=price`。
|
||
5. **结果**:成功写入 `items`;失败写入 `failures`(含 `step` + `reason`),**禁止静默丢弃**。
|
||
|
||
###### failures.step 说明
|
||
|
||
| step | 含义 |
|
||
| --- | --- |
|
||
| `product` | 未找到启用产品 |
|
||
| `snapshot` | 卖价/规则快照解析失败 |
|
||
| `blacklist` | 命中黑名单 |
|
||
| `weight` | 计重规则/进位失败 |
|
||
| `zone` | 国家+邮编未匹配价格分区 |
|
||
| `price` | 分区已匹配但卖价明细未命中(重量区间/货物属性/无启用项) |
|
||
|
||
###### 校验与异常
|
||
|
||
| 场景 | 错误信息 |
|
||
| --- | --- |
|
||
| 目的国为空 | 目的国不能为空 |
|
||
| 重量 ≤ 0 | 货物重量必须大于0 |
|
||
| 长宽高无效 | 长宽高必须大于0 |
|
||
| 目的国不存在 | 目的国不存在 |
|
||
| 全部产品未命中 | `200` + `items: []` + `failures` 逐条原因 |
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- `OrderService.PriceCheckAsync` → `BuildPriceCheckResultAsync`(`OrderService.ParcelQuote.cs`)。
|
||
- **`cargoType`** 经 `ParcelBillingCalculator.MapCargoTypeToAttribute` 映射为 `cargoAttribute`(支持中文「普货」等)。
|
||
- 客户登录时 **`ResolveCurrentCustomerGroupAsync`** 参与卖价 **客户组** 因子;管理员查价时客户组为 `null`。
|
||
- 详见 **§4.3.5**。
|
||
|
||
#### 4.4.3 我要下单
|
||
|
||
| 接口名称 | 请求方式 | 请求路径 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| 模板列表 | GET | /order/template/list | 获取下单模板列表;自定义模板按 **`CustomerId` 隔离** |
|
||
| 下载模板 | GET | /order/template/download | 下载 Excel 模板 |
|
||
| 上传下单 | POST | /order/upload | multipart/form-data;任务 **`CustomerId` 取会话** |
|
||
| 任务进度列表 | GET | /order/task/list | 批量下单任务列表;**仅当前客户** |
|
||
| 任务详情 | GET | /order/task/detail | 任务详情;**须校验任务 `CustomerId`** |
|
||
| 创建小包 | POST | /order/parcel/create | 创建小包订单;**`CustomerId` 与会话一致**(§4.4.0) |
|
||
| 批量创建小包 | 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
|
||
{
|
||
"requestId": "string", // 幂等键 t_order.RequestId//可选;重复则直接返回已有订单结果
|
||
"clientNo": "string", // 客户单号 t_order.ClientNo//可选
|
||
"trackNo": "string", // 跟踪号 t_order.TrackNo//可选
|
||
"serviceNo": "string", // 运单号 t_order.ServiceNo//可选;未传时服务端自动生成(LP 前缀)
|
||
"productCode": "string", // 产品代码 t_order.ProductCode//必填
|
||
"channelCode": "string", // 渠道代码 t_order.ChannelCode
|
||
"cargoAttribute": "string", // 货物属性 t_order.CargoAttribute:general-普货,electronics-3c&带电,sensitive-敏感货//必填
|
||
"exportCustomsMode": "9610Simple", // 出口报关方式 t_order.ExportCustomsMode,见 §4.2.1 ExportCustomsMode 枚举//可选
|
||
"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//必填
|
||
"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": "US", // 收件国家 ISO 3166-1 alpha-2 二字码(对应 t_country.CountryCode)//必填
|
||
"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": "CN", // 发件国家 ISO 二字码//可选
|
||
"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. 参数解析与基础数据校验
|
||
- **会话客户(优先)**:调用 `ResolveSessionCustomerAsync`,将 **`effectiveCustomerId`** 写入待落库 `customerId`;客户登录 **可不传** `customerId`;管理员代下单 **须传** `customerId`,否则返回 **「客户ID不能为空」**。
|
||
- 校验请求体必填字段:`productCode`、`warehouseId`、`length`、`width`、`height`、`actualWeight`、`receiver.recipientsName`、`receiver.countryCode`、`receiver.zipCode`、`receiver.streetAddress1`、`sender.senderName`、`items`(**不含** `customerId`、`trackNo`、`serviceNo`)。
|
||
- 校验数值合法性:`length/width/height/actualWeight` 必须大于 0。
|
||
- 校验枚举合法性:`cargoAttribute` 仅允许 `general`、`electronics`、`sensitive`。
|
||
- 校验枚举合法性:`exportCustomsMode` **可选**;若传入则须为 §4.2.1 **ExportCustomsMode** 枚举 code,否则返回 **「出口报关方式不合法」**。
|
||
- **客户校验**:按 **`effectiveCustomerId`**(**`t_customer.Id`**)确认客户存在且 `Status` 可用;不可用则返回错误。请求体若传 `customerId` 须与 **`effectiveCustomerId` 一致**,否则 **「客户ID非法」**。
|
||
- **客户隔离(§4.4.0)**:落库 `t_order.CustomerId` **仅**写入 `effectiveCustomerId`。
|
||
- 查询 `t_warehouse_address`,条件:`Id = warehouseId and IsDeleted = 0`,返回字段:`Id`、`WarehouseName`;仓库不存在时返回错误。
|
||
- 查询 `t_country` 校验收件人国家:`receiver.countryCode` 为 **string**,优先按 **`CountryCode`(ISO 二字码,大小写不敏感)** 匹配;亦兼容 **数字字符串** 形式的 `t_country.Id`;亦可按中/英文名匹配。不存在时返回 **「收件国家不存在」**。下列字段任一缺失返回 **「收件人信息不完整」**:`receiver`、`recipientsName`、`countryCode`、`zipCode`、`streetAddress1`。
|
||
|
||
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. 创建订单草稿
|
||
- **`ServiceNo`**:请求体 `serviceNo` 为空时,服务端自动生成唯一运单号(`LP` + 时间戳 + 随机数);`trackNo` 为空则落库 `NULL`。
|
||
- 插入 `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` / `t_order_recipient`:
|
||
- 请求体 `sender.countryCode` / `receiver.countryCode` 为 **ISO 二字码**(见步骤 1);落库 **`Country` 字段为 `t_country.Id`**(int)。
|
||
- 其它字段见下表列名映射。
|
||
- 插入 `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` 写入四行(创建时初始拷贝策略)。**入库**仅写 **`receive` 行**;**出库**仅写 **`ship` 行**;均**不**向其它 `Type` 同步。
|
||
|
||
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`**:**预报阶段**四行同步为同一计费重。**入库**仅写 **`receive` 行`**;**出库**仅写 **`ship` 行`**;均**不**向其它 `Type` 同步。若 **`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)**:全部启用卖价项;**重量**取 **`Type=forecast`**;**upsert** `ChargingStage=forecast`。入库由 `/operation/inbound/create` 再写 `ChargingStage=inbound`。
|
||
- **2)重算应付(§4.3.4.2)**:排货确认服务渠道 → 渠道买价算价 → upsert `ChargingStage=forecast`;回写 **`ChannelCode`** 及下游对接四字段(§4.3.4.2 步骤 1)。
|
||
- 写入字段(各行):`OrderId`、`Type`、`CostItem`、`Amount`、`Currency`、`PriceNo`、`PriceEffectiveTime`、`ChargingStage`;`PriceNo` 应收写 `SalesProductPriceVersion`,应付写渠道买价版本(若有)。
|
||
- **强校验**:存在启用卖价项但未匹配到任何明细时,预报失败。
|
||
- 若应收应算项未匹配到卖价明细,更新 `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`,供订单列表和详情页展示。
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
与上文 **§4.3.5**、**§1.5.4** 对齐;补充要点:
|
||
|
||
| 项 | 实现细节 |
|
||
| --- | --- |
|
||
| 幂等 | `requestId` 非空时先 `GetByRequestIdAsync`,命中则 **`BuildParcelCreateOutputAsync`** 直接返回 |
|
||
| 会话客户 | `ResolveEffectiveCustomerAsync` → 写 `input.CustomerId = sessionCustomer.Id` |
|
||
| 必填校验 | 另含 **`declaredCurrency`、`quantity`(>0)**、申报明细 **`enName/unitPrice/unitWeight/quantity`**;**`clientNo` 非必填** |
|
||
| `channelCode` | 未传时默认 **`product.ProductCode`** |
|
||
| `serviceNo` | 自定义时查重,冲突 → **「运单号已存在」**;否则 **`LP{yyyyMMddHHmmss}{4位随机}`** |
|
||
| 快照 | 见 **§4.3.5**(DB 发布 + Live 降级,**无 Redis**) |
|
||
| 应付 | 排货确认渠道 + **`Type=payable`**(`ChargingStage=forecast`) |
|
||
| 成功 | `OrderStatus=predeclared`;**`labelUrl`** 同 print 逻辑生成 HTML |
|
||
| 失败 | 草稿已建则 **`MarkParcelDraftFailedAsync`**(保持 `draft` + `PredeclareFailReason`)后 **仍抛业务异常** |
|
||
|
||
##### 4.4.3.2 批量创建小包(POST /order/parcel/batch-create)
|
||
|
||
与 **§4.4.3.1 创建小包** 使用同一套单票业务规则(`CreateParcelCoreAsync`)。数组元素字段与单票请求体一致。**差异**:本接口在 **同一 HTTP 请求内同步逐条** 调用单票逻辑,**不创建** `t_order_task`、**不入队**;HTTP 响应返回 **批次号与逐条明细**(含每票 `serviceNo` / `labelUrl` / 失败原因)。
|
||
|
||
###### 1 接口请求示例
|
||
|
||
**请求 /order/parcel/batch-create**(POST)
|
||
|
||
> 请求体为 **`OrderParcelBatchCreateInput`**:与单票结构相同的对象 **数组**(非裸数组根)。单次 **≤ 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": "9610Simple", // 出口报关方式,见 §4.2.1//可选
|
||
"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": "US", // 收件国家 ISO 二字码//必填
|
||
"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": "CN", // 发件国家 ISO 二字码//可选
|
||
"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": {
|
||
"batchNo": "PCB20240509120000123",
|
||
"successCount": 98,
|
||
"failCount": 2,
|
||
"items": [
|
||
{
|
||
"index": 1,
|
||
"requestId": "optional",
|
||
"clientNo": "C001",
|
||
"serviceNo": "LP202405091200011234",
|
||
"trackNo": null,
|
||
"labelUrl": "/uploads/prints/orders/...",
|
||
"success": true,
|
||
"message": "success"
|
||
},
|
||
{
|
||
"index": 2,
|
||
"requestId": null,
|
||
"clientNo": "C002",
|
||
"serviceNo": null,
|
||
"trackNo": null,
|
||
"labelUrl": null,
|
||
"success": false,
|
||
"message": "销售产品不存在或未启用"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
- **`batchNo`**:服务端生成,`PCB` + 时间戳毫秒。
|
||
- **`successCount` / `failCount`**:与 `items.length` 一致;单条失败 **不中断** 后续条目。
|
||
- 失败条目 **`message`** 为业务异常文案;若草稿已创建且后续失败,单票仍可能落库为 **`draft`** + `PredeclareFailReason`(与 §4.4.3.1 一致)。
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. 校验数组非空、`Count ≤ 2000`(常量 `ParcelBatchMaxCount`)。
|
||
2. 生成 **`batchNo`**,按序 **`for` 循环** 调用 **`CreateParcelCoreAsync`**。
|
||
3. 每条 **`try/catch`**:成功累加 `successCount` 并填充 `items`(含 `labelUrl`);失败写入 `items` 且 `success=false`。
|
||
4. 返回 **`OrderParcelBatchCreateOutput`**(**无 `taskId`**)。
|
||
|
||
###### 校验与异常
|
||
|
||
- 空数组 → **「批量创建请求不能为空」**。
|
||
- 超过 2000 条 → **「单次批量创建不能超过2000条」**。
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- `OrderService.BatchCreateParcelAsync`(`OrderService.cs`)。
|
||
- **设计演进说明**:早期文档规划「异步任务 + taskId」模式(见 §9.7 历史条目);**当前代码为同步批处理**。若需异步,应复用 `t_order_task` 或消息队列,并调整响应契约。
|
||
|
||
##### 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 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
- 客户登录:仅返回 **`Type=standard`** 的模板,以及 **`Type=custom` 且 `CustomerId = effectiveCustomerId`** 的自定义模板。
|
||
- 管理员:按 `type`、`keyword` 分页查询,不做客户隔离。
|
||
- 按接口定义完成参数解析与分页返回。
|
||
|
||
###### 校验与异常
|
||
|
||
- 客户门户 **不得**通过 `templateId` 下载或上传他客 `custom` 模板(无权限时返回 **「模板不存在」**)。
|
||
|
||
##### 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 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
- 校验模板存在;客户登录时须通过 **§4.4.3.3** 可见性规则(`standard` 或归属当前客户的 `custom`)。
|
||
- 标准模板 Excel 表头建议包含列 **「出口报关方式」**(与 `exportCustomsMode` / `t_order.ExportCustomsMode` 对应),单元格填写 §4.2.1 **ExportCustomsMode** 枚举 code;列缺失或空单元格按 **未传** 处理,落库 `NULL`。
|
||
- 示例行中 **`CustomerId` 列**对客户门户导出为 **`effectiveCustomerId`**(非模板表上的 `CustomerId` 字段)。
|
||
|
||
###### 校验与异常
|
||
|
||
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
|
||
- 若模板中提供「出口报关方式」列,非空值须为合法枚举 code,否则该行在解析或异步处理阶段失败(错误信息需可定位行号)。
|
||
- 客户访问他客 `custom` 模板时返回 **「模板不存在」**。
|
||
|
||
##### 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 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. **`ResolveEffectiveCustomerAsync`** → `effectiveCustomerId`;校验模板可见性(`standard` 或归属客户的 `custom`)。
|
||
2. 创建 **`t_order_task`**:`TaskName=批量下单_{timestamp}`,初始 **`Status=processing`**,`CustomerId=effectiveCustomerId`。
|
||
3. **同步** MiniExcel 逐行解析(`useHeaderRow: true`,行号从 2 起):
|
||
- 成功:`CreateImportOrderAsync` — **简化落单**(见下)。
|
||
- 失败:写入 **`t_order_task_fail`**(`rowNumber`、`reason`、原始行 JSON)。
|
||
4. 更新任务:`Quantity`、`SuccessCount`、`FailCount`,**`Status=completed`**;返回 **`taskId`**。
|
||
|
||
**`CreateImportOrderAsync` 简化落单(与 §4.4.3.1 不同)**
|
||
|
||
| 项 | 行为 |
|
||
| --- | --- |
|
||
| 订单主表 | 直接 **`OrderStatus=pendingPayment`**(非 `draft`→`predeclared` 链路) |
|
||
| 计费 | **不**绑定快照、**不**算价、**不**写 `t_order_fee` |
|
||
| 子表 | **仅**可选 1 条 **`t_order_weight`(Type=forecast)**;**无**收发件人/申报明细 |
|
||
| 客户 | Excel `CustomerId` 与会话不一致 → 该行失败 **「客户ID非法」** |
|
||
| 运单号 | 同 §4.4.3.1 自动生成逻辑 |
|
||
|
||
> **目标对齐**:后续迭代应将 Excel 行映射为 `OrderParcelCreateInput` 并调用 **`CreateParcelCoreAsync`**,或改为异步入队 + §4.4.3.1 全链路。
|
||
|
||
###### 校验与异常
|
||
|
||
- 文件为空 → **「上传文件不能为空」**。
|
||
- 模板不存在 / 无权限 → **「模板不存在」**。
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- `OrderService.ImportOrdersAsync`(`OrderService.Helpers.cs`)— **同步**处理,非后台消费者。
|
||
|
||
##### 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 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
- 客户登录:查询条件 **强制** `t_order_task.CustomerId = effectiveCustomerId`。
|
||
- 管理员:可按 `status`、`keyword` 分页,不做客户隔离。
|
||
|
||
###### 校验与异常
|
||
|
||
- 对必填参数、状态与数据存在性进行校验,异常场景返回统一错误码与错误信息。
|
||
|
||
##### 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 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. 按 `taskId` 查询任务;不存在时返回 **「任务不存在」**。
|
||
2. 客户登录:校验 **`t_order_task.CustomerId == effectiveCustomerId`**,不匹配时 **同样**返回 **「任务不存在」**。
|
||
3. 返回任务汇总及 `failList`。
|
||
|
||
###### 校验与异常
|
||
|
||
- 禁止跨客户查看批量任务详情。
|
||
|
||
## 5 渠道模块
|
||
|
||
### 5.1 核心需求说明
|
||
|
||
- **销售产品**:管理对外销售的产品(专线/快递/邮政),配置时效、黑名单区、偏远分区、价格分区、计重规则、计重进位等;支持价格配置。
|
||
- **产品配置**:产品基础信息(编码、名称、分组、时效、面单、黑名单区、偏远分区、价格分区、计重规则、计重进位等)。
|
||
- **产品卖价**:为每个产品配置多条价格项(运费、保险费、燃油附加费等);每条价格项通过弹窗配置:
|
||
- **基础信息**:费用项、费用单位、币种、收费条件、计费因素(因素+因素值,如货物类型、分区、重量、仓库、客户等)。
|
||
- **价格表**:根据计费因素动态生成价格表,支持分区/货物类型/重量等组合、计算公式(总价/单价/续单价)、计算值、客户组。
|
||
- **排货规则**:选择排货方案(成本最低/时效最快/综合最优),配置服务商列表(服务商名称、服务渠道、权重、指定客户)。
|
||
- **发布记录**:价格与规则的发布历史,支持查看版本内容、回滚到指定版本。
|
||
- **服务渠道**:管理下游服务渠道(支持渠道组+渠道层级),配置预报单号类型、时效、买价等。
|
||
- **渠道分组**:`GET /channel/service-channel/group/list` 查询服务商分组列表(下拉/左侧树筛选);`group/create`、`group/update`、`group/delete` 维护 `t_service_channel_group`。
|
||
- **渠道配置**:渠道基础信息(渠道代码、名称、时效、预报配置、描述、预报前脚本等)。
|
||
- **渠道买价**:为每个渠道配置多条买价项(运费、保险费、燃油附加费等);每条买价项通过弹窗配置:
|
||
- **基础信息**:费用项、费用单位、币种、收费条件、计费因素(因素+因素值)。
|
||
- **价格表**:根据计费因素动态生成价格表,支持分区/货物类型/重量等组合、计算公式、计算值、客户组。
|
||
- **发布记录**:买价与规则的发布历史,支持查看版本内容、回滚到指定版本。
|
||
- **黑名单区/偏远分区/价格分区**:共用 KpiArea 表,仅通过 `AreaType` 区分;支持分区及明细(国家/省/市/邮编区间)的增删改查。
|
||
- **计重规则/计重进位**:配置计费重量规则与进位方式,明细结构与分区类似。
|
||
- **自动触发**:配置下单/入库/出库时的自动拦截、警告、问题件等触发规则。
|
||
- **问题模板**:按组织维护问题件话术与通知方式,供标记问题件与自动触发消费(一期 CRUD)。
|
||
- **标签设计**:保存 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 |
|
||
| 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 数组(**`t_customer.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 与发布同步(在线计费)
|
||
|
||
> **当前实现(oms0414-back)**:发布接口 **仅** 向 `t_sales_product_publish_record` / `t_service_channel_publish_record` 插入记录(`Content` 为完整快照 JSON)。**未**写入 Redis、**未**推送计费实例刷新内存。运行时算价见订单 **§4.3.5**(读发布表或 Live 组装)。以下 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",
|
||
"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 |
|
||
| 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.2.21 问题模板表 `t_issue_template`
|
||
|
||
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
|
||
| OrganizationId | CHAR | 36 | 外键 → `organization.Id` | 否 | 归属组织 GUID;**NULL = 平台通用** |
|
||
| IssueType | VARCHAR | 32 | - | 是 | 问题类型 code,见 §10.3 |
|
||
| Title | VARCHAR | 128 | - | 是 | 问题标题 |
|
||
| Content | TEXT | - | - | 是 | 问题内容(支持占位符 `{serviceNo}` 等,二期渲染) |
|
||
| NotifyMethod | VARCHAR | 64 | - | 是 | 通知方式,逗号分隔:`inApp,email,sms,feishu,wechat` |
|
||
| Status | TINYINT | - | - | 是 | 是否启用:0-否,1-是 |
|
||
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认0 |
|
||
| ConcurrencyStamp | CHAR | 36 | - | 是 | 并发戳(AggregateRoot 约定) |
|
||
| CreateUserId | INT | - | - | 是 | 创建人 |
|
||
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
|
||
| UpdateUserId | INT | - | - | 否 | 更新人 |
|
||
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
|
||
|
||
**唯一性(业务层)**:同一 `OrganizationId`(含 NULL)+ `IssueType` 仅允许一条未删除记录。
|
||
|
||
### 5.3 核心接口设计
|
||
|
||
> **网关前缀**:生产环境渠道接口挂载为 **`https://api.nanafa.com/admin/channel/...`**;下文路径以 **`/channel/*`** 表示服务内路由。
|
||
|
||
#### 5.3.0 当前实现总览(oms0414-back)
|
||
|
||
> 实现位置:`Oms.Application/Services/Channels/`。Swagger 分组 **`OMS-Channel`**。渠道接口 **无客户数据隔离**(运营/管理员使用);须 **已登录**(`[Authorize]`)。
|
||
|
||
##### 5.3.0.1 公共约定
|
||
|
||
| 项 | 说明 |
|
||
| --- | --- |
|
||
| 分页 | `OmsPageResult` → `{ items, total }`;列表无分页 → `OmsItemsResult` → `{ items }` |
|
||
| 软删除 | 各 `DeleteAsync` 走仓储软删(`IsDeleted=1`) |
|
||
| 唯一性 | 销售产品 **`ProductCode`**、服务渠道 **`ChannelCode`**、KPI 分区 **`AppKey+AreaType+Name`**、渠道分组 **`Name`**、标签模板 **`Name+TemplateType`** |
|
||
| 发布类型 | `operatorType` 仅 **`price`** \| **`rules`**(大小写不敏感,内部归一化小写) |
|
||
| 版本号 | 发布时 **`Version = yyyyMMddHHmmss`**(字符串);`Status` 固定写 **「生效」** |
|
||
| Redis | 发布 **仅写 DB** `t_*_publish_record.Content`;**未**同步 Redis / 未通知计费实例(见 **§5.2.6.2** 实现说明) |
|
||
|
||
##### 5.3.0.2 销售产品(`SalesProductService`)
|
||
|
||
| 接口 | 核心逻辑 |
|
||
| --- | --- |
|
||
| `GET .../list` | 筛选:`productGroup`、`keyword`(匹配 Code/Name)、`isActive`;排序 `UpdateTime desc, Id desc` |
|
||
| `GET .../detail` | 单条;不存在 → **「销售产品不存在」** |
|
||
| `POST .../create` | 校验 Code 唯一 → `InsertAsync`;`Scheme` 空则默认 **`costLowest`** |
|
||
| `POST .../update` | 同 create,带 `Id` |
|
||
| `POST .../delete` | 软删产品 |
|
||
| `POST .../config/save` | 与 update 相同(`[IgnoreApi]`,供前端配置 Tab 专用路径) |
|
||
| `GET .../price/list` | 校验产品存在 → 一次查齐价格项 + factors + details(避免 N+1) |
|
||
| `POST .../price/create` | 插入价格主表 + factor + detail 子表 |
|
||
| `POST .../price/update` | 校验价格项归属 `SalesProductId` → 全量替换 factor/detail |
|
||
| `POST .../price/delete` | 删价格项 |
|
||
| `POST .../price/toggleStatus` | `ToggleStatusInput` 更新启用状态 |
|
||
| `POST .../price/save` | **`ReplacePricesAsync`** 整树替换(临时负数 Id 关联子表);可选 **`publish=true`** 触发价格发布 |
|
||
| `GET .../allocation/get` | 返回 `Scheme` + 排货服务商列表(补 **`ServiceChannelName`**) |
|
||
| `POST .../allocation/save` | 更新产品 `Scheme` + **`ReplaceAllocationProvidersAsync`**;`CustomerIds` 存 JSON;可选 **`publish=true`** 触发规则发布 |
|
||
| `GET .../publish/price\|rules/list` | 分页发布记录;`Content` 反序列化为 JSON 对象返回 |
|
||
| `GET .../publish/preview` | **不落库**预览快照:`BuildPricePublishSnapshotAsync` / `BuildRulesPublishSnapshotAsync` |
|
||
| `POST .../publish` | `CreatePublishRecordAsync`:组装快照 JSON 写入发布表 |
|
||
| `POST .../publish/rollback` | 读指定 `recordId` 的 `Content`:**price** → `SavePriceAsync` 恢复价格树;**rules** → 更新产品配置 + 恢复排货 providers |
|
||
|
||
**规则快照(`operatorType=rules`)组装内容**(`BuildRulesPublishSnapshotAsync`):
|
||
|
||
- 产品基础字段(含 `BlacklistAreaId`、`RemoteAreaId`、`PriceAreaId`、`WeightRuleId`、`WeightRoundingId` 等)
|
||
- 排货 **`Providers`**(**不含** `CreateTime`/`UpdateTime`,与卖价 Slim 策略一致)
|
||
- 通过 `RulesSnapshotHelper` 嵌入:**黑名单/偏远/价格分区明细**、**计重规则明细**、**进位规则明细**
|
||
- 发布前由 **`AllocationProviderSnapshotEnricher`** 对每个 Provider(有 **`ServiceChannelId`**)补充 **下游对接元数据**(见下表)
|
||
|
||
**`Providers[]` 下游 enrich 字段**(写入 `t_sales_product_publish_record.Content`,供排货/轨迹/下游预报读取):
|
||
|
||
| 字段 | 来源 | 说明 |
|
||
| --- | --- | --- |
|
||
| `channelCode` | `t_service_channel.ChannelCode` | 服务渠道编码 |
|
||
| `forecastCode` | `t_service_channel.ForecastCode` | 渠道预报编码(轨迹 **ApiCode**) |
|
||
| `groupId` | `t_service_channel.GroupId` | 服务商分组 Id |
|
||
| `appId` | `t_service_channel_group.AppId` | itfx 应用 Id |
|
||
| `apiServiceProviderId` | `t_service_channel_group.ApiServiceProviderId` | itfx API 服务商 Id |
|
||
| `appKey` | itfx **`GET /App/GetInfo?id={appId}`** | 应用 AppKey |
|
||
| `supplierCode` | itfx **`GET /ServiceProvider/GetServiceProvider?serviceProviderId=`** | API 服务商编码 |
|
||
|
||
**安全与兼容约定**:
|
||
|
||
- **`AppSecret` 不落快照、不落 `t_order`**;运行时按 **`appId` / `downstreamAppId`** 调 itfx App API 获取。
|
||
- itfx 元数据客户端配置:**`Oms:Itfx:BaseUrl`**(默认 `http://api.nanafa.com/itfx`),转发当前请求 **JWT Authorization**。
|
||
- **旧版 rules 快照**(无上述字段):排货时 **`ResolveAllocationProvidersAsync`** 按需 live enrich;确认渠道后仍写入 **`t_order`** 四字段(**`sql/migrate_t_order_downstream_integration.sql`**)。
|
||
|
||
**价格快照(`operatorType=price`)组装内容**(`BuildPricePublishSnapshotAsync`):
|
||
|
||
- 全部卖价项(Slim 后)+ factors + details
|
||
- 通过 `PriceAreaSnapshotHelper` 嵌入 **`PriceAreaInfos`**(价格分区明细,供运行时匹配 zone key)
|
||
|
||
##### 5.3.0.3 服务渠道(`ServiceChannelService`)
|
||
|
||
与销售产品 **对称**,差异要点:
|
||
|
||
| 项 | 说明 |
|
||
| --- | --- |
|
||
| 列表筛选 | `groupId`、`keyword`(Code/Name)、`isActive`;列表/详情补 **`groupName`** |
|
||
| 分组 | `group/list` 返回 **数组**(非分页);筛选 `keyword`、`appId`、`apiServiceProviderId` |
|
||
| 分组删除 | 分组下仍有渠道 → **「当前分组下仍有关联渠道,无法删除」** |
|
||
| 分组校验 | `Name`、`AppId`、`ApiServiceProviderId` 必填;名称唯一 |
|
||
| `config/save` | 同 update(`[IgnoreApi]`) |
|
||
| 买价 | 与卖价相同 CRUD + **`price/save`** 整树替换 + 可选发布 |
|
||
| 发布/回滚 | 写 **`t_service_channel_publish_record`**;**price** 回滚恢复买价树;**rules** 回滚恢复渠道主配置 |
|
||
| 发布预览 | **`GET .../publish/preview`**(与卖价对称,2026-06 已落地) |
|
||
|
||
##### 5.3.0.4 KPI 分区(`KpiAreaService`)
|
||
|
||
| 接口 | 核心逻辑 |
|
||
| --- | --- |
|
||
| `POST .../list` | 筛选 `appKey`、`areaType`(**blacklist / remote / price**,见 §10.2) |
|
||
| `POST .../create\|update` | 同 **`AppKey+AreaType+Name`** 唯一;update 校验存在 |
|
||
| `POST .../delete` | 软删主表 |
|
||
| `POST .../info/list` | 按 `areaId` 分页明细 |
|
||
| `POST .../info/add\|update\|delete` | 明细 CRUD:国家码、省市区、邮编区间 `LeftPostcode`~`RightPostcode` |
|
||
| `GET .../info/import-template` | 下载 Excel 模板(`KpiAreaInfoImportHelper`) |
|
||
| `POST .../info/import` | multipart:`areaId` + file;逐行解析 → 校验国家码(对照 `t_country`)→ 插入;部分失败返回 **`successCount/failCount/failures[]`** |
|
||
|
||
##### 5.3.0.5 计重规则 / 进位(`WeightRuleService`、`WeightRoundingService`)
|
||
|
||
标准 **主表 + info 子表** CRUD(均为 **POST** 路由):
|
||
|
||
- 主表:list(分页,可按 keyword 筛)、create、update、delete
|
||
- 子表:info/list(按 `ruleId` 或 `roundingId` 分页)、info/add、info/update、info/delete
|
||
- 计重明细字段:`VolumetricFactor`、`Condition`、`BillableWeight`(gw/vw)
|
||
- 进位明细字段:`StartValue`、`EndValue`、`Public`(进位单位 kg)
|
||
|
||
运行时算价见订单 **§4.3.5**(按 `Id` 升序匹配首条计重条件;进位 `start <= raw <= end`)。
|
||
|
||
##### 5.3.0.6 自动触发(`AutoTriggerService`)
|
||
|
||
| 接口 | 核心逻辑 |
|
||
| --- | --- |
|
||
| `GET .../list` | 分页;筛选 `operation`、`triggerTiming` |
|
||
| `POST .../create\|update` | 字段:`Status`、`Operation`、`TriggerTiming`、`TriggerCondition`、`PromptMessage`、`Notifier` |
|
||
| `POST .../delete` | 软删 |
|
||
| `POST .../toggleStatus` | 更新 `Status` |
|
||
|
||
> **运行时触发引擎**(订单/仓储事件消费规则)**尚未在 oms0414-back 实现**;当前仅为规则 CRUD 落库。
|
||
|
||
##### 5.3.0.7 问题模板(`IssueTemplateService`)
|
||
|
||
| 接口 | 核心逻辑 |
|
||
| --- | --- |
|
||
| `GET .../list` | 分页;筛选 `organizationId`、`issueType`、`keyword`、`status` |
|
||
| `GET .../detail` | 单条详情 |
|
||
| `GET .../options` | 启用模板下拉;组织专属优先于平台通用 |
|
||
| `POST .../create\|update` | 字段:`OrganizationId`、`IssueType`、`Title`、`Content`、`NotifyMethod`、`Status`;**OrganizationId+IssueType 唯一** |
|
||
| `POST .../delete` | 软删 |
|
||
| `POST .../toggleStatus` | 更新 `Status` |
|
||
|
||
> **运行时消费**(**§4.4.1.13** 标记问题件发通知、自动触发关联模板)**二期**;当前为配置 CRUD;**`GET .../options`** 供创建问题件弹窗联动。
|
||
|
||
##### 5.3.0.8 标签模板(`LabelTemplateService`)
|
||
|
||
| 接口 | 核心逻辑 |
|
||
| --- | --- |
|
||
| `POST .../label-template/save` | `id>0` 更新,否则新增;**`Name+TemplateType` 唯一**;`templateJson` 序列化存库;返回带 `id` 的完整对象 |
|
||
|
||
下拉供仓内打印见 **`GET /operation/print-label/template-list`**(操作模块)。
|
||
|
||
##### 5.3.0.9 文档有而需对照的实现差异
|
||
|
||
| 设计描述 | 当前实现 |
|
||
| --- | --- |
|
||
| 发布后同步 Redis + 刷新计费内存 | **未实现**;下单读 DB 发布表或 Live 组装(§4.3.5) |
|
||
| 发布时旧版本 `Status` 置失效 | **未显式**更新历史记录状态;算价按 **`PublishTime <= bindTime` 取最新一条** |
|
||
| 服务渠道 `publish/preview` | 与卖价对称 | **已落地**:`GET /channel/service-channel/publish/preview` |
|
||
| 自动触发执行 | **无**后台消费者 |
|
||
|
||
#### 5.3.1 销售产品
|
||
|
||
| 接口名称 | 请求方式 | 请求路径 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| 产品列表 | GET | /channel/sales-product/list | 产品列表 |
|
||
| 产品详情 | GET | /channel/sales-product/detail | 产品详情 |
|
||
| 新增产品 | POST | /channel/sales-product/create | 新增产品 |
|
||
| 更新产品 | POST | /channel/sales-product/update | 更新产品主表 |
|
||
| 保存产品配置 | POST | /channel/sales-product/config/save | 同 update(前端配置 Tab 专用,Swagger 隐藏) |
|
||
| 删除产品 | 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`。
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- 见 **§5.3.0.2** `GET .../list`。
|
||
|
||
##### 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 | 启用/禁用价格项 |
|
||
| 批量保存价格项 | POST | /channel/sales-product/price/save | 整树替换卖价;可选 `publish=true` 同步发布 |
|
||
|
||
##### 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",
|
||
"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",
|
||
"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",
|
||
"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获取规则发布历史 |
|
||
| 发布预览 | GET | /channel/sales-product/publish/preview | 预览即将发布的快照 JSON(不落库) |
|
||
| 发布 | 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 | 更新渠道主表 |
|
||
| 保存渠道配置 | POST | /channel/service-channel/config/save | 同 update(Swagger 隐藏) |
|
||
| 删除渠道 | 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 渠道买价(子表接口)
|
||
|
||
> **与 §5.3.2 产品卖价对称**:主从表结构、弹窗交互(基础信息 + 计费因素 + 价格表)、CRUD、`price/save` 整树替换与发布快照机制一致;差异为关联 **`serviceChannelId`**(非 `salesProductId`),运行时应付算价读取渠道买价快照(§4.3.4.2)。
|
||
|
||
| 接口名称 | 请求方式 | 请求路径 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| 买价项列表 | 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 | 启用/禁用买价项 |
|
||
| 批量保存买价项 | POST | /channel/service-channel/price/save | 整树替换买价;可选 `publish=true` 同步发布 |
|
||
|
||
##### 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",
|
||
"chargingCondition": "",
|
||
"factors": [
|
||
{
|
||
"id": 1,
|
||
"priceId": 1,
|
||
"serialNumber": 1,
|
||
"factor": "货物类型",
|
||
"factorValues": ["普货", "敏感货"]
|
||
},
|
||
{
|
||
"id": 2,
|
||
"priceId": 1,
|
||
"serialNumber": 2,
|
||
"factor": "分区",
|
||
"factorValues": ["欧洲", "美国"]
|
||
}
|
||
],
|
||
"details": [
|
||
{
|
||
"id": 1,
|
||
"priceId": 1,
|
||
"factorValuesJson": {"分区": "欧洲", "货物类型": "普货", "重量": "0-0.5"},
|
||
"calculationFormula": "单价",
|
||
"calculatedValue": 45,
|
||
"customerGroupValues": {"客户组1": 43, "客户组2": 47}
|
||
}
|
||
],
|
||
"createTime": "2024-01-01 12:00:00",
|
||
"updateTime": "2024-01-01 12:00:00"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
- 按 `serviceChannelId` 查询买价项主表,并**一次聚合**返回 `factors`、`details` 子数据(避免 N+1)。
|
||
- 结果按 `serialNumber` 升序组织,便于前端按配置顺序渲染。
|
||
|
||
###### 校验与异常
|
||
|
||
- 校验 `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",
|
||
"chargingCondition": "",
|
||
"factors": [
|
||
{
|
||
"serialNumber": 1,
|
||
"factor": "货物类型",
|
||
"factorValues": ["普货", "敏感货"]
|
||
}
|
||
],
|
||
"details": [
|
||
{
|
||
"factorValuesJson": {"分区": "欧洲", "货物类型": "普货", "重量": "0-0.5"},
|
||
"calculationFormula": "单价",
|
||
"calculatedValue": 45,
|
||
"customerGroupValues": {"客户组1": 43, "客户组2": 47}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
- 新增买价主记录,并写入对应 `factors` 与 `details` 子记录(与 §5.3.2.2 卖价新增一致)。
|
||
- 同步写入审计字段,保持主从数据一致性。
|
||
|
||
###### 校验与异常
|
||
|
||
- 校验 `serviceChannelId`、`priceName`、`costItem`、`costUnit` 等必填字段。
|
||
- 校验同渠道下 `serialNumber` 不冲突;冲突时返回业务错误。
|
||
|
||
##### 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",
|
||
"chargingCondition": "",
|
||
"factors": [],
|
||
"details": []
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
- 按 `id` 更新买价主数据,并按最新请求**重建/同步**因素与明细子数据。
|
||
- 保留版本追溯能力,更新后可参与后续发布流程(§5.3.7)。
|
||
|
||
###### 校验与异常
|
||
|
||
- 校验 `id` 存在且归属 `serviceChannelId`。
|
||
- 校验更新后配置合法(计算公式、阶梯区间、客户组取值等),不合法时返回业务错误。
|
||
|
||
##### 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`)。
|
||
|
||
###### 校验与异常
|
||
|
||
- 校验 `status` 仅允许合法枚举值(0/1)。
|
||
- 校验 `id` 存在,不存在时返回业务错误。
|
||
|
||
##### 5.3.6.6 批量保存买价项(POST /channel/service-channel/price/save)
|
||
|
||
> 与 **§5.3.2** 卖价 `price/save` 一致:前端也可逐条 `create/update`;本接口用于**整树替换**当前渠道下全部买价项(含 factors、details),可选连带发布。
|
||
|
||
###### 1 接口请求示例
|
||
|
||
**请求 /channel/service-channel/price/save**(POST)
|
||
|
||
```json
|
||
{
|
||
"serviceChannelId": 1,
|
||
"publish": false,
|
||
"prices": [
|
||
{
|
||
"serialNumber": 1,
|
||
"status": 1,
|
||
"priceName": "运费",
|
||
"costItem": "freight",
|
||
"costUnit": "kg",
|
||
"currency": "RMB",
|
||
"chargingCondition": "",
|
||
"factors": [],
|
||
"details": []
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. 校验 `serviceChannelId` 存在。
|
||
2. 使用临时负数 `Id` 关联子表,调用 **`ReplacePricesAsync`** 整树替换当前渠道买价。
|
||
3. 若 `publish=true`,写入 **`t_service_channel_publish_record`**(`operatorType=price`),快照内容为瘦身后的全部买价项(见 **§5.3.0.3**)。
|
||
|
||
###### 校验与异常
|
||
|
||
- `prices` 可为空数组(表示清空买价);单条校验同 create/update。
|
||
- 发布失败时买价替换已成功则不回滚(与卖价策略一致),返回明确错误信息。
|
||
|
||
#### 5.3.7 服务渠道发布记录(子表接口)
|
||
|
||
| 接口名称 | 请求方式 | 请求路径 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| 价格发布记录列表 | GET | /channel/service-channel/publish/price/list | 按渠道ID获取买价发布历史 |
|
||
| 规则发布记录列表 | GET | /channel/service-channel/publish/rules/list | 按渠道ID获取规则发布历史 |
|
||
| 发布预览 | GET | /channel/service-channel/publish/preview | 预览即将发布的快照 JSON(不落库) |
|
||
| 发布 | 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 | 分区明细列表 |
|
||
| 下载明细导入模板 | GET | /channel/kpi-area/info/import-template | Excel 导入模板 |
|
||
| 导入分区明细 | POST | /channel/kpi-area/info/import | multipart 批量导入明细 |
|
||
|
||
##### 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 问题模板
|
||
|
||
| 接口名称 | 请求方式 | 请求路径 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| 列表 | GET | /channel/issue-template/list | 分页列表 |
|
||
| 详情 | GET | /channel/issue-template/detail | 单条详情 |
|
||
| 下拉选项 | GET | /channel/issue-template/options | 启用模板选项 |
|
||
| 新增 | POST | /channel/issue-template/create | 新增 |
|
||
| 更新 | POST | /channel/issue-template/update | 更新 |
|
||
| 删除 | POST | /channel/issue-template/delete | 软删 |
|
||
| 切换启用状态 | POST | /channel/issue-template/toggleStatus | 切换状态 |
|
||
|
||
##### 5.3.11.1 新增(POST /channel/issue-template/create)
|
||
|
||
```json
|
||
{
|
||
"organizationId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
|
||
"issueType": "address",
|
||
"title": "收件地址无法派送",
|
||
"content": "运单 {serviceNo} 收件地址不完整,请补充门牌号与邮编。",
|
||
"notifyMethod": "inApp,email",
|
||
"status": 1
|
||
}
|
||
```
|
||
|
||
**校验**:`issueType` 合法;`title`/`content` 非空;`notifyMethod` 至少一种;同组织同类型不重复。
|
||
|
||
#### 5.3.12 标签设计
|
||
|
||
| 接口名称 | 请求方式 | 请求路径 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| 保存标签模板 | 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 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
- 按本节接口定义执行业务处理流程,完成参数解析、数据查询/写入与结果返回。
|
||
|
||
###### 校验与异常
|
||
|
||
- 对必填参数、状态流转与数据存在性进行校验;异常场景返回统一错误码与错误信息。
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- `LabelTemplateService.SaveAsync`:见 **§5.3.0.8**;`id=0` 新增,否则更新;**Name+TemplateType** 唯一。
|
||
|
||
## 6 操作模块
|
||
|
||
### 6.1 核心需求说明
|
||
|
||
- **入库扫描**:扫描运单号完成入库,记录实重、尺寸、销售产品、目的国、仓库、操作人、操作时间;**仅覆盖 `Type=receive` 行并重算计费重**(规则见 §4.4.3.1),再调用 **§4.3.4.1** 重算应收(`receive` 计费重,`ChargingStage=inbound`);**不修改** `forecast` / `ship` / `downstream` 行;支持查看最近入库记录。
|
||
- **出库扫描**:扫描运单号完成出库,**记录实重、尺寸、销售产品、目的国、服务渠道ID**、仓库、操作人、操作时间(**`t_outbound_record` 在入库记录字段基础上另存 `ServiceChannelId`**,见 §6.2.2);**仅覆盖 `Type=ship`(发货)行尺寸与重量并重算计费重**(实测尺重见请求体,见 §6.3.2.1);**出库成功后更新 `t_order.OrderStatus = pendingOnline`(待上网)并写入 `ShipTime`**;再调用 **§4.3.4.1** 重算应收(`ship` 计费重,`ChargingStage=outbound`);**不修改** `forecast` / `receive` / `downstream` 行;支持查看最近出库记录。
|
||
- **标签打印**:按运单号/客户单号/跟踪号/其他单号选择标签模板打印;记录打印结果与操作人。
|
||
|
||
### 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 / 1_000_000`。
|
||
- **对该 `receive` 行**按本单绑定 **`WeightRuleId` / `WeightRoundingId`**(及 rules 快照或子表明细)执行 **§4.4.3.1 步骤 7、8 相同算法**,重算 **`VolumetricWeight`、`ChargeableWeight`**(**不修改 `forecast` / `ship` / `downstream` 行**)。
|
||
- 更新 **`t_order.OrderStatus = pendingPayment`**,写入 **`ReceiveTime`**。
|
||
- 入库完成后:**§4.3.4.1** 重算应收(**`receive` 行**计费重,`ChargingStage=inbound`)。
|
||
|
||
###### 校验与异常
|
||
|
||
- 对必填参数、订单状态、单号与订单关联、仓库权限等进行校验;件重尺非法、计重规则未命中、进位无匹配等按业务错误返回。
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- `InboundRecordService.CreateAsync` → `ApplyReceiveBillingByWaybillAsync`:已联动订单状态与 **`receive` 行**计重、应收/应付 **`ChargingStage=inbound`**,并 **重新排货确认 `ChannelCode`**。
|
||
|
||
##### 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) 必填**(发货复量实测,写入 **`Type=ship` 行**);`salesProductId` 销售产品ID;`destinationCountry` 目的国二字码;**`serviceChannelId` 服务渠道ID**(对应 **`t_service_channel.Id`**,可选,未传时服务端按订单绑定/排货兜底并写入 `t_outbound_record`);`warehouseId` 仓库ID(可选,不传则从订单或默认仓库获取)。**`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 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. **定位订单**:解析 `waybillNo`,按运单号关联 **`t_order`** 及本单 **`t_order_weight` 四行**(`forecast` / `receive` / `ship` / `downstream`)。
|
||
2. **前置校验**:
|
||
- 订单须存在且 **`OrderStatus = pendingShip`(待发货)**;**`pendingPayment` / `received`** → **「订单未付款,禁止出库」**;其它状态 → **「当前订单状态不允许出库」**。
|
||
- 须已存在 **`Type=ship`(发货)** 行(预报成功时随主单创建)。
|
||
3. **写入出库记录**:落库 **`t_outbound_record`**(实重、尺寸、销售产品、目的国、**`ServiceChannelId`**、仓库、操作人、操作时间;件重尺与业务维度与 **`t_inbound_record`** 对齐,**并含 `ServiceChannelId`**;`Weight/Length/Width/Height` 取值以请求、订单绑定及下文 **`ship` 行最终件重尺** 为准)。
|
||
4. **更新发货件重尺(`t_order_weight.Type = ship`)**:以请求 **`weight` / `length` / `width` / `height`** 写入 **`ship` 行**的 **`Length`、`Width`、`Height`、`ActualWeight`**(`weight` → `ActualWeight`),并重算 **`Cubic = Length × Width × Height / 1_000_000`**(**不读取、不覆盖其它 `Type` 行**)。
|
||
5. **计重**:对 **`ship` 行**按订单绑定 **`WeightRuleId` / `WeightRoundingId`**(及 rules 快照或子表明细)执行 **§4.4.3.1 步骤 7、8 相同算法**,重算 **`VolumetricWeight`、`ChargeableWeight`**(**不修改 `forecast` / `receive` / `downstream` 行**)。
|
||
6. **更新订单主表**:**`OrderStatus = pendingOnline`(待上网)**,**`ShipTime = 操作时间`**,刷新 **`UpdateTime` / `UpdateUserId`**;持久化 **`ship` 行**与主表。
|
||
7. **阶段计费**(待实现):按 **§4.3.4.1** 以 **`ship` 行** `ChargeableWeight` 重算应收(`ChargingStage=outbound`)。
|
||
|
||
> **与入库对称**:入库 **仅**更新 **`receive` 行** → **`OrderStatus=pendingPayment`** + **`ReceiveTime`**;出库 **仅**更新 **`ship` 行** → **`OrderStatus=pendingOnline`** + **`ShipTime`**。四行件重尺各自独立维护。
|
||
|
||
###### 校验与异常
|
||
|
||
- **`waybillNo`** 必填;**`weight` / `length` / `width` / `height` 必填且均 > 0**。
|
||
- 订单须 **`pendingShip`**;**`pendingPayment` / `received`** 禁止出库;**`pendingOnline` 及以后**(已出库)→ **「订单已出库」**。
|
||
- 缺少 **`Type=ship`** 行 → 业务错误。
|
||
- 计重规则未命中、进位无匹配、价格分区未匹配(若触发 §4.3.4 算价)→ 明确错误信息;**事务回滚**,不写入出库记录与状态变更。
|
||
|
||
###### 3 当前实现(oms0414-back)
|
||
|
||
- `OutboundRecordService.CreateAsync`:**仅**插入 **`t_outbound_record`**(`WaybillNo`、`WarehouseId`、`OperateTime`、`OperatorId`);**校验**订单须为 **`pendingShip`**。
|
||
- **未**覆盖 **`t_order_weight.Type=ship`** 件重尺、**未**更新 **`t_order.OrderStatus` / `ShipTime`**、**未**触发 §4.3.4 算价(与上文设计目标 **§6.3.2.1 核心流程** 尚未对齐)。
|
||
- **待实现**:抽取 **`OrderService.ApplyShipBillingByWaybillAsync`**(对称于 **`ApplyReceiveBillingByWaybillAsync`**),由 `CreateAsync` 在写 **`t_outbound_record`** 前/同一事务内调用。
|
||
|
||
##### 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 核心需求说明
|
||
|
||
- **充值流程**:客户提交充值金额 → 选择支付方式(微信/支付宝)→ 上传充值截图 → 提交审核 → 管理员审核(通过/驳回)→ 审核通过后更新客户余额并 **写入账户资金流水**。
|
||
- **充值记录**:完整留存充值全流程信息(`t_recharge_record`),支持查询、导出;充值状态包含:待审核、审核通过、审核驳回。
|
||
- **资金流水**:所有 **余额变动**(充值入账、订单支付等)统一记入 **`t_customer_account_flow`**,前端「资金流水」页查询 **`GET /customer/account-flow/list`**。
|
||
|
||
### 7.2 表结构设计
|
||
|
||
#### 7.2.1 充值记录表 `t_recharge_record`
|
||
|
||
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| Id | BIGINT | - | 主键 | 是 | 自增唯一标识 |
|
||
| CustomerId | BIGINT | - | 外键 → `t_customer.Id` | 是 | 充值归属客户业务主键;子客户登录时是否归并主客户 Id 见 **§1.4** |
|
||
| 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.2.2 客户账户资金流水表 `t_customer_account_flow`
|
||
|
||
> **定位**:客户 **`t_customer.Balance` 的唯一变动账本**;与 **`t_recharge_record`**(充值申请/审核)分离——后者管「充值业务单据」,本表管「账户余额每一次增减」。
|
||
|
||
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| Id | INT | - | 主键 | 是 | 自增唯一标识 |
|
||
| CustomerId | INT | - | 外键 → `t_customer.Id` | 是 | 流水归属客户 |
|
||
| SerialNumber | VARCHAR | 64 | 唯一索引 | 是 | 流水号,规则:**`FLOW` + yyyyMMddHHmmss + 6 位随机数** |
|
||
| BizType | VARCHAR | 32 | - | 是 | 业务类型:`recharge` 充值入账;`orderPay` 订单支付(可扩展) |
|
||
| BizId | INT | - | - | 否 | 关联业务主键(如 `t_recharge_record.Id`、`t_order.Id`) |
|
||
| BizNo | VARCHAR | 64 | - | 否 | 关联业务单号(如充值流水号、运单号 `ServiceNo`) |
|
||
| Amount | DECIMAL | 18,2 | - | 是 | **变动金额(带符号)**:正数=入账,负数=出账 |
|
||
| BalanceBefore | DECIMAL | 18,2 | - | 是 | 变动前账户余额 |
|
||
| BalanceAfter | DECIMAL | 18,2 | - | 是 | 变动后账户余额(= BalanceBefore + Amount) |
|
||
| Currency | VARCHAR | 8 | - | 是 | 币种,默认 **CNY** |
|
||
| Remark | VARCHAR | 512 | - | 否 | 备注 |
|
||
| IsDeleted | TINYINT | - | - | 是 | 软删,默认 0 |
|
||
| ConcurrencyStamp | CHAR | 36 | - | 是 | 并发戳(AggregateRoot 约定);新建表可用 DEFAULT(UUID()),已有表补列见 `migrate_t_customer_account_flow_concurrency.sql`(ALTER 勿用 UUID 默认值,避免复制 Error 1674) |
|
||
| CreateUserId | INT | - | - | 是 | 创建人 |
|
||
| CreateTime | DATETIME | - | - | 是 | 创建时间(流水发生时间) |
|
||
| UpdateUserId | INT | - | - | 否 | 更新人 |
|
||
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
|
||
|
||
**索引**:
|
||
|
||
| 索引名 | 字段 | 说明 |
|
||
| --- | --- | --- |
|
||
| `uk_customer_account_flow_serial` | SerialNumber | 流水号唯一 |
|
||
| `idx_customer_account_flow_customer_time` | CustomerId, CreateTime | 客户流水列表 |
|
||
| `idx_customer_account_flow_biz` | BizType, BizId | 按业务反查 |
|
||
|
||
**迁移脚本**:`sql/migrate_t_customer_account_flow.sql`
|
||
|
||
### 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 | 审核通过则 **`CustomerBalanceMutator` 增余额 + 写 `t_customer_account_flow`**,更新审核时间、状态、审核人 |
|
||
| 查询充值记录 | GET | /recharge/recharge-record/list | customerId, serialNumber, startTime, endTime, status | list, total | 多条件分页查询充值记录 |
|
||
| 导出充值记录 | GET | /recharge/export | customerId, startTime, endTime | 文件流 | 导出充值记录为Excel文件 |
|
||
| 查询账户资金流水 | GET | /customer/account-flow/list | page, pageSize, keyword, bizType, customerId, startTime, endTime | items, total | 分页查询余额变动流水,见 **§7.4** |
|
||
|
||
##### 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` 为 **`t_customer.Id`**(客户端可不传:用 JWT **`mapId`** 查 `t_customer.UserId` 得到对应 `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 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
1. 校验记录 **`RechargeStatus = Pending`**,否则 **「只有待审核记录才能审核通过」**。
|
||
2. 更新 **`t_recharge_record`**:状态=审核通过,写入审核人、审核时间、备注。
|
||
3. 调用 **`CustomerBalanceMutator.CreditRechargeAsync`**:
|
||
- **`t_customer.Balance += RechargeAmount`**;
|
||
- 插入 **`t_customer_account_flow`**:`BizType=recharge`,`BizId=record.Id`,`BizNo=record.SerialNumber`,**`Amount` 为正数**,记录变动前后余额。
|
||
|
||
> 步骤 2~3 在同一工作单元内提交;**不再**单独 `Balance +=` 而不写流水。
|
||
|
||
###### 校验与异常
|
||
|
||
- 校验记录状态必须为待审核;重复审核或状态冲突时返回业务错误。
|
||
- 客户不存在 → **「客户不存在」**。
|
||
|
||
##### 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 文件流。
|
||
|
||
###### 校验与异常
|
||
|
||
- 校验导出条件与数据权限,导出失败时返回统一错误信息。
|
||
|
||
### 7.4 客户账户资金流水
|
||
|
||
#### 7.4.1 设计说明
|
||
|
||
| 概念 | 表 / 组件 | 说明 |
|
||
| --- | --- | --- |
|
||
| 账户余额(权威) | `t_customer.Balance` | 客户当前可用余额;**仅通过 `CustomerBalanceMutator` 变更** |
|
||
| 充值业务单据 | `t_recharge_record` | 充值申请、截图、审核状态;**不等同于**资金流水 |
|
||
| 资金流水(账本) | `t_customer_account_flow` | 每一次余额增减的 **不可变记录**,含变动前后余额 |
|
||
| 余额变动服务 | `CustomerBalanceMutator` | 统一 **改 Balance + 插流水**;充值审核、订单支付均调用 |
|
||
|
||
**BizType 枚举(当前)**:
|
||
|
||
| BizType | 中文 | 触发场景 | Amount 符号 |
|
||
| --- | --- | --- | --- |
|
||
| `recharge` | 充值入账 | `POST /recharge/recharge-record/approve` 审核通过 | **正** |
|
||
| `orderPay` | 订单支付 | `POST /order/pay` 待支付订单扣款 | **负** |
|
||
|
||
#### 7.4.2 余额变动业务逻辑(`CustomerBalanceMutator`)
|
||
|
||
**公共步骤 `ApplyAsync`**:
|
||
|
||
1. 读取 **`balanceBefore = customer.Balance`**,计算 **`balanceAfter = balanceBefore + signedAmount`**。
|
||
2. 若 **`signedAmount == 0`** → **「流水金额不能为 0」**;若 **`balanceAfter < 0`** → **「账户余额不足」**。
|
||
3. 更新 **`customer.Balance = balanceAfter`** 并 **`UpdateAsync`**。
|
||
4. 生成唯一 **`SerialNumber`**(`FLOW{yyyyMMddHHmmss}{random6}`,冲突重试最多 5 次)。
|
||
5. 插入 **`t_customer_account_flow`**,写入 `BizType`、`BizId`、`BizNo`、`Amount`(带符号)、`BalanceBefore`、`BalanceAfter`、`Currency=CNY`、`Remark`。
|
||
|
||
**封装方法**:
|
||
|
||
| 方法 | 调用方 | 说明 |
|
||
| --- | --- | --- |
|
||
| `CreditRechargeAsync` | `RechargeService.ApproveAsync` | 入账金额为正 |
|
||
| `DebitOrderPayAsync` | `OrderService.PayAsync` | 出账金额为负;**payAmount 须 > 0** |
|
||
|
||
> 实现类:`Oms.Application.Shared.CustomerBalanceMutator`;仓储:`ICustomerAccountFlowRepository` / `CustomerAccountFlowRepository`。
|
||
|
||
#### 7.4.3 查询账户流水(GET /customer/account-flow/list)
|
||
|
||
**服务**:`CustomerAccountFlowService.GetPaginatedListAsync`
|
||
**分组**:OpenAPI **`OMS-Customer`**
|
||
|
||
**Query 参数**:
|
||
|
||
| 参数 | 类型 | 必填 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| page | int | 否 | 页码,默认 1 |
|
||
| pageSize | int | 否 | 每页条数,默认 20 |
|
||
| keyword | string | 否 | 模糊匹配 **流水号 SerialNumber** 或 **关联单号 BizNo** |
|
||
| bizType | string | 否 | `recharge` / `orderPay`;空或 `all` 表示全部 |
|
||
| customerId | int | 否 | 管理员可按客户筛选;**客户登录时由会话强制过滤为本客户 `t_customer.Id`** |
|
||
| startTime | datetime | 否 | 创建时间起(与 endTime 成对使用) |
|
||
| endTime | datetime | 否 | 创建时间止 |
|
||
|
||
**响应 `items[]` 字段**:
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| id | 流水主键 |
|
||
| customerId | 客户 Id |
|
||
| customerCode | 客户编码(列表补齐) |
|
||
| serialNumber | 流水号 |
|
||
| bizType | 业务类型码 |
|
||
| bizTypeName | 业务类型中文(充值入账 / 订单支付) |
|
||
| bizId | 关联业务 Id |
|
||
| bizNo | 关联单号 |
|
||
| amount | 变动金额(正/负) |
|
||
| balanceBefore | 变动前余额 |
|
||
| balanceAfter | 变动后余额 |
|
||
| currency | 币种 |
|
||
| remark | 备注 |
|
||
| createTime | 流水时间 |
|
||
|
||
**请求示例**
|
||
|
||
```
|
||
GET /customer/account-flow/list?page=1&pageSize=50&bizType=orderPay&startTime=2026-06-01%2000:00:00&endTime=2026-06-30%2023:59:59
|
||
```
|
||
|
||
**响应示例**
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": {
|
||
"items": [
|
||
{
|
||
"id": 1,
|
||
"customerId": 10,
|
||
"customerCode": "C001",
|
||
"serialNumber": "FLOW20260626164200123456",
|
||
"bizType": "orderPay",
|
||
"bizTypeName": "订单支付",
|
||
"bizId": 24,
|
||
"bizNo": "LP202606261642156945",
|
||
"amount": -128.50,
|
||
"balanceBefore": 500.00,
|
||
"balanceAfter": 371.50,
|
||
"currency": "CNY",
|
||
"remark": "订单支付 LP202606261642156945",
|
||
"createTime": "2026-06-26 16:42:15"
|
||
}
|
||
],
|
||
"total": 1
|
||
}
|
||
}
|
||
```
|
||
|
||
###### 校验与隔离
|
||
|
||
- 客户门户:**`ResolveListCustomerIdAsync`** 强制 `CustomerId` 过滤,**不可**查看他人流水。
|
||
- 管理员:`customerId` 为空时查全部客户流水;传 `customerId` 时按指定客户过滤。
|
||
|
||
#### 7.4.4 前端(itfx-front)
|
||
|
||
| 项 | 说明 |
|
||
| --- | --- |
|
||
| 路由 | `/user/accountFlow`,菜单 **客户 → 资金流水** |
|
||
| 页面 | `src/views/user/accountFlow/index.vue` |
|
||
| 表格 | **HandsontablePro** + **PaginationPro** |
|
||
| 入口 | 顶部导航余额下拉「资金流水」;支付成功后触发 **`oms:customer-balance-changed`** 刷新余额展示 |
|
||
|
||
----------------------------------------------------------------------------------------
|
||
|
||
----------------------------------------------------------------------------------------
|
||
## 8 系统主数据
|
||
|
||
### 8.1 核心需求说明
|
||
|
||
- **仓库地址**:管理仓库收货地址信息,包括主仓/中转仓/退货仓/其他类型;支持收件人、联系电话、收货地址、客服电话、客服微信及二维码等配置。
|
||
- **国家管理**:维护国家基础资料(中文名称、英文名称、二字代码),供渠道分区、下单地址等模块复用。
|
||
- **字典管理**:维护 **`t_dict`**,供全平台下拉与状态映射复用。**与客户、内部员工相关的 `DictType` 及编码含义**在 **`docs/rbac系统设计-060801.md`** 定义与迭代;OMS 仅保证业务侧按 **`DictType` + `DictCode`** 引用时与实现一致。本篇表结构中 **`DictType` 示例**以 **仓库、通知等纯业务枚举** 为主。
|
||
- **其他配置**:维护 **`t_system_other_config`**,以 **卡片式** 页面(路由 **`/system/otherConfig`**)管理第三方集成参数;当前首条业务为 **小包轨迹服务**(`ConfigCode = track_service`),供订单预报成功后异步调用外部 **`POST {BaseUrl}/OrderV2/UpsertSmallBag`**。运行时优先读库,缺省字段回退 **`appsettings` → `Oms:TrackService`**。
|
||
|
||
### 8.2 表结构设计
|
||
|
||
#### 8.2.1 与 RBAC 文档的职责划分
|
||
|
||
**权限系统**(认证、内部用户、客户主数据、角色权限、菜单、组织等)**全部由 `docs/rbac系统设计-060801.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`**(见 **§10.3.5**) |
|
||
| ReceiverIds | JSON | - | - | 是 | 接收人 ID 列表;存 **`user.userid`**(INT)数组,非 `t_customer.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`** |
|
||
| ReceiverId | BIGINT | - | - | 是 | 已读主体 ID = **`user.userid`**(JWT `mapId`) |
|
||
| 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.2.7 系统其他配置表 `t_system_other_config`
|
||
|
||
> **迁移脚本**:`sql/migrate_t_system_other_config.sql`(增量);全量建表见根目录 **`db.sql`**。
|
||
|
||
| 字段名 | 字段类型 | 长度 | 主键/外键 | 非空 | 说明 |
|
||
| --- | --- | --- | --- | --- | --- |
|
||
| Id | INT | - | 主键 | 是 | 自增唯一标识 |
|
||
| ConfigCode | VARCHAR | 64 | - | 是 | 配置编码,**全局唯一**(如 **`track_service`**) |
|
||
| ConfigName | VARCHAR | 128 | - | 是 | 配置名称(展示用,如「小包轨迹服务」) |
|
||
| Enabled | TINYINT | - | - | 是 | 是否启用:0-否,1-是,默认 0 |
|
||
| BaseUrl | VARCHAR | 512 | - | 否 | 第三方服务根地址(轨迹示例:`http://api.nanafa.com/track`) |
|
||
| AppKey | VARCHAR | 128 | - | 否 | 接入 AppKey |
|
||
| AppSecret | VARCHAR | 256 | - | 否 | 接入 AppSecret(**敏感**,列表接口脱敏) |
|
||
| TimeoutSeconds | INT | - | - | 是 | HTTP 超时秒数,默认 30 |
|
||
| Remark | VARCHAR | 512 | - | 否 | 备注 |
|
||
| IsDeleted | TINYINT | - | - | 是 | 是否删除:0-否,1-是,默认 0 |
|
||
| CreateUserId | INT | - | - | 是 | 创建人 |
|
||
| CreateTime | DATETIME | - | - | 是 | 创建时间 |
|
||
| UpdateUserId | INT | - | - | 否 | 更新人 |
|
||
| UpdateTime | DATETIME | - | - | 否 | 更新时间 |
|
||
|
||
**索引**
|
||
|
||
| 索引名 | 类型 | 字段 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| uk_system_other_config_code | UNIQUE | ConfigCode | 配置编码唯一 |
|
||
| idx_system_other_config_enabled | KEY | Enabled, Id | 按启用状态筛选 |
|
||
|
||
**预置配置编码(`ConfigCode`)**
|
||
|
||
| ConfigCode | ConfigName(建议) | 消费方 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| `track_service` | 小包轨迹服务 | 订单预报成功后异步轨迹同步 | 调用 **`POST {BaseUrl}/OrderV2/UpsertSmallBag`**;请求公共参数含 `appKey`、`timestamp`、`sign`;**签名**:`MD5("appKey={appKey}×tamp={timestamp}&appSecret={appSecret}")` 转 **大写十六进制** |
|
||
|
||
**运行时读取优先级(以 `track_service` 为例)**
|
||
|
||
1. 查询 **`t_system_other_config`**:`ConfigCode = track_service` 且未删除;各字段非空则覆盖对应运行时参数。
|
||
2. 若库中无记录或某字段为空,回退 **`Oms:TrackService`**(`appsettings` / Consul):`Enabled`、`BaseUrl`、`AppKey`、`AppSecret`、`TimeoutSeconds`。
|
||
3. `Enabled = false` 或 `BaseUrl` / `AppKey` / `AppSecret` 不完整时,**跳过** UpsertSmallBag(仅记 Debug 日志,不影响订单主流程)。
|
||
|
||
### 8.3 核心接口设计
|
||
|
||
> **登录、刷新、当前用户**:`POST /rbac/auth/login`、`POST /rbac/auth/refresh-token`、`GET /rbac/auth/get-current-user` 等路径与契约见 **`docs/rbac系统设计-060801.md`** 与 OpenAPI;**不在本篇逐条列出**。
|
||
|
||
| 接口名称 | 请求方式 | 请求路径 | 说明 |
|
||
| --- | --- | --- | --- |
|
||
| 国家列表 | 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/other-config/list | 分页查询其他配置(AppSecret 脱敏) |
|
||
| 其他配置详情 | GET | /system/other-config/detail | 按 Id 查询详情(含完整 AppSecret,供编辑) |
|
||
| 新增其他配置 | POST | /system/other-config/create | 新增 |
|
||
| 更新其他配置 | POST | /system/other-config/update | 更新(AppSecret 留空或 `******` 表示不修改) |
|
||
| 删除其他配置 | POST | /system/other-config/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` / `t_system_other_config`**;**认证、令牌、权限码、RBAC/客户 HTTP** 见 **`docs/rbac系统设计-060801.md`** 与 **OpenAPI/Swagger**(`/rbac/*`、`/customer/*`)。
|
||
|
||
##### 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¬iceType=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` 幂等写入已读记录(**`ReceiverId` = 当前 JWT `mapId`**,即 `user.userid`)。
|
||
|
||
###### 校验与异常
|
||
|
||
- 校验通知存在且未删除。
|
||
- 校验当前主体在通知接收范围内,不在范围内时返回无权限错误。
|
||
|
||
##### 8.4.1.16 其他配置列表(GET /system/other-config/list)
|
||
|
||
###### 1 接口请求示例
|
||
|
||
**请求 /system/other-config/list**(GET,Query 参数)
|
||
|
||
```
|
||
?page=1&pageSize=30&keyword=track
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": {
|
||
"items": [
|
||
{
|
||
"id": 1,
|
||
"configCode": "track_service",
|
||
"configName": "小包轨迹服务",
|
||
"enabled": true,
|
||
"baseUrl": "http://api.nanafa.com/track",
|
||
"appKey": "YOUR_APP_KEY",
|
||
"appSecret": "******",
|
||
"timeoutSeconds": 30,
|
||
"remark": "",
|
||
"createTime": "2026-06-27 10:00:00",
|
||
"updateTime": "2026-06-27 10:00:00"
|
||
}
|
||
],
|
||
"total": 1
|
||
}
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
- 按关键词(匹配 `ConfigCode`、`ConfigName`)分页查询 `t_system_other_config`,按 `Id` 降序返回。
|
||
- 列表项 **`appSecret`** 固定脱敏为 **`******`**(未配置时返回空)。
|
||
|
||
###### 校验与异常
|
||
|
||
- 校验分页参数合法性;仅返回未删除记录。
|
||
|
||
##### 8.4.1.17 其他配置详情(GET /system/other-config/detail)
|
||
|
||
###### 1 接口请求示例
|
||
|
||
**请求 /system/other-config/detail**(GET,Query 参数)
|
||
|
||
```
|
||
?id=1
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": {
|
||
"id": 1,
|
||
"configCode": "track_service",
|
||
"configName": "小包轨迹服务",
|
||
"enabled": true,
|
||
"baseUrl": "http://api.nanafa.com/track",
|
||
"appKey": "YOUR_APP_KEY",
|
||
"appSecret": "YOUR_APP_SECRET",
|
||
"timeoutSeconds": 30,
|
||
"remark": "",
|
||
"createTime": "2026-06-27 10:00:00",
|
||
"updateTime": "2026-06-27 10:00:00"
|
||
}
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
- 根据主键查询单条配置,返回完整字段(含 **未脱敏** `appSecret`),供管理端编辑表单回填。
|
||
|
||
###### 校验与异常
|
||
|
||
- 校验 `id` 必填且存在,不存在时返回业务错误 **「配置不存在」**。
|
||
|
||
##### 8.4.1.18 新增其他配置(POST /system/other-config/create)
|
||
|
||
###### 1 接口请求示例
|
||
|
||
**请求 /system/other-config/create**(POST)
|
||
|
||
```json
|
||
{
|
||
"configCode": "track_service",
|
||
"configName": "小包轨迹服务",
|
||
"enabled": true,
|
||
"baseUrl": "http://api.nanafa.com/track",
|
||
"appKey": "YOUR_APP_KEY",
|
||
"appSecret": "YOUR_APP_SECRET",
|
||
"timeoutSeconds": 30,
|
||
"remark": ""
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
- 校验入参后写入 `t_system_other_config` 并记录创建审计信息。
|
||
- 当 `ConfigCode = track_service` 时,后续订单 **预报成功** 将按本配置异步调用轨迹 UpsertSmallBag。
|
||
|
||
###### 校验与异常
|
||
|
||
- `configCode`、`configName` 必填;`configCode` **全局唯一**,重复时返回 **「配置编码已存在」**。
|
||
- 当 `configCode = track_service` 时:`baseUrl`、`appKey`、`appSecret` 均必填,否则分别返回 **「轨迹服务地址不能为空」**、**「轨迹服务 AppKey 不能为空」**、**「轨迹服务 AppSecret 不能为空」**。
|
||
- `timeoutSeconds` 小于等于 0 时落库按 **30** 处理。
|
||
|
||
##### 8.4.1.19 更新其他配置(POST /system/other-config/update)
|
||
|
||
###### 1 接口请求示例
|
||
|
||
**请求 /system/other-config/update**(POST)
|
||
|
||
```json
|
||
{
|
||
"id": 1,
|
||
"configCode": "track_service",
|
||
"configName": "小包轨迹服务",
|
||
"enabled": true,
|
||
"baseUrl": "http://api.nanafa.com/track",
|
||
"appKey": "YOUR_APP_KEY",
|
||
"appSecret": "",
|
||
"timeoutSeconds": 30,
|
||
"remark": "生产环境"
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
- 根据 `id` 更新配置;**`appSecret` 为空或为 `******` 时不修改库中已有密钥**(其余字段正常更新)。
|
||
|
||
###### 校验与异常
|
||
|
||
- 校验 `id` 存在;`configCode` 唯一性(排除自身)。
|
||
- `configCode = track_service` 时:`baseUrl`、`appKey` 仍必填;`appSecret` 仅在传入新值时更新。
|
||
|
||
##### 8.4.1.20 删除其他配置(POST /system/other-config/delete)
|
||
|
||
###### 1 接口请求示例
|
||
|
||
**请求 /system/other-config/delete**(POST)
|
||
|
||
```json
|
||
{
|
||
"id": 1
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"msg": "success",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
###### 2 接口业务逻辑
|
||
|
||
###### 核心流程
|
||
|
||
- 根据主键执行逻辑删除;删除后轨迹同步回退 **`Oms:TrackService`** 或跳过(视 appsettings 与 `Enabled` 而定)。
|
||
|
||
###### 校验与异常
|
||
|
||
- 校验 `id` 必填且存在,不存在时返回 **「配置不存在」**。
|
||
|
||
----------------------------------------------------------------------------------------
|
||
|
||
----------------------------------------------------------------------------------------
|
||
|
||
## 9 需求迭代与变更记录
|
||
|
||
### 9.1 需求迭代目录(按日期管理)
|
||
|
||
> 迭代编号建议使用 `YYYYMMDD-序号`,按日期倒序维护;不同功能迭代可使用 `-01`、`-02` 递增。
|
||
|
||
| 迭代日期 | 迭代编号 | 迭代功能 | 变更模块 | 备注 |
|
||
| --- | --- | --- | --- | --- |
|
||
| 2026-07-04 | 20260704-01 | 创建问题件 / 创建理赔件 | 订单 §4.2.1、§4.2.9、§4.4.1.13~§4.4.1.16;渠道 §5.3.0.7;附录 §10.3.6~§10.3.8 | 问题模板消费;`t_order_exception_event`;权限 `Order.Problematic.Create` 等 |
|
||
| 2026-06-27 | 20260627-02 | 规则快照下游元数据 + 订单绑定 | 订单 §4.2.1、§4.3.4.2、§4.4.1.11~§4.4.1.12;渠道 §5.3.0.2 | `Providers` enrich;`t_order` 四字段;轨迹/下游预报优先读订单绑定 |
|
||
| 2026-06-27 | 20260627-01 | 系统其他配置(小包轨迹) | 系统主数据 §8.1、§8.2.7、§8.3、§8.4.1.16~§8.4.1.20 | `t_system_other_config` + `/system/other-config/*`;卡片页 `/system/otherConfig` |
|
||
| 2026-06-26 | 20260626-02 | 渠道模块反向对齐 oms0414-back | 渠道模块 §5.3.0、§5.2.6.2、§10.6.2 | 发布/价格 save/导入等 |
|
||
| 2026-06-26 | 20260626-01 | 反向对齐 oms0414-back 接口实现 | §1.5、§4.3.5、§4.4、§10.6、§9.7 | 快照 Live 降级、同步 batch-create、简化 upload 等 |
|
||
| 2026-06-24 | 20260624-03 | 查价规则与失败原因 | 订单模块 §4.4.2.1 | unit 公式、重量左开右闭、failures |
|
||
| 2026-06-26 | 20260626-01 | 移除收费时机 | 订单 §4.3.4、渠道卖价/买价 | 删除 ChargingTime;应收按 forecast/receive 计重触发 |
|
||
| 2026-06-24 | 20260624-02 | 小包创建与会话/字段约定落地 | 订单模块 §4.2.1、§4.4.3.1~§4.4.3.7 | 客户 Id 会话补全、国家二字码、报关方式枚举、可选运单号等 |
|
||
| 2026-06-24 | 20260624-01 | 订单接口客户数据隔离 | 订单模块 §4.4.0、§3.3.5 | `/admin/order/*` 增删改查强制 `CustomerId` 隔离 |
|
||
| 2026-04-30 | 20260430-03 | 获取渠道分组 | 渠道模块 | 之前漏写该接口 |
|
||
| — | — | **与客户、内部用户、认证、RBAC 相关的迭代** | — | **一律在 `docs/rbac系统设计-060801.md` 维护**;OMS 本篇仅记录业务域迭代 |
|
||
|
||
### 9.2 20260704-01 差异明细(创建问题件 / 创建理赔件)
|
||
|
||
#### 9.2.1 表结构(迁移脚本,待执行)
|
||
|
||
| 脚本 | 说明 |
|
||
| --- | --- |
|
||
| `sql/migrate_t_order_handle_status.sql` | **`HandleStatus`** 列(**已编写**;列表子 Tab 已依赖) |
|
||
| `sql/migrate_t_order_exception_fields.sql` | **`t_order`** 扩展:问题件/理赔件快照字段 + **`PreviousOrderStatus`** |
|
||
| `sql/migrate_t_order_exception_event.sql` | 新建 **`t_order_exception_event`**(§4.2.9) |
|
||
|
||
#### 9.2.2 新增接口
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
| --- | --- | --- |
|
||
| POST | `/order/mark-problematic` | §4.4.1.13 |
|
||
| POST | `/order/mark-claimed` | §4.4.1.14 |
|
||
| POST | `/order/handle-status/update` | §4.4.1.15 |
|
||
|
||
#### 9.2.3 权限码(RBAC 菜单待配置)
|
||
|
||
| 权限码 | 说明 |
|
||
| --- | --- |
|
||
| `Order.Problematic.Create` | 创建问题件 |
|
||
| `Order.Claim.Create` | 创建理赔件 |
|
||
| `Order.HandleStatus.Update` | 标记已处理 |
|
||
|
||
#### 9.2.4 实现分期
|
||
|
||
| 期次 | 范围 | 状态 |
|
||
| --- | --- | --- |
|
||
| **一期** | 表结构 + 三个 HTTP 接口 + `myOrders` 弹窗/按钮 + 列表字段回显 | **已落地** |
|
||
| **二期** | 占位符渲染、`NotifyMethod` 投递、自动触发消费问题模板 | **部分已落地**(见 §9.2.5) |
|
||
|
||
#### 9.2.5 二期实现说明(2026-07-06)
|
||
|
||
**占位符渲染**(`IssuePlaceholderRenderer`)
|
||
|
||
- 支持 `{serviceNo}`、`{clientNo}`、`{trackNo}`、`{productCode}`、`{channelCode}`、`{issueType}`、`{issueTypeName}`、`{issueTitle}` 及中文别名(如 `{运单号}`、`{客户单号}`)。
|
||
- 标记问题件时按 **单订单** 渲染标题/内容后写入 `t_order` 与 `t_order_exception_event`。
|
||
|
||
**通知投递**(`OrderIssueNotificationService`)
|
||
|
||
| `NotifyMethod` | 行为 |
|
||
| --- | --- |
|
||
| `inApp` | 写入 `t_notice`(`noticeType=message`),接收人为订单客户 `t_customer.UserId` + 自动触发 `notifier` 中的用户 Id |
|
||
| `email` | 使用 **当前操作人** 的 `t_system_batch_email_account` SMTP 配置,发送至客户 `Email` |
|
||
| `sms` / `feishu` / `wechat` | 记录日志并跳过(通道待对接) |
|
||
|
||
**自动触发运行时**(`AutoTriggerRuntimeService`)
|
||
|
||
| 挂钩点 | `TriggerTiming` 关键字示例 |
|
||
| --- | --- |
|
||
| 小包预报成功 | 下单、order、create、parcel、预报 |
|
||
| `/operation/inbound/create` | 入库、inbound、receive、收货 |
|
||
| `/operation/outbound/create` | 出库、outbound、ship、发货 |
|
||
|
||
- **问题件**:按 `triggerCondition` 解析 `issueType`(支持 JSON `{"issueType":"address",...}` 或纯 code),匹配 **问题模板** 后自动标记并通知。
|
||
- **拦截**:`IsIntercept=1`。
|
||
- **警告**:仅发站内信(不改编订单状态)。
|
||
|
||
### 9.3 20260627-02 差异明细(规则快照下游元数据 + 订单绑定)
|
||
|
||
#### 9.2.1 表结构差异
|
||
|
||
**`t_order`**(迁移 **`sql/migrate_t_order_downstream_integration.sql`**)
|
||
|
||
| 字段 | 说明 |
|
||
| --- | --- |
|
||
| `DownstreamSupplierCode` | itfx **`supplierCode`** |
|
||
| `DownstreamAppKey` | itfx AppKey |
|
||
| `DownstreamAppId` | itfx 应用 Id(Secret 不落库) |
|
||
| `ChannelForecastCode` | 渠道 **ForecastCode** / 轨迹 ApiCode |
|
||
|
||
#### 9.2.2 接口与契约差异
|
||
|
||
**规则发布(`POST /channel/sales-product/publish`,`operatorType=rules`)**
|
||
|
||
- `Content.Providers[]` 增加 **`channelCode`、`forecastCode`、`groupId`、`appId`、`apiServiceProviderId`、`appKey`、`supplierCode`**(**不含 `appSecret`**)。
|
||
|
||
**排货确认(`ConfirmServiceChannelAndPayableAsync`)**
|
||
|
||
- 除 **`ChannelCode`** 外,写入上述四字段至 **`t_order`**。
|
||
|
||
**`POST /order/track-upsert-small-bag`**
|
||
|
||
- 新增;手动同步轨迹 UpsertSmallBag;**ApiCode** 优先 **`ChannelForecastCode`**。
|
||
|
||
**「预报下游订单」(前端 itfx)**
|
||
|
||
- 非 OMS 后端路由;凭证 **`resolveOrderDownstreamCredentials`** 优先订单 **`downstream*`** 字段。
|
||
|
||
**列表/详情 `OrderOutput`**
|
||
|
||
- 响应增加 **`downstreamSupplierCode`、`downstreamAppKey`、`downstreamAppId`、`channelForecastCode`**。
|
||
|
||
---
|
||
|
||
### 9.4 20260624-03 差异明细(查价对接卖价最新版本算价)
|
||
|
||
#### 9.2.1 表结构差异
|
||
|
||
- 无
|
||
|
||
#### 9.2.2 接口与契约差异
|
||
|
||
**`POST /order/price-check`**
|
||
|
||
| 项 | 说明 |
|
||
| --- | --- |
|
||
| 匹配 | 仅按国家+邮编、运输方式、重量尺寸、货物属性匹配快照 |
|
||
| 重量区间 | 左开右闭 `(start,end]`;单值 `0.5` → `(0,0.5]` |
|
||
| 公式 | 字典编码 `unit` / `total` / `continueUnit` 等 |
|
||
| 响应 | `items` + `failures`(`step` + `reason`) |
|
||
|
||
---
|
||
|
||
### 9.5 20260624-02 差异明细(小包创建与会话/字段约定)
|
||
|
||
#### 9.3.1 表结构差异
|
||
|
||
- 无
|
||
|
||
#### 9.3.2 接口与契约差异
|
||
|
||
**`POST /order/parcel/create`、`POST /order/parcel/batch-create`**
|
||
|
||
| 项 | 原约定/易错点 | 现约定(与实现对齐) |
|
||
| --- | --- | --- |
|
||
| `customerId` | 请求体必填 | **可选**;客户登录由 `mapId → t_customer.Id` **会话补全**;不一致 → **「客户ID非法」** |
|
||
| `trackNo` / `serviceNo` | 文档未明确可选 | **可选**;`serviceNo` 空则服务端自动生成 |
|
||
| `receiver.countryCode` | 传 `t_country.Id`(数字) | 传 **ISO 二字码 string**(如 `CN`、`US`);兼容数字字符串 Id |
|
||
| `sender.countryCode` | 数字 Id | 同收件人,**可选** |
|
||
| `exportCustomsMode` | 易传 `9610`、字典 `1` | 须为 §4.2.1 六个 **code**;非法 → **「出口报关方式不合法」** |
|
||
| 收件人校验失败 | — | 缺 `recipientsName` / `countryCode` / `zipCode` / `streetAddress1` → **「收件人信息不完整」** |
|
||
|
||
**`POST /order/upload`**
|
||
|
||
- `t_order_task.CustomerId`、导入行 `CustomerId` **均取会话** `effectiveCustomerId`,不取模板表 `CustomerId`。
|
||
|
||
**`POST /order/list`、`POST /order/export`、`GET /order/task/list`、`GET /order/task/detail`**
|
||
|
||
- 客户门户 **按 `t_order.CustomerId` / `t_order_task.CustomerId` 隔离**(非 `CreateUserId`);跨客户单条访问返回 **「订单不存在」** / **「任务不存在」**。
|
||
|
||
**`GET /order/template/list`、`GET /order/template/download`**
|
||
|
||
- 客户仅见 `standard` + 自有 `custom` 模板;他客模板返回 **「模板不存在」**。
|
||
|
||
**响应字段说明**
|
||
|
||
- 列表/详情中 `sender.countryCode` / `receiver.countryCode` 仍为 **`t_country.Id`(int)**;与 **创建请求体 ISO 二字码** 区分使用。
|
||
|
||
---
|
||
|
||
### 9.6 20260624-01 差异明细(订单客户数据隔离)
|
||
|
||
#### 9.4.1 表结构差异
|
||
|
||
- 无(沿用 `t_order.CustomerId`、`t_order_task.CustomerId`、`t_order_template.CustomerId`)
|
||
|
||
#### 9.4.2 接口差异
|
||
|
||
**更新接口(订单模块全部 `/order/*`)**
|
||
|
||
- 统一增加 **§4.4.0** 约束:会话解析 `effectiveCustomerId`,列表强制过滤、单条/批量写操作归属校验;跨客户访问返回「订单不存在」或 403。
|
||
|
||
**新增文档章节**
|
||
|
||
- **§3.3.5** 订单模块客户数据隔离(公共规范)
|
||
- **§4.4.0** 客户数据隔离(强制,含实现检查清单)
|
||
|
||
---
|
||
|
||
### 9.7 20260427-03 差异明细(按表结构与接口)
|
||
|
||
> 对比范围:`oms表结构与接口设计20260427.md` vs `oms表结构与接口设计20260430.md`。**与认证、客户、内部用户、RBAC 相关的历史差异**不再在此归档,见 **`docs/rbac系统设计-060801.md`**。
|
||
|
||
#### 9.5.1 表结构差异
|
||
|
||
**新增表(0)**
|
||
|
||
- 无
|
||
|
||
**更新表(0)**
|
||
|
||
- 无
|
||
|
||
**删除表(0)**
|
||
|
||
- 无
|
||
|
||
#### 9.5.2 接口差异
|
||
|
||
**新增接口(1)**
|
||
|
||
- `GET | /channel/service-channel/group/list`(获取渠道分组列表)
|
||
|
||
**更新接口(0)**
|
||
|
||
- 无
|
||
|
||
**删除接口(0)**
|
||
|
||
- 无
|
||
|
||
---
|
||
|
||
### 9.8 20260626-01 差异明细(反向对齐 oms0414-back)
|
||
|
||
> 对照仓库:`C:\Users\wilso\Desktop\oms0414-back`。本文在保留原设计目标的前提下,用 **「当前实现」** 小节标注代码真实行为。
|
||
|
||
#### 9.6.1 新增文档章节
|
||
|
||
- **§1.5** 后端实现对照(技术栈、Service 映射、会话隔离、设计 vs 实现速查)
|
||
- **§4.3.5** 快照解析与算价(`ResolveSnapshotBindingAsync`、`ParcelBillingCalculator`)
|
||
- **§5.3.0** 渠道模块当前实现总览(销售产品、服务渠道、KPI、计重、自动触发、标签)
|
||
- **§10.6** 接口实现状态总表
|
||
|
||
#### 9.6.2 接口与行为差异(设计 → 代码)
|
||
|
||
| 接口 / 能力 | 原设计 / 文档描述 | 当前实现 |
|
||
| --- | --- | --- |
|
||
| 快照读取 | Redis + 内存为主 | **DB 发布记录** + **Live 实时组装**,版本 `LIVE-{id}` |
|
||
| `POST /order/parcel/create` | 应收 + 应付预报 | **仅应收**(`CalculateForecastReceivableFees`) |
|
||
| `POST /order/parcel/batch-create` | 异步 `taskId` | **同步** `batchNo` + `items[]`,逐条 `CreateParcelCoreAsync` |
|
||
| `POST /order/upload` | 等同批量创建全链路 | **简化落单** `pendingPayment`,无算价/子表 |
|
||
| `POST /order/pay` | 扣余额 + 写流水 | **已落地**:`pendingPayment` → 扣应收合计 → `orderPay` 流水 → `pendingShip` |
|
||
| `POST /order/fee/batch-recalculate` | 批量重算 | **无 HTTP 接口** |
|
||
| 入库 / 出库 | 触发 §4.3.4 分阶段算价 | **仅**作业记录表 |
|
||
| 管理员订单列表 | §4.4.0 客户门户 | **管理员不过滤** `CustomerId` |
|
||
| 渠道发布后 Redis 同步 | 写 Redis + 刷新计费内存 | **仅写 DB** 发布记录 |
|
||
| 自动触发规则 | 订单/仓储事件消费 | **仅 CRUD 落库**,无运行时引擎 |
|
||
| 服务渠道 publish/preview | 同销售产品 | **已落地** |
|
||
|
||
#### 9.6.4 渠道模块已落地要点(详见 §5.3.0)
|
||
|
||
- 销售产品 **`price/save`**、排货 **`allocation/save`** 支持 **`publish=true`** 连带发布
|
||
- 销售产品 **`publish/preview`** 预览快照;**`publish/rollback`** 从 `Content` 恢复价格树或规则+排货
|
||
- 服务渠道 **`group/delete`** 有关联渠道时拒绝删除
|
||
- KPI **`info/import`** 逐行导入,失败行记入 **`failures`**
|
||
- 发布版本号 **`yyyyMMddHHmmss`**,`operatorType` 仅 **`price` \| `rules`**
|
||
|
||
#### 9.6.3 已确认与实现对齐(补充到正文)
|
||
|
||
- **`requestId`** 幂等(`t_order.RequestId`)
|
||
- 查价 **`failures[].step`**:`product` / `snapshot` / `blacklist` / `weight` / `zone` / `price`
|
||
- 充值审核 **`CustomerBalanceMutator.CreditRechargeAsync`**(`Pending` 可审,**Balance 与 `t_customer_account_flow` 同事务**)
|
||
- 订单支付 **`CustomerBalanceMutator.DebitOrderPayAsync`**(见 **§4.4.1.3、§7.4**)
|
||
- 面单 **`labelUrl` / `printUrl`**:HTML 文件 `/uploads/prints/orders/`
|
||
|
||
---
|
||
|
||
## 10 附录
|
||
|
||
### 10.1 接口路径规范(kebab-case 汇总)
|
||
|
||
> **网关前缀**:订单模块生产 URL 为 **`https://api.nanafa.com/admin/order/...`**;下表「服务内路径」为 **`/order/...`**,与网关拼接规则:`{VITE_API_ADMIN_URL}` + `/order/{action}`。
|
||
|
||
| 模块 | 网关前缀(生产) | 服务内路径 | 示例 |
|
||
| --- | --- | --- | --- |
|
||
| 订单 | `/admin/order/` | `/order/` | list, detail, pay, pre-declare, cancel-intercept, delete, price-check, parcel/create, parcel/batch-create;**设计中**:fee/batch-recalculate, mark-problematic, mark-claimed, handle-status/update |
|
||
| 渠道-销售产品 | `/admin/channel/`(同订单网关) | `/channel/sales-product/` | list, detail, create, update, config/save, delete, price/list, price/save, allocation/get, publish/preview |
|
||
| 渠道-服务渠道 | `/admin/channel/` | `/channel/service-channel/` | list, detail, create, update, config/save, delete, group/list, price/save, publish/preview, publish/rollback |
|
||
| 渠道-计重 | `/admin/channel/` | `/channel/weight-rule/`、`/channel/weight-rounding/` | list, create, update, delete, info/list, info/add |
|
||
| 渠道-自动触发 | `/admin/channel/` | `/channel/auto-trigger/` | list, create, update, delete, toggleStatus |
|
||
| 渠道-问题模板 | `/admin/channel/` | `/channel/issue-template/` | list, detail, options, create, update, delete, toggleStatus |
|
||
| 渠道-分区(KpiArea) | `/admin/channel/` | `/channel/kpi-area/` | create, update, delete, list, info/*, info/import, info/import-template |
|
||
| 渠道-标签 | `/admin/channel/` | `/channel/label-template/save` | save |
|
||
| 操作 | `/admin/operation/` | `/operation/` | inbound/create, inbound/recent-list, outbound/create, print-label/print |
|
||
| 权限与客户域(非 OMS) | — | — | **`docs/rbac系统设计-060801.md`**(`/customer/*`、`/rbac/*` 等) |
|
||
| 充值 | `/admin/recharge/` | `/recharge/` | recharge/submit, recharge-record/list, approve, reject |
|
||
| 客户账户流水 | `/admin/customer/`(同 admin 网关) | `/customer/account-flow/` | **list**(分页查询资金流水,见 **§7.4**) |
|
||
| 系统 | `/admin/system/` | `/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 用户(按 `user.userid`) | **`ReceiverIds` / `ReceiverId` 均存 `user.userid`(INT)**,与 JWT Claim **`mapId`** 一致 |
|
||
|
||
**与 RBAC 登录主体的关系**(见 **`docs/rbac系统设计-060801.md`**):
|
||
|
||
- 内部用户、客户主账号、子客户登录后 JWT 均带 **`mapId` = 对应行的 `user.userid`**。
|
||
- 通知模块**当前实现**(`NoticeService`)**仅接受 `receiverType=user`**,创建/更新时校验 `ReceiverIds` 均在 RBAC `user` 表存在;**不支持** `receiverType=customer`。
|
||
- 若需按 `t_customer.Id` 广播给客户,须在实现层扩展 `receiverType` 与校验逻辑,并同步改 OpenAPI 与本文 **§8.2.4 / §8.2.5**。
|
||
|
||
**说明**:**客户分组、客户状态** 等枚举见 RBAC 文档 **`CustomerStatus` / `CustomerGroup`**(`docs/rbac系统设计-060801.md` §6)。
|
||
|
||
#### 10.3.6 问题类型(`IssueTypeCodes`)
|
||
|
||
| code | 中文 |
|
||
| --- | --- |
|
||
| address | 地址异常 |
|
||
| customs | 清关问题 |
|
||
| weight | 重量异常 |
|
||
| document | 单证不全 |
|
||
| cargo | 货物异常 |
|
||
| channel | 渠道异常 |
|
||
| payment | 费用异常 |
|
||
| other | 其他 |
|
||
|
||
#### 10.3.7 理赔类型(`ClaimTypeCodes`,待实现)
|
||
|
||
| code | 中文 |
|
||
| --- | --- |
|
||
| lost | 丢件 |
|
||
| damaged | 破损 |
|
||
| delay | 延误 |
|
||
| customs | 清关理赔 |
|
||
| fee | 费用争议 |
|
||
| other | 其他 |
|
||
|
||
#### 10.3.8 订单处理子状态(`OrderHandleStatusCodes`)
|
||
|
||
| code | 中文 | 适用 `OrderStatus` |
|
||
| --- | --- | --- |
|
||
| pending | 待处理 | `problematic`、`claimed` |
|
||
| processed | 已处理 | `problematic`、`claimed` |
|
||
|
||
**说明**:非问题件/理赔件订单 `HandleStatus` 应为 **NULL**;列表筛选 `handleStatus` 仅在 Tab 为 `problematic` / `claimed` 时传入。
|
||
|
||
### 10.6 接口实现状态总表(oms0414-back,2026-07-04)
|
||
|
||
> 状态:**已落地** = 有 `ApplicationService` 方法且与下表逻辑一致;**部分** = 已暴露 HTTP 但业务未达设计;**设计中** = 本文有契约、代码无接口。
|
||
|
||
#### 10.6.1 订单 `/order/*`
|
||
|
||
| 方法 | 路径 | 状态 | 实现类 / 备注 |
|
||
| --- | --- | --- | --- |
|
||
| POST | `/order/list` | 已落地 | `OrderService.GetPaginatedListAsync` — 客户过滤 `CustomerId` |
|
||
| GET | `/order/detail` | 已落地 | 含收发件人、申报、四行重量 |
|
||
| POST | `/order/pay` | 已落地 | 扣余额 + `t_customer_account_flow`;见 **§4.4.1.3** |
|
||
| POST | `/order/pre-declare` | 部分 | 纯状态置 `predeclared` |
|
||
| POST | `/order/intercept` | 已落地 | `IsIntercept=1` |
|
||
| POST | `/order/cancel-intercept` | 已落地 | `IsIntercept=0` |
|
||
| POST | `/order/delete` | 已落地 | 软删 + `deleted` |
|
||
| POST | `/order/print` | 已落地 | HTML 面单 URL |
|
||
| POST | `/order/export` | 已落地 | Excel,24h 过期 |
|
||
| POST | `/order/price-check` | 已落地 | 见 §4.3.5、§4.4.2.1 |
|
||
| GET | `/order/template/list` | 已落地 | 客户见 standard + 自有 custom |
|
||
| GET | `/order/template/download` | 已落地 | 动态生成 Excel 表头 |
|
||
| POST | `/order/upload` | 部分 | 同步简化导入,见 §4.4.3.5 |
|
||
| GET | `/order/task/list` | 已落地 | 任务列表,客户隔离 |
|
||
| GET | `/order/task/detail` | 已落地 | 含 `failList` |
|
||
| POST | `/order/parcel/create` | 部分 | 卖价预报应收;无应付;Live 快照 |
|
||
| POST | `/order/parcel/batch-create` | 部分 | 同步批处理,见 §4.4.3.2 |
|
||
| POST | `/order/fee/batch-recalculate` | 设计中 | §4.4.1.10 |
|
||
| POST | `/order/track-upsert-small-bag` | 已落地 | 手动轨迹 UpsertSmallBag;见 **§4.4.1.11** |
|
||
| POST | `/order/mark-problematic` | 设计中 | §4.4.1.13;批量标记问题件 + 问题模板 |
|
||
| POST | `/order/mark-claimed` | 设计中 | §4.4.1.14;批量创建理赔件 |
|
||
| POST | `/order/handle-status/update` | 设计中 | §4.4.1.15;待处理→已处理 |
|
||
| POST | `/order/debug/track-upsert-small-bag` | 已落地 | Swagger 联调轨迹 |
|
||
|
||
#### 10.6.2 渠道 `/channel/*`
|
||
|
||
> 明细见 **§5.3.0**。
|
||
|
||
| 前缀 | 状态 | Service / 备注 |
|
||
| --- | --- | --- |
|
||
| `/channel/sales-product/*` | 已落地 | `SalesProductService` — 18 个端点:CRUD、`config/save`、价格 CRUD + **`price/save`**、排货、发布列表/预览/发布/回滚 |
|
||
| `/channel/service-channel/*` | 已落地 | `ServiceChannelService` — CRUD、分组 CRUD、买价 CRUD + **`price/save`**、**`publish/preview`**、发布/回滚 |
|
||
| `/channel/kpi-area/*` | 已落地 | 10 个端点:主表 CRUD + 明细分页 CRUD + **Excel 导入/模板** |
|
||
| `/channel/weight-rule/*` | 已落地 | 8 个端点:主表 + info 子表 CRUD(POST 路由) |
|
||
| `/channel/weight-rounding/*` | 已落地 | 8 个端点:同上 |
|
||
| `/channel/auto-trigger/*` | 部分 | CRUD + toggleStatus **已落地**;**运行时触发未实现** |
|
||
| `/channel/label-template/save` | 已落地 | upsert,`Name+TemplateType` 唯一 |
|
||
|
||
**渠道发布与订单算价衔接**:发布快照写入 DB 后,订单 **`ResolveSnapshotBindingAsync`** 按 `PublishTime <= bindTime` 读取;**Redis 同步未实现**(§5.2.6.2)。
|
||
|
||
#### 10.6.3 操作 `/operation/*`
|
||
|
||
| 方法 | 路径 | 状态 | 备注 |
|
||
| --- | --- | --- | --- |
|
||
| POST | `/operation/inbound/create` | 部分 | 仅写 `t_inbound_record`,不联动订单算价 |
|
||
| GET | `/operation/inbound/recent-list` | 已落地 | |
|
||
| POST | `/operation/outbound/create` | 部分 | 仅写 `t_outbound_record` + 状态校验;不更新 `ship` 件重尺 / `pendingOnline` |
|
||
| GET | `/operation/outbound/recent-list` | 已落地 | |
|
||
| POST | `/operation/print-label/print` | 已落地 | 标签打印记录 |
|
||
| GET | `/operation/print-label/recent-list` | 已落地 | |
|
||
| GET | `/operation/print-label/template-list` | 已落地 | |
|
||
|
||
#### 10.6.4 充值 `/recharge/*`
|
||
|
||
| 方法 | 路径 | 状态 | 备注 |
|
||
| --- | --- | --- | --- |
|
||
| POST | `/recharge/recharge/submit` | 已落地 | multipart,最小 ¥0.01,流水 `RECHARGE{ts}{random}` |
|
||
| POST | `/recharge/recharge-record/approve` | 已落地 | 待审核 → 通过;**`CustomerBalanceMutator` + 流水 `recharge`** |
|
||
| POST | `/recharge/recharge-record/reject` | 已落地 | |
|
||
| GET | `/recharge/recharge-record/list` | 已落地 | 客户过滤;补齐 customerCode/phone |
|
||
| GET | `/recharge/export` | 已落地 | MiniExcel |
|
||
|
||
#### 10.6.4.1 客户账户流水 `/customer/account-flow/*`
|
||
|
||
| 方法 | 路径 | 状态 | 备注 |
|
||
| --- | --- | --- | --- |
|
||
| GET | `/customer/account-flow/list` | 已落地 | `CustomerAccountFlowService` — keyword/bizType/时间/customerId 分页;客户门户强制隔离 |
|
||
|
||
#### 10.6.5 系统 `/system/*`
|
||
|
||
| 方法 | 路径 | 状态 |
|
||
| --- | --- | --- |
|
||
| GET | `/system/country/list` | 已落地 |
|
||
| GET/POST | `/system/dict/*` | 已落地 — list/create/update/delete |
|
||
| GET/POST | `/system/warehouse/*` | 已落地 |
|
||
| GET/POST | `/system/notice/*` | 已落地 — 含 read-confirm |
|
||
|
||
#### 10.6.6 宿主 SimpleAdmin.Api(非 OMS.Application)
|
||
|
||
| 前缀 | 说明 |
|
||
| --- | --- |
|
||
| `/rbac/auth/*` | `AuthService` — 管理员/客户登录、微信登录、刷新、改密等 |
|
||
| `/wechat/*` | 公众号验签、扫码登录、OAuth 注册/绑定 |
|
||
|
||
客户 **`/customer/*`**、RBAC 管理 **`/rbac/user|role|menu|...`** 见 **`docs/rbac系统设计-060801.md`**。
|
||
|
||
---
|
||
|
||
*文档版本:v1.7 | 最后更新:2026-07-04 | 已补充 **§4.2.9** 订单异常事件表;**§4.4.1.13~§4.4.1.16** 创建问题件/理赔件与处理子状态;**§10.3.6~§10.3.8** 枚举;迭代 **20260704-01***
|
||
|
||
|