From 32a7bf49d0daf09d1d423ad2de56d777a8fd8941 Mon Sep 17 00:00:00 2001 From: haoliang <821644@qq.com> Date: Wed, 29 Apr 2026 00:46:08 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D20=E4=B8=AA=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=8E=A5=E5=8F=A3=E8=BF=94=E5=9B=9E=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E4=B8=8EMock=E4=B8=8D=E4=B8=80=E8=87=B4=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=8C=E6=96=B0=E5=A2=9E5.2.1=20Mock=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E7=BB=93=E6=9E=84=E5=AF=B9=E9=BD=90=E8=A7=84=E8=8C=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/04-后端开发规范.md | 42 +++++++++++++++++++ src/CncWebApi/Controllers/BrandController.cs | 6 ++- .../Controllers/DashboardController.cs | 12 ++++-- src/CncWebApi/Controllers/OptionController.cs | 15 ++++--- .../Controllers/ScreenConfigController.cs | 6 ++- src/CncWebApi/Controllers/ScreenController.cs | 15 ++++--- .../Controllers/SettingsController.cs | 6 ++- 7 files changed, 82 insertions(+), 20 deletions(-) diff --git a/docs/04-后端开发规范.md b/docs/04-后端开发规范.md index da41af1..c5463e6 100644 --- a/docs/04-后端开发规范.md +++ b/docs/04-后端开发规范.md @@ -242,6 +242,48 @@ public class ApiResponse } ``` +### 5.2.1 Mock数据结构对齐(强制) + +> **核心原则:后端 API 返回的 `data` 内部结构必须严格匹配 Mock 文件中的定义。前端代码按 Mock 结构取值,后端返回格式不一致将导致前端取不到数据。** + +#### 规则说明 + +1. **列表接口**:Mock 返回 `{ code: 0, data: { items: [...], total: N, page: N, pageSize: N } }`,后端必须返回 `ApiResponse>`,`PagedResult` 包含 `items/total/page/pageSize` 四个字段 +2. **非分页列表接口**(如车间下拉、品牌下拉等):Mock 返回 `{ code: 0, data: { items: [...] } }`,后端必须返回 `ApiResponse` 并将列表包装为 `new { items = list }`,**禁止**直接返回 `ApiResponse>` +3. **单对象接口**(如详情、统计):Mock 返回 `{ code: 0, data: { ... } }`,后端返回 `ApiResponse` 即可 +4. **写操作接口**(新增/编辑/删除):Mock 返回 `{ code: 0, message: "success", data: null }` 或 `{ code: 0, data: { id: N } }`,后端保持一致 +5. **字段名必须与 Mock 一致**:经 CamelCase 序列化后的 JSON 字段名必须与 Mock 中的 key 完全匹配 + +#### 错误示例与正确示例 + +```csharp +// ❌ 错误:直接返回 List,data 变成数组,前端 r.data.items 为 undefined +return Ok(ApiResponse>.Success(result)); +// JSON: { "code": 0, "data": [{...}, {...}] } + +// ✅ 正确:包装为 items 对象,与 Mock 的 { data: { items: [...] } } 一致 +return Ok(ApiResponse.Success(new { items = result })); +// JSON: { "code": 0, "data": { "items": [{...}, {...}] } } +``` + +```csharp +// ❌ 错误:PagedResult 的 Items 字段不匹配(虽然是 PagedResult) +// 如果前端期望 { items, total, page, pageSize },PagedResult 字段名必须完全匹配 +// 当前 PagedResult 已定义为 Items/Total/Page/PageSize,经 CamelCase 序列化为 items/total/page/pageSize ✅ + +// ✅ 分页列表直接返回 PagedResult 即可 +return Ok(ApiResponse>.Success(result)); +// JSON: { "code": 0, "data": { "items": [...], "total": 160, "page": 1, "pageSize": 20 } } +``` + +#### 检查方法 + +每个接口开发完成后,必须: +1. 打开对应的 Mock 文件(`frontend/mock/*.ts`),找到该 URL 的 `response` 定义 +2. 确认 Mock 中 `data` 的结构(是 `{ items: [...] }` 还是 `{ ... }` 或是 `null`) +3. 确认后端 `ApiResponse` 中的 `T` 能序列化出相同结构 +4. 如有差异,调整后端返回格式以匹配 Mock + ### 5.3 异常处理 ```csharp diff --git a/src/CncWebApi/Controllers/BrandController.cs b/src/CncWebApi/Controllers/BrandController.cs index 2c11c21..51d3d1d 100644 --- a/src/CncWebApi/Controllers/BrandController.cs +++ b/src/CncWebApi/Controllers/BrandController.cs @@ -33,7 +33,8 @@ namespace CncWebApi.Controllers public IHttpActionResult GetList() { var result = _brandService.GetList(); - return Ok(ApiResponse>.Success(result)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items = result })); } /// @@ -117,7 +118,8 @@ namespace CncWebApi.Controllers public IHttpActionResult GetStandardFields() { var result = _brandService.GetStandardFields(); - return Ok(ApiResponse>.Success(result)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items = result })); } } } diff --git a/src/CncWebApi/Controllers/DashboardController.cs b/src/CncWebApi/Controllers/DashboardController.cs index fc3623d..8b80c20 100644 --- a/src/CncWebApi/Controllers/DashboardController.cs +++ b/src/CncWebApi/Controllers/DashboardController.cs @@ -46,7 +46,8 @@ namespace CncWebApi.Controllers public IHttpActionResult GetWorkshopProduction(DateTime? startDate = null, DateTime? endDate = null) { var result = _dashboardService.GetWorkshopProduction(startDate, endDate); - return Ok(ApiResponse>.Success(result)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items = result })); } /// @@ -58,7 +59,8 @@ namespace CncWebApi.Controllers public IHttpActionResult GetMachineRank(DateTime? startDate = null, DateTime? endDate = null, int top = 10) { var result = _dashboardService.GetMachineRank(startDate, endDate, top); - return Ok(ApiResponse>.Success(result)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items = result })); } /// @@ -70,7 +72,8 @@ namespace CncWebApi.Controllers public IHttpActionResult GetWorkerRank(DateTime? startDate = null, DateTime? endDate = null, int top = 10) { var result = _dashboardService.GetWorkerRank(startDate, endDate, top); - return Ok(ApiResponse>.Success(result)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items = result })); } /// @@ -106,7 +109,8 @@ namespace CncWebApi.Controllers public IHttpActionResult GetRecentAlerts(int count = 5) { var result = _dashboardService.GetRecentAlerts(count); - return Ok(ApiResponse>.Success(result)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items = result })); } /// diff --git a/src/CncWebApi/Controllers/OptionController.cs b/src/CncWebApi/Controllers/OptionController.cs index 39bd3ad..399fb5a 100644 --- a/src/CncWebApi/Controllers/OptionController.cs +++ b/src/CncWebApi/Controllers/OptionController.cs @@ -48,7 +48,8 @@ namespace CncWebApi.Controllers { var list = _workshopService.GetList(null); var items = list.Select(w => new SimpleOption { Value = w.Id.ToString(), Label = w.Name }).ToList(); - return Ok(ApiResponse>.Success(items)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items })); } /// @@ -61,7 +62,8 @@ namespace CncWebApi.Controllers { var list = _brandService.GetList(); var items = list.Select(b => new SimpleOption { Value = b.Id.ToString(), Label = b.BrandName }).ToList(); - return Ok(ApiResponse>.Success(items)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items })); } /// @@ -75,7 +77,8 @@ namespace CncWebApi.Controllers var query = new CncModels.Dto.Machine.MachineQuery { Page = 1, PageSize = 1000 }; var paged = _machineService.GetList(query); var items = paged.Items.Select(m => new SimpleOption { Value = m.Id.ToString(), Label = m.Name ?? m.DeviceCode }).ToList(); - return Ok(ApiResponse>.Success(items)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items })); } /// @@ -89,7 +92,8 @@ namespace CncWebApi.Controllers var query = new CncModels.Dto.Worker.WorkerQuery { Page = 1, PageSize = 1000 }; var paged = _workerService.GetList(query); var items = paged.Items.Select(w => new SimpleOption { Value = w.Id.ToString(), Label = w.Name + "(" + w.Code + ")" }).ToList(); - return Ok(ApiResponse>.Success(items)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items })); } /// @@ -103,7 +107,8 @@ namespace CncWebApi.Controllers var query = new CncModels.Dto.CollectAddress.CollectAddressQuery { Page = 1, PageSize = 1000 }; var paged = _collectAddressService.GetList(query); var items = paged.Items.Select(a => new SimpleOption { Value = a.Id.ToString(), Label = a.Name }).ToList(); - return Ok(ApiResponse>.Success(items)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items })); } } } diff --git a/src/CncWebApi/Controllers/ScreenConfigController.cs b/src/CncWebApi/Controllers/ScreenConfigController.cs index 4edd501..f603240 100644 --- a/src/CncWebApi/Controllers/ScreenConfigController.cs +++ b/src/CncWebApi/Controllers/ScreenConfigController.cs @@ -36,7 +36,8 @@ namespace CncWebApi.Controllers public IHttpActionResult GetConfigs() { var result = _screenService.GetConfigs(); - return Ok(ApiResponse>.Success(result)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items = result })); } /// @@ -94,7 +95,8 @@ namespace CncWebApi.Controllers public IHttpActionResult GetFilters(string screenKey = null) { var result = _screenService.GetFilters(screenKey ?? "main_screen"); - return Ok(ApiResponse>.Success(result)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items = result })); } /// diff --git a/src/CncWebApi/Controllers/ScreenController.cs b/src/CncWebApi/Controllers/ScreenController.cs index 1c23178..e6c08d3 100644 --- a/src/CncWebApi/Controllers/ScreenController.cs +++ b/src/CncWebApi/Controllers/ScreenController.cs @@ -64,7 +64,8 @@ namespace CncWebApi.Controllers public IHttpActionResult GetWorkshopProduction() { var result = _dashboardService.GetWorkshopProduction(null, null); - return Ok(ApiResponse>.Success(result)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items = result })); } /// @@ -88,7 +89,8 @@ namespace CncWebApi.Controllers public IHttpActionResult GetMachineRank(int top = 10) { var result = _dashboardService.GetMachineRank(null, null, top); - return Ok(ApiResponse>.Success(result)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items = result })); } /// @@ -100,7 +102,8 @@ namespace CncWebApi.Controllers public IHttpActionResult GetWorkerRank(int top = 10) { var result = _dashboardService.GetWorkerRank(null, null, top); - return Ok(ApiResponse>.Success(result)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items = result })); } /// @@ -112,7 +115,8 @@ namespace CncWebApi.Controllers public IHttpActionResult GetMachineStatus() { var result = _dashboardService.GetMachineStatusDistribution(); - return Ok(ApiResponse.Success(result)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items = result })); } /// @@ -129,7 +133,8 @@ namespace CncWebApi.Controllers Value = f.FilterValue, Label = f.FilterType }).ToList(); - return Ok(ApiResponse>.Success(items)); + // Mock约定:data: { items: [...] } + return Ok(ApiResponse.Success(new { items })); } /// diff --git a/src/CncWebApi/Controllers/SettingsController.cs b/src/CncWebApi/Controllers/SettingsController.cs index 4db6df0..df8d606 100644 --- a/src/CncWebApi/Controllers/SettingsController.cs +++ b/src/CncWebApi/Controllers/SettingsController.cs @@ -49,7 +49,8 @@ namespace CncWebApi.Controllers ValueType = c.ValueType, Description = c.Description }).ToList(); - return Ok(ApiResponse>.Success(items)); + // Mock约定:data: { items: [...] },不能直接返回数组 + return Ok(ApiResponse.Success(new { items })); } /// @@ -95,7 +96,8 @@ namespace CncWebApi.Controllers public IHttpActionResult GetWorkshopList(string keyword = null) { var result = _workshopService.GetList(keyword); - return Ok(ApiResponse>.Success(result)); + // Mock约定:data: { items: [...] },不能直接返回数组 + return Ok(ApiResponse.Success(new { items = result })); } ///