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

246 lines
7.3 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.

# 标签获取时间字段实现方案
## 1. 需求概述
`label_replace_requests` 表中添加一个新字段 `LabelRetrievedAt`,用于记录客户获取标签的时间,并通过数据库触发器自动更新该字段。
## 2. 实现步骤
### 2.1 修改数据模型
已在 `LabelReplaceEntity` 类中添加了 `LabelRetrievedAt` 字段:
```csharp
/// <summary>
/// 获取标签时间
/// </summary>
[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` 字段更新逻辑保持一致
在实际应用中,应根据系统的具体情况(如更新频率、性能要求等)选择合适的实现方式。