# 到货交接单和出货交接单模块规格说明 ## 1. 需求概述 在系统中增加两个新模块:到货交接单和出货交接单,用于记录和管理货物的交接过程。 ### 1.1 到货交接单 - **交接单号**:唯一标识 - **头程物流商送达时间**:物流商送达时间 - **收货时间**:实际收货时间 - **POD**:图片链接,多张用逗号隔开 - **备注**:交接备注信息 - **创建人**:创建该交接单的用户 - **创建时间**:交接单创建时间 - **修改时间**:交接单最后修改时间 ### 1.2 出货交接单 - **交接单号**:唯一标识,格式如 BOL-GOFO-202 - **大包数**:大包数量 - **小包数**:小包数量 - **渠道**:物流渠道 - **交货时间**:实际交货时间 - **POD**:图片链接,多张用逗号隔开 - **备注**:交接备注信息 - **创建人**:创建该交接单的用户 - **创建时间**:交接单创建时间 - **修改时间**:交接单最后修改时间 ## 2. 技术方案 ### 2.1 数据库设计 #### 2.1.1 到货交接单表 (`arrival_handover_forms`) | 字段名 | 数据类型 | 约束 | 描述 | |--------|----------|------|------| | `Id` | `INT` | `PRIMARY KEY, AUTO_INCREMENT` | 主键ID | | `HandoverNumber` | `VARCHAR(100)` | `NOT NULL, UNIQUE` | 交接单号 | | `LogisticsProviderArrivalTime` | `DATETIME` | `NULL` | 头程物流商送达时间 | | `ReceiptTime` | `DATETIME` | `NULL` | 收货时间 | | `POD` | `TEXT` | `NULL` | 图片链接,多张用逗号隔开 | | `Remarks` | `TEXT` | `NULL` | 备注 | | `Creator` | `VARCHAR(50)` | `NOT NULL` | 创建人 | | `CreatedAt` | `DATETIME` | `NOT NULL` | 创建时间 | | `UpdatedAt` | `DATETIME` | `NOT NULL` | 修改时间 | #### 2.1.2 出货交接单表 (`shipping_handover_forms`) | 字段名 | 数据类型 | 约束 | 描述 | |--------|----------|------|------| | `Id` | `INT` | `PRIMARY KEY, AUTO_INCREMENT` | 主键ID | | `HandoverNumber` | `VARCHAR(100)` | `NOT NULL, UNIQUE` | 交接单号 | | `BigBagCount` | `INT` | `NOT NULL` | 大包数 | | `SmallBagCount` | `INT` | `NOT NULL` | 小包数 | | `Channel` | `VARCHAR(100)` | `NOT NULL` | 渠道 | | `DeliveryTime` | `DATETIME` | `NULL` | 交货时间 | | `POD` | `TEXT` | `NULL` | 图片链接,多张用逗号隔开 | | `Remarks` | `TEXT` | `NULL` | 备注 | | `Creator` | `VARCHAR(50)` | `NOT NULL` | 创建人 | | `CreatedAt` | `DATETIME` | `NOT NULL` | 创建时间 | | `UpdatedAt` | `DATETIME` | `NOT NULL` | 修改时间 | ### 2.2 模型设计 #### 2.2.1 到货交接单模型 (`ArrivalHandoverFormEntity`) ```csharp using System; using SqlSugar; namespace MDL.Models { [SugarTable("arrival_handover_forms")] public class ArrivalHandoverFormEntity { [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] public int Id { get; set; } [SugarColumn(Length = 100, IsNullable = false, IsUnique = true)] public string HandoverNumber { get; set; } [SugarColumn(IsNullable = true)] public DateTime? LogisticsProviderArrivalTime { get; set; } [SugarColumn(IsNullable = true)] public DateTime? ReceiptTime { get; set; } [SugarColumn(ColumnDataType = "TEXT", IsNullable = true)] public string POD { get; set; } [SugarColumn(ColumnDataType = "TEXT", IsNullable = true)] public string Remarks { get; set; } [SugarColumn(Length = 50, IsNullable = false)] public string Creator { get; set; } [SugarColumn(IsNullable = false)] public DateTime CreatedAt { get; set; } = DateTime.UtcNow; [SugarColumn(IsNullable = false)] public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; } } ``` #### 2.2.2 出货交接单模型 (`ShippingHandoverFormEntity`) ```csharp using System; using SqlSugar; namespace MDL.Models { [SugarTable("shipping_handover_forms")] public class ShippingHandoverFormEntity { [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] public int Id { get; set; } [SugarColumn(Length = 100, IsNullable = false, IsUnique = true)] public string HandoverNumber { get; set; } [SugarColumn(IsNullable = false)] public int BigBagCount { get; set; } [SugarColumn(IsNullable = false)] public int SmallBagCount { get; set; } [SugarColumn(Length = 100, IsNullable = false)] public string Channel { get; set; } [SugarColumn(IsNullable = true)] public DateTime? DeliveryTime { get; set; } [SugarColumn(ColumnDataType = "TEXT", IsNullable = true)] public string POD { get; set; } [SugarColumn(ColumnDataType = "TEXT", IsNullable = true)] public string Remarks { get; set; } [SugarColumn(Length = 50, IsNullable = false)] public string Creator { get; set; } [SugarColumn(IsNullable = false)] public DateTime CreatedAt { get; set; } = DateTime.UtcNow; [SugarColumn(IsNullable = false)] public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; } } ``` ### 2.3 仓库设计 #### 2.3.1 到货交接单仓库接口 (`IArrivalHandoverFormRepository`) ```csharp using System.Collections.Generic; using System.Threading.Tasks; using MDL.Models; namespace DAL.Interfaces { public interface IArrivalHandoverFormRepository { Task InsertAsync(ArrivalHandoverFormEntity form); Task GetByIdAsync(int id); Task GetByHandoverNumberAsync(string handoverNumber); Task> GetAllAsync(); Task UpdateAsync(ArrivalHandoverFormEntity form); Task DeleteAsync(int id); Task ExistsByHandoverNumberAsync(string handoverNumber); Task<(List Forms, int TotalCount)> GetArrivalHandoverFormsBatchAsync( int page, int pageSize, string sortBy, string sortOrder, string handoverNumber, string creator); } } ``` #### 2.3.2 出货交接单仓库接口 (`IShippingHandoverFormRepository`) ```csharp using System.Collections.Generic; using System.Threading.Tasks; using MDL.Models; namespace DAL.Interfaces { public interface IShippingHandoverFormRepository { Task InsertAsync(ShippingHandoverFormEntity form); Task GetByIdAsync(int id); Task GetByHandoverNumberAsync(string handoverNumber); Task> GetAllAsync(); Task UpdateAsync(ShippingHandoverFormEntity form); Task DeleteAsync(int id); Task ExistsByHandoverNumberAsync(string handoverNumber); Task<(List Forms, int TotalCount)> GetShippingHandoverFormsBatchAsync( int page, int pageSize, string sortBy, string sortOrder, string handoverNumber, string channel, string creator); } } ``` ### 2.4 服务设计 #### 2.4.1 到货交接单服务接口 (`IArrivalHandoverFormService`) ```csharp using System.Collections.Generic; using System.Threading.Tasks; using MDL.Models; namespace BLL.Interfaces { public interface IArrivalHandoverFormService { Task CreateArrivalHandoverFormAsync(ArrivalHandoverFormEntity form); Task GetArrivalHandoverFormByIdAsync(int id); Task GetArrivalHandoverFormByNumberAsync(string handoverNumber); Task> GetAllArrivalHandoverFormsAsync(); Task UpdateArrivalHandoverFormAsync(ArrivalHandoverFormEntity form); Task DeleteArrivalHandoverFormAsync(int id); Task GenerateArrivalHandoverNumberAsync(); Task<(List Forms, int TotalCount)> GetArrivalHandoverFormsBatchAsync( int page, int pageSize, string sortBy, string sortOrder, string handoverNumber, string creator); } } ``` #### 2.4.2 出货交接单服务接口 (`IShippingHandoverFormService`) ```csharp using System.Collections.Generic; using System.Threading.Tasks; using MDL.Models; namespace BLL.Interfaces { public interface IShippingHandoverFormService { Task CreateShippingHandoverFormAsync(ShippingHandoverFormEntity form); Task GetShippingHandoverFormByIdAsync(int id); Task GetShippingHandoverFormByNumberAsync(string handoverNumber); Task> GetAllShippingHandoverFormsAsync(); Task UpdateShippingHandoverFormAsync(ShippingHandoverFormEntity form); Task DeleteShippingHandoverFormAsync(int id); Task GenerateShippingHandoverNumberAsync(); Task<(List Forms, int TotalCount)> GetShippingHandoverFormsBatchAsync( int page, int pageSize, string sortBy, string sortOrder, string handoverNumber, string channel, string creator); } } ``` ### 2.5 控制器设计 #### 2.5.1 到货交接单控制器 (`ArrivalHandoverFormController`) ```csharp using BLL.Interfaces; using MDL.Models; using Microsoft.AspNetCore.Mvc; using System; using System.Threading.Tasks; namespace CONTROLLER.Controllers { [Route("api/arrival-handover")] [ApiController] public class ArrivalHandoverFormController : ControllerBase { private readonly IArrivalHandoverFormService _arrivalHandoverFormService; public ArrivalHandoverFormController(IArrivalHandoverFormService arrivalHandoverFormService) { _arrivalHandoverFormService = arrivalHandoverFormService; } [HttpPost("create")] public async Task CreateArrivalHandoverForm([FromBody] ArrivalHandoverFormEntity form) { try { if (string.IsNullOrEmpty(form.HandoverNumber)) { form.HandoverNumber = await _arrivalHandoverFormService.GenerateArrivalHandoverNumberAsync(); } form.CreatedAt = DateTime.UtcNow; form.UpdatedAt = DateTime.UtcNow; var result = await _arrivalHandoverFormService.CreateArrivalHandoverFormAsync(form); return Ok(new { code = 0, message = "success", data = result }); } catch (Exception ex) { return Ok(new { code = 9999, message = ex.Message }); } } [HttpGet("get/{id}")] public async Task GetArrivalHandoverFormById(int id) { try { var form = await _arrivalHandoverFormService.GetArrivalHandoverFormByIdAsync(id); return Ok(new { code = 0, message = "success", data = form }); } catch (Exception ex) { return Ok(new { code = 9999, message = ex.Message }); } } [HttpGet("get-by-number/{handoverNumber}")] public async Task GetArrivalHandoverFormByNumber(string handoverNumber) { try { var form = await _arrivalHandoverFormService.GetArrivalHandoverFormByNumberAsync(handoverNumber); return Ok(new { code = 0, message = "success", data = form }); } catch (Exception ex) { return Ok(new { code = 9999, message = ex.Message }); } } [HttpGet("list")] public async Task GetArrivalHandoverForms( [FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] string sortBy = "CreatedAt", [FromQuery] string sortOrder = "desc", [FromQuery] string handoverNumber = "", [FromQuery] string creator = "") { try { var (forms, totalCount) = await _arrivalHandoverFormService.GetArrivalHandoverFormsBatchAsync( page, pageSize, sortBy, sortOrder, handoverNumber, creator); return Ok(new { code = 0, message = "success", data = new { forms, totalCount } }); } catch (Exception ex) { return Ok(new { code = 9999, message = ex.Message }); } } [HttpPut("update")] public async Task UpdateArrivalHandoverForm([FromBody] ArrivalHandoverFormEntity form) { try { form.UpdatedAt = DateTime.UtcNow; var result = await _arrivalHandoverFormService.UpdateArrivalHandoverFormAsync(form); return Ok(new { code = 0, message = "success", data = result }); } catch (Exception ex) { return Ok(new { code = 9999, message = ex.Message }); } } [HttpDelete("delete/{id}")] public async Task DeleteArrivalHandoverForm(int id) { try { var result = await _arrivalHandoverFormService.DeleteArrivalHandoverFormAsync(id); return Ok(new { code = 0, message = "success", data = result }); } catch (Exception ex) { return Ok(new { code = 9999, message = ex.Message }); } } [HttpGet("generate-number")] public async Task GenerateArrivalHandoverNumber() { try { var number = await _arrivalHandoverFormService.GenerateArrivalHandoverNumberAsync(); return Ok(new { code = 0, message = "success", data = number }); } catch (Exception ex) { return Ok(new { code = 9999, message = ex.Message }); } } } } ``` #### 2.5.2 出货交接单控制器 (`ShippingHandoverFormController`) ```csharp using BLL.Interfaces; using MDL.Models; using Microsoft.AspNetCore.Mvc; using System; using System.Threading.Tasks; namespace CONTROLLER.Controllers { [Route("api/shipping-handover")] [ApiController] public class ShippingHandoverFormController : ControllerBase { private readonly IShippingHandoverFormService _shippingHandoverFormService; public ShippingHandoverFormController(IShippingHandoverFormService shippingHandoverFormService) { _shippingHandoverFormService = shippingHandoverFormService; } [HttpPost("create")] public async Task CreateShippingHandoverForm([FromBody] ShippingHandoverFormEntity form) { try { if (string.IsNullOrEmpty(form.HandoverNumber)) { form.HandoverNumber = await _shippingHandoverFormService.GenerateShippingHandoverNumberAsync(); } form.CreatedAt = DateTime.UtcNow; form.UpdatedAt = DateTime.UtcNow; var result = await _shippingHandoverFormService.CreateShippingHandoverFormAsync(form); return Ok(new { code = 0, message = "success", data = result }); } catch (Exception ex) { return Ok(new { code = 9999, message = ex.Message }); } } [HttpGet("get/{id}")] public async Task GetShippingHandoverFormById(int id) { try { var form = await _shippingHandoverFormService.GetShippingHandoverFormByIdAsync(id); return Ok(new { code = 0, message = "success", data = form }); } catch (Exception ex) { return Ok(new { code = 9999, message = ex.Message }); } } [HttpGet("get-by-number/{handoverNumber}")] public async Task GetShippingHandoverFormByNumber(string handoverNumber) { try { var form = await _shippingHandoverFormService.GetShippingHandoverFormByNumberAsync(handoverNumber); return Ok(new { code = 0, message = "success", data = form }); } catch (Exception ex) { return Ok(new { code = 9999, message = ex.Message }); } } [HttpGet("list")] public async Task GetShippingHandoverForms( [FromQuery] int page = 1, [FromQuery] int pageSize = 10, [FromQuery] string sortBy = "CreatedAt", [FromQuery] string sortOrder = "desc", [FromQuery] string handoverNumber = "", [FromQuery] string channel = "", [FromQuery] string creator = "") { try { var (forms, totalCount) = await _shippingHandoverFormService.GetShippingHandoverFormsBatchAsync( page, pageSize, sortBy, sortOrder, handoverNumber, channel, creator); return Ok(new { code = 0, message = "success", data = new { forms, totalCount } }); } catch (Exception ex) { return Ok(new { code = 9999, message = ex.Message }); } } [HttpPut("update")] public async Task UpdateShippingHandoverForm([FromBody] ShippingHandoverFormEntity form) { try { form.UpdatedAt = DateTime.UtcNow; var result = await _shippingHandoverFormService.UpdateShippingHandoverFormAsync(form); return Ok(new { code = 0, message = "success", data = result }); } catch (Exception ex) { return Ok(new { code = 9999, message = ex.Message }); } } [HttpDelete("delete/{id}")] public async Task DeleteShippingHandoverForm(int id) { try { var result = await _shippingHandoverFormService.DeleteShippingHandoverFormAsync(id); return Ok(new { code = 0, message = "success", data = result }); } catch (Exception ex) { return Ok(new { code = 9999, message = ex.Message }); } } [HttpGet("generate-number")] public async Task GenerateShippingHandoverNumber() { try { var number = await _shippingHandoverFormService.GenerateShippingHandoverNumberAsync(); return Ok(new { code = 0, message = "success", data = number }); } catch (Exception ex) { return Ok(new { code = 9999, message = ex.Message }); } } } } ``` ## 3. 数据库SQL脚本 ### 3.1 到货交接单表创建脚本 ```sql CREATE TABLE IF NOT EXISTS `arrival_handover_forms` ( `Id` INT NOT NULL AUTO_INCREMENT, `HandoverNumber` VARCHAR(100) NOT NULL, `LogisticsProviderArrivalTime` DATETIME NULL, `ReceiptTime` DATETIME NULL, `POD` TEXT NULL, `Remarks` TEXT NULL, `Creator` VARCHAR(50) NOT NULL, `CreatedAt` DATETIME NOT NULL, `UpdatedAt` DATETIME NOT NULL, PRIMARY KEY (`Id`), UNIQUE INDEX `UQ_HandoverNumber` (`HandoverNumber`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` ### 3.2 出货交接单表创建脚本 ```sql CREATE TABLE IF NOT EXISTS `shipping_handover_forms` ( `Id` INT NOT NULL AUTO_INCREMENT, `HandoverNumber` VARCHAR(100) NOT NULL, `BigBagCount` INT NOT NULL, `SmallBagCount` INT NOT NULL, `Channel` VARCHAR(100) NOT NULL, `DeliveryTime` DATETIME NULL, `POD` TEXT NULL, `Remarks` TEXT NULL, `Creator` VARCHAR(50) NOT NULL, `CreatedAt` DATETIME NOT NULL, `UpdatedAt` DATETIME NOT NULL, PRIMARY KEY (`Id`), UNIQUE INDEX `UQ_HandoverNumber` (`HandoverNumber`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; ``` ## 4. 实现计划 1. **创建模型类**:在MDL项目中创建ArrivalHandoverFormEntity和ShippingHandoverFormEntity 2. **创建仓库接口**:在DAL项目中创建IArrivalHandoverFormRepository和IShippingHandoverFormRepository 3. **实现仓库类**:在DAL项目中实现ArrivalHandoverFormRepository和ShippingHandoverFormRepository 4. **创建服务接口**:在BLL项目中创建IArrivalHandoverFormService和IShippingHandoverFormService 5. **实现服务类**:在BLL项目中实现ArrivalHandoverFormService和ShippingHandoverFormService 6. **创建控制器**:在CONTROLLER项目中创建ArrivalHandoverFormController和ShippingHandoverFormController 7. **更新依赖注入**:在Program.cs中注册新的服务和仓库 8. **测试API**:验证所有CRUD操作是否正常工作 ## 5. 验收标准 1. 数据库表结构正确创建 2. 所有API接口正常工作 3. 交接单号生成规则正确 4. POD字段支持多个图片链接(用逗号隔开) 5. 分页查询功能正常 6. 所有CRUD操作返回正确的响应格式