# 标签获取时间字段实现方案 ## 1. 需求概述 在 `label_replace_requests` 表中添加一个新字段 `LabelRetrievedAt`,用于记录客户获取标签的时间,并通过数据库触发器自动更新该字段。 ## 2. 实现步骤 ### 2.1 修改数据模型 已在 `LabelReplaceEntity` 类中添加了 `LabelRetrievedAt` 字段: ```csharp /// /// 获取标签时间 /// [SugarColumn(IsNullable = true)] public DateTime? LabelRetrievedAt { get; set; } ``` ### 2.2 数据库表结构更新 **方法1:使用代码优先方式** 当应用程序启动时,SqlSugar 的 CodeFirst 功能会自动创建新字段。 **方法2:手动执行SQL语句** ```sql ALTER TABLE label_replace_requests ADD COLUMN LabelRetrievedAt DATETIME NULL COMMENT '获取标签时间'; ``` ### 2.3 创建触发器 #### 2.3.1 基本触发器 ```sql 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 包含条件判断的触发器(可选) ```sql 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. **创建测试记录**: ```sql INSERT INTO label_replace_requests (NeutralWaybillNumber, ReplaceStatus) VALUES ('TEST123456', 'Y'); ``` 2. **查询初始状态**: ```sql SELECT LabelRetrievedAt, UpdatedAt FROM label_replace_requests WHERE NeutralWaybillNumber = 'TEST123456'; ``` 预期结果:`LabelRetrievedAt` 为 `NULL` 3. **设置标签**: ```sql UPDATE label_replace_requests SET Label = 'base64_encoded_pdf' WHERE NeutralWaybillNumber = 'TEST123456'; ``` 4. **验证时间戳是否更新**: ```sql SELECT LabelRetrievedAt, UpdatedAt FROM label_replace_requests WHERE NeutralWaybillNumber = 'TEST123456'; ``` 预期结果:`LabelRetrievedAt` 和 `UpdatedAt` 都被更新为当前时间 5. **更新其他字段**: ```sql UPDATE label_replace_requests SET FinalMileTrackingNumber = 'FM123456' WHERE NeutralWaybillNumber = 'TEST123456'; ``` 6. **验证LabelRetrievedAt是否保持不变**: ```sql SELECT LabelRetrievedAt, UpdatedAt FROM label_replace_requests WHERE NeutralWaybillNumber = 'TEST123456'; ``` 预期结果:`LabelRetrievedAt` 保持不变,`UpdatedAt` 被更新为当前时间 ### 4.2 性能测试 1. **批量更新测试**: ```sql -- 创建测试数据 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. **验证批量更新结果**: ```sql 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 修改触发器 ```sql -- 先删除旧触发器 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 删除触发器 ```sql DROP TRIGGER IF EXISTS update_label_retrieved_timestamp; ``` ## 7. 代码优化建议 ### 7.1 数据模型优化 考虑在 `LabelReplaceEntity` 类中添加构造函数,确保所有时间字段都有合理的默认值: ```csharp public LabelReplaceEntity() { CreatedAt = DateTime.UtcNow; UpdatedAt = DateTime.UtcNow; ReplaceStatus = "Y"; LabelRetrievedAt = null; } ``` ### 7.2 服务层优化 在 `LabelReplaceService.ProcessLabelReplaceAsync` 方法中,当设置或修改标签时,可以考虑在代码层面也设置 `LabelRetrievedAt` 字段,以确保即使不使用触发器也能正确记录时间: ```csharp // 更新现有记录时 if (request.Label != null) { existingRecord.Label = request.Label; existingRecord.LabelRetrievedAt = DateTime.UtcNow; } ``` ## 8. 总结 通过添加 `LabelRetrievedAt` 字段并创建相应的数据库触发器,可以自动记录客户获取标签的时间。这种方法的优点是: 1. **自动记录**:无需在应用代码中手动设置,触发器会自动处理 2. **准确性**:仅在标签被设置或修改时才更新时间戳 3. **一致性**:与现有的 `UpdatedAt` 字段更新逻辑保持一致 在实际应用中,应根据系统的具体情况(如更新频率、性能要求等)选择合适的实现方式。