# 收货扫描记录表(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. **错误处理策略**:扫描记录插入失败时,是静默忽略(不影响接口返回)还是抛出异常?建议静默忽略,确保核心业务不受影响。