上传源代码版本
This commit is contained in:
399
.trae/documents/caller-header-implementation-plan.md
Normal file
399
.trae/documents/caller-header-implementation-plan.md
Normal file
@@ -0,0 +1,399 @@
|
||||
# Caller Header 接收改造实施计划
|
||||
|
||||
## 概述
|
||||
|
||||
根据《后端Caller字段接收清单.md》的要求,需要在 11 个 API 接口中从 HTTP Header 读取 `Caller` 字段(请求人姓名)。
|
||||
|
||||
**核心业务含义**:`Caller` 记录了该次请求是由谁发起的,对于创建类接口需要**写入系统的创建人字段**,对于所有接口都需要**通过 Serilog 记录审计日志**。
|
||||
|
||||
**扩展性设计**:采用 Middleware 统一提取 + RequestTrackingContext 建模的方案。后续新增 `Device-Id`、`Request-Id` 等 Header 时,只需:
|
||||
|
||||
1. 在 `RequestTrackingContext` 类中加一个属性
|
||||
2. 在 Middleware 中加一行读取代码
|
||||
|
||||
无需修改任何 Controller。
|
||||
|
||||
***
|
||||
|
||||
## 架构设计
|
||||
|
||||
```
|
||||
HTTP Request (Header: Caller, Device-Id, Request-Id, ...)
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────┐
|
||||
│ RequestTrackingMiddleware │ ← 统一提取所有追踪 Header
|
||||
│ 存入 HttpContext.Items │ 写入 RequestTrackingContext
|
||||
└───────────┬─────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────┐
|
||||
│ Controller Action │ ← HttpContext.GetRequestTrackingContext().Caller
|
||||
│ - A 类:读取 + 记录日志 │ _logger.LogInformation(...)
|
||||
│ - B 类:读取 + 写创建人 │ entity.Creator = ...GetCaller();
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## 涉及的文件
|
||||
|
||||
| # | 文件 | 操作 | 说明 |
|
||||
| - | -------------------------------------------------------------------- | -------- | ---------------- |
|
||||
| 1 | `src/CONTROLLER/Models/RequestTrackingContext.cs` | **新建** | 追踪上下文模型 |
|
||||
| 2 | `src/CONTROLLER/Middleware/RequestTrackingMiddleware.cs` | **新建** | 统一提取 Header |
|
||||
| 3 | `src/CONTROLLER/Extensions/HttpContextExtensions.cs` | **新建** | HttpContext 扩展方法 |
|
||||
| 4 | `src/CONTROLLER/Program.cs` | **修改** | 注册中间件 |
|
||||
| 5 | `src/CONTROLLER/Controllers/LabelController.cs` | 修改 2 个方法 | 已有 Logger |
|
||||
| 6 | `src/CONTROLLER/Controllers/BagTagController.cs` | 修改 4 个方法 | 需注入 Logger |
|
||||
| 7 | `src/CONTROLLER/Controllers/ShippingHandoverFormController.cs` | 修改 3 个方法 | 需注入 Logger |
|
||||
| 8 | `src/CONTROLLER/Controllers/ShippingHandoverFormBagTagController.cs` | 修改 1 个方法 | 需注入 Logger |
|
||||
|
||||
***
|
||||
|
||||
## 接口改造分类
|
||||
|
||||
### A 类:仅记录 Caller 日志(7 个读操作接口)
|
||||
|
||||
| # | 方法 | 端点 |
|
||||
| -- | --- | -------------------------------------------------------- |
|
||||
| 1 | GET | `api/label/label-replace/waybill/{number}/download` |
|
||||
| 2 | GET | `api/label/label-replace/waybill/{number}/downloadNoTri` |
|
||||
| 3 | GET | `api/bagtag/{tagNumber}/print` |
|
||||
| 4 | GET | `api/shipping-handover/{bolNumber}/print` |
|
||||
| 7 | GET | `api/shipping-handover/generate-number` |
|
||||
| 9 | GET | `api/bagtag/available` |
|
||||
| 10 | GET | `api/shipping-handover/bag-tag/associate-by-number/{id}` |
|
||||
|
||||
### B 类:记录日志 + 写入系统创建人字段(3 个创建/操作接口)
|
||||
|
||||
| # | 方法 | 端点 | 当前代码 | 改造内容 |
|
||||
| - | ---- | ------------------------------ | ------------------------ | ---------------------- |
|
||||
| 5 | POST | `api/bagtag/generate` | `creator` 硬编码 `"system"` | → 从 `Caller` Header 读取 |
|
||||
| 6 | POST | `api/bagtag/auto-pack/start` | `request.Creator` 从请求体取 | → 用 `Caller` Header 覆盖 |
|
||||
| 8 | GET | `api/shipping-handover/create` | `Creator` 从 URL 参数取 | → 用 `Caller` Header 覆盖 |
|
||||
|
||||
***
|
||||
|
||||
## 实施步骤
|
||||
|
||||
### 步骤 1:创建 `RequestTrackingContext` 模型
|
||||
|
||||
`src/CONTROLLER/Models/RequestTrackingContext.cs`
|
||||
|
||||
```csharp
|
||||
namespace CONTROLLER.Models
|
||||
{
|
||||
public class RequestTrackingContext
|
||||
{
|
||||
public string Caller { get; set; } = "system";
|
||||
// 后续扩展预留:
|
||||
// public string DeviceId { get; set; }
|
||||
// public string RequestId { get; set; }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* 所有追踪 Header 的值集中在一个模型中
|
||||
|
||||
* 默认值 `"system"` 作为回退
|
||||
|
||||
* 后续新增 Header:添加属性 → Middleware 中加一行读取 → 完成
|
||||
|
||||
***
|
||||
|
||||
### 步骤 2:创建 `RequestTrackingMiddleware` 中间件
|
||||
|
||||
`src/CONTROLLER/Middleware/RequestTrackingMiddleware.cs`
|
||||
|
||||
```csharp
|
||||
using CONTROLLER.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CONTROLLER.Middleware
|
||||
{
|
||||
public class RequestTrackingMiddleware
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public RequestTrackingMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
var trackingContext = new RequestTrackingContext();
|
||||
|
||||
var caller = context.Request.Headers["Caller"].FirstOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace(caller))
|
||||
{
|
||||
trackingContext.Caller = caller;
|
||||
}
|
||||
// 后续扩展只需加一行:
|
||||
// var deviceId = context.Request.Headers["Device-Id"].FirstOrDefault();
|
||||
// if (!string.IsNullOrWhiteSpace(deviceId)) trackingContext.DeviceId = deviceId;
|
||||
|
||||
context.Items["RequestTrackingContext"] = trackingContext;
|
||||
await _next(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* 在管道最前端统一提取所有追踪 Header
|
||||
|
||||
* 存入 `HttpContext.Items["RequestTrackingContext"]`
|
||||
|
||||
* 仅在 Header 有值时才覆盖默认值(保持回退逻辑)
|
||||
|
||||
***
|
||||
|
||||
### 步骤 3:创建 `HttpContextExtensions` 扩展方法
|
||||
|
||||
`src/CONTROLLER/Extensions/HttpContextExtensions.cs`
|
||||
|
||||
```csharp
|
||||
using CONTROLLER.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace CONTROLLER.Extensions
|
||||
{
|
||||
public static class HttpContextExtensions
|
||||
{
|
||||
public static RequestTrackingContext GetRequestTrackingContext(this HttpContext context)
|
||||
{
|
||||
return context.Items["RequestTrackingContext"] as RequestTrackingContext
|
||||
?? new RequestTrackingContext();
|
||||
}
|
||||
|
||||
public static string GetCaller(this HttpContext context)
|
||||
{
|
||||
return context.GetRequestTrackingContext().Caller;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* `GetRequestTrackingContext()` — 获取完整上下文(支持未来扩展)
|
||||
|
||||
* `GetCaller()` — 便捷方法,直接获取调用人
|
||||
|
||||
***
|
||||
|
||||
### 步骤 4:在 `Program.cs` 中注册中间件
|
||||
|
||||
在 `app.UseResponseCompression();` 之前(或其他合适位置)添加:
|
||||
|
||||
```csharp
|
||||
app.UseMiddleware<RequestTrackingMiddleware>();
|
||||
```
|
||||
|
||||
需要添加 using:
|
||||
|
||||
```csharp
|
||||
using CONTROLLER.Middleware;
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### 步骤 5:改造 `LabelController.cs`(A 类:2 个接口)
|
||||
|
||||
已有 `ILogger<LabelController> _logger`,添加 using + 在方法入口处记录 Caller 日志:
|
||||
|
||||
添加 using:
|
||||
|
||||
```csharp
|
||||
using CONTROLLER.Extensions;
|
||||
```
|
||||
|
||||
**接口 #1** — `DownloadLabelByWaybillNumber`:
|
||||
|
||||
```csharp
|
||||
var caller = HttpContext.GetCaller();
|
||||
_logger.LogInformation("[Caller: {Caller}] DownloadLabel, WaybillNumber: {WaybillNumber}", caller, waybillNumber);
|
||||
```
|
||||
|
||||
**接口 #2** — `DownloadLabelByWaybillNumberNoTrigger`:
|
||||
|
||||
```csharp
|
||||
var caller = HttpContext.GetCaller();
|
||||
_logger.LogInformation("[Caller: {Caller}] DownloadLabelNoTrigger, WaybillNumber: {WaybillNumber}", caller, waybillNumber);
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### 步骤 6:改造 `BagTagController.cs`(A 类 2 个 + B 类 2 个)
|
||||
|
||||
**前置改造**:注入 `ILogger<BagTagController>`:
|
||||
|
||||
* 添加 `using Microsoft.Extensions.Logging;` 和 `using CONTROLLER.Extensions;`
|
||||
|
||||
* 添加构造函数参数和私有字段 `_logger`
|
||||
|
||||
**A 类 — 接口 #3** `PrintBagTag`:
|
||||
|
||||
```csharp
|
||||
var caller = HttpContext.GetCaller();
|
||||
_logger.LogInformation("[Caller: {Caller}] PrintBagTag, TagNumber: {TagNumber}", caller, tagNumber);
|
||||
```
|
||||
|
||||
**B 类 — 接口 #5** `GenerateBagTags`:
|
||||
|
||||
* **当前**:`var creator = /*...*/ "system";`(硬编码)
|
||||
|
||||
* **改为**:
|
||||
|
||||
```csharp
|
||||
var caller = HttpContext.GetCaller();
|
||||
_logger.LogInformation("[Caller: {Caller}] GenerateBagTags, Channel: {Channel}, Count: {Count}", caller, request.ChannelName, request.Count);
|
||||
var generatedTags = await _bagTagService.GenerateBagTagsAsync(request.ChannelName, request.Count, caller);
|
||||
```
|
||||
|
||||
**B 类 — 接口 #6** `StartAutoPack`:
|
||||
|
||||
```csharp
|
||||
var caller = HttpContext.GetCaller();
|
||||
_logger.LogInformation("[Caller: {Caller}] StartAutoPack, TagNumber: {TagNumber}", caller, request.TagNumber);
|
||||
request.Creator = caller;
|
||||
var result = await _bagTagService.StartAutoPackAsync(request.TagNumber, request.Creator);
|
||||
```
|
||||
|
||||
**A 类 — 接口 #9** `GetAvailableBagTags`:
|
||||
|
||||
```csharp
|
||||
var caller = HttpContext.GetCaller();
|
||||
_logger.LogInformation("[Caller: {Caller}] GetAvailableBagTags, Channel: {Channel}", caller, channel);
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### 步骤 7:改造 `ShippingHandoverFormController.cs`(A 类 2 个 + B 类 1 个)
|
||||
|
||||
**前置改造**:注入 `ILogger<ShippingHandoverFormController>`:
|
||||
|
||||
* 添加 `using Microsoft.Extensions.Logging;` 和 `using CONTROLLER.Extensions;`
|
||||
|
||||
* 添加构造函数参数和私有字段 `_logger`
|
||||
|
||||
**B 类 — 接口 #8** `CreateShippingHandoverForm`:
|
||||
|
||||
```csharp
|
||||
var caller = HttpContext.GetCaller();
|
||||
_logger.LogInformation("[Caller: {Caller}] CreateShippingHandoverForm, HandoverNumber: {Number}, Channel: {Channel}", caller, HandoverNumber, Channel);
|
||||
// 用 Caller 覆盖 Creator,Caller 为空时回退为 "system"
|
||||
form.Creator = caller;
|
||||
```
|
||||
|
||||
**A 类 — 接口 #7** `GenerateShippingHandoverNumber`:
|
||||
|
||||
```csharp
|
||||
var caller = HttpContext.GetCaller();
|
||||
_logger.LogInformation("[Caller: {Caller}] GenerateShippingHandoverNumber, Channel: {Channel}", caller, channel);
|
||||
```
|
||||
|
||||
**A 类 — 接口 #4/#11** `PrintBillOfLading`:
|
||||
|
||||
```csharp
|
||||
var caller = HttpContext.GetCaller();
|
||||
_logger.LogInformation("[Caller: {Caller}] PrintBillOfLading, BolNumber: {BolNumber}", caller, bolNumber);
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### 步骤 8:改造 `ShippingHandoverFormBagTagController.cs`(A 类:1 个接口)
|
||||
|
||||
**前置改造**:注入 `ILogger<ShippingHandoverFormBagTagController>`:
|
||||
|
||||
* 添加 `using Microsoft.Extensions.Logging;` 和 `using CONTROLLER.Extensions;`
|
||||
|
||||
* 添加构造函数参数和私有字段 `_logger`
|
||||
|
||||
**A 类 — 接口 #10** `AssociateBagTagsByNumber`:
|
||||
|
||||
```csharp
|
||||
var caller = HttpContext.GetCaller();
|
||||
_logger.LogInformation("[Caller: {Caller}] AssociateBagTagsByNumber, ShippingHandoverFormId: {Id}, TagNumbers: {Tags}", caller, shippingHandoverFormId, bagTagNumbers);
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
### 步骤 9:验证
|
||||
|
||||
```bash
|
||||
dotnet build src/CONTROLLER/CONTROLLER.csproj
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## 未来扩展示例
|
||||
|
||||
假设后续要加 `Device-Id` 和 `Request-Id` 两个 Header:
|
||||
|
||||
**只需修改 2 个文件:**
|
||||
|
||||
1. `RequestTrackingContext.cs` — 加 2 个属性:
|
||||
|
||||
```csharp
|
||||
public string DeviceId { get; set; }
|
||||
public string RequestId { get; set; }
|
||||
```
|
||||
|
||||
1. `RequestTrackingMiddleware.cs` — 加 2 行:
|
||||
|
||||
```csharp
|
||||
var deviceId = context.Request.Headers["Device-Id"].FirstOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace(deviceId)) trackingContext.DeviceId = deviceId;
|
||||
|
||||
var requestId = context.Request.Headers["Request-Id"].FirstOrDefault();
|
||||
if (!string.IsNullOrWhiteSpace(requestId)) trackingContext.RequestId = requestId;
|
||||
```
|
||||
|
||||
**Controller 中可直接使用**:
|
||||
|
||||
```csharp
|
||||
var ctx = HttpContext.GetRequestTrackingContext();
|
||||
_logger.LogInformation("Caller: {C}, Device: {D}, Request: {R}", ctx.Caller, ctx.DeviceId, ctx.RequestId);
|
||||
```
|
||||
|
||||
**无需修改任何 Controller 的业务逻辑。**
|
||||
|
||||
***
|
||||
|
||||
## B 类接口改造对照表(写入创建人字段)
|
||||
|
||||
| # | 方法 | 当前代码 | 改造后代码 |
|
||||
| - | ---------------------------- | ------------------------- | -------------------------------------------------- |
|
||||
| 5 | `GenerateBagTags` | `var creator = "system";` | `var caller = HttpContext.GetCaller();` 传入 service |
|
||||
| 6 | `StartAutoPack` | `request.Creator` | `request.Creator = HttpContext.GetCaller();` |
|
||||
| 8 | `CreateShippingHandoverForm` | `form.Creator = Creator;` | `form.Creator = HttpContext.GetCaller();` |
|
||||
|
||||
***
|
||||
|
||||
## A 类接口日志格式
|
||||
|
||||
```
|
||||
[Caller: zhangsan] DownloadLabel, WaybillNumber: YW202605270001
|
||||
[Caller: system] PrintBagTag, TagNumber: BT202605270001
|
||||
[Caller: zhangsan] PrintBillOfLading, BolNumber: BOL202605270001
|
||||
```
|
||||
|
||||
***
|
||||
|
||||
## 改造汇总
|
||||
|
||||
| 步骤 | 文件 | 操作 | A 类 | B 类 |
|
||||
| ------ | ----------------------------------------- | -------------------- | ----- | ----- |
|
||||
| 1 | `Models/RequestTrackingContext.cs` | 新建 | — | — |
|
||||
| 2 | `Middleware/RequestTrackingMiddleware.cs` | 新建 | — | — |
|
||||
| 3 | `Extensions/HttpContextExtensions.cs` | 新建 | — | — |
|
||||
| 4 | `Program.cs` | 注册中间件 | — | — |
|
||||
| 5 | `LabelController.cs` | 修改 2 个方法 | 2 | 0 |
|
||||
| 6 | `BagTagController.cs` | 添加 Logger + 修改 4 个方法 | 2 | 2 |
|
||||
| 7 | `ShippingHandoverFormController.cs` | 添加 Logger + 修改 3 个方法 | 2 | 1 |
|
||||
| 8 | `ShippingHandoverFormBagTagController.cs` | 添加 Logger + 修改 1 个方法 | 1 | 0 |
|
||||
| 9 | 构建验证 | `dotnet build` | — | — |
|
||||
| **合计** | 8 个文件 | <br /> | **7** | **3** |
|
||||
|
||||
Reference in New Issue
Block a user