# MES 接口调用日志页面实施计划 > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. **目标:** 在 WMS 系统中添加 MES 接口调用日志查看页面,提供综合性的日志查询、统计和管理功能。 **架构:** 后端使用 C# / ASP.NET Core,前端使用 Vue 3 + Element Plus。复用现有 view-grid 模式,添加自定义统计卡片。 **技术栈:** .NET 8, SqlSugar, Vue 3, Element Plus 2.2.14, Vite **版本:** v0.3 (修订版 - 修复审查问题) --- ## 设计文档 参考: `docs/superpowers/specs/2026-04-13-mes-api-log-page-design.md` --- ## 文件结构 ### 后端新建文件 ``` WMS/WIDESEA_WMSServer/ ├── WIDESEA_DTO/MES/ │ ├── MesLogQueryDto.cs # 查询请求 DTO │ ├── MesLogStatisticsDto.cs # 统计数据 DTO │ ├── MesLogListItemDto.cs # 列表项 DTO │ └── MesLogDetailDto.cs # 详情 DTO ├── WIDESEA_IMesService/ │ └── IMesLogService.cs # 扩展服务接口 ├── WIDESEA_MesService/ │ └── MesLogService.cs # 服务实现 └── WIDESEA_WMSServer/Controllers/Mes/ └── MesLogController.cs # API 控制器 ``` ### 前端新建文件 ``` WMS/WIDESEA_WMSClient/src/ ├── components/ │ └── MesLogStatistics.vue # 统计卡片组件 ├── views/system/ │ └── Mes_Log.vue # 日志页面 └── extension/system/ └── Mes_Log.jsx # 业务扩展逻辑 ``` ### 修改文件 ``` WMS/WIDESEA_WMSClient/src/ └── router/viewGird.js # 添加路由配置 ``` --- ## Task 1: 创建后端 DTO 文件 **Files:** - Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/MesLogQueryDto.cs` - Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/MesLogStatisticsDto.cs` - Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/MesLogListItemDto.cs` - Create: `WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/MesLogDetailDto.cs` ### Step 1: 创建 MesLogQueryDto.cs ```csharp using System; namespace WIDESEA_DTO.MES { /// /// MES日志查询请求DTO /// public class MesLogQueryDto { /// /// 接口类型: BindContainer, UnBindContainer, ContainerNgReport, InboundInContainer, OutboundInContainer /// public string ApiType { get; set; } /// /// 成功状态: null-全部, true-成功, false-失败 /// public bool? IsSuccess { get; set; } /// /// 开始时间(前端 dateRange 字段映射:dateRange[0] → StartTime) /// public DateTime? StartTime { get; set; } /// /// 结束时间(前端 dateRange 字段映射:dateRange[1] → EndTime) /// public DateTime? EndTime { get; set; } /// /// 创建人(操作人) /// public string Creator { get; set; } /// /// 最小耗时(毫秒)(前端 elapsedRange 字段映射:elapsedRange[0] → MinElapsedMs) /// public int? MinElapsedMs { get; set; } /// /// 最大耗时(毫秒)(前端 elapsedRange 字段映射:elapsedRange[1] → MaxElapsedMs) /// public int? MaxElapsedMs { get; set; } /// /// 错误信息关键字 /// public string ErrorKeyword { get; set; } /// /// 请求JSON关键字 /// public string JsonRequestKeyword { get; set; } /// /// 响应JSON关键字 /// public string JsonResponseKeyword { get; set; } } } ``` ### Step 2: 创建 MesLogStatisticsDto.cs ```csharp using System; using System.Collections.Generic; namespace WIDESEA_DTO.MES { /// /// MES日志统计数据DTO /// public class MesLogStatisticsDto { /// /// 总调用次数 /// public int TotalCount { get; set; } /// /// 成功次数 /// public int SuccessCount { get; set; } /// /// 成功率(百分比) /// public double SuccessRate { get; set; } /// /// 失败次数 /// public int FailedCount { get; set; } /// /// 平均耗时(毫秒) /// public double AvgElapsedMs { get; set; } /// /// 最大耗时(毫秒) /// public int MaxElapsedMs { get; set; } /// /// 今日调用次数 /// public int TodayCount { get; set; } /// /// 各接口类型调用次数统计 /// public Dictionary ApiTypeCounts { get; set; } } } ``` ### Step 3: 创建 MesLogListItemDto.cs ```csharp using System; namespace WIDESEA_DTO.MES { /// /// MES日志列表项DTO /// public class MesLogListItemDto { /// /// 主键ID /// public long Id { get; set; } /// /// 接口类型 /// public string ApiType { get; set; } /// /// 是否成功 /// public bool IsSuccess { get; set; } /// /// 请求JSON预览(前200字符) /// public string RequestJsonPreview { get; set; } /// /// 响应JSON预览(前200字符) /// public string ResponseJsonPreview { get; set; } /// /// 错误信息 /// public string ErrorMessage { get; set; } /// /// 耗时(毫秒) /// public int ElapsedMs { get; set; } /// /// 创建时间 /// public DateTime CreateDate { get; set; } /// /// 创建人 /// public string Creator { get; set; } } } ``` ### Step 4: 创建 MesLogDetailDto.cs ```csharp using System; namespace WIDESEA_DTO.MES { /// /// MES日志详情DTO /// public class MesLogDetailDto : MesLogListItemDto { /// /// 完整请求JSON /// public string RequestJson { get; set; } /// /// 完整响应JSON /// public string ResponseJson { get; set; } /// /// 修改时间 /// public DateTime? ModifyDate { get; set; } /// /// 修改人 /// public string Modifier { get; set; } } } ``` ### Step 5: 提交 DTO 文件 ```bash git add WMS/WIDESEA_WMSServer/WIDESEA_DTO/MES/MesLog*.cs git commit -m "feat(MES): 添加MES日志查询DTO文件 - MesLogQueryDto: 查询请求参数 - MesLogStatisticsDto: 统计数据 - MesLogListItemDto: 列表项 - MesLogDetailDto: 详情 Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 2: 扩展 IMesLogService 接口 **Files:** - Modify: `WMS/WIDESEA_WMSServer/WIDESEA_IMesService/IMesLogService.cs` ### Step 1: 读取现有接口 ```bash cat WMS/WIDESEA_WMSServer/WIDESEA_IMesService/IMesLogService.cs ``` ### Step 2: 添加新方法到接口 在现有接口中添加以下方法: ```csharp /// /// 分页查询MES日志 /// /// 查询条件 /// 页码 /// 每页数量 /// 日志列表和总数 Task<(List items, int total)> GetPageAsync(MesLogQueryDto query, int page, int pageSize); /// /// 获取单条日志详情 /// /// 日志ID /// 日志详情 Task GetDetailAsync(long id); /// /// 获取统计数据 /// /// 查询条件 /// 统计数据 Task GetStatisticsAsync(MesLogQueryDto query); /// /// 导出日志数据 /// /// 查询条件 /// Excel文件字节数组 Task ExportAsync(MesLogQueryDto query); ``` ### Step 3: 添加 using 语句 确保文件顶部有: ```csharp using WIDESEA_DTO.MES; ``` ### Step 4: 提交接口扩展 ```bash git add WMS/WIDESEA_WMSServer/WIDESEA_IMesService/IMesLogService.cs git commit -m "feat(MES): 扩展IMesLogService接口 - 添加分页查询方法 GetPageAsync - 添加详情查询方法 GetDetailAsync - 添加统计查询方法 GetStatisticsAsync - 添加导出方法 ExportAsync Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 3: 实现 MesLogService 方法 **Files:** - Modify: `WMS/WIDESEA_WMSServer/WIDESEA_MesService/MesLogService.cs` ### Step 1: 读取现有服务实现 ```bash cat WMS/WIDESEA_WMSServer/WIDESEA_MesService/MesLogService.cs ``` ### Step 2: 添加 GetPageAsync 实现 ```csharp /// /// 分页查询MES日志 /// public async Task<(List items, int total)> GetPageAsync(MesLogQueryDto query, int page, int pageSize) { var dbQuery = _db.Queryable(); // 动态条件筛选 if (!string.IsNullOrEmpty(query.ApiType)) { dbQuery = dbQuery.Where(x => x.ApiType == query.ApiType); } if (query.IsSuccess.HasValue) { dbQuery = dbQuery.Where(x => x.IsSuccess == query.IsSuccess.Value); } if (query.StartTime.HasValue) { dbQuery = dbQuery.Where(x => x.CreateDate >= query.StartTime.Value); } if (query.EndTime.HasValue) { dbQuery = dbQuery.Where(x => x.CreateDate <= query.EndTime.Value); } if (!string.IsNullOrEmpty(query.Creator)) { dbQuery = dbQuery.Where(x => x.Creator.Contains(query.Creator)); } if (query.MinElapsedMs.HasValue) { dbQuery = dbQuery.Where(x => x.ElapsedMs >= query.MinElapsedMs.Value); } if (query.MaxElapsedMs.HasValue) { dbQuery = dbQuery.Where(x => x.ElapsedMs <= query.MaxElapsedMs.Value); } if (!string.IsNullOrEmpty(query.ErrorKeyword)) { dbQuery = dbQuery.Where(x => x.ErrorMessage.Contains(query.ErrorKeyword)); } if (!string.IsNullOrEmpty(query.JsonRequestKeyword)) { dbQuery = dbQuery.Where(x => x.RequestJson.Contains(query.JsonRequestKeyword)); } if (!string.IsNullOrEmpty(query.JsonResponseKeyword)) { dbQuery = dbQuery.Where(x => x.ResponseJson.Contains(query.JsonResponseKeyword)); } // 获取总数 var total = await dbQuery.CountAsync(); // 分页查询 var entities = await dbQuery .OrderByDescending(x => x.CreateDate) .Skip((page - 1) * pageSize) .Take(pageSize) .ToListAsync(); // 映射到 DTO var items = entities.Select(e => new MesLogListItemDto { Id = e.Id, ApiType = e.ApiType, IsSuccess = e.IsSuccess, RequestJsonPreview = e.RequestJson?.Length > 200 ? e.RequestJson.Substring(0, 200) + "..." : e.RequestJson, ResponseJsonPreview = e.ResponseJson?.Length > 200 ? e.ResponseJson.Substring(0, 200) + "..." : e.ResponseJson, ErrorMessage = e.ErrorMessage, ElapsedMs = e.ElapsedMs, CreateDate = e.CreateDate, Creator = e.Creator }).ToList(); return (items, total); } ``` ### Step 3: 添加 GetDetailAsync 实现 ```csharp /// /// 获取单条日志详情 /// public async Task GetDetailAsync(long id) { var entity = await _db.Queryable() .FirstAsync(x => x.Id == id); if (entity == null) { return null; } return new MesLogDetailDto { Id = entity.Id, ApiType = entity.ApiType, IsSuccess = entity.IsSuccess, RequestJson = entity.RequestJson, ResponseJson = entity.ResponseJson, RequestJsonPreview = entity.RequestJson?.Length > 200 ? entity.RequestJson.Substring(0, 200) + "..." : entity.RequestJson, ResponseJsonPreview = entity.ResponseJson?.Length > 200 ? entity.ResponseJson.Substring(0, 200) + "..." : entity.ResponseJson, ErrorMessage = entity.ErrorMessage, ElapsedMs = entity.ElapsedMs, CreateDate = entity.CreateDate, Creator = entity.Creator, ModifyDate = entity.ModifyDate, Modifier = entity.Modifier }; } ``` ### Step 4: 添加 GetStatisticsAsync 实现 ```csharp /// /// 获取统计数据 /// public async Task GetStatisticsAsync(MesLogQueryDto query) { var dbQuery = _db.Queryable(); // 应用相同的筛选条件(但不分页) if (!string.IsNullOrEmpty(query.ApiType)) { dbQuery = dbQuery.Where(x => x.ApiType == query.ApiType); } if (query.IsSuccess.HasValue) { dbQuery = dbQuery.Where(x => x.IsSuccess == query.IsSuccess.Value); } if (query.StartTime.HasValue) { dbQuery = dbQuery.Where(x => x.CreateDate >= query.StartTime.Value); } if (query.EndTime.HasValue) { dbQuery = dbQuery.Where(x => x.CreateDate <= query.EndTime.Value); } var allData = await dbQuery.ToListAsync(); // 今日数据 var today = DateTime.Today; var todayData = allData.Where(x => x.CreateDate >= today).ToList(); // 计算统计数据 var totalCount = allData.Count; var successCount = allData.Count(x => x.IsSuccess); var failedCount = totalCount - successCount; var successRate = totalCount > 0 ? (successCount * 100.0 / totalCount) : 0; return new MesLogStatisticsDto { TotalCount = totalCount, SuccessCount = successCount, FailedCount = failedCount, SuccessRate = Math.Round(successRate, 2), AvgElapsedMs = totalCount > 0 ? allData.Average(x => x.ElapsedMs) : 0, MaxElapsedMs = totalCount > 0 ? allData.Max(x => x.ElapsedMs) : 0, TodayCount = todayData.Count, ApiTypeCounts = allData.GroupBy(x => x.ApiType) .ToDictionary(g => g.Key, g => g.Count()) }; } ``` ### Step 5: 添加 ExportAsync 实现 ```csharp /// /// 导出日志数据 /// public async Task ExportAsync(MesLogQueryDto query) { // 获取所有符合条件的数据(不分页) var (items, _) = await GetPageAsync(query, 1, int.MaxValue); // 使用框架内置的 Excel 导出功能 // 注意:这里需要根据项目实际使用的导出库调整 // 参考 ServiceBase.Export() 的实现方式 // 临时方案:使用 CSV 格式 using var memoryStream = new MemoryStream(); using var writer = new StreamWriter(memoryStream, System.Text.Encoding.UTF8); // CSV 头部 writer.WriteLine("ID,接口类型,状态,耗时(ms),错误信息,创建时间,创建人"); // CSV 数据行 foreach (var item in items) { writer.WriteLine($"{item.Id},{item.ApiType},{(item.IsSuccess ? "成功" : "失败")},{item.ElapsedMs},\"{item.ErrorMessage?.Replace("\"", "\"\"")}\",{item.CreateDate:yyyy-MM-dd HH:mm:ss},{item.Creator}"); } writer.Flush(); return memoryStream.ToArray(); } ``` ### Step 6: 添加 using 语句 确保文件顶部有: ```csharp using WIDESEA_DTO.MES; ``` ### Step 7: 提交服务实现 ```bash git add WMS/WIDESEA_WMSServer/WIDESEA_MesService/MesLogService.cs git commit -m "feat(MES): 实现MesLogService查询方法 - 实现分页查询 GetPageAsync - 实现详情查询 GetDetailAsync - 实现统计查询 GetStatisticsAsync - 实现导出 ExportAsync (CSV格式) Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 4: 创建 MesLogController **Files:** - Create: `WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Mes/MesLogController.cs` ### Step 1: 创建控制器文件 ```csharp using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using WIDESEA_Core; using WIDESEA_DTO.MES; using WIDESEA_IBasicService; using WIDESEA_IMesService; namespace WIDESEA_WMSServer.Controllers.Mes { /// /// MES接口日志控制器 /// [Route("api/MesLog")] [ApiController] [Authorize] public class MesLogController : ControllerBase { private readonly IMesLogService _mesLogService; /// /// 构造函数 /// public MesLogController(IMesLogService mesLogService) { _mesLogService = mesLogService; } /// /// 分页查询MES日志 /// /// 查询条件 /// 页码,默认1 /// 每页数量,默认20 /// 分页结果 [HttpPost("page")] public async Task GetPage([FromBody] MesLogQueryDto query, [FromQuery] int page = 1, [FromQuery] int pageSize = 20) { var response = new WebResponseContent(); try { var (items, total) = await _mesLogService.GetPageAsync(query, page, pageSize); return response.OK(null, new { rows = items, total = total }); } catch (Exception ex) { return response.Error($"查询失败: {ex.Message}"); } } /// /// 获取日志详情 /// /// 日志ID /// 日志详情 [HttpGet("{id}")] public async Task GetDetail(long id) { var response = new WebResponseContent(); try { var detail = await _mesLogService.GetDetailAsync(id); if (detail == null) { return response.Error("日志不存在"); } return response.OK(null, detail); } catch (Exception ex) { return response.Error($"查询失败: {ex.Message}"); } } /// /// 获取统计数据 /// /// 查询条件(可选) /// 统计数据 [HttpGet("statistics")] public async Task GetStatistics([FromQuery] MesLogQueryDto query) { var response = new WebResponseContent(); try { var statistics = await _mesLogService.GetStatisticsAsync(query ?? new MesLogQueryDto()); return response.OK(null, statistics); } catch (Exception ex) { return response.Error($"查询失败: {ex.Message}"); } } /// /// 导出日志 /// /// 查询条件 /// Excel文件 [HttpPost("export")] public async Task Export([FromBody] MesLogQueryDto query) { try { var data = await _mesLogService.ExportAsync(query ?? new MesLogQueryDto()); var fileName = $"MES接口日志_{DateTime.Now:yyyyMMdd_HHmmss}.csv"; return File(data, "text/csv; charset=utf-8", fileName); } catch (Exception ex) { return BadRequest(new { error = ex.Message }); } } } } ``` ### Step 2: 提交控制器 ```bash git add WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Mes/MesLogController.cs git commit -m "feat(MES): 添加MesLogController控制器 - POST /api/MesLog/page - 分页查询 - GET /api/MesLog/{id} - 详情查询 - GET /api/MesLog/statistics - 统计数据 - POST /api/MesLog/export - 导出CSV Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 5: 创建前端统计卡片组件 **Files:** - Create: `WMS/WIDESEA_WMSClient/src/components/MesLogStatistics.vue` ### Step 1: 创建组件文件 ```vue ``` ### Step 2: 提交组件 ```bash git add WMS/WIDESEA_WMSClient/src/components/MesLogStatistics.vue git commit -m "feat(MES): 添加MesLogStatistics统计卡片组件 - 显示总调用次数、成功率、平均耗时、今日调用 - 使用 el-card 自定义实现(兼容 Element Plus 2.2.14) - 颜色标识:蓝色/绿色/橙色/灰色 Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 7: 创建前端扩展逻辑 **Files:** - Create: `WMS/WIDESEA_WMSClient/src/extension/system/Mes_Log.jsx` ### Step 1: 创建扩展文件 ```jsx import { h, resolveComponent } from 'vue'; let extension = { components: { // 动态扩充组件或组件路径 gridHeader: "", gridBody: '', gridFooter: "", modelHeader: "", modelBody: "", modelFooter: "" }, buttons: [], // 扩展的按钮 methods: { // 事件扩展 onInit() { console.log("mes_log init"); this.setFiexdSearchForm(true); }, onInited() { this.height = this.height - 240; // 为统计卡片预留空间 // 添加预览方法 this.previewJson = (jsonStr) => { if (!jsonStr) return '-'; try { const obj = JSON.parse(jsonStr); return JSON.stringify(obj, null, 2).substring(0, 200) + '...'; } catch { return String(jsonStr).substring(0, 200) + '...'; } }; }, // 行点击事件 - 显示 JSON 详情 rowClick({ row, column }) { // 如果点击的是请求或响应列,显示详情弹窗 if (column.property === 'requestJson' || column.property === 'responseJson') { this.showJsonDetail(row); } }, // 显示 JSON 详情弹窗 showJsonDetail(row) { const elDialog = resolveComponent('el-dialog'); // 创建临时弹窗 this.$alert('', 'JSON 详情', { message: h('div', { class: 'json-detail-dialog' }, [ h('el-tabs', { modelValue: 'request' }, [ h('el-tab-pane', { label: '请求', name: 'request' }, [ h('pre', { class: 'json-content' }, this.formatJson(row.requestJson)) ]), h('el-tab-pane', { label: '响应', name: 'response' }, [ h('pre', { class: 'json-content' }, this.formatJson(row.responseJson)) ]) ]) ]), customClass: 'mes-json-detail-dialog', dangerouslyUseHTMLString: false }); }, // 格式化 JSON formatJson(jsonStr) { if (!jsonStr) return '{}'; try { const obj = typeof jsonStr === 'string' ? JSON.parse(jsonStr) : jsonStr; return JSON.stringify(obj, null, 2); } catch { return String(jsonStr); } } } }; export default extension; ``` ### Step 2: 提交扩展文件 ```bash git add WMS/WIDESEA_WMSClient/src/extension/system/Mes_Log.jsx git commit -m "feat(MES): 添加Mes_Log扩展逻辑 - 添加 previewJson 辅助函数 - 添加行点击事件处理 - 添加 JSON 详情显示功能 - 调整高度为统计卡片预留空间 Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 8: 创建前端页面 **Files:** - Create: `WMS/WIDESEA_WMSClient/src/views/system/Mes_Log.vue` ### Step 1: 创建页面文件 ```vue ``` ### Step 2: 提交页面文件 ```bash git add WMS/WIDESEA_WMSClient/src/views/system/Mes_Log.vue git commit -m "feat(MES): 添加Mes_Log日志页面 - 使用 view-grid 组件 - 集成统计卡片组件 - 配置搜索表单和列表列 - 支持多维度筛选 Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 9: 添加路由配置 **Files:** - Modify: `WMS/WIDESEA_WMSClient/src/router/viewGird.js` ### Step 1: 读取路由文件 ```bash cat WMS/WIDESEA_WMSClient/src/router/viewGird.js | head -50 ``` ### Step 2: 在 Sys_Log 路由后添加 Mes_Log 路由 在 `viewGird.js` 的路由数组中,找到 `path: '/Sys_Log'` 配置块(大约在文件开头),在其后添加: ```javascript { path: '/Sys_Log', name: 'sys_Log', component: () => import('@/views/system/Sys_Log.vue') }, { path: '/Mes_Log', // ← 添加此块 name: 'mes_Log', component: () => import('@/views/system/Mes_Log.vue') }, { path: '/Sys_User', // 下一个路由 name: 'Sys_User', component: () => import('@/views/system/Sys_User.vue') }, ``` 插入位置:在 `Sys_Log` 路由块之后,`Sys_User` 路由块之前 ### Step 3: 提交路由配置 ```bash git add WMS/WIDESEA_WMSClient/src/router/viewGird.js git commit -m "feat(MES): 添加Mes_Log路由配置 - 添加 /Mes_Log 路由 - 指向 views/system/Mes_Log.vue Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 10: 数据库配置 ### Step 1: 执行索引创建脚本 ```sql -- 在 SQL Server Management Studio 中执行 -- 接口类型索引 IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_ApiType' AND object_id = OBJECT_ID('Dt_MesApiLog')) BEGIN CREATE INDEX IX_MesApiLog_ApiType ON Dt_MesApiLog(ApiType); END -- 创建时间索引 IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_CreateDate' AND object_id = OBJECT_ID('Dt_MesApiLog')) BEGIN CREATE INDEX IX_MesApiLog_CreateDate ON Dt_MesApiLog(CreateDate DESC); END -- 成功状态索引 IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_IsSuccess' AND object_id = OBJECT_ID('Dt_MesApiLog')) BEGIN CREATE INDEX IX_MesApiLog_IsSuccess ON Dt_MesApiLog(IsSuccess); END -- 创建人索引 IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = 'IX_MesApiLog_Creator' AND object_id = OBJECT_ID('Dt_MesApiLog')) BEGIN CREATE INDEX IX_MesApiLog_Creator ON Dt_MesApiLog(Creator); END ``` ### Step 2: 添加菜单记录 ```sql -- 先查询系统管理菜单的 ID SELECT Id, MenuName FROM Dt_Menu WHERE MenuName LIKE '%系统%' OR MenuName LIKE '%System%'; -- 假设系统管理菜单 ID 为 XXX,插入 MES 接口日志菜单 INSERT INTO Dt_Menu (ParentId, MenuName, Url, Component, Permission, Sort, Icon, CreateDate, Modifier) VALUES ( XXX, -- 替换为实际的系统管理菜单 ID 'MES接口日志', '/Mes_Log', 'views/system/Mes_Log', 'Mes_Log:view', (SELECT ISNULL(MAX(Sort), 0) + 1 FROM Dt_Menu WHERE ParentId = XXX), 'el-icon-document', GETDATE(), 'System' ); ``` ### Step 3: 添加数据字典记录 ```sql -- 接口类型字典 DECLARE @DictId INT; SELECT @DictId = Id FROM Dt_Dictionary WHERE DictKey = 'mesApiType'; IF @DictId IS NULL BEGIN INSERT INTO Dt_Dictionary (DictKey, DictValue, CreateDate, Modifier) VALUES ('mesApiType', 'MES接口类型', GETDATE(), 'System'); SET @DictId = SCOPE_IDENTITY(); END -- 添加接口类型选项 INSERT INTO Dt_DictionaryList (DictId, Value, Key, DisplayOrder, CreateDate, Modifier) SELECT @DictId, '电芯绑定', 'BindContainer', 1, GETDATE(), 'System' WHERE NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'BindContainer'); INSERT INTO Dt_DictionaryList (DictId, Value, Key, DisplayOrder, CreateDate, Modifier) SELECT @DictId, '电芯解绑', 'UnBindContainer', 2, GETDATE(), 'System' WHERE NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'UnBindContainer'); INSERT INTO Dt_DictionaryList (DictId, Value, Key, DisplayOrder, CreateDate, Modifier) SELECT @DictId, 'NG上报', 'ContainerNgReport', 3, GETDATE(), 'System' WHERE NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'ContainerNgReport'); INSERT INTO Dt_DictionaryList (DictId, Value, Key, DisplayOrder, CreateDate, Modifier) SELECT @DictId, '托盘进站', 'InboundInContainer', 4, GETDATE(), 'System' WHERE NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'InboundInContainer'); INSERT INTO Dt_DictionaryList (DictId, Value, Key, DisplayOrder, CreateDate, Modifier) SELECT @DictId, '托盘出站', 'OutboundInContainer', 5, GETDATE(), 'System' WHERE NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @DictId AND [Key] = 'OutboundInContainer'); -- 调用状态字典 DECLARE @StatusDictId INT; SELECT @StatusDictId = Id FROM Dt_Dictionary WHERE DictKey = 'mesApiStatus'; IF @StatusDictId IS NULL BEGIN INSERT INTO Dt_Dictionary (DictKey, DictValue, CreateDate, Modifier) VALUES ('mesApiStatus', 'MES接口状态', GETDATE(), 'System'); SET @StatusDictId = SCOPE_IDENTITY(); END -- 添加状态选项 INSERT INTO Dt_DictionaryList (DictId, Value, Key, DisplayOrder, CreateDate, Modifier) SELECT @StatusDictId, '成功', 'true', 1, GETDATE(), 'System' WHERE NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @StatusDictId AND [Key] = 'true'); INSERT INTO Dt_DictionaryList (DictId, Value, Key, DisplayOrder, CreateDate, Modifier) SELECT @StatusDictId, '失败', 'false', 2, GETDATE(), 'System' WHERE NOT EXISTS (SELECT 1 FROM Dt_DictionaryList WHERE DictId = @StatusDictId AND [Key] = 'false'); ``` ### Step 4: 保存数据库脚本 ```bash # 将上述 SQL 脚本保存到文件 cat > WMS/WIDESEA_WMSServer/Database/Scripts/20260413_MesLogPage_Config.sql << 'EOF' -- 这里粘贴上面的 SQL 脚本 EOF ``` ### Step 5: 提交数据库脚本 ```bash git add WMS/WIDESEA_WMSServer/Database/Scripts/20260413_MesLogPage_Config.sql git commit -m "feat(MES): 添加MES日志页面数据库配置 - 创建性能索引 - 添加菜单记录 - 添加数据字典(接口类型、状态) Co-Authored-By: Claude Opus 4.6 " ``` --- ## Task 11: 后端编译测试 ### Step 1: 编译后端项目 ```bash cd WMS/WIDESEA_WMSServer dotnet build WIDESEA_WMSServer.sln ``` ### Step 2: 检查编译结果 预期输出:编译成功,无错误 ### Step 3: 如果有错误,修复并重新编译 --- ## Task 12: 前端编译测试 ### Step 1: 安装依赖(如需要) ```bash cd WMS/WIDESEA_WMSClient npm install ``` ### Step 2: 编译前端项目 ```bash npm run build ``` ### Step 3: 检查编译结果 预期输出:编译成功,无错误 --- ## Task 13: 手动功能测试 ### 测试清单 - [ ] **分页查询**: 能正常显示日志列表 - [ ] **筛选功能**: 接口类型、状态、时间范围筛选正常 - [ ] **统计卡片**: 显示正确的统计数据 - [ ] **JSON 详情**: 点击请求/响应列能查看完整 JSON - [ ] **导出功能**: 能导出 CSV 文件 --- ## Task 14: 代码审查与最终提交 ### Step 1: 查看所有变更 ```bash git status git log --oneline -10 ``` ### Step 2: 最终确认 确认所有任务已完成,功能测试通过 --- ## 附录: 参考文档 - 设计文档: `docs/superpowers/specs/2026-04-13-mes-api-log-page-design.md` - 现有系统日志: `WMS/WIDESEA_WMSClient/src/views/system/Sys_Log.vue` - 现有扩展: `WMS/WIDESEA_WMSClient/src/extension/system/Sys_Log.jsx` - MES 实体: `WMS/WIDESEA_WMSServer/WIDESEA_Model/Models/Mes/Dt_MesApiLog.cs`