Files
LabelChange-server/Label_Retrieved_Timestamp_Implementation.md
2026-06-01 16:30:29 +08:00

7.3 KiB
Raw Blame History

标签获取时间字段实现方案

1. 需求概述

label_replace_requests 表中添加一个新字段 LabelRetrievedAt,用于记录客户获取标签的时间,并通过数据库触发器自动更新该字段。

2. 实现步骤

2.1 修改数据模型

已在 LabelReplaceEntity 类中添加了 LabelRetrievedAt 字段:

/// <summary>
/// 获取标签时间
/// </summary>
[SugarColumn(IsNullable = true)]
public DateTime? LabelRetrievedAt { get; set; }

2.2 数据库表结构更新

方法1使用代码优先方式

当应用程序启动时SqlSugar 的 CodeFirst 功能会自动创建新字段。

方法2手动执行SQL语句

ALTER TABLE label_replace_requests 
ADD COLUMN LabelRetrievedAt DATETIME NULL COMMENT '获取标签时间';

2.3 创建触发器

2.3.1 基本触发器

DELIMITER $$
CREATE TRIGGER update_label_retrieved_timestamp
BEFORE UPDATE ON label_replace_requests
FOR EACH ROW
BEGIN
    -- 当Label字段被设置或修改时更新LabelRetrievedAt
    IF NEW.Label IS NOT NULL AND (OLD.Label IS NULL OR NEW.Label != OLD.Label) THEN
        SET NEW.LabelRetrievedAt = UTC_TIMESTAMP();
    END IF;
    -- 同时更新UpdatedAt字段
    SET NEW.UpdatedAt = UTC_TIMESTAMP();
END$$
DELIMITER ;

2.3.2 包含条件判断的触发器(可选)

DELIMITER $$
CREATE TRIGGER update_label_retrieved_timestamp
BEFORE UPDATE ON label_replace_requests
FOR EACH ROW
BEGIN
    -- 当Label字段被设置或修改时更新LabelRetrievedAt
    IF NEW.Label IS NOT NULL AND (OLD.Label IS NULL OR NEW.Label != OLD.Label) THEN
        SET NEW.LabelRetrievedAt = UTC_TIMESTAMP();
    END IF;
    
    -- 仅当记录确实被修改时才更新UpdatedAt
    IF NOT (NEW.BillOfLadingNumber <=> OLD.BillOfLadingNumber AND 
            NEW.MasterPackageNumber <=> OLD.MasterPackageNumber AND 
            NEW.ReferenceNumber <=> OLD.ReferenceNumber AND 
            NEW.NeutralWaybillNumber <=> OLD.NeutralWaybillNumber AND 
            NEW.FinalMileTrackingNumber <=> OLD.FinalMileTrackingNumber AND 
            NEW.Label <=> OLD.Label AND 
            NEW.ReplaceStatus <=> OLD.ReplaceStatus AND 
            NEW.CustomerId <=> OLD.CustomerId) THEN
        SET NEW.UpdatedAt = UTC_TIMESTAMP();
    END IF;
END$$
DELIMITER ;

3. 触发器说明

3.1 触发时机

  • BEFORE UPDATE:在更新操作执行前触发
  • FOR EACH ROW:对每一行更新都会触发

3.2 逻辑说明

  • Label 字段从 NULL 变为非 NULL,或其值被修改时,更新 LabelRetrievedAt 为当前时间
  • 同时更新 UpdatedAt 字段,保持与现有逻辑一致

3.3 时间戳设置

  • 使用 UTC_TIMESTAMP() 函数获取当前UTC时间与代码中使用的 DateTime.UtcNow 保持一致
  • LabelRetrievedAt 字段为可为空的日期时间类型,仅在标签被设置时才更新

4. 测试验证

4.1 功能测试

  1. 创建测试记录

    INSERT INTO label_replace_requests (NeutralWaybillNumber, ReplaceStatus) 
    VALUES ('TEST123456', 'Y');
    
  2. 查询初始状态

    SELECT LabelRetrievedAt, UpdatedAt FROM label_replace_requests WHERE NeutralWaybillNumber = 'TEST123456';
    

    预期结果:LabelRetrievedAtNULL

  3. 设置标签

    UPDATE label_replace_requests SET Label = 'base64_encoded_pdf' 
    WHERE NeutralWaybillNumber = 'TEST123456';
    
  4. 验证时间戳是否更新

    SELECT LabelRetrievedAt, UpdatedAt FROM label_replace_requests WHERE NeutralWaybillNumber = 'TEST123456';
    

    预期结果:LabelRetrievedAtUpdatedAt 都被更新为当前时间

  5. 更新其他字段

    UPDATE label_replace_requests SET FinalMileTrackingNumber = 'FM123456' 
    WHERE NeutralWaybillNumber = 'TEST123456';
    
  6. 验证LabelRetrievedAt是否保持不变

    SELECT LabelRetrievedAt, UpdatedAt FROM label_replace_requests WHERE NeutralWaybillNumber = 'TEST123456';
    

    预期结果:LabelRetrievedAt 保持不变,UpdatedAt 被更新为当前时间

4.2 性能测试

  1. 批量更新测试

    -- 创建测试数据
    INSERT INTO label_replace_requests (NeutralWaybillNumber, ReplaceStatus) 
    VALUES 
    ('TEST001', 'Y'),
    ('TEST002', 'Y'),
    ('TEST003', 'Y'),
    ('TEST004', 'Y'),
    ('TEST005', 'Y');
    
    -- 批量更新标签
    UPDATE label_replace_requests SET Label = 'base64_encoded_pdf' 
    WHERE NeutralWaybillNumber LIKE 'TEST%';
    
  2. 验证批量更新结果

    SELECT NeutralWaybillNumber, LabelRetrievedAt FROM label_replace_requests WHERE NeutralWaybillNumber LIKE 'TEST%';
    

    预期结果:所有记录的 LabelRetrievedAt 都被更新为当前时间

5. 注意事项

5.1 权限要求

  • 需要 CREATE TRIGGER 权限
  • 需要对 label_replace_requests 表有 TRIGGER 权限
  • 需要对 label_replace_requests 表有 ALTER 权限(用于添加新字段)

5.2 性能影响

  • 触发器会在每次更新操作时执行,可能影响高频更新场景的性能
  • 建议在生产环境中进行性能测试

5.3 维护性

  • 触发器逻辑存储在数据库中,与应用代码分离,可能增加维护难度
  • 建议在代码中添加注释,说明触发器的存在和功能

5.4 兼容性

  • 确保MySQL版本支持触发器MySQL 5.0及以上版本支持)
  • 不同MySQL版本的触发器语法可能略有差异

6. 触发器管理

6.1 修改触发器

-- 先删除旧触发器
DROP TRIGGER IF EXISTS update_label_retrieved_timestamp;

-- 再创建新触发器
DELIMITER $$
CREATE TRIGGER update_label_retrieved_timestamp
BEFORE UPDATE ON label_replace_requests
FOR EACH ROW
BEGIN
    -- 当Label字段被设置或修改时更新LabelRetrievedAt
    IF NEW.Label IS NOT NULL AND (OLD.Label IS NULL OR NEW.Label != OLD.Label) THEN
        SET NEW.LabelRetrievedAt = UTC_TIMESTAMP();
    END IF;
    -- 同时更新UpdatedAt字段
    SET NEW.UpdatedAt = UTC_TIMESTAMP();
END$$
DELIMITER ;

6.2 删除触发器

DROP TRIGGER IF EXISTS update_label_retrieved_timestamp;

7. 代码优化建议

7.1 数据模型优化

考虑在 LabelReplaceEntity 类中添加构造函数,确保所有时间字段都有合理的默认值:

public LabelReplaceEntity()
{
    CreatedAt = DateTime.UtcNow;
    UpdatedAt = DateTime.UtcNow;
    ReplaceStatus = "Y";
    LabelRetrievedAt = null;
}

7.2 服务层优化

LabelReplaceService.ProcessLabelReplaceAsync 方法中,当设置或修改标签时,可以考虑在代码层面也设置 LabelRetrievedAt 字段,以确保即使不使用触发器也能正确记录时间:

// 更新现有记录时
if (request.Label != null)
{
    existingRecord.Label = request.Label;
    existingRecord.LabelRetrievedAt = DateTime.UtcNow;
}

8. 总结

通过添加 LabelRetrievedAt 字段并创建相应的数据库触发器,可以自动记录客户获取标签的时间。这种方法的优点是:

  1. 自动记录:无需在应用代码中手动设置,触发器会自动处理
  2. 准确性:仅在标签被设置或修改时才更新时间戳
  3. 一致性:与现有的 UpdatedAt 字段更新逻辑保持一致

在实际应用中,应根据系统的具体情况(如更新频率、性能要求等)选择合适的实现方式。