diff --git a/.kiro/settings/mcp.json b/.kiro/settings/mcp.json
index dda36b1..1cbd4f7 100644
--- a/.kiro/settings/mcp.json
+++ b/.kiro/settings/mcp.json
@@ -18,6 +18,38 @@
"create_design_system_rules",
"whoami"
]
+ },
+ "admin-sqlserver": {
+ "command": "node",
+ "args": [
+ "C:/mcp/mssql-mcp-server/index.js"
+ ],
+ "env": {
+ "MSSQL_SERVER": "192.168.195.15",
+ "MSSQL_PORT": "1433",
+ "MSSQL_USER": "sa",
+ "MSSQL_PASSWORD": "Dbt@com@123",
+ "MSSQL_DATABASE": "MiAssessment_Admin"
+ },
+ "autoApprove": [
+ "execute_sql"
+ ]
+ },
+ "api-sqlserver": {
+ "command": "node",
+ "args": [
+ "C:/mcp/mssql-mcp-server/index.js"
+ ],
+ "env": {
+ "MSSQL_SERVER": "192.168.195.15",
+ "MSSQL_PORT": "1433",
+ "MSSQL_USER": "sa",
+ "MSSQL_PASSWORD": "Dbt@com@123",
+ "MSSQL_DATABASE": "MiAssessment_Business"
+ },
+ "autoApprove": [
+ "execute_sql"
+ ]
}
}
-}
+}
\ No newline at end of file
diff --git a/.kiro/steering/development-standards.md b/.kiro/steering/development-standards.md
new file mode 100644
index 0000000..b6aa188
--- /dev/null
+++ b/.kiro/steering/development-standards.md
@@ -0,0 +1,476 @@
+# 学业邑规划 - 开发规范
+
+本文档定义了 MiAssessment 项目的开发规范和编码标准,所有开发工作必须遵循这些规范。
+
+## 一、技术栈
+
+| 层级 | 技术 |
+|---|---|
+| 后端框架 | .NET 10 Web API (C#) |
+| 数据库 | SQL Server 2022 |
+| ORM | Entity Framework Core |
+| 缓存 | Redis |
+| 接口风格 | RPC 风格(仅 GET / POST 请求) |
+| 前端 | UniApp + Vue 3 + TypeScript |
+
+## 二、项目结构
+
+```
+server/MiAssessment/src/
+├── MiAssessment.Api/ # 小程序 API 接口
+├── MiAssessment.Admin/ # 后台管理系统基础模块
+├── MiAssessment.Admin.Business/ # 后台业务模块(主要开发区域)
+├── MiAssessment.Core/ # 核心业务逻辑
+├── MiAssessment.Infrastructure/ # 基础设施
+└── MiAssessment.Model/ # 数据模型
+```
+
+### Admin.Business 项目结构
+
+```
+MiAssessment.Admin.Business/
+├── Controllers/ # 控制器(API 入口)
+├── Services/ # 业务服务
+│ └── Interfaces/ # 服务接口
+├── Models/ # 请求/响应模型(DTO)
+│ ├── Common/ # 通用模型
+│ └── {Module}/ # 各模块模型
+├── Entities/ # 数据库实体类
+├── Data/ # 数据库上下文
+└── Attributes/ # 自定义特性
+```
+
+## 三、命名规范
+
+### 3.1 数据库命名
+
+| 类型 | 规范 | 示例 |
+|---|---|---|
+| 表名 | snake_case(小写下划线) | `users`, `assessment_records` |
+| 字段名 | PascalCase | `UserId`, `CreateTime` |
+| 主键 | `Id` (bigint 自增) | `Id` |
+| 外键 | `{表名}Id` | `UserId`, `OrderId` |
+| 时间字段 | `CreateTime`, `UpdateTime` | - |
+| 状态字段 | `Status` | - |
+| 软删除 | `IsDeleted` (bit) | - |
+
+### 3.2 C# 代码命名
+
+| 类型 | 规范 | 示例 |
+|---|---|---|
+| 命名空间 | PascalCase | `MiAssessment.Admin.Business.Services` |
+| 类名 | PascalCase | `UserService`, `OrderController` |
+| 接口 | I + PascalCase | `IUserService`, `IOrderService` |
+| 方法 | PascalCase + Async 后缀 | `GetUserListAsync`, `CreateOrderAsync` |
+| 属性 | PascalCase | `UserId`, `CreateTime` |
+| 私有字段 | _camelCase | `_userService`, `_dbContext` |
+| 参数 | camelCase | `userId`, `request` |
+| 常量 | PascalCase | `MaxPageSize`, `DefaultStatus` |
+
+### 3.3 文件命名
+
+| 类型 | 规范 | 示例 |
+|---|---|---|
+| 实体类 | 单数形式 | `User.cs`, `Order.cs` |
+| 服务接口 | I + 服务名 | `IUserService.cs` |
+| 服务实现 | 服务名 | `UserService.cs` |
+| 控制器 | 模块名 + Controller | `UserController.cs` |
+| DTO 模型 | 功能 + Request/Dto | `CreateUserRequest.cs`, `UserDto.cs` |
+
+## 四、代码规范
+
+### 4.1 实体类规范
+
+```csharp
+using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
+
+namespace MiAssessment.Admin.Business.Entities;
+
+///
+/// 用户表
+///
+[Table("users")]
+public class User
+{
+ ///
+ /// 主键ID
+ ///
+ [Key]
+ public long Id { get; set; }
+
+ ///
+ /// 用户UID
+ ///
+ [Required]
+ [MaxLength(6)]
+ public string Uid { get; set; } = null!;
+
+ ///
+ /// 创建时间
+ ///
+ public DateTime CreateTime { get; set; }
+
+ ///
+ /// 更新时间
+ ///
+ public DateTime UpdateTime { get; set; }
+
+ ///
+ /// 软删除标记
+ ///
+ public bool IsDeleted { get; set; }
+}
+```
+
+**要点**:
+- 使用 `[Table("表名")]` 指定数据库表名
+- 使用 `[Key]` 标记主键
+- 使用 `[Required]` 标记必填字段
+- 使用 `[MaxLength(n)]` 限制字符串长度
+- 所有属性必须有 XML 注释
+- 字符串属性使用 `= null!` 初始化
+
+### 4.2 服务接口规范
+
+```csharp
+namespace MiAssessment.Admin.Business.Services.Interfaces;
+
+///
+/// 用户服务接口
+///
+public interface IUserService
+{
+ ///
+ /// 获取用户列表
+ ///
+ Task> GetUserListAsync(UserQueryRequest request);
+
+ ///
+ /// 获取用户详情
+ ///
+ Task GetUserDetailAsync(long id);
+
+ ///
+ /// 更新用户状态
+ ///
+ Task UpdateUserStatusAsync(long id, int status);
+}
+```
+
+### 4.3 服务实现规范
+
+```csharp
+namespace MiAssessment.Admin.Business.Services;
+
+///
+/// 用户服务实现
+///
+public class UserService : IUserService
+{
+ private readonly AdminBusinessDbContext _dbContext;
+ private readonly ILogger _logger;
+
+ public UserService(
+ AdminBusinessDbContext dbContext,
+ ILogger logger)
+ {
+ _dbContext = dbContext;
+ _logger = logger;
+ }
+
+ ///
+ public async Task> GetUserListAsync(UserQueryRequest request)
+ {
+ var query = _dbContext.Users
+ .Where(u => !u.IsDeleted);
+
+ // 条件筛选
+ if (!string.IsNullOrEmpty(request.Phone))
+ {
+ query = query.Where(u => u.Phone.Contains(request.Phone));
+ }
+
+ // 获取总数
+ var total = await query.CountAsync();
+
+ // 分页查询
+ var items = await query
+ .OrderByDescending(u => u.CreateTime)
+ .Skip(request.Skip)
+ .Take(request.PageSize)
+ .Select(u => new UserDto
+ {
+ Id = u.Id,
+ Uid = u.Uid,
+ // ...
+ })
+ .ToListAsync();
+
+ return new PagedResult(items, total, request.Page, request.PageSize);
+ }
+}
+```
+
+### 4.4 控制器规范
+
+```csharp
+namespace MiAssessment.Admin.Business.Controllers;
+
+///
+/// 用户管理控制器
+///
+[Route("api/admin/user")]
+public class UserController : BusinessControllerBase
+{
+ private readonly IUserService _userService;
+
+ public UserController(IUserService userService)
+ {
+ _userService = userService;
+ }
+
+ ///
+ /// 获取用户列表
+ ///
+ [HttpGet("getList")]
+ public async Task GetList([FromQuery] UserQueryRequest request)
+ {
+ var result = await _userService.GetUserListAsync(request);
+ return Ok(result);
+ }
+
+ ///
+ /// 更新用户状态
+ ///
+ [HttpPost("updateStatus")]
+ public async Task UpdateStatus([FromBody] UpdateStatusRequest request)
+ {
+ var success = await _userService.UpdateUserStatusAsync(request.Id, request.Status);
+ return success ? Ok() : Error(ErrorCodes.UserNotFound, "用户不存在");
+ }
+}
+```
+
+## 五、API 接口规范
+
+### 5.1 接口风格
+
+- **仅使用 GET 和 POST 请求**
+- GET:查询操作,参数通过 Query String 传递
+- POST:新增、修改、删除操作,参数通过 Request Body (JSON) 传递
+
+### 5.2 路由规范
+
+```
+/api/admin/{module}/{action}
+```
+
+| 操作 | 方法 | 路由示例 |
+|---|---|---|
+| 获取列表 | GET | `/api/admin/user/getList` |
+| 获取详情 | GET | `/api/admin/user/getDetail?id=1` |
+| 创建 | POST | `/api/admin/user/create` |
+| 更新 | POST | `/api/admin/user/update` |
+| 删除 | POST | `/api/admin/user/delete` |
+| 更新状态 | POST | `/api/admin/user/updateStatus` |
+| 更新排序 | POST | `/api/admin/user/updateSort` |
+
+### 5.3 响应格式
+
+```json
+{
+ "code": 0,
+ "message": "success",
+ "data": {}
+}
+```
+
+- `code`: 0 表示成功,非 0 表示错误
+- `message`: 提示信息
+- `data`: 业务数据
+
+### 5.4 分页响应
+
+```json
+{
+ "code": 0,
+ "message": "success",
+ "data": {
+ "items": [],
+ "total": 100,
+ "page": 1,
+ "pageSize": 20,
+ "totalPages": 5
+ }
+}
+```
+
+## 六、错误码规范
+
+| 范围 | 说明 |
+|---|---|
+| 0 | 成功 |
+| 1000-1999 | 通用错误 |
+| 2000-2999 | 业务错误 |
+| 3000-3099 | 配置模块错误 |
+| 3100-3199 | 内容模块错误 |
+| 3200-3299 | 测评模块错误 |
+| 3300-3399 | 用户模块错误 |
+| 3400-3499 | 订单模块错误 |
+| 3500-3599 | 规划师模块错误 |
+| 3600-3699 | 分销模块错误 |
+| 5000-5999 | 系统错误 |
+
+## 七、注释规范
+
+### 7.1 XML 注释
+
+所有公开的类、方法、属性必须有 XML 注释:
+
+```csharp
+///
+/// 获取用户列表
+///
+/// 查询参数
+/// 分页用户列表
+public async Task> GetUserListAsync(UserQueryRequest request)
+```
+
+### 7.2 代码注释
+
+- 使用中文注释
+- 复杂逻辑需要添加说明注释
+- 避免无意义的注释
+
+## 八、数据库操作规范
+
+### 8.1 查询规范
+
+```csharp
+// 始终过滤软删除记录
+var query = _dbContext.Users.Where(u => !u.IsDeleted);
+
+// 使用 AsNoTracking 提高只读查询性能
+var users = await _dbContext.Users
+ .AsNoTracking()
+ .Where(u => !u.IsDeleted)
+ .ToListAsync();
+```
+
+### 8.2 软删除
+
+```csharp
+// 删除操作使用软删除
+public async Task DeleteAsync(long id)
+{
+ var entity = await _dbContext.Users.FindAsync(id);
+ if (entity == null) return false;
+
+ entity.IsDeleted = true;
+ entity.UpdateTime = DateTime.Now;
+ await _dbContext.SaveChangesAsync();
+ return true;
+}
+```
+
+### 8.3 更新时间
+
+```csharp
+// 更新操作自动设置 UpdateTime
+entity.UpdateTime = DateTime.Now;
+await _dbContext.SaveChangesAsync();
+```
+
+## 九、状态值定义
+
+### 9.1 通用状态
+
+| 值 | 说明 |
+|---|---|
+| 0 | 禁用/下线 |
+| 1 | 启用/正常 |
+
+### 9.2 订单状态
+
+| 值 | 说明 |
+|---|---|
+| 1 | 待支付 |
+| 2 | 已支付 |
+| 3 | 已完成 |
+| 4 | 退款中 |
+| 5 | 已退款 |
+| 6 | 已取消 |
+
+### 9.3 用户等级
+
+| 值 | 说明 |
+|---|---|
+| 1 | 普通用户 |
+| 2 | 合伙人 |
+| 3 | 渠道合伙人 |
+
+## 十、测试规范
+
+### 10.1 测试框架
+
+- 单元测试:xUnit + Moq
+- 属性测试:FsCheck
+
+### 10.2 测试命名
+
+```csharp
+[Fact]
+public async Task GetUserListAsync_WithValidRequest_ReturnsPagedResult()
+{
+ // Arrange
+ // Act
+ // Assert
+}
+```
+
+### 10.3 属性测试注释
+
+```csharp
+///
+/// Property: 分页查询返回的记录数不超过 PageSize
+/// **Validates: Requirements 9.1**
+///
+[Property]
+public Property PaginationReturnsCorrectCount()
+{
+ // ...
+}
+```
+
+## 十一、Git 提交规范
+
+### 11.1 提交信息格式
+
+```
+():
+
+
+```
+
+### 11.2 Type 类型
+
+| Type | 说明 |
+|---|---|
+| feat | 新功能 |
+| fix | 修复 bug |
+| docs | 文档更新 |
+| style | 代码格式 |
+| refactor | 重构 |
+| test | 测试 |
+| chore | 构建/工具 |
+
+### 11.3 示例
+
+```
+feat(user): 添加用户列表分页查询功能
+
+- 实现 GetUserListAsync 方法
+- 支持按手机号、昵称筛选
+- 添加分页参数验证
+```
diff --git a/docs/开发规范/1-编程规约/1.1-命名风格.md b/docs/开发规范/1-编程规约/1.1-命名风格.md
new file mode 100644
index 0000000..cae4d2b
--- /dev/null
+++ b/docs/开发规范/1-编程规约/1.1-命名风格.md
@@ -0,0 +1,740 @@
+# 1.1 命名风格
+
+---
+
+## 1. 通用命名原则
+- 使用有意义且可读的名称
+ ```csharp
+ // ❌ 不建议:缩写和无意义的名称
+ var d = DateTime.Now;
+ var usr = new User();
+ int n = 100;
+
+ // ✅ 建议:清晰表达意图
+ var currentDate = DateTime.Now;
+ var newUser = new User();
+ int maxRetryCount = 100;
+ ```
+
+- 不建议使用拼音和中文
+ ```csharp
+ // ❌ 不建议
+ var yongHu = new User();
+ string xingMing = "张三";
+ var keHuMingCheng = "张三";
+ var 订单数量 = 10;
+
+ // ✅ 建议
+ var user = new User();
+ string fullName = "张三";
+ ```
+
+- 不建议过度缩写
+ ```csharp
+ // ❌ 不建议
+ var cSvc = new CustomerService();
+ var ordMgr = new OrderManager();
+
+ // ✅ 建议
+ var customerService = new CustomerService();
+ var orderManager = new OrderManager();
+
+ // ✅ 可接受含义广为人知的缩写
+ var customerId = 1; // Id
+ var htmlContent = ""; // Html
+ var xmlDocument = ""; // Xml
+ var urlAddress = ""; // Url
+ var httpClient = new HttpClient(); // Http
+ ```
+
+- 不建议使用下划线开头(除私有字段外)
+ ```csharp
+ // ❌ 不建议
+ public string _customerName;
+ public void _ProcessOrder() { }
+
+ // ✅ 建议
+ public string CustomerName { get; set; }
+ private string _customerName; // 私有字段可以使用
+ public void ProcessOrder() { }
+ ```
+## 2. 命名约定
+### 2.1 类
+- 使用 PascalCase(帕斯卡命名法)
+ ```csharp
+ // ✅ 建议的类命名
+ public class Customer { }
+ public class OrderService { }
+ public class ProductRepository { }
+ public class PaymentProcessor { }
+ ```
+
+- 使用名词或名词短语
+ ```csharp
+ // ✅ 建议:名词形式
+ public class Invoice { }
+ public class ShoppingCart { }
+ public class UserProfile { }
+ public class EmailValidator { }
+ public class UserAuthentication { }
+
+ // ❌ 不建议:动词形式
+ public class ProcessOrder { } // 应该是 OrderProcessor
+ public class ValidateUser { } // 应该是 UserValidator
+ public class Calculate { } // 适用于方法命名
+ ```
+
+- 建议的命名方式
+ ```csharp
+ // 服务类
+ // ✅ 以 Service 结尾
+ public class CustomerService { }
+ public class EmailService { }
+ // ✅ 以职责命名
+ public class OrderProcessor { }
+ public class EmailSender { }
+ public class PaymentGateway { }
+ public class CacheManager { }
+
+ // 仓储类
+ // ✅ 以 Repository 结尾
+ public class OrderRepository { }
+ public class ProductRepository { }
+
+ // ✅ Provider
+ public class AuthenticationProvider { }
+ public class DataProvider { }
+
+ // ✅ Helper/Utility
+ public class StringHelper { }
+ public class DateTimeUtility { }
+
+ // 异常类
+ // ✅ 以 Exception 结尾
+ public class OrderNotFoundException { }
+ public class InvalidOperationException { }
+
+ // ✅ Attribute
+ public class ValidateAttribute { }
+ public class AuthorizeAttribute { }
+
+ // ✅ DTO/ViewModel
+ public class CustomerDto { }
+ public class OrderViewModel { }
+ public class CreateProductRequest { }
+ public class ProductResponse { }
+ public class DoSomethingInput { }
+ public class DoSomethingOutput { }
+
+ // ✅ Entity/Model
+ public class Order { }
+ public class Product { }
+ public class Customer { }
+
+ // 验证器
+ // ✅ 以 Validator 结尾
+ public class OrderValidator { }
+ public class EmailValidator { }
+ public class CreateOrderRequestValidator { }
+ ```
+- 不建议的命名方式
+ ```csharp
+ // ❌ 不建议:使用下划线或特殊字符
+ public class Order_Service { }
+ public class Product$Manager { }
+
+ // ❌ 不建议:使用前缀
+ public class COrder { }
+ public class TCustomer { }
+
+ // ❌ 不建议:过于通用
+ public class Data { }
+ public class Info { }
+ public class Manager { } // 太宽泛,应该是具体的 xxxManager
+ ```
+### 2.2 接口
+- 使用 `I` 前缀 + PascalCase
+ ```csharp
+ // ✅ 正确的接口命名
+ public interface ICustomerService { }
+ public interface IOrderRepository { }
+ public interface IPaymentProcessor { }
+ public interface ILogger { }
+ ```
+
+- 使用名词、名词短语或形容词
+ ```csharp
+ // ✅ 建议:名词 (一般表示为业务接口)
+ public interface IRepository { }
+ public interface IValidator { }
+ public interface IOrderService { }
+ public interface ILogger { }
+ public interface ICache { }
+
+ // ✅ 建议:形容词(一般表示为能力接口)
+ public interface IComparable { }
+ public interface IDisposable { }
+ public interface IEnumerable { }
+ public interface IQueryable { }
+
+ // ✅ 建议:名词短语
+ public interface IDataAccess { }
+ public interface IUserAuthentication { }
+ ```
+
+- 避免使用 Interface 后缀
+ ```csharp
+ // ❌ 不建议
+ public interface IOrderServiceInterface { }
+
+ // ✅ 建议
+ public interface IOrderService { }
+ ```
+### 2.3 方法
+- 使用 PascalCase
+ ```csharp
+ public class CustomerService
+ {
+ // ✅ 建议
+ public void CreateCustomer() { }
+ public Customer GetCustomerById(int id) { }
+ public void UpdateCustomerProfile() { }
+ public void DeleteCustomer() { }
+ }
+ ```
+
+- 使用动词或动词短语
+ ```csharp
+ // ✅ 建议:明确、清晰的动作
+ public void CreateOrder() { }
+ public void UpdateCustomer() { }
+ public void DeleteProduct() { }
+ public Order GetOrderById(int id) { }
+ public List GetOrdersByCustomer(int customerId) { }
+ public bool ValidateEmail(string email) { }
+ public decimal CalculateDiscount(Order order) { }
+
+ // ❌ 不建议:名词形式
+ public void Order() { } // 应该是 CreateOrder 或 ProcessOrder
+ public void Customer() { }
+ ```
+
+- 常用动词前缀
+ ```csharp
+ // Get - 获取数据(通常有返回值)
+ public Customer GetCustomerById(int id) { }
+ public List GetOrders() { }
+
+ // Find - 查找数据(可能返回 null)
+ public Customer? FindCustomerByEmail(string email) { }
+
+ // Create/Add - 创建新对象
+ public void CreateOrder(Order order) { }
+ public void AddProduct(Product product) { }
+
+ // Update/Modify - 更新现有对象
+ public void UpdateCustomer(Customer customer) { }
+ public void ModifyOrderStatus(int orderId, OrderStatus status) { }
+
+ // Delete/Remove - 删除对象
+ public void DeleteOrder(int orderId) { }
+ public void RemoveProduct(int productId) { }
+
+ // Save - 保存(创建或更新)
+ public void SaveCustomer(Customer customer) { }
+
+ // Validate - 验证
+ public bool ValidateOrder(Order order) { }
+ public ValidationResult ValidateInput(string input) { }
+
+ // Calculate/Compute - 计算
+ public decimal CalculateTotal(List items) { }
+ public int ComputeAge(DateTime birthDate) { }
+
+ // Process/Handle - 处理
+ public void ProcessOrder(Order order) { }
+ public void ProcessPayment(Payment payment) { }
+
+ // 布尔判断
+ // ✅ 建议: 使用 Is、Has、Can、Should 等前缀更能提高可读性
+ public bool IsValid() { }
+ public bool IsActive() { }
+ public bool HasPermission() { }
+ public bool CanProcess() { }
+ public bool ShouldRetry() { }
+ public bool Exists(int id) { }
+ public bool Contains(string value) { }
+ // ❌ 不建议:不清晰的命名
+ public bool Valid() { }
+ public bool Active() { }
+ public bool Permission() { }
+
+ // Convert/Transform - 转换
+ public OrderDto ConvertToDto(Order order) { }
+ public string TransformToJson(object obj) { }
+
+ // Send/Receive - 发送/接收
+ public void SendNotification(string message) { }
+ public Message ReceiveMessage() { }
+ ``
+
+- 常用查询方法命名方式
+ ```csharp
+ // ✅ 单个对象
+ public Customer GetCustomerById(int id) { }
+ public Order FindOrderByNumber(string orderNumber) { }
+
+ // ✅ 集合
+ public List GetAllCustomers() { }
+ public IEnumerable GetOrdersByCustomer(int customerId) { }
+ public List FindProductsByCategory(string category) { }
+
+ // ✅ 条件查询
+ public List GetActiveOrders() { }
+ public List GetVipCustomers() { }
+
+ // ✅ 分页查询
+ public PagedResult GetProducts(int pageIndex, int pageSize) { }
+ public List GetOrdersByPage(int pageNumber, int pageSize) { }
+ ```
+
+- 私有方法也应该使用 PascalCase
+ ```csharp
+ public class OrderProcessor
+ {
+ // ✅ 私有方法也使用 PascalCase
+ private void ValidateOrderItems(Order order) { }
+ private decimal CalculateItemTotal(OrderItem item) { }
+ private bool CheckInventory(int productId) { }
+ }
+ ```
+### 2.4 属性与字段
+- 属性使用 PascalCase
+ ```csharp
+ public class Customer
+ {
+ // ✅ 建议的属性命名
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public string Email { get; set; }
+ public DateTime CreatedDate { get; set; }
+ public bool IsActive { get; set; }
+ public decimal TotalAmount { get; set; }
+ }
+ ```
+
+- 私有字段使用 _camelCase
+ ```csharp
+ public class OrderService
+ {
+ // ✅ 建议的私有字段命名
+ private readonly IOrderRepository _orderRepository;
+ private readonly ILogger _logger;
+ private readonly IEmailService _emailService;
+ private int _retryCount;
+ private bool _isProcessing;
+ }
+ ```
+
+- 只读字段
+ ```csharp
+ public class Configuration
+ {
+ // ✅ 公共只读字段使用 PascalCase
+ public readonly string DefaultCulture = "zh-CN"; //(公共只读字段建议改为属性方式使用)
+
+ // ✅ 私有只读字段使用 _camelCase
+ private readonly string _connectionString;
+ private readonly int _maxRetryCount;
+ }
+ ```
+
+- 静态字段
+ ```csharp
+ public class AppConstants
+ {
+ // ✅ 公共静态字段使用 PascalCase
+ public static readonly int MaxPageSize = 100;
+ public static readonly string DefaultDateFormat = "yyyy-MM-dd";
+
+ // ✅ 私有静态字段使用 _camelCase
+ private static readonly object _lockObject = new object();
+ }
+ ```
+
+- 布尔属性
+ ```csharp
+ public class Order
+ {
+ // ✅ 建议使用 Is、Has、Can 等前缀提高可读性
+ public bool IsCompleted { get; set; }
+ public bool IsPaid { get; set; }
+ public bool HasDiscount { get; set; }
+ public bool CanBeCancelled { get; set; }
+
+ // ❌ 不建议
+ public bool Completed { get; set; }
+ public bool Paid { get; set; }
+ }
+ ```
+
+- 集合属性
+ ```csharp
+ public class Order
+ {
+ // ✅ 建议:使用复数形式
+ public List Items { get; set; }
+ public ICollection Payments { get; set; }
+ public IEnumerable Comments { get; set; }
+
+ // ❌ 不建议:使用单数
+ public List Item { get; set; }
+ }
+ ```
+### 2.5 变量与参数
+- 局部变量使用 camelCase
+ ```csharp
+ public void ProcessOrder(int orderId)
+ {
+ // ✅ 建议的局部变量命名
+ var order = GetOrder(orderId);
+ var totalAmount = CalculateTotal(order);
+ var discountRate = GetDiscountRate(order);
+ var finalPrice = totalAmount * (1 - discountRate);
+ var isValid = ValidateOrder(order);
+ }
+ ```
+
+- 方法参数使用 camelCase
+ ```csharp
+ // ✅ 建议的参数命名
+ public Order CreateOrder(int customerId, List items, decimal discountAmount, string shippingAddress)
+ {
+ // ...existing code...
+ }
+
+ // ✅ 布尔参数命名
+ public List GetOrders(int customerId, bool includeCompleted, bool includeCancelled)
+ {
+ // ...existing code...
+ }
+ ```
+
+- 循环变量命名
+ ```csharp
+ // ✅ 简单循环可以使用单字母
+ for (int i = 0; i < orders.Count; i++)
+ {
+ // ...existing code...
+ }
+
+ // ✅ 有意义的循环变量
+ foreach (var order in orders)
+ {
+ ProcessOrder(order);
+ }
+
+ foreach (var item in order.Items)
+ {
+ ValidateItem(item);
+ }
+
+ // ❌ 不建议:不清晰的变量名
+ foreach (var o in orders) // o 不如 order 清晰
+ {
+ // ...existing code...
+ }
+ ```
+
+- LINQ 查询变量命名
+ ```csharp
+ // ✅ 建议
+ var activeCustomers = customers
+ .Where(c => c.IsActive)
+ .ToList();
+
+ var customerOrders = orders
+ .Where(o => o.CustomerId == customerId)
+ .OrderByDescending(o => o.CreatedDate)
+ .ToList();
+
+ // ✅ 建议:复杂查询使用有意义的变量
+ var highValueOrders = orders
+ .Where(order => order.TotalAmount > 1000)
+ .Select(order => new
+ {
+ OrderId = order.Id,
+ CustomerName = order.Customer.Name,
+ TotalAmount = order.TotalAmount
+ })
+ .ToList();
+ ```
+
+- 临时变量命名
+ ```csharp
+ public decimal CalculateOrderTotal(Order order)
+ {
+ // ✅ 建议:清晰的临时变量
+ var subtotal = order.Items.Sum(item => item.Price * item.Quantity);
+ var taxAmount = subtotal * 0.1m;
+ var shippingFee = CalculateShippingFee(order);
+ var discountAmount = CalculateDiscount(order, subtotal);
+ var total = subtotal + taxAmount + shippingFee - discountAmount;
+
+ return total;
+ }
+ ```
+
+- 避免使用无意义的变量名
+ ```csharp
+ // ❌ 不建议
+ var temp = GetCustomer();
+ var data = ProcessData();
+ var obj = CreateObject();
+ var result = DoSomething();
+
+ // ✅ 建议
+ var customer = GetCustomer();
+ var processedOrders = ProcessOrders();
+ var paymentRecord = CreatePaymentRecord();
+ var validationResult = ValidateInput();
+ ```
+### 2.6 常量与枚举
+- 常量命名
+ ```csharp
+ // ✅ 方式一:PascalCase(推荐)
+ public class OrderConstants
+ {
+ public const int MaxItemsPerOrder = 100;
+ public const decimal MinOrderAmount = 10.0m;
+ public const string DefaultCurrency = "CNY";
+ }
+
+ // ✅ 方式二:UPPER_CASE(C# 中较少使用)
+ public class AppSettings
+ {
+ public const int MAX_RETRY_COUNT = 3;
+ public const string DEFAULT_CULTURE = "zh-CN";
+ }
+
+ // ✅ 私有常量
+ private const int MaxRetryAttempts = 3;
+ private const string ErrorMessageTemplate = "处理订单 {0} 时发生错误";
+ ```
+
+- 枚举命名
+ ```csharp
+ // ✅ 枚举类型使用 PascalCase,单数形式
+ public enum OrderStatus
+ {
+ Pending, // 枚举值使用 PascalCase
+ Processing,
+ Completed,
+ Cancelled,
+ Refunded
+ }
+
+ // ✅ 标志枚举使用复数
+ [Flags]
+ public enum UserPermissions
+ {
+ None = 0,
+ Read = 1,
+ Write = 2,
+ Delete = 4,
+ Admin = 8
+ }
+
+ // ❌ 不建议:使用复数(非标志枚举)
+ public enum OrderStatuses { }
+
+ // ❌ 不建议:枚举值使用前缀
+ public enum OrderStatus
+ {
+ OrderStatusPending, // 不需要前缀
+ OrderStatusCompleted
+ }
+ ```
+
+- 常量类组织
+ ```csharp
+ // ✅ 按功能组织常量
+ public static class ValidationConstants
+ {
+ public const int MinPasswordLength = 8;
+ public const int MaxPasswordLength = 50;
+ public const int MinUsernameLength = 3;
+ public const int MaxUsernameLength = 20;
+ }
+
+ public static class CacheKeys
+ {
+ public const string CustomerPrefix = "customer_";
+ public const string OrderPrefix = "order_";
+ public const string ProductPrefix = "product_";
+ }
+
+ public static class ErrorCodes
+ {
+ public const string OrderNotFound = "ORDER_NOT_FOUND";
+ public const string InsufficientStock = "INSUFFICIENT_STOCK";
+ public const string PaymentFailed = "PAYMENT_FAILED";
+ }
+ ```
+### 2.7 布尔类型
+- 使用肯定的命名方式
+ ```csharp
+ // ✅ 建议:肯定形式
+ public bool IsActive { get; set; }
+ public bool IsEnabled { get; set; }
+ public bool HasPermission { get; set; }
+ public bool CanEdit { get; set; }
+
+ // ❌ 不建议:否定形式(增加理解难度)
+ public bool IsNotActive { get; set; }
+ public bool IsDisabled { get; set; }
+ ```
+
+- 常用布尔命名前缀
+ ```csharp
+ public class Order
+ {
+ // Is - 状态判断
+ public bool IsCompleted { get; set; }
+ public bool IsValid { get; set; }
+ public bool IsPaid { get; set; }
+
+ // Has - 拥有判断
+ public bool HasDiscount { get; set; }
+ public bool HasShipped { get; set; }
+
+ // Can - 能力判断
+ public bool CanBeCancelled { get; set; }
+ public bool CanBeRefunded { get; set; }
+
+ // Should - 应该判断
+ public bool ShouldSendEmail { get; set; }
+ public bool ShouldApplyDiscount { get; set; }
+ }
+ ```
+
+- 布尔方法命名
+ ```csharp
+ public class OrderValidator
+ {
+ // ✅ 建议
+ public bool IsValidOrder(Order order) { }
+ public bool HasSufficientStock(int productId, int quantity) { }
+ public bool CanProcessPayment(Payment payment) { }
+ public bool Exists(int orderId) { }
+
+ // ✅ 验证方法也可以使用 Validate 开头,返回 bool
+ public bool ValidateOrderItems(Order order) { }
+ }
+ ```
+### 2.8 集合类型
+- 使用复数形式
+ ```csharp
+ public class Customer
+ {
+ // ✅ 建议:使用复数
+ public List Orders { get; set; }
+ public ICollection Addresses { get; set; }
+ public IEnumerable Payments { get; set; }
+
+ // ❌ 不建议
+ public List OrderList { get; set; } // 不需要 List 后缀
+ public List Order { get; set; } // 应使用复数
+ }
+ ```
+
+- 集合局部变量命名
+ ```csharp
+ public void ProcessOrders()
+ {
+ // ✅ 建议
+ var orders = GetOrders();
+ var activeOrders = orders.Where(o => o.IsActive).ToList();
+ var orderIds = orders.Select(o => o.Id).ToList();
+ var customerNames = customers.Select(c => c.Name).ToList();
+
+ // ❌ 不建议
+ var orderDictionary = orders.ToDictionary(o => o.Id);
+ var customersByEmail = customers.ToDictionary(c => c.Email);
+ }
+ ```
+
+- 特殊集合命名
+ ```csharp
+ // ✅ 字典
+ private readonly Dictionary _orderCache;
+ private readonly ConcurrentDictionary _customerLookup;
+
+ // ✅ 队列
+ private readonly Queue _orderQueue;
+ private readonly ConcurrentQueue _taskQueue;
+
+ // ✅ 堆栈
+ private readonly Stack _commandHistory;
+ ```
+### 2.9 异步方法
+- 使用 `Async` 后缀
+ ```csharp
+ // ✅ 异步方法使用 Async 后缀
+ public async Task GetOrderAsync(int orderId)
+ {
+ return await _repository.GetByIdAsync(orderId);
+ }
+
+ public async Task> GetOrdersAsync(int customerId)
+ {
+ return await _repository.GetOrdersByCustomerAsync(customerId);
+ }
+
+ public async Task CreateOrderAsync(CreateOrderRequest request)
+ {
+ await _repository.AddAsync(MapToOrder(request));
+ }
+
+ public async Task UpdateOrderAsync(Order order)
+ {
+ await _repository.UpdateAsync(order);
+ }
+
+ public async Task DeleteOrderAsync(int orderId)
+ {
+ await _repository.DeleteAsync(orderId);
+ }
+ ```
+
+- 异步方法返回类型
+ ```csharp
+ // ✅ 返回 Task
+ public async Task SendEmailAsync(string to, string subject)
+ {
+ await _emailService.SendAsync(to, subject);
+ }
+
+ // ✅ 返回 Task
+ public async Task ValidateOrderAsync(Order order)
+ {
+ return await _validator.ValidateAsync(order);
+ }
+
+ // ✅ 返回 ValueTask(性能敏感场景)
+ public async ValueTask GetCachedCountAsync()
+ {
+ if (_cache.TryGetValue("count", out int count))
+ {
+ return count;
+ }
+
+ return await _repository.GetCountAsync();
+ }
+
+ // ❌ 错误:避免使用 async void(除事件处理程序外)
+ public async void ProcessOrderAsync(int orderId) // 错误!
+ {
+ await _service.ProcessAsync(orderId);
+ }
+ ```
\ No newline at end of file
diff --git a/docs/开发规范/1-编程规约/1.2-代码风格.md b/docs/开发规范/1-编程规约/1.2-代码风格.md
new file mode 100644
index 0000000..a43bb25
--- /dev/null
+++ b/docs/开发规范/1-编程规约/1.2-代码风格.md
@@ -0,0 +1,824 @@
+# 1.2 代码风格
+
+---
+
+## 1. 文件组织结构
+- 类成员组织顺序
+ ```csharp
+ public class OrderService : IOrderService
+ {
+ // 1. 常量
+ private const int MaxRetryCount = 3;
+ public const string DefaultCurrency = "CNY";
+
+ // 2. 静态字段
+ private static readonly object _lockObject = new object();
+
+ // 3. 私有字段(只读字段在前)
+ private readonly IOrderRepository _orderRepository;
+ private readonly ILogger _logger;
+ private readonly IMapper _mapper;
+ private int _processCount;
+
+ // 4. 构造函数
+ public OrderService(
+ IOrderRepository orderRepository,
+ ILogger logger,
+ IMapper mapper)
+ {
+ _orderRepository = orderRepository;
+ _logger = logger;
+ _mapper = mapper;
+ }
+
+ // 5. 属性
+ public int ProcessedCount => _processCount;
+
+ // 6. 公共方法(按功能分组)
+ public async Task GetOrderAsync(int orderId)
+ {
+ // ...existing code...
+ }
+
+ public async Task CreateOrderAsync(CreateOrderRequest request)
+ {
+ // ...existing code...
+ }
+
+ // 7. 受保护方法
+ protected virtual bool ValidateOrder(Order order)
+ {
+ // ...existing code...
+ }
+
+ // 8. 私有方法
+ private decimal CalculateDiscount(Order order)
+ {
+ // ...existing code...
+ }
+
+ private void LogOrderCreation(Order order)
+ {
+ // ...existing code...
+ }
+
+ // 9. 嵌套类型(如果有)
+ private class OrderValidationContext
+ {
+ public Order Order { get; set; }
+ public List Errors { get; set; }
+ }
+ }
+ ```
+
+- 一个文件一个主要类原则
+ ```csharp
+ // ✅ 建议:一个文件一个主要类
+ // filepath: Models/Order.cs
+ public class Order
+ {
+ // ...existing code...
+ }
+
+ // ❌ 不建议:一个文件多个不相关的类
+ // filepath: Models.cs
+ public class Order { }
+ public class Customer { }
+ public class Product { }
+
+ // ✅ 例外:紧密相关的小类可以放在一起
+ // filepath: Models/OrderEnums.cs
+ public enum OrderStatus
+ {
+ Pending,
+ Completed
+ }
+
+ public enum PaymentStatus
+ {
+ Unpaid,
+ Paid
+ }
+ ```
+## 2. 缩进与空格
+- 使用 4 个空格缩进
+ ```csharp
+ public class OrderService
+ {
+ public void ProcessOrder(Order order)
+ {
+ if (order != null)
+ {
+ var total = CalculateTotal(order);
+ order.TotalAmount = total;
+ }
+ }
+ }
+ ```
+
+- 运算符周围使用空格
+ ```csharp
+ // ✅ 建议
+ var total = price * quantity;
+ var discount = total > 1000 ? 0.1m : 0;
+ var result = (a + b) * c;
+ var isValid = count > 0 && amount < maxAmount;
+
+ // ❌ 不建议
+ var total=price*quantity;
+ var result=(a+b)*c;
+ ```
+
+- 逗号后使用空格
+ ```csharp
+ // ✅ 建议
+ public void CreateOrder(int customerId, decimal amount, string address)
+ {
+ var items = new List { 1, 2, 3, 4 };
+ ProcessOrder(customerId, amount, address);
+ }
+
+ // ❌ 不建议
+ public void CreateOrder(int customerId,decimal amount,string address)
+ {
+ var items = new List { 1,2,3,4 };
+ }
+ ```
+
+- 方法参数对齐
+ ```csharp
+ // ✅ 短参数列表:单行
+ public Order CreateOrder(int customerId, decimal amount)
+ {
+ // ...existing code...
+ }
+
+ // ✅ 长参数列表:每个参数一行
+ public Order CreateOrder(
+ int customerId,
+ string customerName,
+ List items,
+ string shippingAddress,
+ PaymentMethod paymentMethod,
+ decimal discountAmount)
+ {
+ // ...existing code...
+ }
+
+ // ✅ LINQ 链式调用对齐
+ var result = orders
+ .Where(o => o.IsActive)
+ .OrderByDescending(o => o.CreatedDate)
+ .Select(o => new OrderDto
+ {
+ OrderId = o.Id,
+ CustomerName = o.Customer.Name,
+ TotalAmount = o.TotalAmount
+ })
+ .ToList();
+ ```
+## 3. 大括号
+- 大括号独占一行(Allman 风格)
+ ```csharp
+ // ✅ 建议:大括号独占一行
+ public void ProcessOrder(Order order)
+ {
+ if (order != null)
+ {
+ ValidateOrder(order);
+ SaveOrder(order);
+ }
+ else
+ {
+ throw new ArgumentNullException(nameof(order));
+ }
+ }
+
+ // ❌ 不建议:K&R 风格(不推荐在 C# 中使用)
+ public void ProcessOrder(Order order) {
+ if (order != null) {
+ ValidateOrder(order);
+ }
+ }
+ ```
+
+- 单行语句也使用大括号
+ ```csharp
+ // ✅ 建议:即使单行也使用大括号
+ if (order.IsValid)
+ {
+ ProcessOrder(order);
+ }
+
+ for (int i = 0; i < count; i++)
+ {
+ ProcessItem(i);
+ }
+
+ // ❌ 不建议:省略大括号(易出错、可读性差)
+ if (order.IsValid)
+ ProcessOrder(order);
+ LogOrder(order); // 这行不在 if 块内!
+ ```
+## 4. 空行
+- 方法之间使用空行
+ ```csharp
+ public class OrderService
+ {
+ public Order GetOrder(int id)
+ {
+ return _repository.GetById(id);
+ }
+ // 空行分隔方法
+ public void CreateOrder(Order order)
+ {
+ _repository.Add(order);
+ }
+ // 空行分隔方法
+ public void UpdateOrder(Order order)
+ {
+ _repository.Update(order);
+ }
+ }
+ ```
+
+- 逻辑块之间使用空行
+ ```csharp
+ public async Task CreateOrderAsync(CreateOrderRequest request)
+ {
+ // 验证输入
+ if (request == null)
+ {
+ throw new ArgumentNullException(nameof(request));
+ }
+
+ ValidateRequest(request);
+ // 空行分隔逻辑块
+ // 创建订单
+ var order = new Order
+ {
+ CustomerId = request.CustomerId,
+ CreatedDate = DateTime.Now
+ };
+ // 空行分隔逻辑块
+ // 保存并返回
+ await _repository.AddAsync(order);
+ await _unitOfWork.CommitAsync();
+
+ return order;
+ }
+ ```
+
+- 不要使用多个连续空行
+ ```csharp
+ // ✅ 建议:使用单个空行
+ public void Method1()
+ {
+ // ...existing code...
+ }
+
+ public void Method2()
+ {
+ // ...existing code...
+ }
+
+ // ❌ 不建议:多个连续空行
+
+
+
+
+ public void Method3()
+ {
+ // ...existing code...
+ }
+ ```
+## 5. 语句换行
+- 长条件表达式换行
+ ```csharp
+ // ✅ 建议:在逻辑运算符前换行
+ if (customer.IsActive
+ && customer.TotalOrders > 10
+ && customer.TotalAmount > 10000
+ && !customer.IsBlacklisted)
+ {
+ ApplyVipDiscount(customer);
+ }
+
+ // ✅ 复杂条件时可提取为变量
+ var isEligibleForDiscount = customer.IsActive
+ && customer.TotalOrders > 10
+ && customer.TotalAmount > 10000
+ && !customer.IsBlacklisted;
+
+ if (isEligibleForDiscount)
+ {
+ ApplyVipDiscount(customer);
+ }
+ ```
+
+- LINQ 查询换行
+ ```csharp
+ // ✅ 建议:每个操作符一行
+ var result = orders
+ .Where(o => o.IsActive)
+ .Where(o => o.TotalAmount > 1000)
+ .OrderByDescending(o => o.CreatedDate)
+ .Select(o => new OrderDto
+ {
+ Id = o.Id,
+ CustomerName = o.Customer.Name,
+ TotalAmount = o.TotalAmount
+ })
+ .ToList();
+ ```
+## 6. using 指令组织
+- 移除未使用的 `using`
+ ```csharp
+ // ✅ 建议:只保留使用的命名空间
+ using System;
+ using System.Linq;
+ using CompanyName.ProjectName.Core.Entities;
+
+ // ❌ 不建议:包含未使用的命名空间
+ using System;
+ using System.Collections.Generic; // 未使用
+ using System.Text; // 未使用
+ using System.Linq;
+ ```
+
+- 使用 `global using` (`C#` 10 +)
+ ```csharp
+ global using System;
+ global using System.Collections.Generic;
+ global using System.Linq;
+ global using System.Threading.Tasks;
+ global using Microsoft.Extensions.Logging;
+ ```
+- `using static` 使用
+ ```csharp
+ // ✅ 建议:简化静态成员调用
+ using static System.Math;
+
+ public class Calculator
+ {
+ public double CalculateArea(double radius)
+ {
+ return PI * Pow(radius, 2); // 无需 Math.PI 和 Math.Pow
+ }
+ }
+
+ // ❌ 不建议:过度使用会降低可读性
+ using static System.Console; // 不推荐,WriteLine 来源不明确
+ ```
+## 7. 变量与数据类型
+### 7.1 变量声明原则
+- 就近声明原则
+ ```csharp
+ // ✅ 建议:在使用前声明
+ public void ProcessOrder(int orderId)
+ {
+ var order = GetOrder(orderId);
+ if (order == null)
+ {
+ return;
+ }
+
+ // 在需要时才声明
+ var discount = CalculateDiscount(order);
+ order.DiscountAmount = discount;
+
+ // 在循环中声明
+ foreach (var item in order.Items)
+ {
+ var itemTotal = item.Price * item.Quantity;
+ ValidateItem(item, itemTotal);
+ }
+ }
+
+ // ❌ 不建议:过早声明
+ public void ProcessOrder(int orderId)
+ {
+ var order = GetOrder(orderId);
+ var discount = 0m; // 过早声明
+ var itemTotal = 0m; // 过早声明
+
+ if (order == null)
+ {
+ return;
+ }
+
+ discount = CalculateDiscount(order);
+ // ...existing code...
+ }
+ ```
+
+- 最小作用域原则
+ ```csharp
+ // ✅ 建议:限制变量作用域
+ public decimal CalculateTotal(Order order)
+ {
+ decimal total = 0;
+ {
+ var taxRate = GetTaxRate();
+ var tax = order.Subtotal * taxRate;
+ total = order.Subtotal + tax;
+ }
+
+ // taxRate 和 tax 在此处不可访问
+ return total;
+ }
+
+ // ✅ 在 if 语句中声明
+ if (TryGetOrder(orderId, out var order))
+ {
+ ProcessOrder(order);
+ }
+ // order 在此处不可访问
+ ```
+
+- 初始化时声明
+ ```csharp
+ // ✅ 建议:声明时初始化
+ var customerName = "张三";
+ var orderCount = 0;
+ var items = new List();
+ var isValid = ValidateInput();
+
+ // ❌ 不建议:分离声明和初始化
+ string customerName;
+ int orderCount;
+ List items;
+
+ customerName = "张三";
+ orderCount = 0;
+ items = new List();
+ ```
+### 7.2 `null` 处理
+- 使用可空类型
+ ```csharp
+ // ✅ 建议:使用可空类型表示可能为 null 的值
+ public class Order
+ {
+ public int Id { get; set; }
+ public DateTime? CompletedDate { get; set; } // 可能未完成
+ public decimal? DiscountAmount { get; set; } // 可能无折扣
+ public string? Note { get; set; } // 可能无备注 (C# 8.0+)
+ }
+ ```
+
+- `null` 检查
+ ```csharp
+ // ✅ 建议:参数 null 检查
+ public void ProcessOrder(Order order)
+ {
+ if (order == null)
+ {
+ throw new ArgumentNullException(nameof(order));
+ }
+
+ // 或使用 C# 11+ 参数 null 检查
+ public void ProcessOrder(Order order!!)
+ {
+ // order 不为 null
+ }
+ }
+
+ // ✅ 返回值 null 检查
+ var order = GetOrder(orderId);
+ if (order == null)
+ {
+ _logger.LogWarning("订单 {OrderId} 不存在", orderId);
+ return;
+ }
+
+ // ✅ 使用模式匹配
+ if (GetOrder(orderId) is Order order)
+ {
+ ProcessOrder(order);
+ }
+ ```
+
+- 使用 `?.` 和 `??` 操作符
+ ```csharp
+ // ✅ null 条件运算符 ?.
+ var customerName = order?.Customer?.Name;
+ var itemCount = order?.Items?.Count ?? 0;
+
+ // ✅ null 合并运算符 ??
+ var discountAmount = order.DiscountAmount ?? 0m;
+ var shippingAddress = order.ShippingAddress ?? order.Customer.DefaultAddress;
+
+ // ✅ null 合并赋值运算符 ??=
+ order.Note ??= "无备注";
+ _cache ??= new Dictionary();
+
+ // ✅ 链式使用
+ var cityName = order?.Customer?.Address?.City ?? "未知";
+ ```
+
+- 避免返回 `null`
+ ```csharp
+ // ❌ 不建议:返回 null
+ public List GetOrders(int customerId)
+ {
+ var orders = _repository.GetByCustomerId(customerId);
+ return orders; // 可能返回 null
+ }
+
+ // ✅ 建议:返回空集合
+ public List GetOrders(int customerId)
+ {
+ var orders = _repository.GetByCustomerId(customerId);
+ return orders ?? new List();
+ }
+
+ // ✅ 建议:使用 LINQ
+ public IEnumerable GetOrders(int customerId)
+ {
+ return _repository.GetByCustomerId(customerId)
+ ?? Enumerable.Empty();
+ }
+ ```
+
+- 使用 `nullable reference types` (`C#` 8.0+)
+ ```csharp
+ #nullable enable
+
+ public class OrderService
+ {
+ // 不可为 null
+ private readonly IOrderRepository _repository;
+
+ // 可为 null
+ private ILogger? _logger;
+
+ public OrderService(IOrderRepository repository)
+ {
+ _repository = repository; // 必须赋值
+ }
+
+ public Order? GetOrder(int id) // 明确返回可能为 null
+ {
+ return _repository.GetById(id);
+ }
+
+ public void ProcessOrder(Order order) // 参数不可为 null
+ {
+ // order 保证不为 null
+ var total = order.TotalAmount;
+ }
+ }
+ ```
+### 7.3 字符串操作
+- 字符转拼接
+ ```csharp
+ // ❌ 不建议:循环中使用 + 拼接
+ string result = "";
+ for (int i = 0; i < 1000; i++)
+ {
+ result += i.ToString(); // 性能差,产生大量临时对象
+ }
+
+ // ✅ 建议:使用 StringBuilder
+ var sb = new StringBuilder();
+ for (int i = 0; i < 1000; i++)
+ {
+ sb.Append(i);
+ }
+ string result = sb.ToString();
+
+ // ✅ 少量拼接可以使用字符串插值
+ string name = "张三";
+ int age = 25;
+ string message = $"姓名:{name},年龄:{age}"; // 推荐
+
+ // ✅ 使用 string.Join
+ var items = new[] { "苹果", "香蕉", "橙子" };
+ string result = string.Join(", ", items); // "苹果, 香蕉, 橙子"
+ ```
+
+- 字符串比较
+ ```csharp
+ // ✅ 建议:使用 StringComparison
+ if (string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase))
+ {
+ // 忽略大小写比较
+ }
+
+ // ✅ 检查 null 或空
+ if (string.IsNullOrEmpty(customerName))
+ {
+ throw new ArgumentException("客户名称不能为空");
+ }
+
+ if (string.IsNullOrWhiteSpace(note))
+ {
+ note = "无备注";
+ }
+
+ // ❌ 不建议:使用 == 比较可能为 null 的字符串
+ if (str1 == str2) // 如果都为 null 会返回 true
+ {
+ // ...existing code...
+ }
+ ```
+
+- 字符串格式化
+ ```csharp
+ // ✅ 推荐使用字符串插值
+ decimal price = 99.99m;
+ string message = $"价格:{price:C}"; // "价格:¥99.99"
+
+ // ✅ 格式化数字
+ int number = 1234567;
+ string formatted = $"{number:N0}"; // "1,234,567"
+ string hex = $"{number:X}"; // "12D687"
+
+ // ✅ 格式化日期
+ DateTime now = DateTime.Now;
+ string dateStr = $"{now:yyyy-MM-dd HH:mm:ss}"; // "2025-11-26 14:30:00"
+ string shortDate = $"{now:d}"; // "2025/11/26"
+
+ // ✅ 对齐和填充
+ string name = "张三";
+ string aligned = $"{name, 10}"; // 右对齐,总宽度10
+ string leftAligned = $"{name, -10}"; // 左对齐
+ ```
+### 7.4 集合
+- 集合初始化
+ ```csharp
+ // ✅ 集合初始化器
+ var numbers = new List { 1, 2, 3, 4, 5 };
+
+ var dict = new Dictionary
+ {
+ ["apple"] = 1,
+ ["banana"] = 2,
+ ["orange"] = 3
+ };
+
+ // ✅ 指定初始容量(已知大小时)
+ var largeList = new List(1000); // 避免多次扩容
+
+ // ✅ 数组初始化
+ int[] scores = { 85, 90, 78, 92 };
+ int[] grades = new int[5]; // 固定大小
+ ```
+
+- 集合选择
+ ```csharp
+ // ✅ List - 需要按索引访问和频繁添加
+ var customers = new List();
+
+ // ✅ HashSet - 需要唯一性检查和快速查找
+ var uniqueIds = new HashSet();
+ uniqueIds.Add(1);
+ uniqueIds.Add(1); // 不会重复添加
+
+ // ✅ Dictionary - 键值对存储和快速查找
+ var customerCache = new Dictionary();
+
+ // ✅ Queue - 先进先出
+ var taskQueue = new Queue();
+
+ // ✅ Stack - 后进先出
+ var history = new Stack();
+
+ // ✅ ImmutableList - 不可变集合(线程安全)
+ var immutableList = ImmutableList.Create(1, 2, 3);
+ ```
+### 7.5 常量与魔法数字处理
+- 常量定义
+ ```csharp
+ // ✅ 定义常量类
+ public static class OrderConstants
+ {
+ public const int MaxItemsPerOrder = 100;
+ public const decimal MinOrderAmount = 50.0m;
+ public const int OrderTimeoutMinutes = 30;
+
+ // 字符串常量
+ public const string DefaultCurrency = "CNY";
+ public const string DateFormat = "yyyy-MM-dd";
+ }
+
+ // ✅ 在类中使用私有常量
+ public class OrderService
+ {
+ private const int MaxRetryCount = 3;
+ private const string LogPrefix = "[OrderService]";
+
+ public void ProcessOrder(Order order)
+ {
+ if (order.Items.Count > OrderConstants.MaxItemsPerOrder)
+ {
+ throw new BusinessException($"订单商品数量不能超过{OrderConstants.MaxItemsPerOrder}");
+ }
+ }
+ }
+ ```
+
+- 避免魔法数字
+ ```csharp
+ // ❌ 不建议:魔法数字
+ public decimal CalculateDiscount(decimal amount)
+ {
+ if (amount > 1000)
+ {
+ return amount * 0.1m; // 0.1 是什么?
+ }
+
+ if (amount > 500)
+ {
+ return amount * 0.05m; // 0.05 是什么?
+ }
+
+ return 0;
+ }
+
+ // ✅ 建议:使用命名常量
+ public class DiscountCalculator
+ {
+ private const decimal HighAmountThreshold = 1000m;
+ private const decimal MediumAmountThreshold = 500m;
+ private const decimal HighDiscountRate = 0.1m;
+ private const decimal MediumDiscountRate = 0.05m;
+
+ public decimal CalculateDiscount(decimal amount)
+ {
+ if (amount > HighAmountThreshold)
+ {
+ return amount * HighDiscountRate;
+ }
+
+ if (amount > MediumAmountThreshold)
+ {
+ return amount * MediumDiscountRate;
+ }
+
+ return 0;
+ }
+ }
+ ```
+### 7.6 枚举类型
+-
+ ```csharp
+ public class OrderService
+ {
+ // ✅ 使用枚举而非字符串或数字
+ public void UpdateOrderStatus(int orderId, OrderStatus status)
+ {
+ // 类型安全,编译时检查
+ switch (status)
+ {
+ case OrderStatus.Pending:
+ // 处理待处理状态
+ break;
+ case OrderStatus.Confirmed:
+ // 处理已确认状态
+ break;
+ default:
+ throw new ArgumentException($"不支持的订单状态:{status}");
+ }
+ }
+
+ // ✅ 枚举转换字符串
+ public string GetStatusName(OrderStatus status)
+ {
+ return status.ToString(); // "Pending"
+ }
+
+ // ✅ 字符串转枚举(安全)
+ public OrderStatus ParseStatus(string statusText)
+ {
+ if (Enum.TryParse(statusText, out var status))
+ {
+ return status;
+ }
+
+ throw new ArgumentException($"无效的订单状态:{statusText}");
+ }
+
+ // ✅ 获取所有枚举值
+ public IEnumerable GetAllStatuses()
+ {
+ return Enum.GetValues();
+ }
+
+ // ✅ 标志枚举操作
+ public void ManagePermissions()
+ {
+ var permissions = FilePermissions.None;
+
+ // 添加权限
+ permissions |= FilePermissions.Read;
+ permissions |= FilePermissions.Write;
+
+ // 检查权限
+ bool canRead = permissions.HasFlag(FilePermissions.Read);
+
+ // 移除权限
+ permissions &= ~FilePermissions.Write;
+ }
+ }
+ ```
\ No newline at end of file
diff --git a/docs/开发规范/1-编程规约/1.3-注释规约.md b/docs/开发规范/1-编程规约/1.3-注释规约.md
new file mode 100644
index 0000000..3c2e5c9
--- /dev/null
+++ b/docs/开发规范/1-编程规约/1.3-注释规约.md
@@ -0,0 +1,476 @@
+# 1.3 注释规约
+
+---
+
+## 1. XML 文档注释
+- 类注释
+ ```csharp
+ ///
+ /// 订单服务,提供订单管理相关业务逻辑
+ ///
+ ///
+ /// 此服务负责:
+ /// - 订单创建、查询、更新、删除
+ /// - 订单状态流转
+ /// - 订单金额计算
+ ///
+ public class OrderService : IOrderService
+ {
+ // ...existing code...
+ }
+ ```
+
+- 接口注释
+ ```csharp
+ ///
+ /// 订单仓储接口,定义订单数据访问操作
+ ///
+ public interface IOrderRepository
+ {
+ ///
+ /// 根据订单ID获取订单详情
+ ///
+ /// 订单ID
+ /// 订单实体,如果不存在返回 null
+ Task GetByIdAsync(int orderId);
+ }
+ ```
+
+- 方法注释
+ ```csharp
+ ///
+ /// 创建新订单
+ ///
+ /// 订单创建请求对象
+ /// 创建成功的订单实体
+ /// 当 request 为 null 时抛出
+ /// 当订单数据验证失败时抛出
+ /// 当库存不足时抛出
+ ///
+ /// 此方法执行以下步骤:
+ /// 1. 验证请求数据
+ /// 2. 检查库存
+ /// 3. 计算订单金额
+ /// 4. 创建订单记录
+ /// 5. 发送确认邮件
+ ///
+ ///
+ ///
+ /// var request = new CreateOrderRequest
+ /// {
+ /// CustomerId = 123,
+ /// Items = new List<OrderItemDto>
+ /// {
+ /// new() { ProductId = 1, Quantity = 2 }
+ /// }
+ /// };
+ /// var order = await orderService.CreateOrderAsync(request);
+ ///
+ ///
+ public async Task CreateOrderAsync(CreateOrderRequest request)
+ {
+ // ...existing code...
+ }
+ ```
+
+- 属性注释
+ ```csharp
+ ///
+ /// 获取或设置订单ID
+ ///
+ public int Id { get; set; }
+
+ ///
+ /// 获取或设置客户ID
+ ///
+ public int CustomerId { get; set; }
+
+ ///
+ /// 获取或设置订单总金额(含税)
+ ///
+ /// 订单总金额,单位:元
+ public decimal TotalAmount { get; set; }
+
+ ///
+ /// 获取或设置订单是否已完成
+ ///
+ ///
+ /// true 表示订单已完成;否则为 false
+ ///
+ public bool IsCompleted { get; set; }
+ ```
+
+- 枚举注释
+ ```csharp
+ ///
+ /// 订单状态枚举
+ ///
+ public enum OrderStatus
+ {
+ ///
+ /// 待处理
+ ///
+ Pending = 0,
+
+ ///
+ /// 处理中
+ ///
+ Processing = 1,
+
+ ///
+ /// 已完成
+ ///
+ Completed = 2,
+
+ ///
+ /// 已取消
+ ///
+ Cancelled = 3
+ }
+ ```
+
+- 泛型参数注释
+ ```csharp
+ ///
+ /// 通用仓储接口
+ ///
+ /// 实体类型,必须是引用类型
+ public interface IRepository where T : class
+ {
+ ///
+ /// 根据ID获取实体
+ ///
+ /// 实体ID
+ /// 实体对象,如果不存在返回 null
+ Task GetByIdAsync(int id);
+ }
+ ```
+## 2. 行内注释使用场景
+- 解释复杂业务逻辑
+ ```csharp
+ public decimal CalculateDiscount(Order order)
+ {
+ // 根据业务规则BR-2023-001:
+ // VIP客户订单金额超过1000元享受9折优惠
+ // 新客户首单享受95折优惠
+ // 优惠不可叠加,取最优惠方案
+
+ if (order.Customer.IsVip && order.TotalAmount > 1000)
+ {
+ return order.TotalAmount * 0.1m;
+ }
+
+ if (order.Customer.IsNewCustomer && order.IsFirstOrder)
+ {
+ return order.TotalAmount * 0.05m;
+ }
+
+ return 0;
+ }
+ ```
+
+- 解释不明显的代码意图
+ ```csharp
+ public void ProcessOrder(Order order)
+ {
+ // 将订单状态设置为处理中,防止并发处理同一订单
+ order.Status = OrderStatus.Processing;
+ order.LastModifiedDate = DateTime.UtcNow;
+
+ // 使用UTC时间避免时区问题
+ var processStartTime = DateTime.UtcNow;
+
+ // 临时解决方案:忽略已删除的订单项
+ // TODO: 后续需要从数据库层面过滤
+ var activeItems = order.Items.Where(i => !i.IsDeleted).ToList();
+ }
+ ```
+
+- 标记临时代码或待优化代码
+ ```csharp
+ public async Task> GetOrdersAsync(int customerId)
+ {
+ // HACK: 临时解决方案,直接加载所有订单
+ // 性能问题:当订单量大时会导致内存占用过高
+ // TODO: 实现分页加载
+ var orders = await _context.Orders
+ .Where(o => o.CustomerId == customerId)
+ .ToListAsync();
+
+ return orders;
+ }
+ ```
+
+- 解释算法或公式
+ ```csharp
+ public decimal CalculateShippingFee(Order order)
+ {
+ // 运费计算公式:
+ // 基础运费 = 10元
+ // 重量费用 = 总重量 * 2元/kg
+ // 距离费用 = 距离 * 0.5元/km
+ // 如果订单金额超过200元,免运费
+
+ if (order.TotalAmount >= 200)
+ {
+ return 0;
+ }
+
+ var baseFee = 10m;
+ var weightFee = order.TotalWeight * 2m;
+ var distanceFee = order.DeliveryDistance * 0.5m;
+
+ return baseFee + weightFee + distanceFee;
+ }
+ ```
+## 3. `TODO`、`FIXME`、`HACK`、`NOTE`/`IMPORTANT` 标记使用
+- `TODO` - 待实现功能
+ ```csharp
+ public class OrderService
+ {
+ public async Task CreateOrderAsync(Order order)
+ {
+ await _repository.AddAsync(order);
+
+ // TODO: 实现订单创建后发送确认邮件
+ // TODO: 实现库存扣减逻辑
+ // TODO: 集成支付网关
+ }
+
+ // TODO: [张三] 2024-01-15 实现退款功能
+ public Task RefundOrderAsync(int orderId)
+ {
+ throw new NotImplementedException();
+ }
+ }
+ ```
+
+- `FIXME` - 已知问题需要修复
+ ```csharp
+ public async Task> GetOrdersAsync(int pageIndex, int pageSize)
+ {
+ // FIXME: 当pageSize过大时会导致性能问题
+ // FIXME: 未处理pageIndex为负数的情况
+
+ return await _context.Orders
+ .Skip(pageIndex * pageSize)
+ .Take(pageSize)
+ .ToListAsync();
+ }
+
+ public decimal CalculateTotal(Order order)
+ {
+ // FIXME: 并发情况下可能导致重复计算
+ // 需要添加锁机制或使用数据库事务
+
+ return order.Items.Sum(i => i.Price * i.Quantity);
+ }
+ ```
+
+- `HACK` - 临时解决方案
+ ```csharp
+ public async Task ProcessOrderAsync(int orderId)
+ {
+ // HACK: 临时方案,硬编码重试3次
+ // 应该从配置文件读取重试次数
+ for (int i = 0; i < 3; i++)
+ {
+ try
+ {
+ await ProcessAsync(orderId);
+ break;
+ }
+ catch (Exception ex)
+ {
+ if (i == 2)
+ {
+ throw;
+ }
+ await Task.Delay(1000);
+ }
+ }
+ }
+ ```
+
+- `NOTE`/`IMPORTANT` - 重要说明
+ ```csharp
+ public class PaymentService
+ {
+ // NOTE: 此方法会修改订单状态,调用前请确保已获取锁
+ // IMPORTANT: 必须在事务中调用此方法
+ public async Task ProcessPaymentAsync(Payment payment)
+ {
+ // ...existing code...
+ }
+ }
+ ```
+## 4. 复杂业务逻辑注释要求
+- 业务规则注释
+ ```csharp
+ public bool CanApplyDiscount(Order order, Customer customer)
+ {
+ // 业务规则 BR-2024-001: 折扣适用条件
+ // 1. 客户必须是激活状态
+ // 2. 订单金额必须大于等于100元
+ // 3. 客户当月订单数量不超过10个
+ // 4. 产品不在促销黑名单中
+
+ if (!customer.IsActive)
+ {
+ return false;
+ }
+
+ if (order.TotalAmount < 100)
+ {
+ return false;
+ }
+
+ var monthlyOrderCount = GetMonthlyOrderCount(customer.Id);
+ if (monthlyOrderCount > 10)
+ {
+ return false;
+ }
+
+ var hasBlacklistedProducts = order.Items.Any(i => IsProductBlacklisted(i.ProductId));
+
+ return !hasBlacklistedProducts;
+ }
+ ```
+
+- 状态机转换注释
+ ```csharp
+ public void UpdateOrderStatus(Order order, OrderStatus newStatus)
+ {
+ // 订单状态转换规则:
+ // Pending -> Processing (允许)
+ // Pending -> Cancelled (允许)
+ // Processing -> Completed (允许)
+ // Processing -> Cancelled (不允许,必须先退款)
+ // Completed -> Refunded (允许,需要走退款流程)
+ // Cancelled -> * (不允许任何转换)
+
+ var allowedTransitions = new Dictionary>
+ {
+ { OrderStatus.Pending, new List { OrderStatus.Processing, OrderStatus.Cancelled } },
+ { OrderStatus.Processing, new List { OrderStatus.Completed } },
+ { OrderStatus.Completed, new List { OrderStatus.Refunded } }
+ };
+
+ if (!allowedTransitions.ContainsKey(order.Status) || !allowedTransitions[order.Status].Contains(newStatus))
+ {
+ throw new InvalidOperationException($"不允许从 {order.Status} 转换到 {newStatus}");
+ }
+
+ order.Status = newStatus;
+ }
+ ```
+
+- 复杂计算注释
+ ```csharp
+ public decimal CalculateCommission(Order order, Salesperson salesperson)
+ {
+ // 佣金计算规则(2024年Q1版本):
+ //
+ // 基础佣金率:
+ // - 初级销售:订单金额的 3%
+ // - 中级销售:订单金额的 5%
+ // - 高级销售:订单金额的 8%
+ //
+ // 阶梯奖励(累加):
+ // - 月度销售额超过 10万,额外 +1%
+ // - 月度销售额超过 50万,额外 +2%
+ // - 月度销售额超过100万,额外 +3%
+ //
+ // 特殊产品加成:
+ // - 高端产品类别,佣金率 *1.5
+
+ var baseRate = salesperson.Level switch
+ {
+ SalesLevel.Junior => 0.03m,
+ SalesLevel.Intermediate => 0.05m,
+ SalesLevel.Senior => 0.08m,
+ _ => 0.03m
+ };
+
+ var monthlySales = GetMonthlySales(salesperson.Id);
+ var bonusRate = monthlySales switch
+ {
+ >= 1000000 => 0.03m,
+ >= 500000 => 0.02m,
+ >= 100000 => 0.01m,
+ _ => 0m
+ };
+
+ var finalRate = baseRate + bonusRate;
+
+ var hasHighEndProducts = order.Items.Any(i => i.Product.Category == ProductCategory.HighEnd);
+
+ if (hasHighEndProducts)
+ {
+ finalRate *= 1.5m;
+ }
+
+ return order.TotalAmount * finalRate;
+ }
+ ```
+## 5. 注释的维护与更新
+- 注释应与代码同步更新
+ ```csharp
+ // ❌ 错误:注释已过时
+ // 返回所有订单(实际代码已改为返回激活订单)
+ public List GetOrders()
+ {
+ return _context.Orders
+ .Where(o => o.IsActive) // 代码已修改,但注释未更新
+ .ToList();
+ }
+
+ // ✅ 正确:更新注释
+ ///
+ /// 获取所有激活状态的订单
+ ///
+ public List GetActiveOrders()
+ {
+ return _context.Orders
+ .Where(o => o.IsActive)
+ .ToList();
+ }
+ ```
+
+- 删除无用注释
+ ```csharp
+ // ❌ 错误:保留已注释的旧代码
+ public void ProcessOrder(Order order)
+ {
+ // var oldDiscount = order.TotalAmount * 0.05m;
+ // order.DiscountAmount = oldDiscount;
+
+ // 新的折扣计算逻辑
+ var newDiscount = CalculateDiscount(order);
+ order.DiscountAmount = newDiscount;
+ }
+
+ // ✅ 正确:删除已注释代码,使用版本控制系统管理历史
+ public void ProcessOrder(Order order)
+ {
+ var discount = CalculateDiscount(order);
+ order.DiscountAmount = discount;
+ }
+ ```
+
+- 避免显而易见的注释
+ ```csharp
+ // ❌ 错误:显而易见的注释
+ // 设置客户名称
+ customer.Name = "张三";
+
+ // 增加计数器
+ counter ++;
+
+ // ✅ 正确:只注释需要解释的内容
+ // 使用UTC时间避免时区转换问题
+ order.CreatedDate = DateTime.UtcNow;
+
+ // 预留30天的订单保留期
+ var retentionDays = 30;
+ ```
\ No newline at end of file
diff --git a/docs/开发规范/1-编程规约/1.4-多线程与异步规范.md b/docs/开发规范/1-编程规约/1.4-多线程与异步规范.md
new file mode 100644
index 0000000..09d6b1c
--- /dev/null
+++ b/docs/开发规范/1-编程规约/1.4-多线程与异步规范.md
@@ -0,0 +1,184 @@
+# 1.4 多线程与异步规范
+
+本规范旨在指导 .NET 中的多线程与异步编程,确保代码的高效性、稳定性和可维护性,避免常见的死锁、线程饥饿和竞态条件问题。
+
+## 1. 异步编程 (Async/Await)
+
+### 1.1 避免 `async void`
+
+* **规则**:严禁使用 `async void`,唯一的例外是事件处理程序 (Event Handlers)。
+* **理由**:
+ * `async void` 方法无法被等待 (await)。
+ * `async void` 方法中抛出的异常无法被调用方捕获,会导致进程崩溃(除非在 SynchronizationContext 中捕获)。
+ * 难以测试和组合。
+* **反例** :
+ ```csharp
+ // ❌ [不推荐] 使用 async void,异常无法被外部捕获
+ private async void ExecuteAsync(object obj)
+ {
+ try
+ {
+ await _jobPerformer.Perform(...);
+ }
+ catch (Exception e)
+ {
+ // 必须在内部吞掉所有异常,否则进程崩溃
+ Console.WriteLine(e);
+ }
+ }
+ ```
+* **正例**:
+ ```csharp
+ // ✅ [推荐] 返回 Task,允许调用方等待和处理异常
+ private async Task ExecuteAsync(object obj)
+ {
+ try
+ {
+ await _jobPerformer.Perform(...);
+ }
+ catch (Exception e)
+ {
+ _logger.LogError(e, "Error occurred");
+ }
+ }
+ ```
+
+### 1.2 异步全链路 (Async All the Way)
+
+* **规则**:一旦开始使用异步,应在整个调用链路中保持异步。
+* **理由**:避免同步/异步混合导致的死锁(Sync-over-Async)和线程池饥饿。
+* **反例**:
+ ```csharp
+ // ❌ [禁止] 在异步方法中阻塞等待
+ public void DoSomething()
+ {
+ DoSomethingAsync().Result; // 或者 .Wait()
+ }
+ ```
+* **正例**:
+ ```csharp
+ // ✅ [推荐] 使用 await
+ public async Task DoSomething()
+ {
+ await DoSomethingAsync();
+ }
+ ```
+
+### 1.3 库代码使用 `ConfigureAwait(false)`
+
+* **规则**:在通用类库 (非 UI 层、非 ASP.NET Core Controller 层) 代码中,应使用 `.ConfigureAwait(false)`。
+* **理由**:避免在不需要特定 `SynchronizationContext` (如 UI 线程) 的情况下强制切回原上下文,提高性能并减少死锁风险。
+* **注意**:在 ASP.NET Core 应用层代码中通常不需要,因为 ASP.NET Core 没有 SynchronizationContext。但在编写底层 SDK (如 `Aegis.Caching`) 时必须遵守。
+* **正例**:
+ ```csharp
+ // ✅ [推荐] 类库代码
+ public async Task GetDataAsync()
+ {
+ var result = await _httpClient.GetStringAsync(url).ConfigureAwait(false);
+ return result;
+ }
+ ```
+
+### 1.4 传入 CancellationToken
+
+* **规则**:异步方法应尽可能支持 `CancellationToken`,以便在操作不再需要时取消。
+* **正例**:
+ ```csharp
+ // ✅ [推荐] 支持取消
+ public async Task ProcessAsync(CancellationToken cancellationToken = default)
+ {
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ await Task.Delay(1000, cancellationToken);
+ // ... 业务逻辑
+ }
+ }
+ ```
+
+## 2. 线程与任务 (Threads & Tasks)
+
+### 2.1 优先使用 Task 而非 Thread
+
+* **规则**:使用 `Task.Run` 或 `Task.Factory.StartNew` (带有 `TaskCreationOptions.LongRunning`),尽量避免直接 `new Thread()`。
+* **理由**:`Task` 基于线程池,资源利用率更高,且 API 更易于组合和异常处理。
+* **例外**:需要设置线程优先级、名称或必须是前台线程的特殊场景。
+
+### 2.2 避免长时间阻塞线程池线程
+
+* **规则**:如果任务是 CPU 密集型且运行时间很长,应指定 `TaskCreationOptions.LongRunning`,或者使用独立的线程,避免耗尽线程池导致吞吐量下降。
+
+## 3. 线程安全与锁 (Locking)
+
+### 3.1 锁的选择
+
+* **规则**:
+ * **同步代码**:首选 `lock` (即 `Monitor`)。
+ * **异步代码**:必须使用 `SemaphoreSlim`,严禁在 `lock` 块中使用 `await`。
+ * **高性能/轻量级**:`Interlocked` 用于简单的计数器或原子交换。
+ * **自旋锁**:慎用 `SpinLock` 或 `SpinWait`,除非非常短的临界区且你非常清楚自己在做什么。
+ *建议:对于大多数业务场景,普通的 `lock` 或 `SemaphoreSlim` 足够且更安全。除非经过 Benchmark 证明必须使用自旋锁。*
+
+* **正例 (异步锁)**:
+ ```csharp
+ private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1);
+
+ public async Task SafeOperationAsync()
+ {
+ await _lock.WaitAsync();
+ try
+ {
+ // ✅ 可以在锁内 await
+ await DoSomethingAsync();
+ }
+ finally
+ {
+ _lock.Release();
+ }
+ }
+ ```
+
+### 3.2 线程安全的集合
+
+* **规则**:在多线程环境下读写集合,应使用 `System.Collections.Concurrent` 命名空间下的集合 (如 `ConcurrentDictionary`, `ConcurrentQueue`),而不是手动加锁的 `List` 或 `Dictionary`。
+* **正例**:
+ ```csharp
+ private readonly ConcurrentDictionary _cache = new();
+
+ public void AddUser(User user)
+ {
+ // ✅ 线程安全
+ _cache.TryAdd(user.Id, user);
+ }
+ ```
+
+### 3.3 静态成员的线程安全
+
+* **规则**:静态成员 (Static Members) 在多线程环境下是共享的,必须保证其线程安全。
+* **建议**:
+ * 尽量设计为不可变 (Immutable)。
+ * 如果必须可变,使用 `lock` 或 `Concurrent` 类型保护。
+
+## 4. 并行处理 (Parallel)
+
+### 4.1 控制并发度
+
+* **规则**:使用 `Parallel.ForEach` 或 `Task.WhenAll` 时,必须控制并发数量 (`MaxDegreeOfParallelism`),防止瞬间并发过高压垮下游服务 (数据库、Redis、外部 API)。
+* **正例**:
+ ```csharp
+ var options = new ParallelOptions { MaxDegreeOfParallelism = 5 };
+ await Parallel.ForEachAsync(items, options, async (item, token) =>
+ {
+ await ProcessItemAsync(item, token);
+ });
+ ```
+
+### 4.2 避免 Parallel 里的共享状态写入
+
+* **规则**:避免在并行循环中写入共享变量,这通常是非线程安全的。应使用线程局部变量或并发集合。
+
+## 5. 最佳实践总结
+
+1. **不要阻塞异步代码**:避免 `.Result`, `.Wait()`。
+2. **异常处理**:`Task` 中的异常会被包装在 `AggregateException` 中 (如果使用 `.Result` 或 `.Wait()`),但在 `await` 时会解包抛出第一个异常。建议始终使用 `await`。
+3. **避免线程饥饿**:不要在线程池线程中执行长时间的同步 IO 操作。
+4. **不可变性**:尽可能设计不可变对象,天然线程安全。
diff --git a/docs/开发规范/1-编程规约/1.5-OOP规范.md b/docs/开发规范/1-编程规约/1.5-OOP规范.md
new file mode 100644
index 0000000..f77aba1
--- /dev/null
+++ b/docs/开发规范/1-编程规约/1.5-OOP规范.md
@@ -0,0 +1,799 @@
+# 1.5 OOP规范
+
+---
+
+## 1. 类的职责划分
+### 1.1 单一职责原则
+```csharp
+// ❌ 不建议:一个类承担多个职责
+public class User
+{
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public string Email { get; set; }
+ // 数据访问职责
+ public void SaveToDatabase() { }
+ // 邮件发送职责
+ public void SendWelcomeEmail() { }
+ // 日志记录职责
+ public void LogUserAction() { }
+ // 数据验证职责
+ public bool ValidateEmail() { }
+}
+// ✅ 建议:职责分离
+public class User
+{
+ public int Id { get; set; }
+ public string Name { get; set; }
+ public string Email { get; set; }
+}
+public class UserRepository
+{
+ public async Task SaveAsync(User user)
+ {
+ // 数据访问逻辑
+ }
+}
+public class UserEmailService
+{
+ public async Task SendWelcomeEmailAsync(User user)
+ {
+ // 邮件发送逻辑
+ }
+}
+public class UserValidator
+{
+ public bool IsValidEmail(string email)
+ {
+ // 验证逻辑
+ }
+}
+```
+### 1.2 类的大小控制
+```csharp
+// - 方法数量:建议不超过 20 个
+// - 代码行数:建议不超过 300 行
+// - 依赖数量:建议不超过 5 个
+public class OrderService
+{
+ // 依赖注入(建议最多5个依赖)
+ private readonly IOrderRepository _orderRepository;
+ private readonly IProductService _productService;
+ private readonly IEmailService _emailService;
+ private readonly ILogger _logger;
+ public OrderService(
+ IOrderRepository orderRepository,
+ IProductService productService,
+ IEmailService emailService,
+ ILogger logger)
+ {
+ _orderRepository = orderRepository;
+ _productService = productService;
+ _emailService = emailService;
+ _logger = logger;
+ }
+ // 核心业务方法
+ public async Task CreateOrderAsync(CreateOrderRequest request)
+ {
+ // 方法体不超过30行
+ }
+}
+// ❌ 如果类过大,考虑拆分
+// 例如:将订单创建、订单查询、订单取消拆分为不同的服务类
+public class OrderCreationService { }
+public class OrderQueryService { }
+public class OrderCancellationService
+```
+## 2. 封装原则
+### 2.1 字段私有化
+```csharp
+// ❌ 不建议:公开字段
+public class BankAccount
+{
+ public decimal Balance; // 可被外部直接修改
+}
+// ✅ 建议:私有字段 + 公共属性
+public class BankAccount
+{
+ private decimal _balance;
+ public decimal Balance
+ {
+ get => _balance;
+ private set => _balance = value; // 只能内部修改
+ }
+ // 通过方法控制状态变化
+ public void Deposit(decimal amount)
+ {
+ if (amount <= 0)
+ {
+ throw new ArgumentException("存款金额必须大于0");
+ }
+ _balance += amount;
+ }
+ public void Withdraw(decimal amount)
+ {
+ if (amount <= 0)
+ {
+ throw new ArgumentException("取款金额必须大于0");
+ }
+ if (amount > _balance)
+ {
+ throw new InvalidOperationException("余额不足");
+ }
+ _balance -= amount;
+ }
+}
+```
+### 2.2 属性设计
+```csharp
+public class Product
+{
+ // ✅ 自动属性(简单场景)
+ public int Id { get; set; }
+ public string Name { get; set; }
+ // ✅ 只读属性(外部只能读取)
+ public DateTime CreateTime { get; private set; }
+ // ✅ 计算属性(不存储值)
+ public decimal TotalPrice => Price * Quantity;
+ // ✅ 带验证的属性
+ private decimal _price;
+ public decimal Price
+ {
+ get => _price;
+ set
+ {
+ if (value < 0)
+ {
+ throw new ArgumentException("价格不能为负数");
+ }
+ _price = value;
+ }
+ }
+ // ✅ 延迟初始化属性
+ private List _reviews;
+ public List Reviews => _reviews ??= new List();
+ // ✅ init 访问器(只能在构造时设置)
+ public string Code { get; init; }
+ // ✅ required 修饰符(C# 11+)
+ public required string Category { get; init; }
+}
+// 使用示例
+var product = new Product
+{
+ Code = "P001",
+ Category = "电子产品", // 必须设置
+ Price = 999.99m
+};
+// product.Code = "P002"; // 编译错误,init 属性不可修改
+```
+### 2.3 对象不变性
+```csharp
+// ✅ 不可变对象(线程安全)
+public class Money
+{
+ public decimal Amount { get; }
+ public string Currency { get; }
+ public Money(decimal amount, string currency)
+ {
+ if (amount < 0)
+ {
+ throw new ArgumentException("金额不能为负数");
+ }
+ Amount = amount;
+ Currency = currency ?? throw new ArgumentNullException(nameof(currency));
+ }
+ // 操作返回新对象,而不修改当前对象
+ public Money Add(Money other)
+ {
+ if (Currency != other.Currency)
+ {
+ throw new InvalidOperationException("货币类型不同");
+ }
+ return new Money(Amount + other.Amount, Currency);
+ }
+ public Money Multiply(decimal factor)
+ {
+ return new Money(Amount * factor, Currency);
+ }
+}
+// ✅ 使用 record 创建不可变对象
+public record Address(string Street, string City, string ZipCode);
+// 使用
+var address = new Address("中山路100号", "上海", "200000");
+// address.Street = "新地址"; // 编译错误
+// 创建副本并修改
+var newAddress = address with { Street = "南京路200号" };
+```
+## 3. 继承使用原则
+### 3.1 何时使用继承
+```csharp
+// ✅ 正确使用继承
+public abstract class Animal
+{
+ public string Name { get; set; }
+ public abstract void MakeSound();
+ public virtual void Eat()
+ {
+ Console.WriteLine($"{Name} is eating");
+ }
+}
+public class Dog : Animal
+{
+ public override void MakeSound()
+ {
+ Console.WriteLine("Woof!");
+ }
+}
+public class Cat : Animal
+{
+ public override void MakeSound()
+ {
+ Console.WriteLine("Meow!");
+ }
+ public override void Eat()
+ {
+ base.Eat(); // 调用基类实现
+ Console.WriteLine("Cat loves fish");
+ }
+}
+// ❌ 错误使用继承:仅为复用代码
+public class Stack : List // 不应该继承 List
+{
+ // Stack 不是一个 List
+}
+// ✅ 正确:使用组合
+public class Stack
+{
+ private readonly List _items = new();
+ public void Push(int item) => _items.Add(item);
+ public int Pop()
+ {
+ var item = _items[^1];
+ _items.RemoveAt(_items.Count - 1);
+ return item;
+ }
+}
+```
+### 3.2 继承层次控制
+```csharp
+// ✅ 继承深度建议不超过3层
+// Level 1
+public abstract class Entity
+{
+ public int Id { get; set; }
+}
+// Level 2
+public abstract class AuditableEntity : Entity
+{
+ public DateTime CreateTime { get; set; }
+ public DateTime? UpdateTime { get; set; }
+}
+// Level 3
+public class Product : AuditableEntity
+{
+ public string Name { get; set; }
+ public decimal Price { get; set; }
+}
+// ❌ 避免更深的继承层次
+// Level 4 - 过深
+public class SpecialProduct : Product { } // 考虑重新设计
+```
+### 3.3 虚方法使用规范
+```csharp
+public abstract class PaymentProcessor
+{
+ // ✅ 模板方法模式
+ public async Task ProcessPaymentAsync(PaymentRequest request)
+ {
+ // 1. 验证(子类可重写)
+ ValidateRequest(request);
+ // 2. 预处理(子类可重写)
+ await PreProcessAsync(request);
+ // 3. 核心处理(子类必须实现)
+ var result = await ExecutePaymentAsync(request);
+ // 4. 后处理(子类可重写)
+ await PostProcessAsync(result);
+ return result;
+ }
+ // 虚方法(可选重写)
+ protected virtual void ValidateRequest(PaymentRequest request)
+ {
+ if (request.Amount <= 0)
+ {
+ throw new ArgumentException("支付金额必须大于0");
+ }
+ }
+ protected virtual Task PreProcessAsync(PaymentRequest request)
+ {
+ return Task.CompletedTask;
+ }
+ // 抽象方法(必须实现)
+ protected abstract Task ExecutePaymentAsync(PaymentRequest request);
+ protected virtual Task PostProcessAsync(PaymentResult result)
+ {
+ return Task.CompletedTask;
+ }
+}
+// 具体实现
+public class AlipayProcessor : PaymentProcessor
+{
+ protected override async Task ExecutePaymentAsync(PaymentRequest request)
+ {
+ // 支付宝支付逻辑
+ return new PaymentResult { Success = true };
+ }
+ protected override async Task PostProcessAsync(PaymentResult result)
+ {
+ // 发送支付通知
+ await base.PostProcessAsync(result);
+ }
+}
+```
+## 4. 组合优于继承
+### 4.1 使用组合的场景
+```csharp
+// ❌ 错误:使用继承实现不同功能组合
+public class FlyingSwimmingAnimal : Animal { } // 会飞会游泳的动物?
+// ✅ 正确:使用组合和接口
+public interface IFlyable
+{
+ void Fly();
+}
+public interface ISwimmable
+{
+ void Swim();
+}
+public class Duck : Animal, IFlyable, ISwimmable
+{
+ private readonly FlyingAbility _flyingAbility = new();
+ private readonly SwimmingAbility _swimmingAbility = new();
+ public void Fly() => _flyingAbility.Fly();
+ public void Swim() => _swimmingAbility.Swim();
+ public override void MakeSound()
+ {
+ Console.WriteLine("Quack!");
+ }
+}
+public class FlyingAbility
+{
+ public void Fly()
+ {
+ Console.WriteLine("Flying in the sky");
+ }
+}
+public class SwimmingAbility
+{
+ public void Swim()
+ {
+ Console.WriteLine("Swimming in the water");
+ }
+}
+```
+### 4.2 策略模式代替继承
+```csharp
+// ✅ 使用策略模式提供不同行为
+public interface IShippingStrategy
+{
+ decimal CalculateCost(decimal weight, decimal distance);
+}
+public class StandardShipping : IShippingStrategy
+{
+ public decimal CalculateCost(decimal weight, decimal distance)
+ {
+ return weight * 0.5m + distance * 0.1m;
+ }
+}
+public class ExpressShipping : IShippingStrategy
+{
+ public decimal CalculateCost(decimal weight, decimal distance)
+ {
+ return weight * 1.0m + distance * 0.2m + 20m;
+ }
+}
+public class Order
+{
+ private IShippingStrategy _shippingStrategy;
+ public void SetShippingStrategy(IShippingStrategy strategy)
+ {
+ _shippingStrategy = strategy ?? throw new ArgumentNullException(nameof(strategy));
+ }
+ public decimal CalculateShippingCost(decimal weight, decimal distance)
+ {
+ if (_shippingStrategy == null)
+ {
+ throw new InvalidOperationException("运输策略未设置");
+ }
+ return _shippingStrategy.CalculateCost(weight, distance);
+ }
+}
+```
+## 5. 接口设计原则
+### 5.1 接口命名与定义
+```csharp
+// ✅ 接口命名以 I 开头
+public interface ICustomerRepository
+{
+ Task GetByIdAsync(int id);
+ Task> GetAllAsync();
+ Task AddAsync(Customer customer);
+ Task UpdateAsync(Customer customer);
+ Task DeleteAsync(int id);
+}
+// ✅ 能力接口(形容词)
+public interface IDisposable
+{
+ void Dispose();
+}
+public interface IComparable
+{
+ int CompareTo(T other);
+}
+// ✅ 服务接口(名词)
+public interface IEmailService
+{
+ Task SendAsync(string to, string subject, string body);
+}
+public interface ILogger
+{
+ void LogInformation(string message);
+ void LogError(Exception ex, string message);
+}
+```
+### 5.2 接口隔离
+```csharp
+// ❌ 不建议:臃肿的接口
+public interface IRepository
+{
+ // CRUD 操作
+ void Add();
+ void Update();
+ void Delete();
+ void Get();
+ // 批量操作
+ void BulkAdd();
+ void BulkUpdate();
+ void BulkDelete();
+ // 搜索功能
+ void Search();
+ void AdvancedSearch();
+ // 导出功能
+ void ExportToExcel();
+ void ExportToPdf();
+}
+// ✅ 建议:接口隔离
+public interface IReadRepository
+{
+ Task GetByIdAsync(int id);
+ Task> GetAllAsync();
+}
+public interface IWriteRepository
+{
+ Task AddAsync(T entity);
+ Task UpdateAsync(T entity);
+ Task DeleteAsync(int id);
+}
+public interface IBulkRepository
+{
+ Task BulkAddAsync(IEnumerable entities);
+ Task BulkUpdateAsync(IEnumerable entities);
+}
+public interface ISearchableRepository
+{
+ Task> SearchAsync(SearchCriteria criteria);
+}
+// 组合使用
+public interface ICustomerRepository : IReadRepository,
+ IWriteRepository,
+ ISearchableRepository
+{
+ // 特定于 Customer 的方法
+ Task GetByEmailAsync(string email);
+}
+```
+### 5.3 显式接口实现
+```csharp
+public interface IAnimal
+{
+ void Move();
+}
+public interface IRobot
+{
+ void Move();
+}
+// ✅ 显式接口实现解决方法冲突
+public class RobotDog : IAnimal, IRobot
+{
+ // 隐式实现(默认)
+ public void Move()
+ {
+ Console.WriteLine("RobotDog moving");
+ }
+ // 显式实现 IAnimal
+ void IAnimal.Move()
+ {
+ Console.WriteLine("Animal walking");
+ }
+ // 显式实现 IRobot
+ void IRobot.Move()
+ {
+ Console.WriteLine("Robot moving mechanically");
+ }
+}
+// 使用
+var robotDog = new RobotDog();
+robotDog.Move(); // "RobotDog moving"
+((IAnimal)robotDog).Move(); // "Animal walking"
+((IRobot)robotDog).Move(); // "Robot moving mechanically"
+```
+## 6. 抽象类使用场景
+### 6.1 抽象类 vs 接口
+```csharp
+// ✅ 使用抽象类:有共同实现逻辑
+public abstract class Document
+{
+ // 公共属性
+ public string Title { get; set; }
+ public DateTime CreateTime { get; set; }
+ // 公共方法实现
+ public void Save()
+ {
+ ValidateBeforeSave();
+ PerformSave();
+ LogSave();
+ }
+ // 模板方法
+ protected virtual void ValidateBeforeSave()
+ {
+ if (string.IsNullOrWhiteSpace(Title))
+ {
+ throw new ValidationException("标题不能为空");
+ }
+ }
+ // 抽象方法(强制子类实现)
+ protected abstract void PerformSave();
+ // 虚方法(可选重写)
+ protected virtual void LogSave()
+ {
+ Console.WriteLine($"Document '{Title}' saved at {DateTime.Now}");
+ }
+}
+public class PdfDocument : Document
+{
+ protected override void PerformSave()
+ {
+ // PDF 特定的保存逻辑
+ Console.WriteLine("Saving as PDF");
+ }
+}
+public class WordDocument : Document
+{
+ protected override void PerformSave()
+ {
+ // Word 特定的保存逻辑
+ Console.WriteLine("Saving as Word document");
+ }
+ protected override void ValidateBeforeSave()
+ {
+ base.ValidateBeforeSave();
+ // 额外的验证逻辑
+ }
+}
+// ✅ 使用接口:定义契约,无共同实现
+public interface INotificationSender
+{
+ Task SendAsync(string recipient, string message);
+}
+public class EmailSender : INotificationSender
+{
+ public async Task SendAsync(string recipient, string message)
+ {
+ // 邮件发送实现
+ }
+}
+public class SmsSender : INotificationSender
+{
+ public async Task SendAsync(string recipient, string message)
+ {
+ // 短信发送实现
+ }
+}
+```
+### 6.2 抽象类设计要点
+```csharp
+public abstract class BaseEntity
+{
+ // ✅ 抽象类可以有构造函数
+ protected BaseEntity()
+ {
+ CreateTime = DateTime.UtcNow;
+ Id = Guid.NewGuid();
+ }
+ // ✅ 可以有字段
+ private readonly List _domainEvents = new();
+ // 公共属性
+ public Guid Id { get; private set; }
+ public DateTime CreateTime { get; private set; }
+ public DateTime? UpdateTime { get; protected set; }
+ // ✅ 受保护的方法供子类使用
+ protected void AddDomainEvent(DomainEvent domainEvent)
+ {
+ _domainEvents.Add(domainEvent);
+ }
+ protected void MarkAsUpdated()
+ {
+ UpdateTime = DateTime.UtcNow;
+ }
+ // 抽象方法
+ public abstract void Validate();
+}
+public class Customer : BaseEntity
+{
+ public string Name { get; set; }
+ public string Email { get; set; }
+ public override void Validate()
+ {
+ if (string.IsNullOrWhiteSpace(Name))
+ {
+ throw new ValidationException("客户名称不能为空");
+ }
+ if (string.IsNullOrWhiteSpace(Email))
+ {
+ throw new ValidationException("邮箱不能为空");
+ }
+ }
+ public void UpdateEmail(string newEmail)
+ {
+ Email = newEmail;
+ MarkAsUpdated(); // 使用基类方法
+ AddDomainEvent(new CustomerEmailChangedEvent(Id, newEmail));
+ }
+}
+```
+## 7. 密封类使用
+### 7.1 何时使用 `sealed`
+```csharp
+// ✅ 防止被继承(性能优化、设计意图)
+public sealed class ConfigurationManager
+{
+ private readonly Dictionary _settings = new();
+ public string GetSetting(string key)
+ {
+ return _settings.TryGetValue(key, out var value) ? value : null;
+ }
+}
+// ❌ 无法继承
+// public class CustomConfigManager : ConfigurationManager { } // 编译错误
+// ✅ 工具类密封
+public sealed class StringHelper
+{
+ private StringHelper() { } // 私有构造防止实例化
+ public static string Truncate(string value, int maxLength)
+ {
+ if (string.IsNullOrEmpty(value) || value.Length <= maxLength)
+ {
+ return value;
+ }
+ return value.Substring(0, maxLength) + "...";
+ }
+}
+// ✅ 密封重写方法(防止进一步重写)
+public class BaseProcessor
+{
+ public virtual void Process()
+ {
+ Console.WriteLine("Base processing");
+ }
+}
+public class DerivedProcessor : BaseProcessor
+{
+ // 密封此方法,子类不能再重写
+ public sealed override void Process()
+ {
+ Console.WriteLine("Derived processing");
+ base.Process();
+ }
+}
+public class FurtherDerived : DerivedProcessor
+{
+ // ❌ 编译错误:不能重写密封方法
+ // public override void Process() { }
+}
+```
+## 8. 访问修饰符使用规范
+### 8.1 访问级别选择
+```csharp
+public class Product
+{
+ // ✅ private - 只在类内部使用
+ private decimal _costPrice;
+ private List _priceHistory = new();
+ // ✅ private protected - 仅当前类和派生类访问(同一程序集)
+ private protected void RecordPriceChange(decimal oldPrice, decimal newPrice)
+ {
+ _priceHistory.Add(new PriceHistory(oldPrice, newPrice));
+ }
+ // ✅ protected - 当前类和派生类可访问
+ protected decimal CostPrice => _costPrice;
+ // ✅ internal - 同一程序集内可访问
+ internal void SetCostPrice(decimal cost)
+ {
+ _costPrice = cost;
+ }
+ // ✅ protected internal - 同一程序集或派生类
+ protected internal decimal CalculateMargin()
+ {
+ return Price - _costPrice;
+ }
+ // ✅ public - 所有地方可访问
+ public decimal Price { get; set; }
+ public string Name { get; set; }
+ public decimal GetProfitMargin()
+ {
+ return (Price - _costPrice) / Price * 100;
+ }
+}
+// 访问修饰符决策树:
+// 1. 只在当前类使用? -> private
+// 2. 需要子类访问? -> protected
+// 3. 同一程序集内使用? -> internal
+// 4. 需要外部调用? -> public
+// 5. 同程序集或子类? -> protected internal
+// 6. 仅同程序集的子类? -> private protected
+```
+### 8.2 最小权限原则
+```csharp
+// ✅ 建议:默认使用最小访问权限
+public class OrderProcessor
+{
+ // 私有字段
+ private readonly IOrderRepository _repository;
+ private readonly ILogger _logger;
+
+ // 公共构造函数
+ public OrderProcessor(IOrderRepository repository, ILogger logger)
+ {
+ _repository = repository;
+ _logger = logger;
+ }
+
+ // 公共方法(对外API)
+ public async Task ProcessOrderAsync(int orderId)
+ {
+ var order = await GetOrderAsync(orderId);
+ ValidateOrder(order);
+ await SaveOrderAsync(order);
+ return order;
+ }
+
+ // 私有方法(内部实现细节)
+ private async Task GetOrderAsync(int orderId)
+ {
+ return await _repository.GetByIdAsync(orderId);
+ }
+
+ private void ValidateOrder(Order order)
+ {
+ if (order == null)
+ {
+ throw new ArgumentNullException(nameof(order));
+ }
+ }
+
+ private async Task SaveOrderAsync(Order order)
+ {
+ await _repository.UpdateAsync(order);
+ _logger.LogInformation("Order {OrderId} saved", order.Id);
+ }
+}
+
+// ❌ 不建议:不必要的公开
+public class BadOrderProcessor
+{
+ // 不应该公开
+ public IOrderRepository Repository { get; set; }
+
+ // 不应该公开内部实现
+ public void ValidateOrder(Order order) { }
+ public async Task SaveOrderAsync(Order order) { }
+}
+```
\ No newline at end of file
diff --git a/docs/开发规范/2-异常日志/2.1-日志规约.md b/docs/开发规范/2-异常日志/2.1-日志规约.md
new file mode 100644
index 0000000..2e9f3ad
--- /dev/null
+++ b/docs/开发规范/2-异常日志/2.1-日志规约.md
@@ -0,0 +1,45 @@
+# 2.1 日志规约
+
+---
+
+## 【强制】
+
+1. **日志组件**:
+ * **底层框架**:**Aegis.Core.Logs** 使用 **NLog** 作为日志记录提供程序。
+ * **集中收集**:生产环境必须配置 **Seq**或**ELK** 作为日志的集中收集与分析端点。
+ * **抽象使用**:代码中应依赖 `Microsoft.Extensions.Logging.ILogger` 接口进行日志记录,避免直接依赖 NLog 的特定 API。
+
+2. **API 请求日志**:
+ * **自动化记录**:Web API 项目可以通过 **AOP 过滤器**(如 `[TypeFilter(typeof(LogAttribute))]` 或全局过滤器)自动记录 HTTP 请求的入参、出参、执行时间及客户端信息。
+ * **避免冗余**:**禁止**在 Controller 方法内部手动记录 "进入方法"、"离开方法" 等流水账日志,除非有特殊的业务追踪需求。
+
+3. **异常记录规范**:
+ * **完整堆栈**:记录异常时,**必须**将 `Exception` 对象作为 `LogError` 方法的第一个参数传入。
+ * *反例*:`_logger.LogError($"处理失败:{ex.Message}");` (丢失堆栈信息)
+ * *正例*:`_logger.LogError(ex, "处理订单 {OrderId} 失败", orderId);`
+
+4. **结构化日志**:
+ * **使用模板**:必须使用**消息模板**(Message Templates)语法(使用 `{Property}` 占位符),**禁止**使用字符串拼接或 `string.Format`。这确保日志在 Seq 中可被索引和查询。
+ * *正例*:`_logger.LogInformation("用户 {UserId} 修改了状态为 {Status}", userId, status);`
+
+5. **默认上下文信息**:
+ * **自动注入**:框架层(`Aegis.Core.Logs`)已统一配置日志 Layout,自动包含 **RequestId** (`${request-id}`) 和 **调用方法名** (`${callsite}`)。
+ * **无需手动记录**:开发者在记录日志时,**无需**在消息体中重复记录当前方法名或 RequestId。
+ * *说明*:默认 Layout 格式为 `${date}|${level:uppercase=true}|${request-id}|${callsite:includeNamespace=false}|${message}|${exception}`。
+
+## 【推荐】
+
+1. **日志级别定义**:
+ * **Trace/Debug**:仅用于开发环境调试,生产环境默认关闭。
+ * **Information**:记录系统关键流程节点(如:定时任务开始/结束、关键状态流转)。
+ * **Warning**:记录非预期的但系统可自动恢复的业务异常(如:参数校验失败、第三方接口偶尔超时)。
+ * **Error**:记录导致当前请求失败的系统异常(如:数据库连接失败、空指针异常)。
+ * **Critical**:记录导致系统崩溃或无法提供服务的致命错误。
+
+2. **日志保留策略**:
+ * 本地文件日志建议按天滚动(`RollingFile`),保留 7-15 天。
+ * Seq 服务器日志根据磁盘容量配置保留策略(通常 30 天)。
+
+3. **敏感信息脱敏**:
+ * 严禁在日志中明文记录密码、密钥、身份证号、银行卡号等敏感信息。
+
diff --git a/docs/开发规范/2-异常日志/2.2-异常处理.md b/docs/开发规范/2-异常日志/2.2-异常处理.md
new file mode 100644
index 0000000..6c64faf
--- /dev/null
+++ b/docs/开发规范/2-异常日志/2.2-异常处理.md
@@ -0,0 +1,122 @@
+# 2.2 异常处理
+
+---
+
+## 【强制】
+
+1. **业务异常处理**:
+ * **优先校验**:应优先使用条件判断(`if`)来检查业务规则是否满足,在发现问题时提前拦截。
+ * **抛出异常**:当校验不通过需中断流程时,应抛出 `BusinessException` 或其子类,而不是 `System.Exception`。
+ * **消息友好**:异常消息(Message)将直接作为错误提示返回给前端/客户端,必须确保消息内容用户友好且无敏感技术信息。
+ * **记录异常**:在业务逻辑中抛出异常时,必须记录异常信息和堆栈到日志中,以便后续分析和排查问题。
+ * *正例*:
+ ```csharp
+ if (order.Status != OrderStatus.Created)
+ {
+ throw new BusinessException("当前订单状态不允许取消");
+ }
+ ```
+
+2. **全局异常处理**:
+ * **中间件接管**:严禁在 Controller 层进行通用的 `try-catch` 捕获。Aegis 框架已内置全局异常处理中间件,会自动捕获未处理异常并转换为标准 JSON 响应。
+ * **响应格式**:框架会将未处理异常统一转换为如下格式:
+ ```json
+ {
+ "code": 500,
+ "message": "服务器内部错误 / 业务错误提示",
+ "data": null,
+ "success": false
+ }
+ ```
+
+3. **必须通过预检查规避可预见的异常**:对于 `NullReferenceException`, `ArgumentNullException`, `IndexOutOfRangeException` 等可以通过代码检查来避免的运行时异常,不应该使用 `try-catch` 来处理。
+
+
+ *说明*:异常处理的性能开销远高于条件判断。编写健壮的代码,通过防御性编程(如卫语句)来处理可预见的问题,是程序员的基本素养。
+
+ *反例*:
+ ```csharp
+ try
+ {
+ // obj 可能为 null
+ obj.DoSomething();
+ }
+ catch (NullReferenceException ex)
+ {
+ _logger.LogError(ex, "obj was null");
+ }
+ ```
+
+ *正例*:
+ ```csharp
+ if (obj is null)
+ {
+ throw new ArgumentNullException(nameof(obj));
+ }
+ obj.DoSomething();
+ ```
+
+2. **禁止使用异常作为流程控制**:异常是为程序运行中的意外情况设计的,不应用于正常的业务逻辑判断和流程跳转。
+
+3. **精确捕获异常类型**:`catch` 时应尽可能捕获具体的异常类型,而不是宽泛的 `System.Exception`。对不同的异常类型应有不同的处理逻辑。
+
+ *说明*:对大段代码进行笼统的 `try-catch`,会使程序无法对特定错误做出正确响应,不利于问题定位和系统稳定性。
+
+ *正例*:
+ ```csharp
+ try
+ {
+ // ... 数据库操作
+ }
+ catch (SqlException ex)
+ {
+ // 处理数据库相关的特定错误
+ _logger.LogError(ex, "A database error occurred.");
+ }
+ catch (TimeoutException ex)
+ {
+ // 处理连接超时错误
+ _logger.LogWarning(ex, "The database connection timed out.");
+ }
+ ```
+
+4. **捕获后必须处理或转抛**:捕获异常后,必须进行处理(如记录日志、事务回滚、向用户返回错误信息等)。如果当前层无法处理,应使用 `throw;` 语句将原始异常向上抛出。严禁“吞掉”异常(即空的 `catch` 块)。
+
+5. **必须使用 `using` 语句管理资源**:对于实现了 `IDisposable` 接口的资源对象(如流、数据库连接、HttpClient 等),必须使用 `using` 语句来确保资源的正确释放。
+
+ *说明*:`using` 语句会自动生成 `try-finally` 块,并调用 `Dispose()` 方法,这是 .NET 中管理非托管资源的最安全、最简洁的方式。
+
+ *正例*:
+ ```csharp
+ using (var stream = new FileStream("path/to/file", FileMode.Open))
+ {
+ // ... 对 stream 的操作
+ }
+ // 此处 stream 已被自动关闭和释放
+ ```
+
+6. **禁止在 `finally` 块中使用 `return`**:`finally` 块中的 `return` 会覆盖 `try` 或 `catch` 块中的 `return` 或异常,导致非预期的行为。
+
+## 【推荐】
+
+1. **启用并遵循可空引用类型 (`Nullable Reference Types`)**:在项目(`.csproj`)中启用 `enable`,让编译器帮助你从静态层面发现潜在的 `NullReferenceException`。
+
+2. **优雅地处理空引用**:
+ - **级联调用**:使用空条件运算符 `?.` 和 `?[]` 来安全地访问可能为 null 的对象的成员或索引。
+ *正例*: `string? postalCode = customer?.Address?.PostalCode;`
+ - **返回值**:方法的返回值可以为 null,但必须在方法签名和 XML 注释中明确指出。调用方有责任处理 null 情况。
+
+3. **使用自定义异常表达业务错误**:应定义有具体业务含义的自定义异常(继承自 `System.Exception`),而不是直接抛出泛泛的 `new Exception()`。
+
+ *正例*:
+ ```csharp
+ public class OrderNotFoundException : Exception
+ {
+ public OrderNotFoundException(int orderId)
+ : base($"Order with ID {orderId} was not found.") { }
+ }
+ ```
+
+## 【参考】
+
+1. **API 的错误响应**:对于对外暴露的 API(如 ASP.NET Core Web API),应建立一个全局异常处理中间件。该中间件负责捕获所有未处理的异常,记录日志,并将其转换为统一、标准的 JSON 错误响应格式返回给客户端,而不是直接暴露异常堆栈信息。
diff --git a/server/MiAssessment/src/MiAssessment.Admin/appsettings.json b/server/MiAssessment/src/MiAssessment.Admin/appsettings.json
index 9e1f775..22acb96 100644
--- a/server/MiAssessment/src/MiAssessment.Admin/appsettings.json
+++ b/server/MiAssessment/src/MiAssessment.Admin/appsettings.json
@@ -1,13 +1,13 @@
{
"ConnectionStrings": {
- "DefaultConnection": "Server=127.0.0.1;uid=sa;pwd=1qaz!QAZ;Database=MiAssessment_Admin;MultipleActiveResultSets=true;pooling=true;min pool size=5;max pool size=32767;connect timeout=20;Encrypt=True;TrustServerCertificate=True;",
- "BusinessConnection": "Server=127.0.0.1;uid=sa;pwd=1qaz!QAZ;Database=MiAssessment_Business;MultipleActiveResultSets=true;pooling=true;min pool size=5;max pool size=32767;connect timeout=20;Encrypt=True;TrustServerCertificate=True;",
- "Redis": "127.0.0.1:6379,abortConnect=false,connectTimeout=5000"
+ "DefaultConnection": "Server=192.168.195.15,1433;uid=sa;pwd=Dbt@com@123;Database=MiAssessment_Admin;MultipleActiveResultSets=true;pooling=true;min pool size=5;max pool size=32767;connect timeout=20;Encrypt=True;TrustServerCertificate=True;",
+ "BusinessConnection": "Server=192.168.195.15,1433;uid=sa;pwd=Dbt@com@123;Database=MiAssessment_Business;MultipleActiveResultSets=true;pooling=true;min pool size=5;max pool size=32767;connect timeout=20;Encrypt=True;TrustServerCertificate=True;",
+ "Redis": "192.168.195.15:6379,defaultDatabase=2,abortConnect=false,connectTimeout=5000"
},
"Jwt": {
- "Secret": "{{JWT_SECRET_AT_LEAST_32_CHARACTERS}}",
- "Issuer": "{{PROJECT_NAME}}.Admin",
- "Audience": "{{PROJECT_NAME}}.Admin.Client",
+ "Secret": "MiAssessmentAdminSecretKey2024!@#$%^&*()_+",
+ "Issuer": "MiAssessment.Admin",
+ "Audience": "MiAssessment.Admin.Client",
"ExpireMinutes": 1440
},
"Logging": {
diff --git a/server/MiAssessment/src/MiAssessment.Api/appsettings.json b/server/MiAssessment/src/MiAssessment.Api/appsettings.json
index 1419727..e36d8ba 100644
--- a/server/MiAssessment/src/MiAssessment.Api/appsettings.json
+++ b/server/MiAssessment/src/MiAssessment.Api/appsettings.json
@@ -1,7 +1,7 @@
{
"ConnectionStrings": {
- "DefaultConnection": "Server=127.0.0.1;uid=sa;pwd=1qaz!QAZ;Database=MiAssessment_Business;MultipleActiveResultSets=true;pooling=true;min pool size=5;max pool size=32767;connect timeout=20;Encrypt=True;TrustServerCertificate=True;",
- "Redis": "127.0.0.1:6379,abortConnect=false,connectTimeout=5000"
+ "DefaultConnection": "Server=192.168.195.15,1433;uid=sa;pwd=Dbt@com@123;Database=MiAssessment_Business;MultipleActiveResultSets=true;pooling=true;min pool size=5;max pool size=32767;connect timeout=20;Encrypt=True;TrustServerCertificate=True;",
+ "Redis": "192.168.195.15:6379,defaultDatabase=2,abortConnect=false,connectTimeout=5000"
},
"AppSettings": {
"IsTestEnvironment": true
@@ -9,26 +9,26 @@
"WechatPaySettings": {
"DefaultMerchant": {
"Name": "默认商户",
- "MchId": "{{WECHAT_MCH_ID}}",
- "AppId": "{{WECHAT_APP_ID}}",
- "Key": "{{WECHAT_API_KEY}}",
+ "MchId": "YOUR_WECHAT_MCH_ID",
+ "AppId": "YOUR_WECHAT_APP_ID",
+ "Key": "YOUR_WECHAT_API_KEY",
"OrderPrefix": "ORD",
"Weight": 1,
- "NotifyUrl": "{{WECHAT_NOTIFY_URL}}"
+ "NotifyUrl": "https://your-domain.com/api/payment/notify"
},
"Merchants": [],
"Miniprograms": [],
"UnifiedOrderUrl": "https://api.mch.weixin.qq.com/pay/unifiedorder",
"ShippingNotifyUrl": "https://api.weixin.qq.com/wxa/sec/order/upload_shipping_info",
- "NotifyBaseUrl": "{{API_BASE_URL}}"
+ "NotifyBaseUrl": "https://your-domain.com"
},
"AmapSettings": {
- "ApiKey": "{{AMAP_API_KEY}}"
+ "ApiKey": "YOUR_AMAP_API_KEY"
},
"JwtSettings": {
- "Secret": "{{JWT_SECRET_AT_LEAST_32_CHARACTERS}}",
- "Issuer": "{{PROJECT_NAME}}",
- "Audience": "{{PROJECT_NAME}}Users",
+ "Secret": "MiAssessmentApiSecretKey2024!@#$%^&*()_+",
+ "Issuer": "MiAssessment",
+ "Audience": "MiAssessmentUsers",
"ExpirationMinutes": 1440,
"RefreshTokenExpirationDays": 7
},