# CNC机床数据采集系统 - 后端开发规范
> 最后更新:2026-04-28
> 适用范围:ASP.NET Web API 2 后端工程
---
## 一、技术栈
| 项 | 选型 | 版本 |
|----|------|------|
| IDE | Visual Studio 2017 | 15.9+ |
| 框架 | ASP.NET Web API 2 | .NET Framework 4.7.2 |
| ORM | Dapper | 最新稳定版 |
| 数据库 | MariaDB | 11.8 |
| 数据库驱动 | MySqlConnector | 最新稳定版(非Oracle的MySql.Data) |
| 测试框架 | xUnit | 最新稳定版 |
| Mock框架 | Moq | 最新稳定版 |
| JSON序列化 | Newtonsoft.Json | 12.0.3(VS2017兼容) |
| 认证 | JWT Bearer Token | 自行实现,不依赖Identity |
| 日志 | log4net | 最新稳定版 |
---
## 二、解决方案结构
```
E:\opencode\haoliang\
├── CncDataSystem.sln ← VS2017 解决方案文件
│
├── src/ ← 源码目录
│ ├── CncModels/ ← 数据模型层
│ │ ├── CncModels.csproj
│ │ ├── Entity/ ← 数据库表对应的实体类
│ │ ├── Dto/ ← API请求/响应的DTO类
│ │ ├── Enum/ ← 枚举定义
│ │ └── Constants/ ← 常量定义
│ │
│ ├── CncRepository/ ← 数据访问层
│ │ ├── CncRepository.csproj
│ │ ├── Base/ ← 基础仓储(泛型CRUD)
│ │ └── Impl/ ← 各表的具体仓储实现
│ │
│ ├── CncService/ ← 业务逻辑层
│ │ ├── CncService.csproj
│ │ ├── Interface/ ← 服务接口定义
│ │ └── Impl/ ← 服务实现
│ │
│ └── CncWebApi/ ← Web API 主项目
│ ├── CncWebApi.csproj
│ ├── App_Start/ ← WebApiConfig、FilterConfig
│ ├── Controllers/ ← API控制器
│ ├── Filters/ ← 自定义过滤器(认证、异常)
│ ├── Infrastructure/ ← 中间件、扩展方法
│ └── Web.config ← 数据库连接串、JWT密钥
│
├── tests/ ← 测试目录
│ ├── CncModels.Tests/ ← 模型层测试
│ ├── CncRepository.Tests/ ← 仓储层测试
│ ├── CncService.Tests/ ← 服务层测试
│ └── CncWebApi.Tests/ ← 控制器层测试
│
├── frontend/ ← 前端工程(解决方案文件夹引用,不编译)
├── docs/ ← 设计文档
└── database/ ← 数据库脚本
```
### 项目引用关系
```
CncWebApi → CncService → CncRepository → CncModels
↘ ↘ ↘
CncModels CncModels (无依赖)
```
每个项目只引用自己直接依赖的项目,不跨层引用。
---
## 三、命名规范
### 3.1 通用规则
| 项 | 规范 | 示例 |
|----|------|------|
| 类名 | PascalCase | `MachineService` |
| 方法名 | PascalCase | `GetById()` |
| 参数名 | camelCase | `machineId` |
| 局部变量 | camelCase | `totalCount` |
| 私有字段 | _camelCase | `_connectionString` |
| 常量 | PascalCase 或 UPPER_SNAKE | `MaxRetryCount` |
| 枚举值 | PascalCase | `AlertType.CollectFail` |
### 3.2 各层命名
| 层 | 类名后缀 | 示例 | 文件位置 |
|----|---------|------|---------|
| Entity | 无后缀 | `Machine`(对应表 `cnc_machine`) | `CncModels/Entity/` |
| 请求DTO | `Request` 后缀 | `CreateMachineRequest` | `CncModels/Dto/` |
| 响应DTO | `Response` 后缀 | `MachineListResponse` | `CncModels/Dto/` |
| 仓储接口 | `IRepository` 后缀 | `IMachineRepository` | `CncRepository/` |
| 仓储实现 | `Repository` 后缀 | `MachineRepository` | `CncRepository/Impl/` |
| 服务接口 | `IService` 后缀 | `IMachineService` | `CncService/Interface/` |
| 服务实现 | `Service` 后缀 | `MachineService` | `CncService/Impl/` |
| 控制器 | `Controller` 后缀 | `MachineController` | `CncWebApi/Controllers/` |
| 测试类 | `Tests` 后缀 | `MachineServiceTests` | `tests/CncService.Tests/` |
### 3.3 Entity 与数据库表映射
```csharp
// 表名 cnc_machine → 类名 Machine
// 表名 cnc_daily_production → 类名 DailyProduction
// 表名 log_collect_raw → 类名 CollectRaw(跨日志库,仓储中指定库名)
// 表名前缀 cnc_ / log_ 在Entity类名中去掉
```
### 3.4 控制器与路由
```csharp
// 控制器名 → 路由前缀
// MachineController → /api/admin/machine
// DashboardController → /api/admin/dashboard
// ScreenController → /api/screen
// SysConfigController → /api/admin/sys-config
// 路由模板统一使用属性路由
[RoutePrefix("api/admin/machine")]
public class MachineController : ApiController
{
[HttpGet, Route("")]
public IHttpActionResult GetList(...) { }
[HttpGet, Route("{id:int}")]
public IHttpActionResult GetById(int id) { }
[HttpPost, Route("")]
public IHttpActionResult Create([FromBody] CreateMachineRequest request) { }
[HttpPut, Route("{id:int}")]
public IHttpActionResult Update(int id, [FromBody] UpdateMachineRequest request) { }
[HttpDelete, Route("{id:int}")]
public IHttpActionResult Delete(int id) { }
}
```
---
## 四、注释规范
### 4.1 XML文档注释(必须)
所有 **public 类、方法、属性、接口** 必须有 XML 文档注释(`///`)。
```csharp
///
/// 机床管理服务,处理机床的CRUD操作和状态查询
///
public class MachineService : IMachineService
{
///
/// 获取机床分页列表
///
/// 查询条件(关键字、车间ID、在线状态等)
/// 分页结果,包含机床列表和总数
public PagedResult GetList(MachineQuery query)
{
...
}
}
```
### 4.2 行内注释(必须)
复杂逻辑、业务规则、SQL语句必须有行内注释。
```csharp
// 日均单机产量 = 日期范围内总产量 / 天数 / 机床数
// 多天范围时Y轴单位为"件/台/天",单天为"件/台"
var days = Math.Max(1, (endDate - startDate).Days + 1);
var avgQuantity = Math.Round((decimal)totalQuantity / days / machineCount, 1);
```
```csharp
// A-B-C-A-B场景:程序A第二次出现时创建新段记录
// 日汇总按(machine_id, production_date, program_name)合并
if (currentSegment.ProgramName != newProgramName)
{
CloseSegment(currentSegment, "program_change");
CreateSegment(machineId, newProgramName);
}
```
### 4.3 文件头注释(必须)
每个 .cs 文件顶部必须有文件头注释:
```csharp
///
/// 机床管理控制器
/// 对应页面:MachineListPage、MachineDetailPage
/// API文档:docs/03-API接口设计.md → 3.3 设备管理模块
/// 数据库表:cnc_machine, cnc_worker_machine, cnc_collect_record
///
```
---
## 五、编码规范
### 5.1 分层职责
| 层 | 职责 | 不做 |
|----|------|------|
| **Controller** | 参数校验、调用Service、包装响应格式 | 不写业务逻辑、不直接操作数据库 |
| **Service** | 业务逻辑编排、数据转换、事务管理 | 不直接写SQL、不返回IHttpActionResult |
| **Repository** | SQL编写、数据库读写、对象映射 | 不写业务判断、不知道API概念 |
| **Models** | 纯数据定义、枚举、常量 | 不包含任何逻辑 |
### 5.2 统一响应格式
所有 API 返回统一的 `ApiResponse` 包装:
```csharp
///
/// 统一API响应格式
///
public class ApiResponse
{
/// 错误码,0=成功
public int Code { get; set; }
/// 提示信息
public string Message { get; set; }
/// 业务数据
public T Data { get; set; }
public static ApiResponse Success(T data, string message = "success")
=> new ApiResponse { Code = 0, Message = message, Data = data };
public static ApiResponse Fail(int code, string message)
=> new ApiResponse { Code = code, Message = message, Data = default(T) };
}
```
### 5.2.1 Mock数据结构对齐(强制)
> **核心原则:后端 API 返回的 `data` 内部结构必须严格匹配页面文件§9(接口引用与数据结构)中定义的数据结构。页面文件§9是数据结构的唯一来源,Mock文件按§9生成,后端也必须对齐§9。**
>
> **注意:此规则仅约束返回的 JSON 数据结构,不约束 URL 路径和 HTTP 方法。** URL 和方法按 §1.3 RESTful 规范实现,与 Mock 可能有差异(差异对照见 `03-API接口设计.md` 端点清单的双列URL)。
#### 规则说明
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