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

14 KiB
Raw Blame History

Caller Header 接收改造实施计划

概述

根据《后端Caller字段接收清单.md》的要求需要在 11 个 API 接口中从 HTTP Header 读取 Caller 字段(请求人姓名)。

核心业务含义Caller 记录了该次请求是由谁发起的,对于创建类接口需要写入系统的创建人字段,对于所有接口都需要通过 Serilog 记录审计日志

扩展性设计:采用 Middleware 统一提取 + RequestTrackingContext 建模的方案。后续新增 Device-IdRequest-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

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

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

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() — 便捷方法,直接获取调用人


步骤 4Program.cs 中注册中间件

app.UseResponseCompression(); 之前(或其他合适位置)添加:

app.UseMiddleware<RequestTrackingMiddleware>();

需要添加 using

using CONTROLLER.Middleware;

步骤 5改造 LabelController.csA 类2 个接口)

已有 ILogger<LabelController> _logger,添加 using + 在方法入口处记录 Caller 日志:

添加 using

using CONTROLLER.Extensions;

接口 #1DownloadLabelByWaybillNumber

var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] DownloadLabel, WaybillNumber: {WaybillNumber}", caller, waybillNumber);

接口 #2DownloadLabelByWaybillNumberNoTrigger

var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] DownloadLabelNoTrigger, WaybillNumber: {WaybillNumber}", caller, waybillNumber);

步骤 6改造 BagTagController.csA 类 2 个 + B 类 2 个)

前置改造:注入 ILogger<BagTagController>

  • 添加 using Microsoft.Extensions.Logging;using CONTROLLER.Extensions;

  • 添加构造函数参数和私有字段 _logger

A 类 — 接口 #3 PrintBagTag

var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] PrintBagTag, TagNumber: {TagNumber}", caller, tagNumber);

B 类 — 接口 #5 GenerateBagTags

  • 当前var creator = /*...*/ "system";(硬编码)

  • 改为

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

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

var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] GetAvailableBagTags, Channel: {Channel}", caller, channel);

步骤 7改造 ShippingHandoverFormController.csA 类 2 个 + B 类 1 个)

前置改造:注入 ILogger<ShippingHandoverFormController>

  • 添加 using Microsoft.Extensions.Logging;using CONTROLLER.Extensions;

  • 添加构造函数参数和私有字段 _logger

B 类 — 接口 #8 CreateShippingHandoverForm

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

var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] GenerateShippingHandoverNumber, Channel: {Channel}", caller, channel);

A 类 — 接口 #4/#11 PrintBillOfLading

var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] PrintBillOfLading, BolNumber: {BolNumber}", caller, bolNumber);

步骤 8改造 ShippingHandoverFormBagTagController.csA 类1 个接口)

前置改造:注入 ILogger<ShippingHandoverFormBagTagController>

  • 添加 using Microsoft.Extensions.Logging;using CONTROLLER.Extensions;

  • 添加构造函数参数和私有字段 _logger

A 类 — 接口 #10 AssociateBagTagsByNumber

var caller = HttpContext.GetCaller();
_logger.LogInformation("[Caller: {Caller}] AssociateBagTagsByNumber, ShippingHandoverFormId: {Id}, TagNumbers: {Tags}", caller, shippingHandoverFormId, bagTagNumbers);

步骤 9验证

dotnet build src/CONTROLLER/CONTROLLER.csproj

未来扩展示例

假设后续要加 Device-IdRequest-Id 两个 Header

只需修改 2 个文件:

  1. RequestTrackingContext.cs — 加 2 个属性:
public string DeviceId { get; set; }
public string RequestId { get; set; }
  1. RequestTrackingMiddleware.cs — 加 2 行:
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 中可直接使用

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