front/docs/订单批量处理与异步队列方案0707.md
zzh 5191d1981f feat: 仓储模块前端、菜单同步扩展及相关页面优化
新增仓储模块页面与 API、店铺授权/店铺订单页面及路由权限目录;
扩展菜单管理同步能力支持二级子菜单与三级按钮;优化公告、仓库地址、下单等页面。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-11 15:19:35 +08:00

665 lines
25 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

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

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

# 订单批量处理与异步队列方案
> **文档定位**:梳理 OMS 后端现有订单批量相关 API、执行模式与可复用资产,并给出统一异步队列改造建议。
> **对照仓库**:`C:\Users\wilso\Desktop\oms0414-back`(`SimpleAdmin.sln`)
> **前端对照**:`E:\oms0414\src\views\order\myOrders\index.vue`
> **关联文档**:`docs/oms系统设计-050801.md`(订单章)、`docs/rbac系统设计-060801.md`(权限)
> **编写日期**:2026-07-07
> **技术决策**:**选用 RabbitMQ** 作为订单批量异步队列方案(v2.0)
---
## 1 导读
### 1.1 这份文档解决什么问题?
1. **盘点**当前后端所有「批量处理订单」相关 HTTP 接口(含原生批量与前端循环伪批量)。
2. **说明**各接口入参/出参、Service 实现位置、同步/异步执行方式。
3. **指出**现有最接近队列的组件(`t_order_task`、`SmallBagTrackOrderNotifier`)及其局限。
4. **建议**按业务类型划分异步队列、统一批量 Job API 草案与落地优先级。
5. **定义**基于 **RabbitMQ** 的交换机/队列拓扑、消息契约、Consumer 与 Fake 框架集成方式(**已定稿**)。
### 1.2 技术决策
| 项 | 决策 |
| --- | --- |
| 消息中间件 | **RabbitMQ** |
| .NET 接入 | 优先复用 Fake 生态 **`Fake.RabbitMQ`**(连接池/通道池);订单 Job 使用 **Work Queue 模式** 自建 Publisher/Consumer |
| 与 `Fake.EventBus.RabbitMQ` 关系 | 事件总线用于**跨模块集成事件**(如 Job 完成通知);**订单子任务投递不走 EventBus**,走专用 Work Queue |
| 状态持久化 | MySQL **`t_order_batch_job` / `t_order_batch_job_item`** 为权威进度;MQ 仅负责调度与重试 |
| 宿主部署 | `SimpleAdmin.Api` 进程内注册 Consumer(`IHostedService`);后期可拆独立 Worker 项目 |
### 1.3 现状结论(摘要)
| 维度 | 现状 |
| --- | --- |
| API 暴露 | `OrderService`(partial class)上 `[HttpPost]` / `[HttpGet]`,Fake 动态 API,**无手写 Controller** |
| 原生批量 API | 异常件 3 个 + 小包 `batch-create` + Excel 导入 + 条件导出,共 **5 类** |
| 伪批量 | My Orders 页 **8 个单票 API**,前端 `for` 循环逐条调用 |
| 队列基础设施 | **规划 RabbitMQ**(项目基线已约定 MQ,见 `oms0414-back/开发复刻说明.md`);当前代码**尚未引用** `Fake.RabbitMQ` |
| 轻量异步 | `SmallBagTrackOrderNotifier` 使用 `Task.Run`(无持久化、无重试) |
| 任务表 | `t_order_task` 记录 Excel 导入进度,但处理仍在 **HTTP 请求内同步完成** |
| 权限 | 前端 `Order.MyOrders.*`;**后端 API 未做细粒度 PermissionCode 校验** |
---
## 2 接口总览
```mermaid
flowchart LR
subgraph api [SimpleAdmin.Api]
HTTP["/order/batch/jobs"]
OS[OrderService]
end
subgraph db [MySQL]
JOB[t_order_batch_job]
ITEM[t_order_batch_job_item]
end
subgraph rmq [RabbitMQ]
EX["exchange: oms.order.batch"]
QD["queue: order.batch.integration"]
QA["queue: order.batch.account"]
QB["queue: order.batch.state"]
QC["queue: order.batch.exception"]
QE["queue: order.batch.document"]
QF["queue: order.batch.create"]
DLX["DLX: oms.order.batch.dlx"]
end
subgraph consumers [Consumers IHostedService]
CD[IntegrationConsumer]
CA[AccountConsumer]
CB[StateConsumer]
CC[ExceptionConsumer]
CE[DocumentConsumer]
CF[CreateConsumer]
end
HTTP --> JOB
HTTP --> EX
EX --> QD & QA & QB & QC & QE & QF
QD & QA & QB & QC & QE & QF --> consumers
consumers --> ITEM
consumers --> OS
QD & QA & QB & QC & QE & QF -.失败.-> DLX
```
### 2.1 分类一览(改造后目标)
```mermaid
flowchart TB
subgraph native [原生批量 API]
EP["/order/mark-problematic<br/>/order/mark-claimed<br/>/order/handle-status/update"]
PCB["/order/parcel/batch-create"]
UP["/order/upload + task/*"]
EX["/order/export"]
end
subgraph pseudo [伪批量 - 前端循环单票 API]
PAY["/order/pay"]
PD["/order/pre-declare"]
INT["/order/intercept / cancel-intercept"]
DEL["/order/delete"]
PRT["/order/print"]
TRK["/order/track-upsert-small-bag"]
DS["/order/downstream-forward-create-one"]
end
subgraph svc [OrderService 同步处理]
OS[OrderService partial]
end
subgraph async_hint [现有轻量异步]
OT[t_order_task 仅记录]
STN[SmallBagTrackOrderNotifier Task.Run]
end
native --> OS
pseudo --> OS
UP --> OT
PCB --> STN
```
### 2.2 原生批量 API 清单
| 路由 | 方法 | Service 方法 | 文件 | 批量语义 | 上限 | 执行模式 |
| --- | --- | --- | --- | --- | --- | --- |
| `/order/mark-problematic` | POST | `MarkProblematicAsync` | `OrderService.Exception.cs` | `List<int> OrderIds` | 200 | 同步 foreach,部分成功 |
| `/order/mark-claimed` | POST | `MarkClaimedAsync` | 同上 | `List<int> OrderIds` | 200 | 同上 |
| `/order/handle-status/update` | POST | `UpdateHandleStatusAsync` | 同上 | `List<int> OrderIds` | 200 | 同上(仅 `processed`) |
| `/order/parcel/batch-create` | POST | `BatchCreateParcelAsync` | `OrderService.cs` | `List<OrderParcelCreateInput>` | 2000 | 同步 for,逐条明细 |
| `/order/upload` | POST | `UploadAsync` → `ImportOrdersAsync` | `OrderService.Helpers.cs` | Excel 多行 | — | 同步逐行 + 写 `OrderTask` |
| `/order/task/list` | GET | `GetTaskListAsync` | `OrderService.Helpers.cs` | 查询导入任务 | — | 只读 |
| `/order/task/detail` | GET | `GetTaskDetailAsync` | 同上 | 含失败行 | — | 只读 |
| `/order/export` | POST | `ExportAsync` | `OrderService.cs` | 筛选条件全量 | — | 同步生成 xlsx |
### 2.3 伪批量 API 清单(单票入参,前端循环)
| 路由 | 方法 | Service 方法 | 文件 | 业务特点 | 前端权限码(参考) |
| --- | --- | --- | --- | --- | --- |
| `/order/pay` | POST | `PayAsync` | `OrderService.cs` | **扣客户余额**,需事务/幂等 | `Order.MyOrders.Pay` |
| `/order/pre-declare` | POST | `PreDeclareAsync` | 同上 | 改订单状态 | `Order.MyOrders.PreDeclare` |
| `/order/intercept` | POST | `InterceptAsync` | 同上 | `IsIntercept=1` | `Order.MyOrders.Intercept` |
| `/order/cancel-intercept` | POST | `CancelInterceptAsync` | 同上 | `IsIntercept=0` | `Order.MyOrders.CancelIntercept` |
| `/order/delete` | POST | `DeleteAsync` | 同上 | 软删 + 物理删 | `Order.MyOrders.Delete` |
| `/order/print` | POST | `PrintAsync` | `OrderService.Helpers.cs` | 生成打印 HTML | `Order.MyOrders.Print` / `BatchPrint` |
| `/order/track-upsert-small-bag` | POST | `TrackUpsertSmallBagAsync` | `OrderService.ParcelBilling.cs` | HTTP 调轨迹服务 | `Order.MyOrders.CreateTrack` |
| `/order/downstream-forward-create-one` | POST | `DownstreamForwardCreateOneAsync` | `OrderService.DownstreamForward.cs` | HTTP 调下游 itfx | `Order.MyOrders.ForwardDownstream` |
**公共入参** `OrderActionInput`:
```csharp
public class OrderActionInput
{
[Required] public int Id { get; set; }
[StringLength(64)] public string? RequestId { get; set; }
}
```
**辅助只读**:
| 路由 | 方法 | 说明 |
| --- | --- | --- |
| `/order/downstream-create-one-body` | GET | 预览下游请求体,不实际转发 |
### 2.4 单票编辑类 API(详情抽屉,非列表批量)
| 路由 | 方法 | Service 方法 | 文件 |
| --- | --- | --- | --- |
| `/order/update` | POST | `UpdateOrderDetailAsync` | `OrderService.OrderEdit.cs` |
| `/order/weight/update` | POST | `UpdateOrderWeightsAsync` | 同上 |
| `/order/fee/recalculate-receivable` | POST | `RecalculateReceivableFeesAsync` | 同上 |
| `/order/fee/recalculate-payable` | POST | `RecalculatePayableFeesAsync` | 同上 |
| `/order/fee/add` | POST | `AddOrderFeeAsync` | 同上 |
| `/order/parcel/create` | POST | `CreateParcelAsync` | `OrderService.cs` / `ParcelBilling.cs` |
### 2.5 非 OrderIds 的「批量数据处理」
| 路由 | 方法 | 说明 |
| --- | --- | --- |
| `/order/price-check` | POST | 多产品 × 多仓库运费试算,同步嵌套循环 |
| `/order/list` | POST | 分页列表 |
| `/order/status-counts` | GET | 状态 Tab 计数 |
---
## 3 DTO 与出参模型
### 3.1 异常件批量(推荐复用为队列 Job 汇总出参)
**文件**:`Oms.Application/Dtos/Orders/OrderExceptionDtos.cs`
```csharp
// 统一批量出参
public class OrderBatchActionResultOutput
{
public int SuccessCount { get; set; }
public List<OrderBatchActionFailureOutput> Failures { get; set; } = [];
}
public class OrderBatchActionFailureOutput
{
public int OrderId { get; set; }
public string? ServiceNo { get; set; }
public string Reason { get; set; } = string.Empty;
}
// 问题件
public class OrderMarkProblematicInput
{
public List<int> OrderIds { get; set; } = [];
public int? IssueTemplateId { get; set; }
public string? IssueType { get; set; }
public string? Title { get; set; }
public string? Content { get; set; }
public string? Remark { get; set; }
}
// 理赔件
public class OrderMarkClaimedInput
{
public List<int> OrderIds { get; set; } = [];
public string? ClaimType { get; set; }
public decimal? ClaimAmount { get; set; }
public string? ClaimCurrency { get; set; }
public string? ClaimReason { get; set; }
public string? Remark { get; set; }
}
// 标记已处理
public class OrderHandleStatusUpdateInput
{
public List<int> OrderIds { get; set; } = [];
public string? HandleStatus { get; set; }
public string? Remark { get; set; }
}
```
**内部约束**:`MaxExceptionBatchSize = 200`(`OrderService.Exception.cs`)。
### 3.2 小包批量创建
**文件**:`Oms.Application/Dtos/Orders/OrderParcelBatchCreateInput.cs` 等
- **入参**:`OrderParcelBatchCreateInput` 继承 `List<OrderParcelCreateInput>`,单条结构与 `/order/parcel/create` 一致。
- **出参**:`OrderParcelBatchCreateOutput { BatchNo, SuccessCount, FailCount, Items[] }`
- **上限**:`ParcelBatchMaxCount = 2000`(`OrderService.ParcelBilling.cs`)
### 3.3 Excel 导入
| DTO | 字段 |
| --- | --- |
| `OrderUploadInput` | `File` (IFormFile), `TemplateId` |
| `OrderUploadOutput` | `TaskId` |
| `OrderTaskListInput` | 分页查询参数 |
| `OrderTaskOutput` / `OrderTaskDetailOutput` | 任务汇总 + 失败明细 `FailList` |
### 3.4 条件导出
| DTO | 字段 |
| --- | --- |
| `OrderExportInput` | `status`, `keyword`, `createTimeStart`, `createTimeEnd` |
| `OrderExportOutput` | `downloadUrl`, `expireAt`(24h 过期) |
---
## 4 现有执行模式详解
### 4.1 异常件批量(已实现部分成功)
**调用链**:
```
MarkProblematicAsync / MarkClaimedAsync / UpdateHandleStatusAsync
→ NormalizeOrderIds(去重、非空、≤200)
→ ExecuteProblematicBatchAsync / ExecuteExceptionBatchAsync
→ 逐条 try/catch,成功 SuccessCount++,失败写入 Failures
```
**问题件单条逻辑**:`OrderExceptionWorkflowService.TryMarkProblematicAsync`(`Issues/OrderExceptionWorkflowService.cs`),含通知(`OrderIssueNotificationService`)。
### 4.2 小包批量创建
```
BatchCreateParcelAsync
→ 生成 BatchNo
→ for each OrderParcelCreateInput
→ CreateParcelCoreAsync(预报、计费、落库)
→ 成功则 EnqueueSmallBagTrackUpsertAsync(轨迹)
→ 返回逐条 Items 明细
```
### 4.3 Excel 导入(有任务表,非真异步)
```
UploadAsync → ImportOrdersAsync
→ Insert OrderTask(Status=processing)
→ foreach Excel 行
→ CreateImportOrderAsync 或记录 OrderTaskFail
→ 更新 task Quantity / SuccessCount / FailCount / Status=completed
→ 返回 { taskId }
```
**局限**:整个导入阻塞当前 HTTP 请求;`Status=processing` 仅为记录,无独立 Consumer。
### 4.4 轨迹轻量异步(现有唯一 fire-and-forget)
**文件**:`Oms.Application/Integrations/SmallBagTrack/SmallBagTrackOrderNotifier.cs`
```csharp
public void Enqueue(SmallBagTrackUpsertRequest request)
{
_ = Task.Run(async () =>
{
using var scope = scopeFactory.CreateScope();
var client = scope.ServiceProvider.GetRequiredService<ISmallBagTrackOrderClient>();
await client.UpsertAsync(request);
});
}
```
**性质**:单票、内存队列、无持久化、无重试、进程重启即丢失。
### 4.5 前端伪批量行为
**文件**:`src/views/order/myOrders/index.vue`
- 勾选多行后,对 `pay` / `pre-declare` / `intercept` 等 **逐条 POST** `OrderActionInput`。
- 支付等操作:**任一失败即中断**,无部分成功汇总(与异常件 API 不同)。
- 批量打印 / 轨迹 / 下游:循环调用后前端汇总结果弹窗。
---
## 5 现有基础设施盘点
| 组件 | 是否存在 | 说明 |
| --- | --- | --- |
| RabbitMQ Work Queue | ❌ 未接入 | **本期建设** |
| `Fake.RabbitMQ` | ❌ 未引用 | 连接池/通道池,见 `SPEC.md` §9.3 |
| `Fake.EventBus.RabbitMQ` | ❌ 未引用 | 可选,用于 Job 完成等集成事件 |
| 订单专用 `IHostedService` | ❌ | 无 |
| `BackgroundService` | ✅ 1 个 | 仅 `XhsScheduledPublishWorker`(小红书定时发布) |
| `Task.Run` | ✅ | 轨迹 UpsertSmallBag、导出文件清理等 |
| `t_order_task` | ✅ | 导入任务记录 + `t_order_task_fail` 失败行 |
| `SmallBagTrackOrderNotifier` | ✅ | 内存 fire-and-forget |
### 5.1 OrderTask 实体(可演进为通用批量 Job 表)
**表**:`t_order_task`
**实体**:`Oms.Domain/OrdersAggregate/Entities/OrderTask.cs`
| 字段 | 说明 |
| --- | --- |
| `TaskName` | 如 `批量下单_yyyyMMddHHmmss` |
| `Quantity` | 总条数 |
| `SuccessCount` / `FailCount` | 成功/失败计数 |
| `Status` | `processing` → `completed` |
| `TemplateId` | Excel 模板 Id |
| `CustomerId` | 客户归属 |
**演进建议**:扩展 `Action`、`PayloadJson`、`StartedAt`、`FinishedAt`,或新建 `t_order_batch_job` 与之并存。
---
## 6 RabbitMQ 队列分组(与业务 Action 映射)
按 **业务风险 + 耗时 + 外部依赖** 划分 **6 条业务队列** + **1 条死信队列**:
| 队列组 | RabbitMQ Queue | Routing Key | 包含 Action | 并发建议 | Prefetch |
| --- | --- | --- | --- | --- | --- |
| **D. 外部集成** | `oms.order.batch.integration` | `integration.*` | `track-upsert-small-bag`, `downstream-forward-create-one` | 中(限流防打爆下游) | 5 |
| **F. 订单创建** | `oms.order.batch.create` | `create.*` | `parcel-create`, `import-row` | 中 | 10 |
| **A. 账务** | `oms.order.batch.account` | `account.*` | `pay` | **低**(按 CustomerId 串行) | 1 |
| **B. 状态变更** | `oms.order.batch.state` | `state.*` | `pre-declare`, `intercept`, `cancel-intercept`, `delete`, `handle-status-update` | 高 | 20 |
| **C. 异常工作流** | `oms.order.batch.exception` | `exception.*` | `mark-problematic`, `mark-claimed` | 中 | 10 |
| **E. 文档生成** | `oms.order.batch.document` | `document.*` | `print`, `export` | 低 | 3 |
| **死信** | `oms.order.batch.dlx` | — | 超过最大重试的子任务 | — | — |
**Exchange**:`oms.order.batch`(类型 **topic**,持久化 `durable=true`)。
### 6.1 落地顺序建议
1. **D(外部集成)** — 轨迹、下游预报最阻塞 HTTP,且已有 `SmallBagTrackOrderNotifier` 可替换为 Publisher。
2. **F(创建/导入)** — `OrderTask` 表与失败明细已就绪,补 Worker 即可真异步。
3. **A(支付)** — 资金类须严格幂等与对账。
4. **B / C / E** — 按产品优先级迭代。
---
## 7 统一批量 Job API 草案
### 7.1 路由设计
| 路由 | 方法 | 说明 |
| --- | --- | --- |
| `/order/batch/jobs` | POST | 创建批量任务,写 DB 并 Publish 到 RabbitMQ,立即返回 `jobId` |
| `/order/batch/jobs/{id}` | GET | 查询进度与汇总结果 |
| `/order/batch/jobs/{id}/items` | GET | 逐条明细(成功/失败/进行中) |
| `/order/batch/jobs/{id}/cancel` | POST | 取消未执行子任务(可选) |
| `/order/batch/jobs/{id}/retry-failed` | POST | 将 failed 子任务重新 Publish(可选) |
### 7.2 入参草案
```csharp
public class OrderBatchJobCreateInput
{
/// <summary>
/// pay | pre-declare | intercept | cancel-intercept | delete |
/// print | track-upsert-small-bag | downstream-forward-create-one |
/// mark-problematic | mark-claimed | handle-status-update | export | ...
/// </summary>
public string Action { get; set; } = string.Empty;
public List<int> OrderIds { get; set; } = [];
public JsonElement? Payload { get; set; }
public string? RequestId { get; set; }
}
```
### 7.3 出参草案
```csharp
public class OrderBatchJobOutput
{
public long JobId { get; set; }
public string Action { get; set; } = string.Empty;
public string Status { get; set; } = string.Empty;
public int Total { get; set; }
public int SuccessCount { get; set; }
public int FailCount { get; set; }
public DateTime? StartedAt { get; set; }
public DateTime? FinishedAt { get; set; }
}
```
单条明细复用 `OrderBatchActionFailureOutput`,扩展 `Status`:`pending` / `running` / `success` / `failed`。
---
## 8 RabbitMQ 实施方案(已定稿)
### 8.1 为何选 RabbitMQ
| 能力 | 说明 |
| --- | --- |
| 持久化队列 | 进程重启不丢任务(配合 `delivery_mode=2`) |
| 死信 + 延迟重试 | 业务失败与系统失败分离治理 |
| 多队列隔离 | 支付、外部 HTTP、导入互不阻塞 |
| 水平扩展 | 多 API/Worker 实例共享 Consumer |
| 与项目基线一致 | `开发复刻说明.md` 已约定 RabbitMQ;Fake 提供 `Fake.RabbitMQ` 连接池 |
**不采用 Hangfire / DB 轮询的原因**:订单批量含大量外部 HTTP 与长耗时导入,需要独立队列削峰与跨实例消费;DB 轮询难以优雅做按客户串行与 DLX。
### 8.2 NuGet 与模块依赖
在 `SimpleAdmin.Api` / `Oms.Application` 增加:
```xml
<PackageReference Include="Fake.RabbitMQ" Version="$(FakePackageVersion)" />
<!-- 可选:Job 完成集成事件 -->
<PackageReference Include="Fake.EventBus.RabbitMQ" Version="$(FakePackageVersion)" />
```
模块依赖(`OmsApplicationModule` 或新建 `OmsRabbitMqModule`):
```csharp
[DependsOn(typeof(FakeRabbitMqModule))]
public class OmsRabbitMqModule : FakeModule { }
```
### 8.3 配置(appsettings / Consul)
```json
{
"RabbitMQ": {
"Connections": {
"Default": {
"HostName": "rabbitmq",
"Port": 5672,
"UserName": "oms",
"Password": "***",
"VirtualHost": "/oms"
}
}
}
}
```
队列声明在应用启动时由 `OrderBatchQueueInitializer`(`IHostedService`)执行:创建 Exchange、6 条业务 Queue、DLX、绑定关系。
### 8.4 消息契约
**队列消息体** `OrderBatchJobMessage`(JSON,Newtonsoft 驼峰):
```csharp
public class OrderBatchJobMessage
{
public long JobId { get; set; }
public long JobItemId { get; set; }
public string Action { get; set; } = string.Empty; // pay, delete, track-upsert-small-bag, ...
public int OrderId { get; set; }
public int? CustomerId { get; set; } // A 组串行键
public string? PayloadJson { get; set; } // Action 专用参数
public int OperatorUserId { get; set; }
public string MessageId { get; set; } = string.Empty; // 幂等键
public int RetryCount { get; set; }
}
```
**发布流程**:
```
POST /order/batch/jobs
→ 写 t_order_batch_job + items(status=pending)
→ 按 Action 映射 routingKey,逐条 Publish 到 oms.order.batch
→ 立即返回 { jobId }
```
**消费流程**:
```
Consumer 收到消息
→ 查 item 是否已成功(幂等跳过)
→ item.status=running
→ IOrderBatchActionExecutor.ExecuteAsync(message)
→ 成功:item.status=success,更新 job 计数
→ 业务失败:item.status=failed + reason,ACK
→ 系统失败:NACK requeue;RetryCount>=3 → DLX
→ job 全部终态后 status=completed
```
### 8.5 与现有组件的替换关系
| 现有 | 改造后 |
| --- | --- |
| `SmallBagTrackOrderNotifier`(`Task.Run`) | Publish 到 `oms.order.batch.integration`,routingKey=`integration.track-upsert` |
| `ImportOrdersAsync` 同步 foreach | 上传存文件/OSS → 写 job → 每行一条消息到 `oms.order.batch.create` |
| 前端 My Orders 循环 `pay` 等 | 统一 `POST /order/batch/jobs`,轮询 `GET /order/batch/jobs/{id}` |
| 异常件 API `mark-problematic` 等 | **兼容期保留同步 API**;新增 `async=true` 或独立 Job API 走 MQ |
### 8.6 重试与死信策略
| 场景 | 策略 |
| --- | --- |
| 下游 HTTP 超时/5xx | NACK + requeue,指数退避(`x-message-ttl` + DLX 延迟队列,或 Consumer 内 `Task.Delay` 后重发) |
| 业务校验失败(已支付、状态不允许) | 记 failed,**ACK 不重试** |
| 消息重复消费 | `MessageId` + DB 唯一约束,已 success 则直接 ACK |
| DLX 堆积 | 告警 + 管理端「重新投递」接口 |
建议队列参数:
```
x-dead-letter-exchange: oms.order.batch.dlx
x-dead-letter-routing-key: dead
```
### 8.7 部署拓扑
```mermaid
flowchart TB
subgraph prod [生产环境]
API1[SimpleAdmin.Api 实例 1]
API2[SimpleAdmin.Api 实例 2]
RMQ[RabbitMQ Cluster]
MySQL[(MySQL)]
end
API1 & API2 -->|Publish/Consume| RMQ
API1 & API2 --> MySQL
FE[oms0414 前端] -->|创建 Job / 轮询进度| API1
```
- **第一期**:Consumer 与 API 同进程(`IHostedService`),减少运维复杂度。
- **第二期**:拆 `Oms.OrderWorker` 独立控制台项目,仅引用 `Oms.Application` + Consumers。
### 8.8 本地开发
`docker-compose` 增加 RabbitMQ 服务(管理台 15672):
```yaml
rabbitmq:
image: rabbitmq:3.13-management
ports:
- "5672:5672"
- "15672:15672"
environment:
RABBITMQ_DEFAULT_USER: oms
RABBITMQ_DEFAULT_PASS: oms123
RABBITMQ_DEFAULT_VHOST: /oms
```
### 8.9 Consumer 执行原则
1. **复用现有 Service**:`IOrderBatchActionExecutor` 按 `action` 分发,内部调用抽离后的单条逻辑。
2. **幂等**:`MessageId = {jobId}:{orderId}:{action}`;DB 唯一键 `(JobId, OrderId, Action)`;支付额外校验 `IsPaid`。
3. **部分成功**:业务失败 ACK 并写 `failed`;系统失败 NACK requeue,超次进 DLX。
4. **并发**:A 组 `prefetch=1` + `customerId` 串行;D 组全局限流。
5. **可观测**:日志带 `JobId/OrderId/Action`;可选 `OrderBatchJobCompletedEto` 走 `Fake.EventBus.RabbitMQ`。
---
## 9 关键文件索引(规划新增)
| 文件(规划路径) | 职责 |
| --- | --- |
| `Oms.Application/Messaging/OrderBatchJobMessage.cs` | MQ 消息契约 |
| `Oms.Application/Messaging/IOrderBatchJobPublisher.cs` | Publish 抽象 |
| `Oms.Application/Messaging/OrderBatchQueueInitializer.cs` | 启动声明 Exchange/Queue |
| `Oms.Application/Workers/OrderBatch/*Consumer.cs` | 6 组 RabbitMQ Consumer |
| `Oms.Application/Services/Orders/Batch/IOrderBatchActionExecutor.cs` | Action 执行分发 |
| `Oms.Application/Services/Orders/OrderService.cs` | pay/delete/export/batch-create/upload 等主 API |
| `Oms.Application/Services/Orders/OrderService.Exception.cs` | 原生 `OrderIds` 批量异常件 API |
| `Oms.Application/Services/Orders/OrderService.ParcelBilling.cs` | 小包创建、轨迹同步 |
| `Oms.Application/Services/Orders/OrderService.DownstreamForward.cs` | 下游预报转发 |
| `Oms.Application/Services/Orders/OrderService.Helpers.cs` | Excel 导入、打印、任务查询 |
| `Oms.Application/Integrations/SmallBagTrack/SmallBagTrackOrderNotifier.cs` | 待替换为 MQ Publisher |
| `Oms.Domain/OrdersAggregate/Entities/OrderBatchJob.cs` | Job 实体(规划) |
| `src/views/order/myOrders/index.vue` | 前端伪批量循环(待改 Job API) |
---
## 10 与主设计文档的关系
- 订单状态机、费用、模板字段详见 **`docs/oms系统设计-050801.md` 第 4 章**。
- 客户隔离、`CustomerId` 解析见 **OMS 设计 §1.4** 与 **RBAC 设计文档**。
- Fake 框架 RabbitMQ 能力见 **`oms0414-back/SPEC.md` §9.3**(`Fake.RabbitMQ`、`Fake.EventBus.RabbitMQ`)。
---
## 11 后续工作清单(RabbitMQ 实施)
### 阶段一:基础设施(D 组)
- [ ] `docker-compose` / 生产环境部署 RabbitMQ(vhost `/oms`)
- [ ] 引用 `Fake.RabbitMQ`,配置 `RabbitMQ:Connections:Default`
- [ ] 新建 `OmsRabbitMqModule`、`OrderBatchQueueInitializer`
- [ ] 新建 `t_order_batch_job` / `t_order_batch_job_item` 表
- [ ] 实现 `IOrderBatchJobPublisher` + `OrderBatchJobMessage`
- [ ] 实现 `IntegrationBatchConsumer`(轨迹、下游预报)
- [ ] 替换 `SmallBagTrackOrderNotifier` 为 MQ Publish
### 阶段二:创建与导入(F 组)
- [ ] `/order/upload` 改为上传落库 + 按行入队 `oms.order.batch.create`
- [ ] `parcel/batch-create` 改为创建 Job 后异步消费
- [ ] 复用/扩展 `OrderTask` 与 `OrderBatchJob` 关系(如需兼容旧前端)
### 阶段三:统一 Job API + 前端
- [ ] 新增 `/order/batch/jobs` 系列 API
- [ ] 前端 My Orders 批量操作改 Job API + 进度轮询
- [ ] 实现 B/C/E/A 组 Consumer 与 `IOrderBatchActionExecutor`
### 阶段四:治理与测试
- [ ] DLX 监控告警、`retry-failed` 管理接口
- [ ] 补充 `SimpleAdmin.Api.IntegrationTests`(Publish → Consume → DB 状态)
- [ ] 可选:拆 `Oms.OrderWorker` 独立进程
---
*文档版本:v2.0 | 技术决策:RabbitMQ | 2026-07-07*