From ea26fa08ba6c36b4ef3b38e41fe75291e8ea79df Mon Sep 17 00:00:00 2001
From: haoliang <821644@qq.com>
Date: Tue, 28 Apr 2026 16:51:31 +0800
Subject: [PATCH] =?UTF-8?q?docs:=20=E5=90=8E=E7=AB=AF=E5=BC=80=E5=8F=91?=
=?UTF-8?q?=E8=A7=84=E8=8C=83=E5=AE=9A=E7=A8=BF=EF=BC=8C=E5=90=AB=E6=8A=80?=
=?UTF-8?q?=E6=9C=AF=E6=A0=88/=E9=A1=B9=E7=9B=AE=E7=BB=93=E6=9E=84/?=
=?UTF-8?q?=E5=91=BD=E5=90=8D/=E6=B3=A8=E9=87=8A/=E6=B5=8B=E8=AF=95/?=
=?UTF-8?q?=E5=88=86=E5=B1=82/=E4=BE=9D=E8=B5=96=E6=B3=A8=E5=85=A5?=
=?UTF-8?q?=E8=A7=84=E8=8C=83?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
docs/04-后端开发规范.md | 438 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 438 insertions(+)
create mode 100644 docs/04-后端开发规范.md
diff --git a/docs/04-后端开发规范.md b/docs/04-后端开发规范.md
new file mode 100644
index 0000000..5092393
--- /dev/null
+++ b/docs/04-后端开发规范.md
@@ -0,0 +1,438 @@
+# 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.3 异常处理
+
+```csharp
+// Controller 层不 try-catch,由全局异常过滤器统一处理
+// Service 层抛出业务异常
+public class BusinessException : Exception
+{
+ public int Code { get; }
+ public BusinessException(int code, string message) : base(message) { Code = code; }
+}
+
+// 使用示例
+if (existingMachine != null)
+ throw new BusinessException(40003, "设备编码已存在");
+
+// 全局异常过滤器
+public class GlobalExceptionFilter : ExceptionFilterAttribute
+{
+ public override void OnException(HttpActionExecutedContext context)
+ {
+ if (context.Exception is BusinessException bex)
+ {
+ context.Response = ... // 返回 { code: bex.Code, message: bex.Message }
+ }
+ else
+ {
+ // 记录日志,返回 50001
+ }
+ }
+}
+```
+
+### 5.4 数据库连接管理
+
+```csharp
+// Repository 基类提供连接,每个方法 using 自动释放
+public abstract class BaseRepository
+{
+ private readonly string _connectionString;
+
+ protected BaseRepository(string connectionString)
+ {
+ _connectionString = connectionString;
+ }
+
+ ///
+ /// 创建新的数据库连接,调用方需 using 释放
+ ///
+ protected IDbConnection CreateConnection()
+ {
+ return new MySqlConnection(_connectionString);
+ }
+}
+
+// 使用示例
+public Machine GetById(int id)
+{
+ using (var conn = CreateConnection())
+ {
+ return conn.QueryFirstOrDefault(
+ "SELECT * FROM cnc_machine WHERE id = @Id", new { Id = id });
+ }
+}
+```
+
+### 5.5 双库切换
+
+```csharp
+// 业务库和日志库连接串不同,通过两个 BaseRepository 子类区分
+public class BusinessRepository : BaseRepository { ... } // → cnc_business
+public class LogRepository : BaseRepository { ... } // → cnc_log
+
+// 需要访问日志库的仓储继承 LogRepository
+// 其他继承 BusinessRepository
+```
+
+---
+
+## 六、测试规范
+
+### 6.1 覆盖率要求
+
+- **每个 public 方法必须有至少一个测试用例**
+- **分支覆盖**:if/else 每个分支都要测到
+- **异常路径**:参数校验失败、数据不存在等异常场景必须覆盖
+- 目标:**100% 方法覆盖,≥90% 分支覆盖**
+
+### 6.2 测试命名
+
+```
+[Method]_[Scenario]_[ExpectedResult]
+
+示例:
+GetList_WithKeywordFilter_ReturnsFilteredMachines
+GetById_WhenNotExists_ThrowsBusinessException
+Create_WithDuplicateDeviceCode_Throws40003
+```
+
+### 6.3 测试结构(AAA模式)
+
+```csharp
+[Fact]
+public void GetList_WithKeywordFilter_ReturnsFilteredMachines()
+{
+ // Arrange - 准备数据
+ var mockRepo = new Mock();
+ mockRepo.Setup(r => r.GetList(It.IsAny()))
+ .Returns(new List { ... });
+
+ var service = new MachineService(mockRepo.Object);
+
+ // Act - 执行操作
+ var result = service.GetList(new MachineQuery { Keyword = "西" });
+
+ // Assert - 验证结果
+ Assert.Single(result.Items);
+ Assert.Contains("西", result.Items[0].Name);
+}
+```
+
+### 6.4 各层测试策略
+
+| 层 | 测试方式 | Mock对象 |
+|----|---------|---------|
+| **Controller** | 测试路由匹配、参数校验、响应格式 | Mock Service |
+| **Service** | 测试业务逻辑、数据转换、异常抛出 | Mock Repository |
+| **Repository** | 测试SQL正确性、数据映射 | 使用内存数据库或测试库 |
+| **Models** | 测试属性默认值、枚举值、验证逻辑 | 无依赖 |
+
+### 6.5 测试项目配置
+
+```csharp
+// 每个测试项目引用对应的生产项目
+// CncService.Tests → 引用 CncService、CncModels
+// 使用 Moq 框架 mock 接口
+// 测试数据通过 [InlineData] 或测试初始化方法提供
+```
+
+---
+
+## 七、依赖注入
+
+使用 Unity 或手动实现简易 DI 容器(VS2017 默认不支持 .NET Core 风格的内置DI)。
+
+```csharp
+// App_Start/UnityConfig.cs 注册依赖
+container.RegisterType();
+container.RegisterType();
+```
+
+每个 Service 通过构造函数注入依赖的 Repository:
+
+```csharp
+public class MachineService : IMachineService
+{
+ private readonly IMachineRepository _machineRepo;
+ private readonly IWorkerRepository _workerRepo;
+
+ public MachineService(IMachineRepository machineRepo, IWorkerRepository workerRepo)
+ {
+ _machineRepo = machineRepo;
+ _workerRepo = workerRepo;
+ }
+}
+```
+
+---
+
+## 八、Git提交规范
+
+- 每完成一个模块(含测试)提交一次
+- 提交信息中文,格式:`feat(module): 描述`
+- 编译+测试通过后才能提交
+- 与前端共用同一仓库 `main` 分支
+
+---
+
+## 九、开发顺序
+
+按依赖关系从底层往上开发:
+
+| 步骤 | 内容 | 依赖 |
+|------|------|------|
+| 1 | 搭建解决方案 + 项目结构 + NuGet包 | 无 |
+| 2 | CncModels(Entity + Dto + Enum) | 无 |
+| 3 | CncModels.Tests | 步骤2 |
+| 4 | CncRepository(基础仓储 + 连接管理) | 步骤2 |
+| 5 | CncRepository.Tests(登录/系统配置/车间) | 步骤4 |
+| 6 | CncService(业务逻辑) | 步骤4 |
+| 7 | CncService.Tests | 步骤6 |
+| 8 | CncWebApi(控制器 + 路由 + 过滤器) | 步骤6 |
+| 9 | CncWebApi.Tests | 步骤8 |
+
+先跑通 **登录→系统设置→车间管理** 这条最小链路,再逐步扩展其他模块。