# 批量 MES 绑定与解绑接口实施计划 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** 新增两个 WMS 接口(SplitPalletConfirm、GroupPalletConfirm),供 WCS 在任务阶段完成时一次性上传托盘级别的 MES 绑定/解绑数据,减少 MES 调用次数。 **Architecture:** - `Dt_SplitTemp` 临时表:拆盘开始时幂等写入托盘电芯列表,Confirm 时读取并删除 - `SplitPalletAsync` 改造:每次调用时检查临时表,无记录则写入,有记录则跳过 - `SplitPalletConfirm`:从临时表读取电芯 → 调用 MES UnBindContainer → 删除临时表记录 - `GroupPalletConfirm`:按托盘号查 Dt_StockInfoDetail → 调用 MES BindContainer **Tech Stack:** .NET 6/8, C#, SqlSugar ORM, ASP.NET Core WebAPI --- ## 文件变更概览 | 操作 | 文件 | |------|------| | 新增 | `WIDESEA_Model/Models/Stock/Dt_SplitTemp.cs` | | 新增 | `WIDESEA_DTO/Stock/SplitPalletConfirmRequestDto.cs` | | 新增 | `WIDESEA_DTO/Stock/GroupPalletConfirmRequestDto.cs` | | 修改 | `WIDESEA_IStockService/IStockService.cs` | | 修改 | `WIDESEA_StockService/StockService.cs` | | 修改 | `WIDESEA_WMSServer/Controllers/Stock/StockController.cs` | | 修改 | 数据库:新增 `Dt_SplitTemp` 表 | --- ## Task 1: 新建临时表实体 Dt_SplitTemp **Files:** - Create: `WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Stock/Dt_SplitTemp.cs` - [ ] **Step 1: 创建 Dt_SplitTemp 实体** ```csharp using SqlSugar; using WIDESEA_Core.DB.Models; namespace WIDESEA_Model.Models { /// /// 拆盘临时表 - 用于暂存拆盘任务电芯列表,供批量确认时使用 /// [SugarTable(nameof(Dt_SplitTemp), "拆盘临时表")] public class Dt_SplitTemp { /// /// 主键 /// [SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnDescription = "主键")] public int Id { get; set; } /// /// 托盘号 /// [SugarColumn(IsNullable = false, Length = 50, ColumnDescription = "托盘号")] public string PalletCode { get; set; } /// /// 电芯条码列表(JSON格式) /// [SugarColumn(IsNullable = false, Length = -1, ColumnDescription = "电芯条码列表JSON")] public string SfcList { get; set; } /// /// 创建时间 /// [SugarColumn(IsNullable = false, ColumnDescription = "创建时间")] public DateTime CreateTime { get; set; } = DateTime.Now; } } ``` - [ ] **Step 2: Commit** ```bash git add WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Stock/Dt_SplitTemp.cs git commit -m "feat(Stock): 新增Dt_SplitTemp拆盘临时表实体 Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 2: 新建请求 DTO **Files:** - Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/SplitPalletConfirmRequestDto.cs` - Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/GroupPalletConfirmRequestDto.cs` - [ ] **Step 1: 创建 SplitPalletConfirmRequestDto** ```csharp namespace WIDESEA_DTO.Stock { /// /// 批量拆盘确认请求DTO /// public class SplitPalletConfirmRequestDto { /// /// 源托盘号 /// public string PalletCode { get; set; } } } ``` - [ ] **Step 2: 创建 GroupPalletConfirmRequestDto** ```csharp namespace WIDESEA_DTO.Stock { /// /// 批量组盘确认请求DTO /// public class GroupPalletConfirmRequestDto { /// /// 目标托盘号 /// public string PalletCode { get; set; } } } ``` - [ ] **Step 3: Commit** ```bash git add WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/SplitPalletConfirmRequestDto.cs WMS/WIDESEA_WMSServer/WIDESEA_DTO/Stock/GroupPalletConfirmRequestDto.cs git commit -m "feat(DTO): 新增批量组盘拆盘确认请求DTO Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 3: 修改 IStockService 接口 **Files:** - Modify: `WMS/WIDESEA_WMSServer/WIDESEA_IStockService/IStockService.cs`(在接口末尾添加两个新方法) - [ ] **Step 1: 在 IStockService 接口添加两个新方法声明** 在 `UpdateStockInfoAsync` 方法声明之后、接口结束 `}` 之前添加: ```csharp /// /// 批量拆盘确认 - 一次性调用MES解绑整个托盘 /// /// 源托盘号 /// 操作结果 Task SplitPalletConfirmAsync(string palletCode); /// /// 批量组盘确认 - 一次性调用MES绑定整个托盘 /// /// 目标托盘号 /// 操作结果 Task GroupPalletConfirmAsync(string palletCode); ``` - [ ] **Step 2: Commit** ```bash git add WMS/WIDESEA_WMSServer/WIDESEA_IStockService/IStockService.cs git commit -m "feat(IStockService): 新增SplitPalletConfirmAsync和GroupPalletConfirmAsync接口 Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 4: 修改 StockService 实现 - SplitPalletConfirmAsync 和 GroupPalletConfirmAsync **Files:** - Modify: `WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockService.cs` - [ ] **Step 1: 添加 ISqlSugarClient 注入和 Dt_SplitTemp 实体** 在 `StockService` 类中添加: ```csharp using SqlSugar; using WIDESEA_Model.Models; using Newtonsoft.Json; ``` 在类中添加属性: ```csharp /// /// SqlSugar客户端(用于临时表操作) /// public ISqlSugarClient SqlSugarClient { get; } ``` 构造函数中注入: ```csharp public StockService( ..., ISqlSugarClient sqlSugarClient) // 添加到参数末尾 { ... SqlSugarClient = sqlSugarClient; } ``` - [ ] **Step 2: 实现 SplitPalletConfirmAsync 方法** 在类末尾(`UpdateStockInfoAsync` 方法之后、`CreateDetailHistory` 之前)添加: ```csharp /// /// 批量拆盘确认 - 一次性调用MES解绑整个托盘 /// /// 源托盘号 /// 操作结果 public async Task SplitPalletConfirmAsync(string palletCode) { WebResponseContent content = new WebResponseContent(); try { if (string.IsNullOrWhiteSpace(palletCode)) return content.Error("托盘号不能为空"); // 1. 从临时表读取电芯列表 var tempRecord = SqlSugarClient.Queryable() .Where(t => t.PalletCode == palletCode) .First(); if (tempRecord == null) return content.Error("未找到拆盘临时记录,请先执行拆盘操作"); var sfcList = JsonConvert.DeserializeObject>(tempRecord.SfcList); if (sfcList == null || !sfcList.Any()) return content.Error("临时表中电芯列表为空"); // 2. 调用MES解绑 var unbindRequest = new UnBindContainerRequest { EquipmentCode = StockConstants.MES_EQUIPMENT_CODE, ResourceCode = StockConstants.MES_RESOURCE_CODE, LocalTime = DateTime.Now, ContainCode = palletCode, SfcList = sfcList }; var unbindResult = _mesService.UnBindContainer(unbindRequest); if (unbindResult == null || unbindResult.Data == null || !unbindResult.Data.IsSuccess) { return content.Error($"MES解绑失败: {unbindResult?.Data?.Msg ?? unbindResult?.ErrorMessage ?? "未知错误"}"); } // 3. 删除临时表记录 SqlSugarClient.Deleteable().Where(t => t.PalletCode == palletCode).ExecuteCommand(); return content.OK("批量拆盘确认成功"); } catch (Exception ex) { return content.Error($"批量拆盘确认失败: {ex.Message}"); } } /// /// 批量组盘确认 - 一次性调用MES绑定整个托盘 /// /// 目标托盘号 /// 操作结果 public async Task GroupPalletConfirmAsync(string palletCode) { WebResponseContent content = new WebResponseContent(); try { if (string.IsNullOrWhiteSpace(palletCode)) return content.Error("托盘号不能为空"); // 1. 查询该托盘下的所有电芯明细 var stockInfo = StockInfoService.Repository.QueryFirst(s => s.PalletCode == palletCode); if (stockInfo == null) return content.Error("托盘不存在"); var details = StockInfoDetailService.Repository.QueryData(d => d.StockId == stockInfo.Id); if (!details.Any()) return content.Error("托盘下无电芯数据"); // 2. 调用MES绑定 var bindRequest = new BindContainerRequest { ContainerCode = palletCode, EquipmentCode = StockConstants.MES_EQUIPMENT_CODE, ResourceCode = StockConstants.MES_RESOURCE_CODE, LocalTime = DateTime.Now, OperationType = StockConstants.MES_BIND_OPERATION_TYPE, ContainerSfcList = details.Select(d => new ContainerSfcItem { Sfc = d.SerialNumber, Location = d.InboundOrderRowNo.ToString() }).ToList() }; var bindResult = _mesService.BindContainer(bindRequest); if (bindResult == null || bindResult.Data == null || !bindResult.Data.IsSuccess) { return content.Error($"MES绑定失败: {bindResult?.Data?.Msg ?? bindResult?.ErrorMessage ?? "未知错误"}"); } return content.OK("批量组盘确认成功"); } catch (Exception ex) { return content.Error($"批量组盘确认失败: {ex.Message}"); } } ``` - [ ] **Step 3: Commit** ```bash git add WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockService.cs git commit -m "feat(StockService): 实现SplitPalletConfirmAsync和GroupPalletConfirmAsync Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 5: 修改 SplitPalletAsync - 添加临时表幂等写入逻辑 **Files:** - Modify: `WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockService.cs` - [ ] **Step 1: 在 SplitPalletAsync 方法开头添加临时表写入逻辑** 在 `SplitPalletAsync` 方法的 `try` 块开头(`if (stock == null ...` 之后)、在事务 `ExecuteWithinTransactionAsync` 调用之前,添加: ```csharp // 幂等写入:检查临时表是否已有该托盘记录,无则写入 var existingTemp = SqlSugarClient.Queryable() .Where(t => t.PalletCode == stock.SourcePalletNo) .First(); if (existingTemp == null) { // 查询该托盘当前所有电芯,存入临时表 var sourceStockForTemp = StockInfoService.Repository.QueryFirst(s => s.PalletCode == stock.SourcePalletNo); if (sourceStockForTemp != null) { var allDetails = StockInfoDetailService.Repository.QueryData(d => d.StockId == sourceStockForTemp.Id); if (allDetails.Any()) { var sfcListJson = JsonConvert.SerializeObject(allDetails.Select(d => d.SerialNumber).ToList()); SqlSugarClient.Insertable(new Dt_SplitTemp { PalletCode = stock.SourcePalletNo, SfcList = sfcListJson, CreateTime = DateTime.Now }).ExecuteCommand(); } } } ``` 注意:这段代码在 `return await ExecuteWithinTransactionAsync(...)` 之前执行,不在事务内。 - [ ] **Step 2: Commit** ```bash git add WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockService.cs git commit -m "feat(SplitPalletAsync): 添加临时表幂等写入逻辑 Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 6: 修改 StockController - 添加新路由 **Files:** - Modify: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockController.cs` - [ ] **Step 1: 在 StockController 添加两个新路由** 在 `UpdateStockInfoAsync` 方法之后、类结束 `}` 之前添加: ```csharp /// /// 批量拆盘确认 - WCS拆盘任务全部取完时调用 /// /// 拆盘确认请求 /// 操作结果 [HttpPost("SplitPalletConfirm"), AllowAnonymous] public async Task SplitPalletConfirm([FromBody] SplitPalletConfirmRequestDto dto) { return await Service.SplitPalletConfirmAsync(dto.PalletCode); } /// /// 批量组盘确认 - WCS组盘任务全部放完时调用 /// /// 组盘确认请求 /// 操作结果 [HttpPost("GroupPalletConfirm"), AllowAnonymous] public async Task GroupPalletConfirm([FromBody] GroupPalletConfirmRequestDto dto) { return await Service.GroupPalletConfirmAsync(dto.PalletCode); } ``` 同时在文件顶部添加 using: ```csharp using WIDESEA_DTO.Stock; ``` - [ ] **Step 2: Commit** ```bash git add WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockController.cs git commit -m "feat(StockController): 新增SplitPalletConfirm和GroupPalletConfirm接口路由 Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 7: 数据库变更脚本 **Files:** - Create: `WMS/WIDESEA_WMSServer/Database/Scripts/20260416_Dt_SplitTemp.sql` - [ ] **Step 1: 创建临时表 DDL 脚本** ```sql -- 拆盘临时表:用于暂存拆盘任务电芯列表,供批量确认时使用 IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[Dt_SplitTemp]') AND type in (N'U')) BEGIN CREATE TABLE [dbo].[Dt_SplitTemp]( [Id] [int] IDENTITY(1,1) NOT NULL, [PalletCode] [nvarchar](50) NOT NULL, [SfcList] [nvarchar](max) NOT NULL, [CreateTime] [datetime] NOT NULL DEFAULT GETDATE(), CONSTRAINT [PK_Dt_SplitTemp] PRIMARY KEY CLUSTERED ([Id] ASC) ); -- 可选:添加唯一索引防止同一托盘重复写入 CREATE UNIQUE NONCLUSTERED INDEX [IX_Dt_SplitTemp_PalletCode] ON [dbo].[Dt_SplitTemp]([PalletCode] ASC); END GO ``` - [ ] **Step 2: Commit** ```bash git add WMS/WIDESEA_WMSServer/Database/Scripts/20260416_Dt_SplitTemp.sql git commit -m "feat(db): 新增Dt_SplitTemp拆盘临时表 Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 8: 构建验证 - [ ] **Step 1: 运行 dotnet build 验证编译通过** ```bash cd D:\Git\ShanMeiXinNengYuan\Code\WMS\WIDESEA_WMSServer dotnet build WIDESEA_WMSServer.sln ``` Expected: Build succeeded with no errors. --- ## 自检清单 - [ ] 所有 public 方法均有 XML 文档注释 - [ ] `Dt_SplitTemp.SfcList` 使用 `nvarchar(max)` 存储 JSON - [ ] `SplitPalletConfirmAsync` 读取临时表后删除记录 - [ ] `SplitPalletAsync` 中的临时表写入在事务外执行 - [ ] `GroupPalletConfirmAsync` 从 `Dt_StockInfoDetail` 查电芯,不查临时表 - [ ] 两个新 Controller 方法均标记 `[AllowAnonymous]` - [ ] 数据库脚本含 IF NOT EXISTS 防止重复创建