Files
LabelChange-server/.trae/documents/caller-header-implementation-plan.md
2026-06-01 16:30:29 +08:00

400 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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.

# 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 覆盖 CreatorCaller 为空时回退为 "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** |