From ce1292c9cf37195b6abd2699dfc5d6cb3e143c9b Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期日, 12 四月 2026 23:38:19 +0800
Subject: [PATCH] feat(MES): 添加MES接口相关实体和DTO JS扩展文件至JSX格式并更新配置
---
Code/docs/superpowers/plans/2026-04-12-mes-integration-plan.md | 1933 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 1,933 insertions(+), 0 deletions(-)
diff --git a/Code/docs/superpowers/plans/2026-04-12-mes-integration-plan.md b/Code/docs/superpowers/plans/2026-04-12-mes-integration-plan.md
new file mode 100644
index 0000000..3322365
--- /dev/null
+++ b/Code/docs/superpowers/plans/2026-04-12-mes-integration-plan.md
@@ -0,0 +1,1933 @@
+# WMS搴撳瓨椤甸潰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:** 涓篧MS搴撳瓨淇℃伅椤甸潰鍜屽簱瀛樻槑缁嗛〉闈㈡坊鍔犳搷浣滃垪锛岃皟鐢∕ES绯荤粺鐨勮繘绔�/鍑虹珯/缁戝畾/瑙g粦/NG涓婃姤鎺ュ彛
+
+**Architecture:** 鍓嶇鍦ㄥ簱瀛樿〃鏍间腑娣诲姞鎿嶄綔鍒楋紝鐐瑰嚮鍚庡脊鍑虹‘璁ゅ璇濇锛岀‘璁ゅ悗璋冪敤鍚庣API锛涘悗绔帴鏀惰姹傦紝璋冪敤MES鏈嶅姟锛岃褰曞畬鏁存棩蹇楋紝杩斿洖缁撴灉
+
+**Tech Stack:** Vue 3, Element Plus, .NET 8, SqlSugar ORM, SQL Server, HttpClient
+
+---
+
+## 鏂囦欢缁撴瀯
+
+### 鍓嶇鏂囦欢
+
+```
+WMS/WIDESEA_WMSClient/src/
+鈹溾攢鈹� views/stock/
+鈹� 鈹溾攢鈹� stockInfo.vue # 淇敼锛氭坊鍔犳搷浣滃垪
+鈹� 鈹斺攢鈹� stockInfoDetail.vue # 淇敼锛氭坊鍔犳搷浣滃垪
+鈹溾攢鈹� components/
+鈹� 鈹斺攢鈹� MesConfirmDialog.vue # 鏂板锛歁ES纭瀵硅瘽妗�
+鈹斺攢鈹� api/
+ 鈹斺攢鈹� mes.js # 鏂板锛歁ES API璋冪敤
+```
+
+### 鍚庣鏂囦欢
+
+```
+WMS/WIDESEA_WMSServer/
+鈹溾攢鈹� Controllers/
+鈹� 鈹斺攢鈹� Stock/
+鈹� 鈹溾攢鈹� StockInfoController.cs # 淇敼锛氭坊鍔犺繘绔�/鍑虹珯鎺ュ彛
+鈹� 鈹斺攢鈹� StockInfoDetailController.cs # 淇敼锛氭坊鍔犵粦瀹�/瑙g粦/NG涓婃姤鎺ュ彛
+鈹溾攢鈹� Services/
+鈹� 鈹斺攢鈹� Mes/
+鈹� 鈹溾攢鈹� IMesLogService.cs # 鏂板锛氭棩蹇楁湇鍔℃帴鍙�
+鈹� 鈹斺攢鈹� MesLogService.cs # 鏂板锛氭棩蹇楁湇鍔″疄鐜�
+鈹溾攢鈹� DTO/
+鈹� 鈹溾攢鈹� Mes/
+鈹� 鈹� 鈹溾攢鈹� MesApiLogDto.cs # 鏂板锛氭棩蹇桪TO
+鈹� 鈹� 鈹溾攢鈹� InboundInContainerRequestDto.cs # 鏂板锛氳繘绔欒姹侱TO
+鈹� 鈹� 鈹溾攢鈹� OutboundInContainerRequestDto.cs # 鏂板锛氬嚭绔欒姹侱TO
+鈹� 鈹� 鈹溾攢鈹� BindContainerRequestDto.cs # 鏂板锛氱粦瀹氳姹侱TO
+鈹� 鈹� 鈹溾攢鈹� UnbindContainerRequestDto.cs # 鏂板锛氳В缁戣姹侱TO
+鈹� 鈹� 鈹斺攢鈹� ContainerNgReportRequestDto.cs # 鏂板锛歂G涓婃姤璇锋眰DTO
+鈹� 鈹斺攢鈹� Models/
+鈹� 鈹斺攢鈹� Mes/
+鈹� 鈹斺攢鈹� Dt_MesApiLog.cs # 鏂板锛氭棩蹇楀疄浣�
+鈹斺攢鈹� Database/
+ 鈹斺攢鈹� Scripts/
+ 鈹斺攢鈹� 20260412_MesApiLog.sql # 鏂板锛氭棩蹇楄〃鍒涘缓鑴氭湰
+```
+
+---
+
+## Task 1: 鍒涘缓鏁版嵁搴撹〃
+
+**Files:**
+- Create: `WMS/WIDESEA_WMSServer/Database/Scripts/20260412_MesApiLog.sql`
+
+- [ ] **Step 1: 鍒涘缓MES鎺ュ彛鏃ュ織琛⊿QL鑴氭湰**
+
+```sql
+-- =============================================
+-- WMS MES鎺ュ彛璋冪敤鏃ュ織琛�
+-- =============================================
+
+IF NOT EXISTS (SELECT * FROM sys.tables WHERE name = 'Dt_MesApiLog')
+BEGIN
+ CREATE TABLE Dt_MesApiLog (
+ Id BIGINT PRIMARY KEY IDENTITY(1,1),
+ ApiType NVARCHAR(50) NOT NULL, -- 鎺ュ彛绫诲瀷
+ RequestJson NVARCHAR(MAX) NULL, -- 璇锋眰JSON
+ ResponseJson NVARCHAR(MAX) NULL, -- 鍝嶅簲JSON
+ IsSuccess BIT NOT NULL DEFAULT 0, -- 鏄惁鎴愬姛
+ ErrorMessage NVARCHAR(500) NULL, -- 閿欒淇℃伅
+ ElapsedMs INT NOT NULL DEFAULT 0, -- 鑰楁椂(姣)
+ CreateDate DATETIME NOT NULL, -- 鍒涘缓鏃堕棿
+ Creator NVARCHAR(50) NULL, -- 鍒涘缓浜�
+ ModifyDate DATETIME NULL, -- 淇敼鏃堕棿
+ Modifier NVARCHAR(50) NULL -- 淇敼浜�
+ );
+
+ -- 鍒涘缓绱㈠紩
+ CREATE INDEX IX_MesApiLog_ApiType ON Dt_MesApiLog(ApiType);
+ CREATE INDEX IX_MesApiLog_CreateDate ON Dt_MesApiLog(CreateDate);
+ CREATE INDEX IX_MesApiLog_IsSuccess ON Dt_MesApiLog(IsSuccess);
+
+ PRINT 'MES鎺ュ彛鏃ュ織琛� Dt_MesApiLog 鍒涘缓鎴愬姛';
+END
+ELSE
+BEGIN
+ PRINT 'MES鎺ュ彛鏃ュ織琛� Dt_MesApiLog 宸插瓨鍦�';
+END
+
+-- 鎻掑叆MES绯荤粺閰嶇疆
+IF NOT EXISTS (SELECT * FROM Dt_SystemConfig WHERE ConfigKey = 'MES_EquipmentCode')
+BEGIN
+ INSERT INTO Dt_SystemConfig (ConfigKey, ConfigValue, Description, CreateDate, Modifier)
+ VALUES ('MES_EquipmentCode', 'WCS_001', 'MES璁惧缂栫爜', GETDATE(), 'System');
+
+ INSERT INTO Dt_SystemConfig (ConfigKey, ConfigValue, Description, CreateDate, Modifier)
+ VALUES ('MES_ResourceCode', 'RESOURCE_001', 'MES璧勬簮缂栫爜', GETDATE(), 'System');
+
+ INSERT INTO Dt_SystemConfig (ConfigKey, ConfigValue, Description, CreateDate, Modifier)
+ VALUES ('MES_ApiBaseUrl', 'http://mes-server/api', 'MES鎺ュ彛鍦板潃', GETDATE(), 'System');
+
+ INSERT INTO Dt_SystemConfig (ConfigKey, ConfigValue, Description, CreateDate, Modifier)
+ VALUES ('MES_TimeoutSeconds', '30', 'MES鎺ュ彛瓒呮椂鏃堕棿(绉�)', GETDATE(), 'System');
+
+ PRINT 'MES绯荤粺閰嶇疆鎻掑叆鎴愬姛';
+END
+```
+
+- [ ] **Step 2: 鎵цSQL鑴氭湰鍒涘缓琛�**
+
+Run: 鍦⊿QL Server Management Studio涓墽琛岃鑴氭湰
+Expected: 琛ㄥ垱寤烘垚鍔燂紝閰嶇疆鎻掑叆鎴愬姛
+
+- [ ] **Step 3: 鎻愪氦**
+
+```bash
+git add WMS/WIDESEA_WMSServer/Database/Scripts/20260412_MesApiLog.sql
+git commit -m "feat(MES): 娣诲姞MES鎺ュ彛鏃ュ織琛ㄥ拰绯荤粺閰嶇疆
+
+- 鍒涘缓 Dt_MesApiLog 琛ㄨ褰曟帴鍙h皟鐢ㄦ棩蹇�
+- 娣诲姞MES鐩稿叧绯荤粺閰嶇疆椤癸紙璁惧缂栫爜銆佽祫婧愮紪鐮併�佹帴鍙e湴鍧�绛夛級
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 2: 鍒涘缓鍚庣瀹炰綋鍜孌TO
+
+**Files:**
+- Create: `WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Mes/Dt_MesApiLog.cs`
+- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Mes/MesApiLogDto.cs`
+- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Mes/InboundInContainerRequestDto.cs`
+- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Mes/OutboundInContainerRequestDto.cs`
+- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Mes/BindContainerRequestDto.cs`
+- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Mes/UnbindContainerRequestDto.cs`
+- Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/Mes/ContainerNgReportRequestDto.cs`
+
+- [ ] **Step 1: 鍒涘缓MES鏃ュ織瀹炰綋**
+
+```csharp
+using SqlSugar;
+using System;
+
+namespace WIDESEA_Model.Models.Mes
+{
+ /// <summary>
+ /// MES鎺ュ彛璋冪敤鏃ュ織瀹炰綋
+ /// </summary>
+ [SugarTable("Dt_MesApiLog")]
+ public class Dt_MesApiLog
+ {
+ /// <summary>
+ /// 涓婚敭ID
+ /// </summary>
+ [SugarColumn(IsPrimaryKey = true, IsIdentity = true)]
+ public long Id { get; set; }
+
+ /// <summary>
+ /// 鎺ュ彛绫诲瀷锛欼nboundInContainer, OutboundInContainer, BindContainer, UnbindContainer, ContainerNgReport
+ /// </summary>
+ [SugarColumn(Length = 50, IsNullable = false)]
+ public string ApiType { get; set; }
+
+ /// <summary>
+ /// 璇锋眰JSON
+ /// </summary>
+ [SugarColumn(ColumnDataType = "nvarchar(max)", IsNullable = true)]
+ public string RequestJson { get; set; }
+
+ /// <summary>
+ /// 鍝嶅簲JSON
+ /// </summary>
+ [SugarColumn(ColumnDataType = "nvarchar(max)", IsNullable = true)]
+ public string ResponseJson { get; set; }
+
+ /// <summary>
+ /// 鏄惁鎴愬姛
+ /// </summary>
+ [SugarColumn(IsNullable = false)]
+ public bool IsSuccess { get; set; }
+
+ /// <summary>
+ /// 閿欒淇℃伅
+ /// </summary>
+ [SugarColumn(Length = 500, IsNullable = true)]
+ public string ErrorMessage { get; set; }
+
+ /// <summary>
+ /// 鑰楁椂锛堟绉掞級
+ /// </summary>
+ [SugarColumn(IsNullable = false)]
+ public int ElapsedMs { get; set; }
+
+ /// <summary>
+ /// 鍒涘缓鏃堕棿
+ /// </summary>
+ [SugarColumn(IsNullable = false)]
+ public DateTime CreateDate { get; set; }
+
+ /// <summary>
+ /// 鍒涘缓浜�
+ /// </summary>
+ [SugarColumn(Length = 50, IsNullable = true)]
+ public string Creator { get; set; }
+
+ /// <summary>
+ /// 淇敼鏃堕棿
+ /// </summary>
+ public DateTime? ModifyDate { get; set; }
+
+ /// <summary>
+ /// 淇敼浜�
+ /// </summary>
+ [SugarColumn(Length = 50, IsNullable = true)]
+ public string Modifier { get; set; }
+ }
+}
+```
+
+- [ ] **Step 2: 鍒涘缓MES鏃ュ織DTO**
+
+```csharp
+namespace WIDESEA_DTO.Mes
+{
+ /// <summary>
+ /// MES鎺ュ彛鏃ュ織DTO
+ /// </summary>
+ public class MesApiLogDto
+ {
+ /// <summary>
+ /// 鎺ュ彛绫诲瀷
+ /// </summary>
+ public string ApiType { get; set; }
+
+ /// <summary>
+ /// 璇锋眰JSON
+ /// </summary>
+ public string RequestJson { get; set; }
+
+ /// <summary>
+ /// 鍝嶅簲JSON
+ /// </summary>
+ public string ResponseJson { get; set; }
+
+ /// <summary>
+ /// 鏄惁鎴愬姛
+ /// </summary>
+ public bool IsSuccess { get; set; }
+
+ /// <summary>
+ /// 閿欒淇℃伅
+ /// </summary>
+ public string ErrorMessage { get; set; }
+
+ /// <summary>
+ /// 鑰楁椂锛堟绉掞級
+ /// </summary>
+ public int ElapsedMs { get; set; }
+
+ /// <summary>
+ /// 鍒涘缓浜�
+ /// </summary>
+ public string Creator { get; set; }
+ }
+}
+```
+
+- [ ] **Step 3: 鍒涘缓杩涚珯璇锋眰DTO**
+
+```csharp
+namespace WIDESEA_DTO.Mes
+{
+ /// <summary>
+ /// 鎵樼洏杩涚珯璇锋眰DTO
+ /// </summary>
+ public class InboundInContainerRequestDto
+ {
+ /// <summary>
+ /// 鎵樼洏缂栧彿
+ /// </summary>
+ public string PalletCode { get; set; }
+
+ /// <summary>
+ /// 搴撳瓨ID
+ /// </summary>
+ public long StockId { get; set; }
+ }
+}
+```
+
+- [ ] **Step 4: 鍒涘缓鍑虹珯璇锋眰DTO**
+
+```csharp
+using System.Collections.Generic;
+
+namespace WIDESEA_DTO.Mes
+{
+ /// <summary>
+ /// 鎵樼洏鍑虹珯璇锋眰DTO
+ /// </summary>
+ public class OutboundInContainerRequestDto
+ {
+ /// <summary>
+ /// 鎵樼洏缂栧彿
+ /// </summary>
+ public string PalletCode { get; set; }
+
+ /// <summary>
+ /// 搴撳瓨ID
+ /// </summary>
+ public long StockId { get; set; }
+
+ /// <summary>
+ /// 浜у搧鍙傛暟鍒楄〃锛堝彲閫夛級
+ /// </summary>
+ public List<ParamItemDto> ParamList { get; set; }
+
+ /// <summary>
+ /// 鍙傛暟椤笵TO
+ /// </summary>
+ public class ParamItemDto
+ {
+ /// <summary>
+ /// 鍙傛暟缂栫爜
+ /// </summary>
+ public string ParamCode { get; set; }
+
+ /// <summary>
+ /// 鍙傛暟鍊�
+ /// </summary>
+ public string ParamValue { get; set; }
+
+ /// <summary>
+ /// 閲囬泦鏃堕棿
+ /// </summary>
+ public string CollectionTime { get; set; }
+ }
+ }
+}
+```
+
+- [ ] **Step 5: 鍒涘缓缁戝畾璇锋眰DTO**
+
+```csharp
+using System.Collections.Generic;
+
+namespace WIDESEA_DTO.Mes
+{
+ /// <summary>
+ /// 鎵樼洏鐢佃姱缁戝畾璇锋眰DTO
+ /// </summary>
+ public class BindContainerRequestDto
+ {
+ /// <summary>
+ /// 鎵樼洏缂栧彿
+ /// </summary>
+ public string PalletCode { get; set; }
+
+ /// <summary>
+ /// 鐢佃姱鐮佸垪琛�
+ /// </summary>
+ public List<string> SfcList { get; set; }
+
+ /// <summary>
+ /// 浣嶇疆淇℃伅
+ /// </summary>
+ public string Location { get; set; }
+
+ /// <summary>
+ /// 鎿嶄綔绫诲瀷锛�0-榛樿 1-杩涚珯 2-鍑虹珯 3-杩涘嚭绔�
+ /// </summary>
+ public int OperationType { get; set; } = 1;
+ }
+}
+```
+
+- [ ] **Step 6: 鍒涘缓瑙g粦璇锋眰DTO**
+
+```csharp
+using System.Collections.Generic;
+
+namespace WIDESEA_DTO.Mes
+{
+ /// <summary>
+ /// 鎵樼洏鐢佃姱瑙g粦璇锋眰DTO
+ /// </summary>
+ public class UnbindContainerRequestDto
+ {
+ /// <summary>
+ /// 鎵樼洏缂栧彿
+ /// </summary>
+ public string PalletCode { get; set; }
+
+ /// <summary>
+ /// 鐢佃姱鐮佸垪琛�
+ /// </summary>
+ public List<string> SfcList { get; set; }
+ }
+}
+```
+
+- [ ] **Step 7: 鍒涘缓NG涓婃姤璇锋眰DTO**
+
+```csharp
+using System.Collections.Generic;
+
+namespace WIDESEA_DTO.Mes
+{
+ /// <summary>
+ /// 鎵樼洏NG鐢佃姱涓婃姤璇锋眰DTO
+ /// </summary>
+ public class ContainerNgReportRequestDto
+ {
+ /// <summary>
+ /// 鎵樼洏缂栧彿
+ /// </summary>
+ public string PalletCode { get; set; }
+
+ /// <summary>
+ /// NG鐢佃姱鍒楄〃
+ /// </summary>
+ public List<NgSfcItemDto> NgSfcList { get; set; }
+
+ /// <summary>
+ /// NG鐢佃姱椤笵TO
+ /// </summary>
+ public class NgSfcItemDto
+ {
+ /// <summary>
+ /// 浜у搧鏉$爜
+ /// </summary>
+ public string Sfc { get; set; }
+
+ /// <summary>
+ /// NG浠g爜
+ /// </summary>
+ public string NgCode { get; set; }
+
+ /// <summary>
+ /// NG璁惧
+ /// </summary>
+ public string NgEquipmentCode { get; set; }
+
+ /// <summary>
+ /// NG璧勬簮
+ /// </summary>
+ public string NgResourceCode { get; set; }
+ }
+ }
+}
+```
+
+- [ ] **Step 8: 鎻愪氦**
+
+```bash
+git add WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Mes/Dt_MesApiLog.cs
+git add WMS/WIDESEA_WMSServer/WIDESEA_DTO/Mes/*.cs
+git commit -m "feat(MES): 娣诲姞MES鎺ュ彛鐩稿叧瀹炰綋鍜孌TO
+
+- 娣诲姞 Dt_MesApiLog 鏃ュ織瀹炰綋
+- 娣诲姞 MesApiLogDto 鍙婂悇鎺ュ彛璇锋眰DTO
+- 鏀寔杩涚珯銆佸嚭绔欍�佺粦瀹氥�佽В缁戙�丯G涓婃姤鎺ュ彛
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 3: 鍒涘缓MES鏃ュ織鏈嶅姟
+
+**Files:**
+- Create: `WMS/WIDESEA_WMSServer/WIDESEA_IMesService/IMesLogService.cs`
+- Create: `WMS/WIDESEA_WMSServer/WIDESEA_MesService/MesLogService.cs`
+
+- [ ] **Step 1: 鍒涘缓MES鏃ュ織鏈嶅姟鎺ュ彛**
+
+```csharp
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using WIDESEA_Core;
+using WIDESEA_DTO.Mes;
+
+namespace WIDESEA_IMesService
+{
+ /// <summary>
+ /// MES鎺ュ彛鏃ュ織鏈嶅姟鎺ュ彛
+ /// </summary>
+ public interface IMesLogService : IDependency
+ {
+ /// <summary>
+ /// 璁板綍MES鎺ュ彛璋冪敤鏃ュ織
+ /// </summary>
+ /// <param name="log">鏃ュ織DTO</param>
+ /// <returns>鏄惁璁板綍鎴愬姛</returns>
+ Task<bool> LogAsync(MesApiLogDto log);
+
+ /// <summary>
+ /// 鑾峰彇鏈�杩戠殑MES鎺ュ彛璋冪敤璁板綍
+ /// </summary>
+ /// <param name="apiType">鎺ュ彛绫诲瀷</param>
+ /// <param name="count">璁板綍鏁伴噺</param>
+ /// <returns>鏃ュ織鍒楄〃</returns>
+ Task<List<MesApiLogDto>> GetRecentLogsAsync(string apiType, int count = 50);
+ }
+}
+```
+
+- [ ] **Step 2: 鍒涘缓MES鏃ュ織鏈嶅姟瀹炵幇**
+
+```csharp
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using WIDESEA_Core;
+using WIDESEA_DTO.Mes;
+using WIDESEA_IMesService;
+using WIDESEA_Model.Models.Mes;
+
+namespace WIDESEA_MesService
+{
+ /// <summary>
+ /// MES鎺ュ彛鏃ュ織鏈嶅姟瀹炵幇
+ /// </summary>
+ public class MesLogService : IMesLogService
+ {
+ private readonly ISqlSugarClient _db;
+
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ public MesLogService(ISqlSugarClient db)
+ {
+ _db = db;
+ }
+
+ /// <summary>
+ /// 璁板綍MES鎺ュ彛璋冪敤鏃ュ織
+ /// </summary>
+ public async Task<bool> LogAsync(MesApiLogDto log)
+ {
+ try
+ {
+ var entity = new Dt_MesApiLog
+ {
+ ApiType = log.ApiType,
+ RequestJson = log.RequestJson,
+ ResponseJson = log.ResponseJson,
+ IsSuccess = log.IsSuccess,
+ ErrorMessage = log.ErrorMessage,
+ ElapsedMs = log.ElapsedMs,
+ CreateDate = DateTime.Now,
+ Creator = log.Creator
+ };
+
+ var result = await _db.Insertable(entity).ExecuteCommandAsync();
+ return result > 0;
+ }
+ catch (Exception ex)
+ {
+ // 璁板綍鏃ュ織澶辫触涓嶅奖鍝嶄富娴佺▼
+ Console.WriteLine($"璁板綍MES鏃ュ織澶辫触: {ex.Message}");
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// 鑾峰彇鏈�杩戠殑MES鎺ュ彛璋冪敤璁板綍
+ /// </summary>
+ public async Task<List<MesApiLogDto>> GetRecentLogsAsync(string apiType, int count = 50)
+ {
+ try
+ {
+ var entities = await _db.Queryable<Dt_MesApiLog>()
+ .Where(x => x.ApiType == apiType)
+ .OrderByDescending(x => x.CreateDate)
+ .Take(count)
+ .ToListAsync();
+
+ return entities.Select(e => new MesApiLogDto
+ {
+ ApiType = e.ApiType,
+ RequestJson = e.RequestJson,
+ ResponseJson = e.ResponseJson,
+ IsSuccess = e.IsSuccess,
+ ErrorMessage = e.ErrorMessage,
+ ElapsedMs = e.ElapsedMs,
+ Creator = e.Creator
+ }).ToList();
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"鑾峰彇MES鏃ュ織澶辫触: {ex.Message}");
+ return new List<MesApiLogDto>();
+ }
+ }
+ }
+}
+```
+
+- [ ] **Step 3: 鎻愪氦**
+
+```bash
+git add WMS/WIDESEA_WMSServer/WIDESEA_IMesService/IMesLogService.cs
+git add WMS/WIDESEA_WMSServer/WIDESEA_MesService/MesLogService.cs
+git commit -m "feat(MES): 娣诲姞MES鏃ュ織鏈嶅姟
+
+- 瀹炵幇 IMesLogService 鎺ュ彛
+- 鏀寔璁板綍鍜屾煡璇ES鎺ュ彛璋冪敤鏃ュ織
+- 寮傚父澶勭悊涓嶅奖鍝嶄富娴佺▼
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 4: 鎵╁睍StockInfoController娣诲姞杩涚珯/鍑虹珯鎺ュ彛
+
+**Files:**
+- Modify: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoController.cs`
+
+- [ ] **Step 1: 鍦⊿tockInfoController涓坊鍔燤ES鎺ュ彛鏂规硶**
+
+鍦ㄧ幇鏈塖tockInfoController绫讳腑娣诲姞浠ヤ笅鏂规硶锛�
+
+```csharp
+/// <summary>
+/// MES鏃ュ織鏈嶅姟
+/// </summary>
+private readonly IMesLogService _mesLogService;
+
+/// <summary>
+/// MES鏈嶅姟锛堝凡鍦ㄩ」鐩腑瀹氫箟锛�
+/// </summary>
+private readonly IMesService _mesService;
+
+/// <summary>
+/// 绯荤粺閰嶇疆鏈嶅姟
+/// </summary>
+private readonly ISystemConfigService _configService;
+
+// 鍦ㄦ瀯閫犲嚱鏁颁腑娉ㄥ叆杩欎簺鏈嶅姟锛堝鏋滃皻鏈敞鍏ワ級
+```
+
+娣诲姞杩涚珯鎺ュ彛锛�
+
+```csharp
+/// <summary>
+/// 鎵樼洏杩涚珯 - 璋冪敤MES鎺ュ彛
+/// </summary>
+/// <param name="dto">杩涚珯璇锋眰DTO</param>
+/// <returns>鎿嶄綔缁撴灉</returns>
+[HttpPost("inboundInContainer")]
+[Permission("MES_INBOUND")]
+public async Task<WebResponseContent> InboundInContainer([FromBody] InboundInContainerRequestDto dto)
+{
+ var response = new WebResponseContent();
+ var stopwatch = System.Diagnostics.Stopwatch.StartNew();
+
+ try
+ {
+ // 1. 鍙傛暟楠岃瘉
+ if (string.IsNullOrWhiteSpace(dto.PalletCode))
+ {
+ return response.Error("鎵樼洏缂栧彿涓嶈兘涓虹┖");
+ }
+
+ // 2. 鏌ヨ搴撳瓨淇℃伅
+ var stockInfo = await _service.FindAsIQueryable(x => x.Id == dto.StockId)
+ .FirstAsync();
+
+ if (stockInfo == null)
+ {
+ return response.Error("搴撳瓨淇℃伅涓嶅瓨鍦�");
+ }
+
+ // 3. 楠岃瘉搴撳瓨鐘舵�侊紙浠�"寰呭叆搴�"鐘舵�佸厑璁歌繘绔欙級
+ if (stockInfo.Status != 0) // 鍋囪0=寰呭叆搴�
+ {
+ return response.Error($"褰撳墠搴撳瓨鐘舵�佷笉鍏佽杩涚珯鎿嶄綔");
+ }
+
+ // 4. 鑾峰彇绯荤粺閰嶇疆
+ var equipmentCode = await _configService.GetConfigValueAsync("MES_EquipmentCode");
+ var resourceCode = await _configService.GetConfigValueAsync("MES_ResourceCode");
+
+ if (string.IsNullOrWhiteSpace(equipmentCode) || string.IsNullOrWhiteSpace(resourceCode))
+ {
+ return response.Error("MES绯荤粺閰嶇疆涓嶅畬鏁达紝璇疯仈绯荤鐞嗗憳");
+ }
+
+ // 5. 鏋勯�燤ES璇锋眰
+ var mesRequest = new InboundInContainerRequest
+ {
+ EquipmentCode = equipmentCode,
+ ResourceCode = resourceCode,
+ LocalTime = DateTime.Now,
+ ContainerCode = dto.PalletCode
+ };
+
+ string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
+
+ // 6. 璋冪敤MES鎺ュ彛
+ var mesResult = await _mesService.InboundInContainer(mesRequest);
+ stopwatch.Stop();
+
+ // 7. 璁板綍鏃ュ織
+ await _mesLogService.LogAsync(new MesApiLogDto
+ {
+ ApiType = "InboundInContainer",
+ RequestJson = requestJson,
+ ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
+ IsSuccess = mesResult.Success,
+ ErrorMessage = mesResult.ErrorMessage,
+ ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
+ Creator = UserContext.Current.UserName
+ });
+
+ // 8. 杩斿洖缁撴灉
+ if (mesResult.Success)
+ {
+ return response.OK("鎵樼洏杩涚珯鎴愬姛");
+ }
+ else
+ {
+ return response.Error($"MES鎺ュ彛璋冪敤澶辫触: {mesResult.ErrorMessage}");
+ }
+ }
+ catch (System.Exception ex)
+ {
+ stopwatch.Stop();
+
+ // 璁板綍閿欒鏃ュ織
+ await _mesLogService.LogAsync(new MesApiLogDto
+ {
+ ApiType = "InboundInContainer",
+ IsSuccess = false,
+ ErrorMessage = ex.Message,
+ ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
+ Creator = UserContext.Current.UserName
+ });
+
+ return response.Error($"鎵樼洏杩涚珯澶辫触: {ex.Message}");
+ }
+}
+```
+
+娣诲姞鍑虹珯鎺ュ彛锛�
+
+```csharp
+/// <summary>
+/// 鎵樼洏鍑虹珯 - 璋冪敤MES鎺ュ彛
+/// </summary>
+/// <param name="dto">鍑虹珯璇锋眰DTO</param>
+/// <returns>鎿嶄綔缁撴灉</returns>
+[HttpPost("outboundInContainer")]
+[Permission("MES_OUTBOUND")]
+public async Task<WebResponseContent> OutboundInContainer([FromBody] OutboundInContainerRequestDto dto)
+{
+ var response = new WebResponseContent();
+ var stopwatch = System.Diagnostics.Stopwatch.StartNew();
+
+ try
+ {
+ // 1. 鍙傛暟楠岃瘉
+ if (string.IsNullOrWhiteSpace(dto.PalletCode))
+ {
+ return response.Error("鎵樼洏缂栧彿涓嶈兘涓虹┖");
+ }
+
+ // 2. 鏌ヨ搴撳瓨淇℃伅
+ var stockInfo = await _service.FindAsIQueryable(x => x.Id == dto.StockId)
+ .FirstAsync();
+
+ if (stockInfo == null)
+ {
+ return response.Error("搴撳瓨淇℃伅涓嶅瓨鍦�");
+ }
+
+ // 3. 楠岃瘉搴撳瓨鐘舵�侊紙"鍦ㄥ簱"鎴�"鍑哄簱涓�"鐘舵�佸厑璁稿嚭绔欙級
+ if (stockInfo.Status != 1 && stockInfo.Status != 2) // 鍋囪1=鍦ㄥ簱, 2=鍑哄簱涓�
+ {
+ return response.Error($"褰撳墠搴撳瓨鐘舵�佷笉鍏佽鍑虹珯鎿嶄綔");
+ }
+
+ // 4. 鑾峰彇绯荤粺閰嶇疆
+ var equipmentCode = await _configService.GetConfigValueAsync("MES_EquipmentCode");
+ var resourceCode = await _configService.GetConfigValueAsync("MES_ResourceCode");
+
+ if (string.IsNullOrWhiteSpace(equipmentCode) || string.IsNullOrWhiteSpace(resourceCode))
+ {
+ return response.Error("MES绯荤粺閰嶇疆涓嶅畬鏁达紝璇疯仈绯荤鐞嗗憳");
+ }
+
+ // 5. 鏋勯�燤ES璇锋眰
+ var mesRequest = new OutboundInContainerRequest
+ {
+ EquipmentCode = equipmentCode,
+ ResourceCode = resourceCode,
+ LocalTime = DateTime.Now,
+ ContainerCode = dto.PalletCode,
+ ParamList = dto.ParamList?.Select(p => new ParamItem
+ {
+ ParamCode = p.ParamCode,
+ ParamValue = p.ParamValue,
+ CollectionTime = DateTime.TryParse(p.CollectionTime, out var ct) ? ct : DateTime.Now
+ }).ToList()
+ };
+
+ string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
+
+ // 6. 璋冪敤MES鎺ュ彛
+ var mesResult = await _mesService.OutboundInContainer(mesRequest);
+ stopwatch.Stop();
+
+ // 7. 璁板綍鏃ュ織
+ await _mesLogService.LogAsync(new MesApiLogDto
+ {
+ ApiType = "OutboundInContainer",
+ RequestJson = requestJson,
+ ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
+ IsSuccess = mesResult.Success,
+ ErrorMessage = mesResult.ErrorMessage,
+ ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
+ Creator = UserContext.Current.UserName
+ });
+
+ // 8. 杩斿洖缁撴灉
+ if (mesResult.Success)
+ {
+ return response.OK("鎵樼洏鍑虹珯鎴愬姛");
+ }
+ else
+ {
+ return response.Error($"MES鎺ュ彛璋冪敤澶辫触: {mesResult.ErrorMessage}");
+ }
+ }
+ catch (System.Exception ex)
+ {
+ stopwatch.Stop();
+
+ // 璁板綍閿欒鏃ュ織
+ await _mesLogService.LogAsync(new MesApiLogDto
+ {
+ ApiType = "OutboundInContainer",
+ IsSuccess = false,
+ ErrorMessage = ex.Message,
+ ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
+ Creator = UserContext.Current.UserName
+ });
+
+ return response.Error($"鎵樼洏鍑虹珯澶辫触: {ex.Message}");
+ }
+}
+```
+
+- [ ] **Step 2: 娣诲姞蹇呰鐨剈sing璇彞**
+
+鍦ㄦ枃浠堕《閮ㄦ坊鍔狅細
+
+```csharp
+using WIDESEA_DTO.MES;
+using WIDESEA_DTO.Mes;
+using WIDESEA_IMesService;
+```
+
+- [ ] **Step 3: 鎻愪氦**
+
+```bash
+git add WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoController.cs
+git commit -m "feat(MES): 搴撳瓨淇℃伅椤甸潰娣诲姞杩涚珯/鍑虹珯鎺ュ彛
+
+- POST /api/StockInfo/inboundInContainer 鎵樼洏杩涚珯
+- POST /api/StockInfo/outboundInContainer 鎵樼洏鍑虹珯
+- 娣诲姞搴撳瓨鐘舵�佹牎楠�
+- 璁板綍瀹屾暣璋冪敤鏃ュ織
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 5: 鎵╁睍StockInfoDetailController娣诲姞缁戝畾/瑙g粦/NG涓婃姤鎺ュ彛
+
+**Files:**
+- Modify: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoDetailController.cs`
+
+- [ ] **Step 1: 鍦⊿tockInfoDetailController涓坊鍔燤ES鎺ュ彛鏂规硶**
+
+鍦ㄧ幇鏈塖tockInfoDetailController绫讳腑娣诲姞渚濊禆娉ㄥ叆鍜屾帴鍙f柟娉曪細
+
+```csharp
+/// <summary>
+/// MES鏃ュ織鏈嶅姟
+/// </summary>
+private readonly IMesLogService _mesLogService;
+
+/// <summary>
+/// MES鏈嶅姟
+/// </summary>
+private readonly IMesService _mesService;
+
+/// <summary>
+/// 绯荤粺閰嶇疆鏈嶅姟
+/// </summary>
+private readonly ISystemConfigService _configService;
+```
+
+娣诲姞缁戝畾鎺ュ彛锛�
+
+```csharp
+/// <summary>
+/// 鎵樼洏鐢佃姱缁戝畾 - 璋冪敤MES鎺ュ彛
+/// </summary>
+/// <param name="dto">缁戝畾璇锋眰DTO</param>
+/// <returns>鎿嶄綔缁撴灉</returns>
+[HttpPost("bindContainer")]
+[Permission("MES_BIND")]
+public async Task<WebResponseContent> BindContainer([FromBody] BindContainerRequestDto dto)
+{
+ var response = new WebResponseContent();
+ var stopwatch = System.Diagnostics.Stopwatch.StartNew();
+
+ try
+ {
+ // 1. 鍙傛暟楠岃瘉
+ if (string.IsNullOrWhiteSpace(dto.PalletCode))
+ {
+ return response.Error("鎵樼洏缂栧彿涓嶈兘涓虹┖");
+ }
+
+ if (dto.SfcList == null || !dto.SfcList.Any())
+ {
+ return response.Error("鐢佃姱鐮佸垪琛ㄤ笉鑳戒负绌�");
+ }
+
+ // 2. 鑾峰彇绯荤粺閰嶇疆
+ var equipmentCode = await _configService.GetConfigValueAsync("MES_EquipmentCode");
+ var resourceCode = await _configService.GetConfigValueAsync("MES_ResourceCode");
+
+ if (string.IsNullOrWhiteSpace(equipmentCode) || string.IsNullOrWhiteSpace(resourceCode))
+ {
+ return response.Error("MES绯荤粺閰嶇疆涓嶅畬鏁达紝璇疯仈绯荤鐞嗗憳");
+ }
+
+ // 3. 鏋勯�燤ES璇锋眰
+ var mesRequest = new BindContainerRequest
+ {
+ EquipmentCode = equipmentCode,
+ ResourceCode = resourceCode,
+ LocalTime = DateTime.Now,
+ ContainerCode = dto.PalletCode,
+ ContainerSfcList = dto.SfcList.Select(sfc => new ContainerSfcItem
+ {
+ Sfc = sfc,
+ Location = dto.Location ?? ""
+ }).ToList(),
+ OperationType = dto.OperationType
+ };
+
+ string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
+
+ // 4. 璋冪敤MES鎺ュ彛
+ var mesResult = await _mesService.BindContainer(mesRequest);
+ stopwatch.Stop();
+
+ // 5. 璁板綍鏃ュ織
+ await _mesLogService.LogAsync(new MesApiLogDto
+ {
+ ApiType = "BindContainer",
+ RequestJson = requestJson,
+ ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
+ IsSuccess = mesResult.Success,
+ ErrorMessage = mesResult.ErrorMessage,
+ ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
+ Creator = UserContext.Current.UserName
+ });
+
+ // 6. 杩斿洖缁撴灉
+ if (mesResult.Success)
+ {
+ return response.OK($"鎴愬姛缁戝畾 {dto.SfcList.Count} 涓數鑺�");
+ }
+ else
+ {
+ return response.Error($"MES鎺ュ彛璋冪敤澶辫触: {mesResult.ErrorMessage}");
+ }
+ }
+ catch (System.Exception ex)
+ {
+ stopwatch.Stop();
+
+ await _mesLogService.LogAsync(new MesApiLogDto
+ {
+ ApiType = "BindContainer",
+ IsSuccess = false,
+ ErrorMessage = ex.Message,
+ ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
+ Creator = UserContext.Current.UserName
+ });
+
+ return response.Error($"鐢佃姱缁戝畾澶辫触: {ex.Message}");
+ }
+}
+```
+
+娣诲姞瑙g粦鎺ュ彛锛�
+
+```csharp
+/// <summary>
+/// 鎵樼洏鐢佃姱瑙g粦 - 璋冪敤MES鎺ュ彛
+/// </summary>
+/// <param name="dto">瑙g粦璇锋眰DTO</param>
+/// <returns>鎿嶄綔缁撴灉</returns>
+[HttpPost("unbindContainer")]
+[Permission("MES_UNBIND")]
+public async Task<WebResponseContent> UnbindContainer([FromBody] UnbindContainerRequestDto dto)
+{
+ var response = new WebResponseContent();
+ var stopwatch = System.Diagnostics.Stopwatch.StartNew();
+
+ try
+ {
+ // 1. 鍙傛暟楠岃瘉
+ if (string.IsNullOrWhiteSpace(dto.PalletCode))
+ {
+ return response.Error("鎵樼洏缂栧彿涓嶈兘涓虹┖");
+ }
+
+ if (dto.SfcList == null || !dto.SfcList.Any())
+ {
+ return response.Error("鐢佃姱鐮佸垪琛ㄤ笉鑳戒负绌�");
+ }
+
+ // 2. 鑾峰彇绯荤粺閰嶇疆
+ var equipmentCode = await _configService.GetConfigValueAsync("MES_EquipmentCode");
+ var resourceCode = await _configService.GetConfigValueAsync("MES_ResourceCode");
+
+ if (string.IsNullOrWhiteSpace(equipmentCode) || string.IsNullOrWhiteSpace(resourceCode))
+ {
+ return response.Error("MES绯荤粺閰嶇疆涓嶅畬鏁达紝璇疯仈绯荤鐞嗗憳");
+ }
+
+ // 3. 鏋勯�燤ES璇锋眰
+ var mesRequest = new UnBindContainerRequest
+ {
+ EquipmentCode = equipmentCode,
+ ResourceCode = resourceCode,
+ LocalTime = DateTime.Now,
+ ContainCode = dto.PalletCode,
+ SfcList = dto.SfcList
+ };
+
+ string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
+
+ // 4. 璋冪敤MES鎺ュ彛
+ var mesResult = await _mesService.UnBindContainer(mesRequest);
+ stopwatch.Stop();
+
+ // 5. 璁板綍鏃ュ織
+ await _mesLogService.LogAsync(new MesApiLogDto
+ {
+ ApiType = "UnbindContainer",
+ RequestJson = requestJson,
+ ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
+ IsSuccess = mesResult.Success,
+ ErrorMessage = mesResult.ErrorMessage,
+ ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
+ Creator = UserContext.Current.UserName
+ });
+
+ // 6. 杩斿洖缁撴灉
+ if (mesResult.Success)
+ {
+ return response.OK($"鎴愬姛瑙g粦 {dto.SfcList.Count} 涓數鑺�");
+ }
+ else
+ {
+ return response.Error($"MES鎺ュ彛璋冪敤澶辫触: {mesResult.ErrorMessage}");
+ }
+ }
+ catch (System.Exception ex)
+ {
+ stopwatch.Stop();
+
+ await _mesLogService.LogAsync(new MesApiLogDto
+ {
+ ApiType = "UnbindContainer",
+ IsSuccess = false,
+ ErrorMessage = ex.Message,
+ ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
+ Creator = UserContext.Current.UserName
+ });
+
+ return response.Error($"鐢佃姱瑙g粦澶辫触: {ex.Message}");
+ }
+}
+```
+
+娣诲姞NG涓婃姤鎺ュ彛锛�
+
+```csharp
+/// <summary>
+/// 鎵樼洏NG鐢佃姱涓婃姤 - 璋冪敤MES鎺ュ彛
+/// </summary>
+/// <param name="dto">NG涓婃姤璇锋眰DTO</param>
+/// <returns>鎿嶄綔缁撴灉</returns>
+[HttpPost("containerNgReport")]
+[Permission("MES_NG_REPORT")]
+public async Task<WebResponseContent> ContainerNgReport([FromBody] ContainerNgReportRequestDto dto)
+{
+ var response = new WebResponseContent();
+ var stopwatch = System.Diagnostics.Stopwatch.StartNew();
+
+ try
+ {
+ // 1. 鍙傛暟楠岃瘉
+ if (string.IsNullOrWhiteSpace(dto.PalletCode))
+ {
+ return response.Error("鎵樼洏缂栧彿涓嶈兘涓虹┖");
+ }
+
+ if (dto.NgSfcList == null || !dto.NgSfcList.Any())
+ {
+ return response.Error("NG鐢佃姱鍒楄〃涓嶈兘涓虹┖");
+ }
+
+ // 2. 鑾峰彇绯荤粺閰嶇疆
+ var equipmentCode = await _configService.GetConfigValueAsync("MES_EquipmentCode");
+ var resourceCode = await _configService.GetConfigValueAsync("MES_ResourceCode");
+
+ if (string.IsNullOrWhiteSpace(equipmentCode) || string.IsNullOrWhiteSpace(resourceCode))
+ {
+ return response.Error("MES绯荤粺閰嶇疆涓嶅畬鏁达紝璇疯仈绯荤鐞嗗憳");
+ }
+
+ // 3. 鏋勯�燤ES璇锋眰
+ var mesRequest = new ContainerNgReportRequest
+ {
+ EquipmentCode = equipmentCode,
+ ResourceCode = resourceCode,
+ LocalTime = DateTime.Now,
+ ContainerCode = dto.PalletCode,
+ NgSfcList = dto.NgSfcList.Select(ng => new NgSfcItem
+ {
+ Sfc = ng.Sfc,
+ NgCode = ng.NgCode,
+ NgEquipmentCode = ng.NgEquipmentCode,
+ NgResourceCode = ng.NgResourceCode
+ }).ToList()
+ };
+
+ string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
+
+ // 4. 璋冪敤MES鎺ュ彛
+ var mesResult = await _mesService.ContainerNgReport(mesRequest);
+ stopwatch.Stop();
+
+ // 5. 璁板綍鏃ュ織
+ await _mesLogService.LogAsync(new MesApiLogDto
+ {
+ ApiType = "ContainerNgReport",
+ RequestJson = requestJson,
+ ResponseJson = System.Text.Json.JsonSerializer.Serialize(mesResult),
+ IsSuccess = mesResult.Success,
+ ErrorMessage = mesResult.ErrorMessage,
+ ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
+ Creator = UserContext.Current.UserName
+ });
+
+ // 6. 杩斿洖缁撴灉
+ if (mesResult.Success)
+ {
+ return response.OK($"鎴愬姛涓婃姤 {dto.NgSfcList.Count} 涓狽G鐢佃姱");
+ }
+ else
+ {
+ return response.Error($"MES鎺ュ彛璋冪敤澶辫触: {mesResult.ErrorMessage}");
+ }
+ }
+ catch (System.Exception ex)
+ {
+ stopwatch.Stop();
+
+ await _mesLogService.LogAsync(new MesApiLogDto
+ {
+ ApiType = "ContainerNgReport",
+ IsSuccess = false,
+ ErrorMessage = ex.Message,
+ ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
+ Creator = UserContext.Current.UserName
+ });
+
+ return response.Error($"NG涓婃姤澶辫触: {ex.Message}");
+ }
+}
+```
+
+- [ ] **Step 2: 娣诲姞蹇呰鐨剈sing璇彞**
+
+鍦ㄦ枃浠堕《閮ㄦ坊鍔狅細
+
+```csharp
+using WIDESEA_DTO.MES;
+using WIDESEA_DTO.Mes;
+using WIDESEA_IMesService;
+```
+
+- [ ] **Step 3: 鎻愪氦**
+
+```bash
+git add WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoDetailController.cs
+git commit -m "feat(MES): 搴撳瓨鏄庣粏椤甸潰娣诲姞缁戝畾/瑙g粦/NG涓婃姤鎺ュ彛
+
+- POST /api/StockInfoDetail/bindContainer 鎵樼洏鐢佃姱缁戝畾
+- POST /api/StockInfoDetail/unbindContainer 鎵樼洏鐢佃姱瑙g粦
+- POST /api/StockInfoDetail/containerNgReport NG鐢佃姱涓婃姤
+- 璁板綍瀹屾暣璋冪敤鏃ュ織
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 6: 鍒涘缓鍓嶇MES API璋冪敤妯″潡
+
+**Files:**
+- Create: `WMS/WIDESEA_WMSClient/src/api/mes.js`
+
+- [ ] **Step 1: 鍒涘缓MES API妯″潡**
+
+```javascript
+/**
+ * MES鎺ュ彛API妯″潡
+ */
+import axios from 'axios';
+
+const baseURL = '/api';
+
+// 搴撳瓨淇℃伅鐩稿叧MES鎺ュ彛
+export const stockInfoMesApi = {
+ /**
+ * 鎵樼洏杩涚珯
+ * @param {Object} data - 璇锋眰鏁版嵁 { palletCode, stockId }
+ * @returns {Promise}
+ */
+ inboundInContainer(data) {
+ return axios.post(`${baseURL}/StockInfo/inboundInContainer`, data);
+ },
+
+ /**
+ * 鎵樼洏鍑虹珯
+ * @param {Object} data - 璇锋眰鏁版嵁 { palletCode, stockId, paramList }
+ * @returns {Promise}
+ */
+ outboundInContainer(data) {
+ return axios.post(`${baseURL}/StockInfo/outboundInContainer`, data);
+ }
+};
+
+// 搴撳瓨鏄庣粏鐩稿叧MES鎺ュ彛
+export const stockDetailMesApi = {
+ /**
+ * 鎵樼洏鐢佃姱缁戝畾
+ * @param {Object} data - 璇锋眰鏁版嵁 { palletCode, sfcList, location, operationType }
+ * @returns {Promise}
+ */
+ bindContainer(data) {
+ return axios.post(`${baseURL}/StockInfoDetail/bindContainer`, data);
+ },
+
+ /**
+ * 鎵樼洏鐢佃姱瑙g粦
+ * @param {Object} data - 璇锋眰鏁版嵁 { palletCode, sfcList }
+ * @returns {Promise}
+ */
+ unbindContainer(data) {
+ return axios.post(`${baseURL}/StockInfoDetail/unbindContainer`, data);
+ },
+
+ /**
+ * 鎵樼洏NG鐢佃姱涓婃姤
+ * @param {Object} data - 璇锋眰鏁版嵁 { palletCode, ngSfcList }
+ * @returns {Promise}
+ */
+ containerNgReport(data) {
+ return axios.post(`${baseURL}/StockInfoDetail/containerNgReport`, data);
+ }
+};
+
+export default {
+ stockInfo: stockInfoMesApi,
+ stockDetail: stockDetailMesApi
+};
+```
+
+- [ ] **Step 2: 鎻愪氦**
+
+```bash
+git add WMS/WIDESEA_WMSClient/src/api/mes.js
+git commit -m "feat(MES): 娣诲姞鍓嶇MES API璋冪敤妯″潡
+
+- 灏佽搴撳瓨淇℃伅杩涚珯/鍑虹珯鎺ュ彛
+- 灏佽搴撳瓨鏄庣粏缁戝畾/瑙g粦/NG涓婃姤鎺ュ彛
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 7: 鍒涘缓MES纭瀵硅瘽妗嗙粍浠�
+
+**Files:**
+- Create: `WMS/WIDESEA_WMSClient/src/components/MesConfirmDialog.vue`
+
+- [ ] **Step 1: 鍒涘缓纭瀵硅瘽妗嗙粍浠�**
+
+```vue
+<template>
+ <el-dialog
+ v-model="visible"
+ :title="dialogTitle"
+ width="500px"
+ :close-on-click-modal="false"
+ @close="handleClose"
+ >
+ <div class="mes-confirm-content">
+ <p class="operation-text">{{ operationText }}</p>
+
+ <div class="info-section">
+ <div class="info-row" v-for="(item, index) in displayInfo" :key="index">
+ <span class="info-label">{{ item.label }}:</span>
+ <span class="info-value">{{ item.value }}</span>
+ </div>
+ </div>
+
+ <div v-if="errorMessage" class="error-message">
+ <el-icon><Warning /></el-icon>
+ <span>{{ errorMessage }}</span>
+ </div>
+ </div>
+
+ <template #footer>
+ <span class="dialog-footer">
+ <el-button @click="handleClose">鍙栨秷</el-button>
+ <el-button
+ type="primary"
+ :loading="loading"
+ @click="handleConfirm"
+ >
+ 纭鎵ц
+ </el-button>
+ </span>
+ </template>
+ </el-dialog>
+</template>
+
+<script>
+import { ref, computed } from 'vue';
+import { Warning } from '@element-plus/icons-vue';
+import { ElMessage } from 'element-plus';
+
+export default {
+ name: 'MesConfirmDialog',
+
+ components: {
+ Warning
+ },
+
+ props: {
+ modelValue: {
+ type: Boolean,
+ default: false
+ },
+ operationType: {
+ type: String,
+ required: true
+ },
+ palletCode: {
+ type: String,
+ required: true
+ },
+ stockInfo: {
+ type: Object,
+ default: null
+ },
+ detailInfo: {
+ type: Object,
+ default: null
+ }
+ },
+
+ emits: ['update:modelValue', 'confirm'],
+
+ setup(props, { emit }) {
+ const visible = computed({
+ get: () => props.modelValue,
+ set: (val) => emit('update:modelValue', val)
+ });
+
+ const loading = ref(false);
+ const errorMessage = ref('');
+
+ const operationConfig = {
+ inbound: { title: '鎵樼洏杩涚珯', text: '鎮ㄥ嵆灏嗘墽琛屾墭鐩樿繘绔欐搷浣�' },
+ outbound: { title: '鎵樼洏鍑虹珯', text: '鎮ㄥ嵆灏嗘墽琛屾墭鐩樺嚭绔欐搷浣�' },
+ bind: { title: '鐢佃姱缁戝畾', text: '鎮ㄥ嵆灏嗘墽琛岀數鑺粦瀹氭搷浣�' },
+ unbind: { title: '鐢佃姱瑙g粦', text: '鎮ㄥ嵆灏嗘墽琛岀數鑺В缁戞搷浣�' },
+ ngReport: { title: 'NG涓婃姤', text: '鎮ㄥ嵆灏嗘墽琛孨G鐢佃姱涓婃姤鎿嶄綔' }
+ };
+
+ const dialogTitle = computed(() => {
+ return operationConfig[props.operationType]?.title || '纭鎿嶄綔';
+ });
+
+ const operationText = computed(() => {
+ return operationConfig[props.operationType]?.text || '';
+ });
+
+ const displayInfo = computed(() => {
+ const info = [
+ { label: '鎵樼洏鐮�', value: props.palletCode }
+ ];
+
+ if (props.detailInfo) {
+ info.push({ label: '鐢佃姱鏁伴噺', value: props.detailInfo.sfcCount || '-' });
+ }
+
+ return info;
+ });
+
+ const handleClose = () => {
+ visible.value = false;
+ errorMessage.value = '';
+ };
+
+ const handleConfirm = async () => {
+ loading.value = true;
+ errorMessage.value = '';
+
+ try {
+ emit('confirm', {
+ operationType: props.operationType,
+ palletCode: props.palletCode,
+ stockInfo: props.stockInfo,
+ detailInfo: props.detailInfo,
+ onSuccess: () => {
+ visible.value = false;
+ loading.value = false;
+ },
+ onError: (error) => {
+ errorMessage.value = error;
+ loading.value = false;
+ }
+ });
+ } catch (error) {
+ errorMessage.value = error.message || '鎿嶄綔澶辫触';
+ loading.value = false;
+ }
+ };
+
+ return {
+ visible,
+ loading,
+ errorMessage,
+ dialogTitle,
+ operationText,
+ displayInfo,
+ handleClose,
+ handleConfirm
+ };
+ }
+};
+</script>
+
+<style scoped>
+.mes-confirm-content {
+ padding: 10px 0;
+}
+
+.operation-text {
+ font-size: 14px;
+ color: #303133;
+ margin-bottom: 20px;
+ font-weight: 500;
+}
+
+.info-section {
+ background: #f8fafc;
+ border-radius: 8px;
+ padding: 16px;
+ margin-bottom: 16px;
+}
+
+.info-row {
+ display: flex;
+ margin-bottom: 12px;
+ font-size: 14px;
+}
+
+.info-row:last-child {
+ margin-bottom: 0;
+}
+
+.info-label {
+ color: #909399;
+ width: 80px;
+ flex-shrink: 0;
+}
+
+.info-value {
+ color: #303133;
+ font-weight: 500;
+}
+
+.error-message {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 12px;
+ background: #fef0f0;
+ border: 1px solid #fde2e2;
+ border-radius: 6px;
+ color: #f56c6c;
+ font-size: 14px;
+}
+
+.error-message .el-icon {
+ font-size: 18px;
+}
+</style>
+```
+
+- [ ] **Step 2: 鎻愪氦**
+
+```bash
+git add WMS/WIDESEA_WMSClient/src/components/MesConfirmDialog.vue
+git commit -m "feat(MES): 娣诲姞MES鎿嶄綔纭瀵硅瘽妗嗙粍浠�
+
+- 鏀寔澶氱鎿嶄綔绫诲瀷锛堣繘绔欍�佸嚭绔欍�佺粦瀹氥�佽В缁戙�丯G涓婃姤锛�
+- 鏄剧ず鍏抽敭淇℃伅锛堟墭鐩樼爜銆佺數鑺暟閲忕瓑锛�
+- 閿欒鐘舵�佸睍绀�
+- 纭/鍙栨秷鎿嶄綔
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 8: 淇敼stockInfo.vue娣诲姞鎿嶄綔鍒�
+
+**Files:**
+- Modify: `WMS/WIDESEA_WMSClient/src/views/stock/stockInfo.vue`
+
+- [ ] **Step 1: 鍦╯cript涓坊鍔犵粍浠跺紩鍏ュ拰鏂规硶**
+
+鍦ㄧ幇鏈夌殑script setup涓坊鍔狅細
+
+```javascript
+import MesConfirmDialog from '@/components/MesConfirmDialog.vue';
+import { stockInfoMesApi } from '@/api/mes';
+import { ElMessage, ElMessageBox } from 'element-plus';
+import { ref } from 'vue';
+
+// MES瀵硅瘽妗嗙姸鎬�
+const mesDialogVisible = ref(false);
+const currentOperationType = ref('');
+const currentStockRow = ref(null);
+const mesLoading = ref(false);
+
+// 鎵撳紑MES纭瀵硅瘽妗�
+const openMesDialog = (operationType, row) => {
+ currentOperationType.value = operationType;
+ currentStockRow.value = row;
+ mesDialogVisible.value = true;
+};
+
+// 澶勭悊MES纭
+const handleMesConfirm = async ({ onSuccess, onError }) => {
+ const row = currentStockRow.value;
+ const operationType = currentOperationType.value;
+
+ try {
+ let result;
+
+ switch (operationType) {
+ case 'inbound':
+ result = await stockInfoMesApi.inboundInContainer({
+ palletCode: row.palletCode,
+ stockId: row.id
+ });
+ break;
+ case 'outbound':
+ result = await stockInfoMesApi.outboundInContainer({
+ palletCode: row.palletCode,
+ stockId: row.id
+ });
+ break;
+ }
+
+ if (result.data.status) {
+ ElMessage.success(result.data.message || '鎿嶄綔鎴愬姛');
+ onSuccess();
+ // 鍒锋柊鍒楄〃
+ proxy.$refs.grid.load();
+ } else {
+ onError(result.data.message || '鎿嶄綔澶辫触');
+ }
+ } catch (error) {
+ onError(error.response?.data?.message || error.message || '缃戠粶閿欒锛岃绋嶅悗閲嶈瘯');
+ }
+};
+
+// 妫�鏌ユ寜閽槸鍚﹀簲璇ユ樉绀�
+const shouldShowButton = (buttonType, row) => {
+ const status = row.status; // 0=寰呭叆搴�, 1=鍦ㄥ簱, 2=鍑哄簱涓�, 3=閿佸畾
+
+ switch (buttonType) {
+ case 'inbound':
+ return status === 0; // 浠呭緟鍏ュ簱鏄剧ず杩涚珯
+ case 'outbound':
+ return status === 1 || status === 2; // 鍦ㄥ簱鎴栧嚭搴撲腑鏄剧ず鍑虹珯
+ default:
+ return false;
+ }
+};
+```
+
+- [ ] **Step 2: 鍦╟olumns涓坊鍔犳搷浣滃垪**
+
+鍦ㄧ幇鏈夌殑columns ref瀹氫箟涓坊鍔犳搷浣滃垪锛堝湪鏈�鍚庢坊鍔狅級锛�
+
+```javascript
+const columns = ref([
+ // ... 鐜版湁鍒楀畾涔� ...
+ {
+ field: "actions",
+ title: "鎿嶄綔",
+ width: 200,
+ fixed: "right",
+ align: "center",
+ formatter: (row) => {
+ const buttons = [];
+
+ if (shouldShowButton('inbound', row)) {
+ buttons.push(
+ `<el-button type="primary" size="small" onclick="window.handleInbound(${row.id})">杩涚珯</el-button>`
+ );
+ }
+
+ if (shouldShowButton('outbound', row)) {
+ buttons.push(
+ `<el-button type="success" size="small" onclick="window.handleOutbound(${row.id})">鍑虹珯</el-button>`
+ );
+ }
+
+ return buttons.join(' ');
+ }
+ }
+]);
+```
+
+- [ ] **Step 3: 鍦ㄦā鏉夸腑娣诲姞瀵硅瘽妗嗙粍浠�**
+
+鍦╰emplate涓殑view-grid鏍囩鍚庢坊鍔狅細
+
+```vue
+<template>
+ <view-grid ... >
+ </view-grid>
+
+ <!-- MES纭瀵硅瘽妗� -->
+ <MesConfirmDialog
+ v-model="mesDialogVisible"
+ :operation-type="currentOperationType"
+ :pallet-code="currentStockRow?.palletCode"
+ :stock-info="currentStockRow"
+ @confirm="handleMesConfirm"
+ />
+</template>
+```
+
+- [ ] **Step 4: 娣诲姞鍏ㄥ眬鏂规硶鐢ㄤ簬HTML瀛楃涓蹭腑鐨刼nclick**
+
+鍦╯etup鐨勬渶鍚庢坊鍔狅細
+
+```javascript
+// 鎸傝浇鍒皐indow渚汬TML瀛楃涓蹭腑鐨刼nclick浣跨敤
+window.handleInbound = (id) => {
+ const row = proxy.$refs.grid.tableData.find(item => item.id === id);
+ if (row) openMesDialog('inbound', row);
+};
+
+window.handleOutbound = (id) => {
+ const row = proxy.$refs.grid.tableData.find(item => item.id === id);
+ if (row) openMesDialog('outbound', row);
+};
+```
+
+- [ ] **Step 5: 鎻愪氦**
+
+```bash
+git add WMS/WIDESEA_WMSClient/src/views/stock/stockInfo.vue
+git commit -m "feat(MES): 搴撳瓨淇℃伅椤甸潰娣诲姞杩涚珯/鍑虹珯鎿嶄綔鍒�
+
+- 娣诲姞鎿嶄綔鍒楋紝鏍规嵁搴撳瓨鐘舵�佸姩鎬佹樉绀烘寜閽�
+- 寰呭叆搴撶姸鎬佹樉绀鸿繘绔欐寜閽�
+- 鍦ㄥ簱/鍑哄簱涓姸鎬佹樉绀哄嚭绔欐寜閽�
+- 闆嗘垚MES纭瀵硅瘽妗�
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## Task 9: 淇敼stockInfoDetail.vue娣诲姞鎿嶄綔鍒�
+
+**Files:**
+- Modify: `WMS/WIDESEA_WMSClient/src/views/stock/stockInfoDetail.vue`
+
+- [ ] **Step 1: 鍦╯cript涓坊鍔犵粍浠跺紩鍏ュ拰鏂规硶**
+
+鍦ㄧ幇鏈夌殑script setup涓坊鍔狅細
+
+```javascript
+import MesConfirmDialog from '@/components/MesConfirmDialog.vue';
+import { stockDetailMesApi } from '@/api/mes';
+import { ElMessage } from 'element-plus';
+import { ref } from 'vue';
+
+// MES瀵硅瘽妗嗙姸鎬�
+const mesDialogVisible = ref(false);
+const currentOperationType = ref('');
+const currentDetailRow = ref(null);
+const mesLoading = ref(false);
+
+// 鎵撳紑MES纭瀵硅瘽妗�
+const openMesDialog = (operationType, row) => {
+ currentOperationType.value = operationType;
+ currentDetailRow.value = row;
+ mesDialogVisible.value = true;
+};
+
+// 澶勭悊MES纭
+const handleMesConfirm = async ({ onSuccess, onError }) => {
+ const row = currentDetailRow.value;
+ const operationType = currentOperationType.value;
+
+ try {
+ let result;
+
+ switch (operationType) {
+ case 'bind':
+ result = await stockDetailMesApi.bindContainer({
+ palletCode: row.palletCode || 'P001', // 闇�瑕佷粠搴撳瓨淇℃伅鑾峰彇
+ sfcList: [row.serialNumber],
+ location: row.location || '',
+ operationType: 1
+ });
+ break;
+ case 'unbind':
+ result = await stockDetailMesApi.unbindContainer({
+ palletCode: row.palletCode || 'P001',
+ sfcList: [row.serialNumber]
+ });
+ break;
+ case 'ngReport':
+ result = await stockDetailMesApi.containerNgReport({
+ palletCode: row.palletCode || 'P001',
+ ngSfcList: [{
+ sfc: row.serialNumber,
+ ngCode: 'NG001',
+ ngEquipmentCode: 'WCS_001',
+ ngResourceCode: 'RESOURCE_001'
+ }]
+ });
+ break;
+ }
+
+ if (result.data.status) {
+ ElMessage.success(result.data.message || '鎿嶄綔鎴愬姛');
+ onSuccess();
+ proxy.$refs.grid.load();
+ } else {
+ onError(result.data.message || '鎿嶄綔澶辫触');
+ }
+ } catch (error) {
+ onError(error.response?.data?.message || error.message || '缃戠粶閿欒锛岃绋嶅悗閲嶈瘯');
+ }
+};
+
+// 妫�鏌ユ寜閽槸鍚﹀簲璇ユ樉绀�
+const shouldShowButton = (row) => {
+ const status = row.status; // 1=姝e父, 2=寮傚父, 99=宸查攣瀹�
+ return status !== 99; // 闈�"宸查攣瀹�"鐘舵�佹樉绀烘墍鏈夋寜閽�
+};
+```
+
+- [ ] **Step 2: 鍦╟olumns涓坊鍔犳搷浣滃垪**
+
+鍦ㄧ幇鏈夌殑columns ref瀹氫箟涓坊鍔犳搷浣滃垪锛堝湪鏈�鍚庢坊鍔狅級锛�
+
+```javascript
+const columns = ref([
+ // ... 鐜版湁鍒楀畾涔� ...
+ {
+ field: "actions",
+ title: "鎿嶄綔",
+ width: 280,
+ fixed: "right",
+ align: "center",
+ formatter: (row) => {
+ if (!shouldShowButton(row)) {
+ return '<span class="text-muted">鏆傛棤鍙墽琛屾搷浣�</span>';
+ }
+
+ return `
+ <el-button type="primary" size="small" onclick="window.handleBind(${row.id})">缁戝畾</el-button>
+ <el-button type="warning" size="small" onclick="window.handleUnbind(${row.id})">瑙g粦</el-button>
+ <el-button type="danger" size="small" onclick="window.handleNgReport(${row.id})">NG涓婃姤</el-button>
+ `;
+ }
+ }
+]);
+```
+
+- [ ] **Step 3: 鍦ㄦā鏉夸腑娣诲姞瀵硅瘽妗嗙粍浠�**
+
+鍦╰emplate涓殑view-grid鏍囩鍚庢坊鍔狅細
+
+```vue
+<template>
+ <view-grid ... >
+ </view-grid>
+
+ <!-- MES纭瀵硅瘽妗� -->
+ <MesConfirmDialog
+ v-model="mesDialogVisible"
+ :operation-type="currentOperationType"
+ :pallet-code="currentDetailRow?.palletCode"
+ :detail-info="{ sfcCount: 1 }"
+ @confirm="handleMesConfirm"
+ />
+</template>
+```
+
+- [ ] **Step 4: 娣诲姞鍏ㄥ眬鏂规硶**
+
+鍦╯etup鐨勬渶鍚庢坊鍔狅細
+
+```javascript
+window.handleBind = (id) => {
+ const row = proxy.$refs.grid.tableData.find(item => item.id === id);
+ if (row) openMesDialog('bind', row);
+};
+
+window.handleUnbind = (id) => {
+ const row = proxy.$refs.grid.tableData.find(item => item.id === id);
+ if (row) openMesDialog('unbind', row);
+};
+
+window.handleNgReport = (id) => {
+ const row = proxy.$refs.grid.tableData.find(item => item.id === id);
+ if (row) openMesDialog('ngReport', row);
+};
+```
+
+- [ ] **Step 5: 鎻愪氦**
+
+```bash
+git add WMS/WIDESEA_WMSClient/src/views/stock/stockInfoDetail.vue
+git commit -m "feat(MES): 搴撳瓨鏄庣粏椤甸潰娣诲姞缁戝畾/瑙g粦/NG涓婃姤鎿嶄綔鍒�
+
+- 娣诲姞鎿嶄綔鍒楋紝鏍规嵁鐢佃姱鐘舵�佸姩鎬佹樉绀烘寜閽�
+- 闈為攣瀹氱姸鎬佹樉绀虹粦瀹氥�佽В缁戙�丯G涓婃姤鎸夐挳
+- 闆嗘垚MES纭瀵硅瘽妗�
+
+Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"
+```
+
+---
+
+## 娴嬭瘯妫�鏌ユ竻鍗�
+
+瀹屾垚浠ヤ笂鎵�鏈変换鍔″悗锛岃繘琛屼互涓嬫祴璇曪細
+
+### 鍚庣娴嬭瘯
+
+- [ ] 楠岃瘉鏁版嵁搴撹〃 Dt_MesApiLog 宸插垱寤�
+- [ ] 楠岃瘉绯荤粺閰嶇疆宸叉彃鍏�
+- [ ] 娴嬭瘯杩涚珯鎺ュ彛锛圥ostman鎴朣wagger锛�
+- [ ] 娴嬭瘯鍑虹珯鎺ュ彛
+- [ ] 娴嬭瘯缁戝畾鎺ュ彛
+- [ ] 娴嬭瘯瑙g粦鎺ュ彛
+- [ ] 娴嬭瘯NG涓婃姤鎺ュ彛
+- [ ] 楠岃瘉鏃ュ織璁板綍姝g‘
+
+### 鍓嶇娴嬭瘯
+
+- [ ] 搴撳瓨淇℃伅椤甸潰鎿嶄綔鍒楁甯告樉绀�
+- [ ] 鎸夐挳鏍规嵁鐘舵�佹纭樉绀�/闅愯棌
+- [ ] 鐐瑰嚮鎸夐挳寮瑰嚭纭瀵硅瘽妗�
+- [ ] 纭鍚庢垚鍔熻皟鐢ㄦ帴鍙�
+- [ ] 鎴愬姛鍚庡埛鏂板垪琛�
+- [ ] 澶辫触鍚庢樉绀洪敊璇彁绀�
+- [ ] 搴撳瓨鏄庣粏椤甸潰鎿嶄綔鍒楁甯告樉绀�
+- [ ] 鎵�鏈夋寜閽姛鑳芥甯�
+
+### 闆嗘垚娴嬭瘯
+
+- [ ] 瀹屾暣娴佺▼娴嬭瘯锛堢偣鍑烩啋纭鈫掓墽琛屸啋缁撴灉锛�
+- [ ] 缃戠粶寮傚父澶勭悊
+- [ ] MES鏈嶅姟寮傚父澶勭悊
+- [ ] 鏉冮檺鎺у埗娴嬭瘯
+
+---
+
+## 澶囨敞
+
+1. **鐘舵�佸�兼槧灏�**锛氫唬鐮佷腑浣跨敤鐨勭姸鎬佸�硷紙0=寰呭叆搴�, 1=鍦ㄥ簱绛夛級闇�瑕佹牴鎹疄闄呮灇涓惧�艰皟鏁�
+2. **鎵樼洏鐮佽幏鍙�**锛氬簱瀛樻槑缁嗛〉闈腑闇�瑕侀�氳繃stockId鍏宠仈鑾峰彇鎵樼洏鐮�
+3. **鏉冮檺閰嶇疆**锛氶渶瑕佸湪绯荤粺涓坊鍔燤ES鐩稿叧鏉冮檺椤�
+4. **MES鎺ュ彛鍦板潃**锛氬湪绯荤粺閰嶇疆涓缃纭殑MES鎺ュ彛鍦板潃
+5. **閿欒澶勭悊**锛氭牴鎹疄闄匨ES杩斿洖鐨勯敊璇爜璋冩暣鎻愮ず淇℃伅
--
Gitblit v1.9.3