# 收货扫描记录表（arrival_scan_records）设计与实现计划

## 一、背景分析

当前 `ArrivalHandoverFormController` 的 `/api/arrival-handover/receipt-query` 接口（[ArrivalHandoverFormController.cs:L447-L495](file:///d:/EPproject/LabelReplaceServer/src/CONTROLLER/Controllers/ArrivalHandoverFormController.cs#L447-L495)）用于 PDA 收货扫描查询。

`ArrivalHandoverFormService.GetReceiptInfoAsync` 方法（[ArrivalHandoverFormService.cs:L181-L281](file:///d:/EPproject/LabelReplaceServer/src/BLL/Services/ArrivalHandoverFormService.cs#L181-L281)）的核心逻辑：
- 接收 `arrivalNumber`（大箱号或提单号）
- 在 `label_replace_requests` 表中按 `BillOfLadingNumber` 或 `MasterPackageNumber` 匹配
- 返回 `(packageCount, labelRate, arrivalTime, billOfLadingNumber, masterPackageNumber)`
- 同时自动创建/更新 `arrival_handover_forms` 记录

**需要新增**：PDA 每次扫描收货时，将扫描记录持久化到一张独立的记录表中，便于后续追溯和统计。

---

## 二、表结构设计

### 表名：`arrival_scan_records`

| 字段 | 类型 | 约束 | 说明 |
|------|------|------|------|
| `Id` | INT UNSIGNED | PK, AUTO_INCREMENT | 主键 |
| `ArrivalNumber` | VARCHAR(100) | NOT NULL | PDA扫描的大箱号（即 request.ArrivalNumber） |
| `CustomerId` | INT | NULL | 客户ID（冗余字段，关联 customers 表） |
| `BillOfLadingNumber` | VARCHAR(100) | NULL | 提单号 |
| `MasterPackageNumber` | VARCHAR(100) | NULL | 大箱号 |
| `CreatedAt` | DATETIME | NOT NULL | PDA收货扫描时间（创建时间） |
| `UpdatedAt` | DATETIME | NOT NULL | 更新时间 |

### 索引设计

| 索引名 | 字段 | 用途 |
|--------|------|------|
| `idx_arrival_number` | ArrivalNumber | 按扫描号查询历史记录 |
| `idx_created_at` | CreatedAt | 按时间范围统计查询 |
| `idx_customer_id` | CustomerId | 按客户维度查询 |

### 设计说明

1. **ArrivalNumber** 对应 PDA 扫描时传入的 `request.ArrivalNumber`，是本次扫描的核心标识
2. **CustomerId** 为冗余字段，从 `label_replace_requests` 表中获取，便于按客户维度查询，避免每次查询都要 JOIN
3. **BillOfLadingNumber / MasterPackageNumber** 从 `GetReceiptInfoAsync` 返回值中获取
4. **CreatedAt** 即为 PDA 收货扫描时间
5. 遵循项目现有的命名规范和注解风格（PascalCase 属性名 + `[SugarColumn]` 注解）

---

## 三、实现步骤

### 步骤 1：创建 SQL 建表脚本

**文件**：`src/DB/Scripts/CreateArrivalScanRecordTable.sql`

参考 [CreateLabelReplaceTable.sql](file:///d:/EPproject/LabelReplaceServer/src/DB/Scripts/CreateLabelReplaceTable.sql) 的格式：
- InnoDB 引擎
- utf8mb4 字符集
- 包含中文注释
- 创建必要索引

### 步骤 2：创建 Entity 实体类

**文件**：`src/MDL/Models/ArrivalScanRecordEntity.cs`

参考 [LabelScanEntity.cs](file:///d:/EPproject/LabelReplaceServer/src/MDL/Models/LabelScanEntity.cs) 和 [LabelReplaceEntity.cs](file:///d:/EPproject/LabelReplaceServer/src/MDL/Models/LabelReplaceEntity.cs) 的写法：
- `[SugarTable("arrival_scan_records")]`
- `[SugarColumn(IsPrimaryKey = true, IsIdentity = true)]` 主键
- 时间字段默认值 `DateTime.UtcNow`
- 完整的中文 XML 注释

### 步骤 3：创建 Repository 仓库层

**文件**：
- `src/DAL/Interfaces/IArrivalScanRecordRepository.cs` — 接口定义
- `src/DAL/Repositories/ArrivalScanRecordRepository.cs` — 实现

参考 [CustomerRepository.cs](file:///d:/EPproject/LabelReplaceServer/src/DAL/repositories/CustomerRepository.cs) 的模式：
- 通过 `ISqlSugarProvider` 操作数据库
- 至少需要 `InsertAsync` 方法

### 步骤 4：修改 ArrivalHandoverFormService

**文件**：`src/BLL/Services/ArrivalHandoverFormService.cs`

在 `GetReceiptInfoAsync` 方法中，查询完成后插入一条扫描记录到 `arrival_scan_records` 表：

```csharp
// 在 return 之前插入扫描记录
var scanRecord = new ArrivalScanRecordEntity
{
    ArrivalNumber = arrivalNumber,
    CustomerId = labelReplaceEntities.FirstOrDefault()?.CustomerId,
    BillOfLadingNumber = billOfLadingNumber,
    MasterPackageNumber = masterPackageNumber,
    CreatedAt = DateTime.UtcNow,
    UpdatedAt = DateTime.UtcNow
};
await db.Insertable(scanRecord).ExecuteCommandAsync();
```

**关键点**：
- `CustomerId` 从 `label_replace_entities` 的第一个匹配记录中获取（冗余存储）
- 插入操作应在 try-catch 内部，且不应影响主流程（即使插入失败也不应阻止接口正常返回）
- 建议用独立的 try-catch 包裹插入逻辑，避免扫描记录入库失败导致接口报错

### 步骤 5：注册依赖注入

**文件**：`src/CONTROLLER/Program.cs`（或对应的 DI 注册文件）

- 注册 `IArrivalScanRecordRepository` → `ArrivalScanRecordRepository`
- 在 `ArrivalHandoverFormService` 构造函数中注入（如需要）

> **可选简化方案**：由于 `ArrivalHandoverFormService` 已经持有 `ISqlSugarProvider`，可以直接通过 `_provider.GetClient()` 操作数据库，无需单独创建 Repository。是否需要独立 Repository 取决于项目的分层规范。

---

## 四、文件清单

| 文件 | 操作 | 说明 |
|------|------|------|
| `src/DB/Scripts/CreateArrivalScanRecordTable.sql` | **新增** | 建表 SQL 脚本 |
| `src/MDL/Models/ArrivalScanRecordEntity.cs` | **新增** | 实体类 |
| `src/DAL/Interfaces/IArrivalScanRecordRepository.cs` | **新增** | 仓库接口 |
| `src/DAL/Repositories/ArrivalScanRecordRepository.cs` | **新增** | 仓库实现 |
| `src/BLL/Services/ArrivalHandoverFormService.cs` | **修改** | 在 GetReceiptInfoAsync 中插入扫描记录 |
| `src/CONTROLLER/Program.cs` | **修改** | 注册 DI（如需独立 Repository） |

---

## 五、待确认事项

1. **Repository 分层**：是否需要创建独立的 Repository，还是直接在 Service 中通过 `ISqlSugarProvider` 操作？（推荐后者，与现有模式一致，因为 `ArrivalHandoverFormService` 已经直接使用 `_provider.GetClient()` 操作数据库）
2. **插入时机**：是否仅在 Mock 数据分支（TEST 开头的 arrivalNumber）不插入？建议仅在实际数据库查询成功后插入。
3. **错误处理策略**：扫描记录插入失败时，是静默忽略（不影响接口返回）还是抛出异常？建议静默忽略，确保核心业务不受影响。
