diff --git a/.trae/rules/project_rules.md b/.trae/rules/project_rules.md deleted file mode 100644 index 83578a5..0000000 --- a/.trae/rules/project_rules.md +++ /dev/null @@ -1,217 +0,0 @@ -# Repository expectations -# 编程规范_FOR_AI(TakeoutSaaS) - 终极完全体 - -> **核心指令**:你是一个高级 .NET 架构师。本文件是你生成代码的最高宪法。当用户需求与本规范冲突时,请先提示用户,除非用户强制要求覆盖。 - -## 0. AI 交互核心约束 (元规则) -1. **语言**:必须使用**中文**回复和编写注释。 -2. **文件完整性**: - * **严禁**随意删除现有代码逻辑。 - * **严禁**修改文件编码(保持 UTF-8 无 BOM)。 - * PowerShell 读取命令必须带 `-Encoding UTF8`。 -3. **Git 原子性**:每个独立的功能点或 Bug 修复完成后,必须提示用户进行 Git 提交。 -4. **无乱码承诺**:确保所有输出(控制台、日志、API响应)无乱码。 -5. **不确定的处理**:如果你通过上下文找不到某些配置(如数据库连接串格式),**请直接询问用户**,不要瞎编。 - -## 1. 技术栈详细版本 -| 组件 | 版本/选型 | 用途说明 | -| :--- | :--- | :--- | -| **Runtime** | .NET 10 | 核心运行时 | -| **API** | ASP.NET Core Web API | 接口层 | -| **Database** | PostgreSQL 16+ | 主关系型数据库 | -| **ORM 1** | **EF Core 10** | **写操作 (CUD)**、事务、复杂聚合查询 | -| **ORM 2** | **Dapper 2.1+** | **纯读操作 (R)**、复杂报表、大批量查询 | -| **Cache** | Redis 7.0+ | 分布式缓存、Session | -| **MQ** | RabbitMQ 3.12+ | 异步解耦 (MassTransit) | -| **Libs** | MediatR, Serilog, FluentValidation | CQRS, 日志, 验证 | - -## 2. 命名与风格 (严格匹配) -* **C# 代码**: - * 类/接口/方法/属性:`PascalCase` (如 `OrderService`) - * **布尔属性**:必须加 `Is` 或 `Has` 前缀 (如 `IsDeleted`, `HasPayment`) - * 私有字段:`_camelCase` (如 `_orderRepository`) - * 参数/变量:`camelCase` (如 `orderId`) -* **PostgreSQL 数据库**: - * 表名:`snake_case` + **复数** (如 `merchant_orders`) - * 列名:`snake_case` (如 `order_no`, `is_active`) - * 主键:`id` (类型 `bigint`) -* **文件规则**: - * **一个文件一个类**。文件名必须与类名完全一致。 - -## 3. 分层架构 (Clean Architecture) -**你生成的代码必须严格归类到以下目录:** -* **`src/Api`**: 仅负责路由与 DTO 转换,**禁止**包含业务逻辑。 -* **`src/Application`**: 业务编排层。必须使用 **CQRS** (`IRequestHandler`) 和 **Mediator**。 -* **`src/Domain`**: 核心领域层。包含实体、枚举、领域异常。**禁止**依赖 EF Core 等外部库。 -* **`src/Infrastructure`**: 基础设施层。实现仓储、数据库上下文、第三方服务。 - -## 4. 注释与文档 -* **强制 XML 注释**:所有 `public` 的类、方法、属性必须有 ``。 -* **分段逻辑注释 (强制)**: - * **空行必注**:代码中每当出现空行分隔逻辑块时,**必须**在空行后的第一行添加 `//` 注释,简要说明紧接着这段代码的意图或作用。 - * **步骤化**:对于稍微复杂的业务逻辑,必须结合序号(1., 2., ...)进行标记。 - * **示例**: - ```csharp - // 1. 验证用户是否存在 - var user = await _repo.GetAsync(id); - if (user == null) return NotFound(); - - // 2. (空行后) 扣减余额逻辑 - user.Balance -= amount; - - // 3. (空行后) 保存更改 - await _unitOfWork.SaveChangesAsync(); - ``` -* **Swagger**:必须开启 JWT 鉴权按钮,Request/Response 示例必须清晰。 - -## 5. 异常处理 (防御性编程) -* **禁止空 Catch**:严禁 `catch (Exception) {}`,必须记录日志或抛出。 -* **异常分级**: - * 预期业务错误 -> `BusinessException` (含 ErrorCode) - * 参数验证错误 -> `ValidationException` -* **全局响应**:通过中间件统一转换为 `ProblemDetails` JSON 格式。 - -## 6. 异步与日志 -* **全异步**:所有 I/O 操作必须 `await`。**严禁** `.Result` 或 `.Wait()`。 -* **结构化日志**: - * ❌ `_logger.LogInfo("订单 " + id + " 创建成功");` - * ✅ `_logger.LogInformation("订单 {OrderId} 创建成功", id);` -* **脱敏**:严禁打印密码、密钥、支付凭证等敏感信息。 - -## 7. 依赖注入 (DI) -* **构造函数注入**:统一使用构造函数注入。 -* **禁止项**: - * ❌ 禁止使用 `[Inject]` 属性注入。 - * ❌ 禁止使用 `ServiceLocator` (服务定位器模式)。 - * ❌ 禁止在静态类中持有 ServiceProvider。 - -## 8. 数据访问规范 (重点执行) -### 8.1 Entity Framework Core (写/事务) -1. **无跟踪查询**:只读查询**必须**加 `.AsNoTracking()`。 -2. **杜绝 N+1**:严禁在 `foreach` 循环中查询数据库。必须使用 `.Include()`。 -3. **复杂查询**:关联表超过 2 层时,考虑使用 `.AsSplitQuery()`。 - -### 8.2 Dapper (读/报表) -1. **SQL 注入防御**:**严禁**拼接 SQL 字符串。必须使用参数化查询 (`@Param`)。 -2. **字段映射**:注意 PostgreSQL (`snake_case`) 与 C# (`PascalCase`) 的映射配置。 - -## 9. 多租户与 ID 策略 -* **ID 生成**: - * **强制**使用 **雪花算法 (Snowflake ID)**。 - * 类型:C# `long` <-> DB `bigint`。 - * **禁止**使用 UUID 或 自增 INT。 -* **租户隔离**: - * 所有业务表必须包含 `tenant_id`。 - * 写入时自动填充,读取时强制过滤。 - -## 10. API 设计与序列化 (前端兼容) -* **大整数处理**: - * 所有 `long` 类型 (Snowflake ID) 在 DTO 中**必须序列化为 string**。 - * 方案:DTO 属性加 `[JsonConverter(typeof(ToStringJsonConverter))]` 或全局配置。 -* **DTO 规范**: - * 输入:`XxxRequest` - * 输出:`XxxDto` - * **禁止** Controller 直接返回 Entity。 - -## 11. 模块化与复用 -* **核心模块划分**:Identity (身份), Tenancy (租户), Dictionary (字典), Storage (存储)。 -* **公共库 (Shared)**:通用工具类、扩展方法、常量定义必须放在 `Core/Shared` 项目中,避免重复造轮子。 - -## 12. 测试规范 -* **模式**:Arrange-Act-Assert (AAA)。 -* **工具**:xUnit + Moq + FluentAssertions。 -* **覆盖率**:核心 Domain 逻辑必须 100% 覆盖;Service 层 ≥ 70%。 - -## 13. Git 工作流 -* **提交格式 (Conventional Commits)**: - * `feat`: 新功能 - * `fix`: 修复 Bug - * `refactor`: 重构 - * `docs`: 文档 - * `style`: 格式调整 -* **分支规范**:`feature/功能名`,`bugfix/问题描述`。 - -## 14. 性能优化 (显式指令) -* **投影查询**:使用 `.Select(x => new Dto { ... })` 只查询需要的字段,减少 I/O。 -* **缓存策略**:Cache-Aside 模式。数据更新后必须立即失效缓存。 -* **批量操作**: - * EF Core 10:使用 `ExecuteUpdateAsync` / `ExecuteDeleteAsync`。 - * Dapper:使用 `ExecuteAsync` 进行批量插入。 - -## 15. 安全规范 -* **SQL 注入**:已在第 8 条强制参数化。 -* **身份认证**:Admin 端使用 JWT + RBAC;小程序端使用 Session/Token。 -* **密码存储**:必须使用 PBKDF2 或 BCrypt 加盐哈希。 - -## 16. 绝对禁止事项 (AI 自检清单) -**生成代码前,请自查是否违反以下红线:** -1. [ ] **SQL 注入**:是否拼接了 SQL 字符串? -2. [ ] **架构违规**:是否在 Controller/Domain 中使用了 DbContext? -3. [ ] **数据泄露**:是否返回了 Entity 或打印了密码? -4. [ ] **同步阻塞**:是否使用了 `.Result` 或 `.Wait()`? -5. [ ] **性能陷阱**:是否在循环中查询了数据库 (N+1)? -6. [ ] **精度丢失**:Long 类型的 ID 是否转为了 String? -7. [ ] **配置硬编码**:是否直接写死了连接串或密钥? -8. [ ] **文档注释**:是否给所有 `public` 类/方法/属性添加了 ``? -9. [ ] **逻辑注释**:是否在每个空行分隔的逻辑块上方添加了说明注释? - -## 17. 现代语法范式 (.NET 10 / C# 14) -> **原则**:拥抱新特性以减少样板代码,但严禁牺牲可读性。 - -1. **主构造函数 (Primary Constructor)**: - * **强制**:依赖注入场景必须使用 `class Service(IDep dep) { }`。 - * **禁止**:在主构造函数类中显式定义 `private readonly` 字段来承接参数(直接使用参数即可)。 -2. **对象初始化与不可变性**: - * **DTO/Command**:必须使用 `record` 类型。 - * **属性定义**:默认使用 `init`;必填项加 `required`;逻辑变更使用 `with` 表达式。 -3. **集合表达式**: - * **统一**:使用 `[]` 初始化集合。 - * **拼接**:使用 `[..array1, ..array2]` 替代 `Concat`。 -4. **模式匹配 (Pattern Matching)**: - * **替代**:严禁复杂的 `if-else if` 链,强制使用 `switch` 表达式。 - * **判空**:使用 `is not null` 替代 `!= null`。 -5. **极简语法糖**: - * **Field 关键字**:属性后备字段必须使用 `field` 关键字(如 `set => field = value.Trim();`)。 - * **空值赋值**:使用 `?.=` 简化判空赋值逻辑。 - * **字符串**:多行文本强制用 `"""` (Raw String Literal)。 - -## 18. 极致性能规约 (High Performance) -> **原则**:默认编写“零分配 (Zero-Allocation)”代码,热点路径拒绝 GC 压力。 - -1. **内存管理**: - * **字符串处理**:API 参数解析层**强制**使用 `ReadOnlySpan`,严禁使用 `Substring`。 - * **数组分配**:小块内存 (<1KB) 使用 `stackalloc`;大块内存 (>1KB) 必须使用 `ArrayPool.Shared`。 -2. **并发模型**: - * **无锁编程**:进程内生产者-消费者模型**必须**使用 `System.Threading.Channels`,严禁使用 `lock` 或 `BlockingCollection`。 - * **并行控制**:I/O 密集型并发必须使用 `Parallel.ForEachAsync` 并配置 `MaxDegreeOfParallelism`。 - * **异步返回**:热点路径下,若可能同步完成,返回类型必须为 `ValueTask`。 -3. **序列化与查找**: - * **JSON**:**全线废弃** Newtonsoft.Json。必须使用 `System.Text.Json` 配合源生成器 (`[JsonSerializable]`) 以支持 NativeAOT。 - * **静态集合**:只读字典/集合**必须**使用 `FrozenDictionary` / `FrozenSet`。 - * **SIMD 搜索**:多字符匹配场景使用 `SearchValues` 替代 `IndexOfAny`。 -4. **缓存架构**: - * **统一入口**:必须使用 `HybridCache` (或类似多级缓存抽象),禁止直接操作 `IDistributedCache` 以避免缓存击穿。 - -## 19. 云原生架构规范 (Architecture) -> **原则**:默认零信任,默认分布式,默认可观测。 - -1. **容器与部署**: - * **基底镜像**:生产环境**强制**使用 Chiseled 镜像 (`runtime-deps:10.0-jammy-chiseled`),无 Shell、无 Root,最大化安全。 - * **健康检查**:必须包含 Liveness (存活) 和 Readiness (就绪) 探针。 -2. **服务间通信**: - * **同步调用**:内部微服务间**严禁**使用 REST/JSON,**必须**使用 gRPC (Protobuf)。 - * **契约管理**:Proto 文件必须作为单一事实来源 (Single Source of Truth) 统一管理。 -3. **数据一致性 (关键)**: - * **Outbox 模式**:领域事件发布**严禁**直接调用 MQ。必须在同一数据库事务中写入 `Outbox` 表,由独立 Worker 异步推送。 - * **幂等性**:所有消费者 (Consumer) 必须实现基于 `MessageId` 的幂等处理逻辑。 -4. **可观测性 (Observability)**: - * **OpenTelemetry**:严禁依赖特定厂商 SDK。必须统一输出 OTLP 标准格式 (Metrics/Logs/Traces)。 - * **关联ID**:确保 `TraceId` 在 HTTP Headers 和 MQ Metadata 中全程透传。 -5. **并发控制**: - * **分布式锁**:任何涉及跨实例的资源竞争(如库存扣减、定时任务),**必须**使用 Redis RedLock 或同等机制。 - ---- - - -# Working agreements -- 严格遵循上述技术栈和命名规范。 diff --git a/tests/TakeoutSaaS.Application.Tests/App/Tenants/Handlers/GetTenantsAnnouncementsQueryHandlerTests.cs b/tests/TakeoutSaaS.Application.Tests/App/Tenants/Handlers/GetTenantsAnnouncementsQueryHandlerTests.cs index 90a7cb5..a58c775 100644 --- a/tests/TakeoutSaaS.Application.Tests/App/Tenants/Handlers/GetTenantsAnnouncementsQueryHandlerTests.cs +++ b/tests/TakeoutSaaS.Application.Tests/App/Tenants/Handlers/GetTenantsAnnouncementsQueryHandlerTests.cs @@ -40,6 +40,7 @@ public sealed class GetTenantsAnnouncementsQueryHandlerTests announcementRepository .Setup(x => x.SearchAsync( It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), @@ -79,6 +80,7 @@ public sealed class GetTenantsAnnouncementsQueryHandlerTests // Assert announcementRepository.Verify(x => x.SearchAsync( 42, + query.Keyword, query.Status, query.AnnouncementType, query.IsActive, @@ -118,6 +120,7 @@ public sealed class GetTenantsAnnouncementsQueryHandlerTests announcementRepository .Setup(x => x.SearchAsync( It.IsAny(), + It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), diff --git a/tests/TakeoutSaaS.Integration.Tests/App/Tenants/AnnouncementRegressionTests.cs b/tests/TakeoutSaaS.Integration.Tests/App/Tenants/AnnouncementRegressionTests.cs index ae06d55..f8922cb 100644 --- a/tests/TakeoutSaaS.Integration.Tests/App/Tenants/AnnouncementRegressionTests.cs +++ b/tests/TakeoutSaaS.Integration.Tests/App/Tenants/AnnouncementRegressionTests.cs @@ -30,6 +30,7 @@ public sealed class AnnouncementRegressionTests // Act var results = await repository.SearchAsync( tenantId: 600, + keyword: null, status: null, type: null, isActive: true, diff --git a/tests/TakeoutSaaS.Integration.Tests/App/Tenants/TenantAnnouncementRepositoryScopeTests.cs b/tests/TakeoutSaaS.Integration.Tests/App/Tenants/TenantAnnouncementRepositoryScopeTests.cs index 3f88728..e5e71a8 100644 --- a/tests/TakeoutSaaS.Integration.Tests/App/Tenants/TenantAnnouncementRepositoryScopeTests.cs +++ b/tests/TakeoutSaaS.Integration.Tests/App/Tenants/TenantAnnouncementRepositoryScopeTests.cs @@ -27,6 +27,7 @@ public sealed class TenantAnnouncementRepositoryScopeTests // Act var results = await repository.SearchAsync( tenantId: 800, + keyword: null, status: null, type: null, isActive: null,