# 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(); ``` 需要添加 using: ```csharp using CONTROLLER.Middleware; ``` *** ### 步骤 5:改造 `LabelController.cs`(A 类:2 个接口) 已有 `ILogger _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`: * 添加 `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`: * 添加 `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`: * 添加 `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 个文件 |
| **7** | **3** |