fix(docs): 修复 project_rules.md P0 损坏并同步所有引用

- 根目录 project_rules.md 损坏(72 字节乱码),已删除
- 从 CICD 项目迁移完整版至 .trae/rules/project_rules.md(17881 字节)
- 更新 MIGRATION_GUIDE/README/coding-standards 引用至 .trae/rules/
- P0 紧急修复,影响所有 AI 工作流程
This commit is contained in:
SpecialX
2026-07-08 12:50:00 +08:00
parent 4629de1926
commit 3fbb97791d
5 changed files with 644 additions and 242 deletions

View File

@@ -5,7 +5,8 @@
> 状态:基线发布
> 适用范围Edu 微服务架构TS + Go + Python + protobuf
> 关联文档:
> - [项目规则](../../project_rules.md)
>
> - [项目规则](../../.trae/rules/project_rules.md)
> - [迁移指南](../../MIGRATION_GUIDE.md)
> - [Git 工作流](./git-workflow.md)
> - [架构总览](../architecture/001_architecture_overview.md)
@@ -85,7 +86,7 @@ import type { UserEntity } from "@/modules/user/domain/user.entity";
#### 2.4.1 模块组织
每个 NestJS 模块对应一个 DDD 聚合,结构见 [project_rules.md §4.1](../../project_rules.md#41-nestjs-业务微服务标准结构)。
每个 NestJS 模块对应一个 DDD 聚合,结构见 [project_rules.md §3.3](../../.trae/rules/project_rules.md#33-模块标准结构ddd)。
#### 2.4.2 装饰器规则
@@ -111,6 +112,7 @@ export class UserController {
```
**规则**
- Controller 必须使用 `@Controller(path)` 装饰器path 使用 kebab-case 复数
- Controller 类必须使用 `@RequirePermission()` 装饰器(类级默认权限)
- 每个 Handler 可选覆盖类级权限(更细粒度)
@@ -131,6 +133,7 @@ export class UserService {
```
**规则**
- 依赖通过构造函数注入,使用 `readonly` 修饰符
- 接口绑定在 Module 的 `providers` 中:`{ provide: "UserRepository", useClass: UserRepoImpl }`
- 禁止使用属性注入(`@Inject()` 属性装饰器)
@@ -152,6 +155,7 @@ export class UserModule {}
```
**规则**
- Module 类名 PascalCase + `Module` 后缀
- `exports` 仅暴露 Application Service不暴露 Repository
- 跨 Module 通信通过 exports 的 Service不直接访问对方 Repository
@@ -190,6 +194,7 @@ export class GetUserByIdHandler implements IQueryHandler<GetUserByIdQuery> {
```
**规则**
- Command 走写路径Command → Handler → Domain → Repository → MySQL + Outbox
- Query 走读路径Query → Handler → Read Model禁止查主库
- Command Handler 必须在事务内写 Outbox 表
@@ -207,7 +212,12 @@ export class UserEntity {
) {}
static create(props: UserCreateProps): UserEntity {
return new UserEntity(crypto.randomUUID(), props.email, props.name, new Date());
return new UserEntity(
crypto.randomUUID(),
props.email,
props.name,
new Date(),
);
}
rename(newName: string): UserRenamedEvent {
@@ -218,6 +228,7 @@ export class UserEntity {
```
**规则**
- Entity 构造函数私有,通过静态工厂方法创建
- Entity 字段私有,通过方法变更状态
- 状态变更方法返回领域事件,由 Application Service 发布
@@ -239,6 +250,7 @@ export class CreateUserDto {
```
**规则**
- DTO 类名 `Create[Entity]Dto` / `Update[Entity]Dto` / `[Entity]Response`
- DTO 字段使用 `readonly` 修饰
- 使用 `class-validator` 装饰器校验
@@ -264,13 +276,13 @@ export class CreateUserDto {
### 2.10 状态管理(沿用 CICD 5 层模型)
| 层级 | 场景 | 方案 |
|------|------|------|
| L1 URL | 可分享、可刷新的状态 | nuqs |
| L2 Server | 服务端数据 | TanStack Query |
| L3 Client Business | 客户端业务状态 | Zustand slice |
| L4 Global UI | 全局 UI 状态 | Zustand ui-store + ModalRoot |
| L5 Form | 表单状态 | react-hook-form + zodResolver |
| 层级 | 场景 | 方案 |
| ------------------ | -------------------- | ----------------------------- |
| L1 URL | 可分享、可刷新的状态 | nuqs |
| L2 Server | 服务端数据 | TanStack Query |
| L3 Client Business | 客户端业务状态 | Zustand slice |
| L4 Global UI | 全局 UI 状态 | Zustand ui-store + ModalRoot |
| L5 Form | 表单状态 | react-hook-form + zodResolver |
---
@@ -328,6 +340,7 @@ user, _ := h.userService.GetUser(ctx, id)
```
**规则**
- 错误必须显式处理,**禁止 `_ = err`**
- 使用 `errors.Is``errors.As` 判断错误类型,禁止字符串匹配
- 自定义错误类型使用 `fmt.Errorf("...: %w", err)` 包装
@@ -349,6 +362,7 @@ type UserService struct {
```
**规则**
- `context.Context` 作为函数第一个参数传递
- **禁止**将 context 存储在结构体字段中
- 超时/取消通过 context 传递,禁止使用 `time.Sleep` 等待
@@ -368,6 +382,7 @@ if err := g.Wait(); err != nil {
```
**规则**
- 优先使用 `errgroup` 管理并发 goroutine
- 禁止裸 `go func()` 不带 recover 和 context
- 共享状态使用 channel 或 `sync` 包,禁止使用 `sync.Mutex` 嵌套锁
@@ -388,6 +403,7 @@ func RegisterRoutes(r *gin.Engine, h *Handler, mw *Middleware) {
```
**规则**
- 路由分组按 API 版本(`/api/v1`
- 中间件链顺序Recovery → RequestID → Logger → RateLimit → Auth → RequirePermission
- Handler 函数签名固定:`func(c *gin.Context)`
@@ -404,6 +420,7 @@ logger.Info("user login", "user_id", userID, "ip", ip)
```
**规则**
- 使用标准库 `log/slog` 结构化日志
- 日志字段使用 kebab-case key
- 必须包含 `request_id` 用于链路追踪
@@ -459,6 +476,7 @@ def get_user(user_id):
```
**规则**
- 所有函数必须标注参数和返回值类型
- 使用 `from __future__ import annotations` 启用延迟注解求值
- 使用 `Optional[T]``T | None`Python 3.10+)标注可选类型
@@ -480,6 +498,7 @@ async def fetch_user(user_id: str) -> User:
```
**规则**
- I/O 操作HTTP、DB、文件必须使用 async/await
- 禁止在 async 函数中调用同步阻塞 I/O必须用 `asyncio.to_thread` 或 async 客户端
- CPU 密集任务用 `asyncio.to_thread` 或进程池
@@ -509,6 +528,7 @@ class UserResponse(BaseModel):
```
**规则**
- 请求模型命名 `[Action][Entity]Request`,响应模型命名 `[Entity]Response`
- 必须使用 `Field` 添加描述、约束
- 复杂校验使用 `@field_validator``@model_validator`
@@ -534,6 +554,7 @@ async def get_user(
```
**规则**
- 路由分组使用 `APIRouter`,按 API 版本组织
- 必须标注 `response_model`
- 权限校验通过 `Depends` 注入,每个 endpoint 显式调用 `require_permission`
@@ -557,6 +578,7 @@ settings = Settings()
```
**规则**
- 配置使用 `pydantic-settings``BaseSettings`
- 环境变量前缀按服务名(`INSIGHT_AI_`
- 禁止在业务代码中直接读取环境变量(`os.getenv`),统一通过 `settings`
@@ -567,39 +589,40 @@ settings = Settings()
### 5.1 命名通用规则
| 对象 | 风格 | 示例 |
|------|------|------|
| 目录 | kebab-case | `user-profile/` |
| 常量 | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` |
| 布尔值 | `is/has/can/should` 前缀 | `isVisible``is_active``hasPermission` |
| 类/接口/结构体 | PascalCase | `UserService``UserFetcher` |
| 服务名 | 小写单数 | `identity``teaching` |
| Kafka topic | 点分小写 | `edu.identity.user.created` |
| protobuf message | PascalCase | `UserCreatedEvent` |
| protobuf 字段 | snake_case | `user_id``created_at` |
| 对象 | 风格 | 示例 |
| ---------------- | ------------------------ | ----------------------------------------- |
| 目录 | kebab-case | `user-profile/` |
| 常量 | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT` |
| 布尔值 | `is/has/can/should` 前缀 | `isVisible``is_active``hasPermission` |
| 类/接口/结构体 | PascalCase | `UserService``UserFetcher` |
| 服务名 | 小写单数 | `identity``teaching` |
| Kafka topic | 点分小写 | `edu.identity.user.created` |
| protobuf message | PascalCase | `UserCreatedEvent` |
| protobuf 字段 | snake_case | `user_id``created_at` |
### 5.2 文件行数通用规则
| 文件类型 | 建议行数 | 硬性上限 |
|---------|---------|---------|
| 配置/常量/类型/proto | 无限制 | 无限制 |
| React 组件 | ≤ 500 | 800 |
| NestJS Controller/Service | ≤ 500 | 800 |
| Go handler/middleware | ≤ 400 | 600 |
| Python endpoint/service | ≤ 400 | 600 |
| 工具函数 | ≤ 40 | - |
| 自定义 Hook | ≤ 80 | - |
| **任何文件** | - | **1000超过必须拆分** |
| 文件类型 | 建议行数 | 硬性上限 |
| ------------------------- | -------- | ---------------------- |
| 配置/常量/类型/proto | 无限制 | 无限制 |
| React 组件 | ≤ 500 | 800 |
| NestJS Controller/Service | ≤ 500 | 800 |
| Go handler/middleware | ≤ 400 | 600 |
| Python endpoint/service | ≤ 400 | 600 |
| 工具函数 | ≤ 40 | - |
| 自定义 Hook | ≤ 80 | - |
| **任何文件** | - | **1000超过必须拆分** |
### 5.3 错误处理通用规则
| 语言 | 规则 |
|------|------|
| 语言 | 规则 |
| ---------- | -------------------------------------------------------------- |
| TypeScript | 错误通过抛出异常Application Service 必须捕获并转为结构化响应 |
| Go | 错误必须显式处理,禁止 `_ = err`,使用 `errors.Is/As` 判断类型 |
| Python | 使用异常层次结构,自定义异常继承 `Exception`,禁止裸 `except:` |
| Go | 错误必须显式处理,禁止 `_ = err`,使用 `errors.Is/As` 判断类型 |
| Python | 使用异常层次结构,自定义异常继承 `Exception`,禁止裸 `except:` |
**通用规则**
- 错误信息对内详细(含上下文、堆栈),对外脱敏(不泄露实现细节)
- 错误必须分类业务错误4xx、系统错误5xx、依赖错误502/503
- 错误必须记录日志,包含 request_id 用于链路追踪
@@ -607,13 +630,14 @@ settings = Settings()
### 5.4 日志通用规则
| 语言 | 工具 | 说明 |
|------|------|------|
| 语言 | 工具 | 说明 |
| ---------- | -------------------- | ---------------- |
| TypeScript | NestJS Logger + pino | 结构化 JSON 日志 |
| Go | log/slog | 标准库结构化日志 |
| Python | structlog 或 loguru | 结构化 JSON 日志 |
| Go | log/slog | 标准库结构化日志 |
| Python | structlog 或 loguru | 结构化 JSON 日志 |
**通用规则**
- 日志必须结构化JSON禁止纯文本
- 必须包含 `timestamp``level``service``request_id``trace_id`
- 日志级别DEBUG开发、INFO关键业务、WARN异常可恢复、ERROR系统错误
@@ -622,13 +646,14 @@ settings = Settings()
### 5.5 测试通用规则
| 语言 | 单元测试框架 | 覆盖率目标 |
|------|------------|-----------|
| TypeScript | Vitest + nestjs/testing | ≥ 80% |
| Go | 标准 testing 包 + testify | ≥ 80% |
| Python | pytest + pytest-asyncio | ≥ 80% |
| 语言 | 单元测试框架 | 覆盖率目标 |
| ---------- | ------------------------- | ---------- |
| TypeScript | Vitest + nestjs/testing | ≥ 80% |
| Go | 标准 testing 包 + testify | ≥ 80% |
| Python | pytest + pytest-asyncio | ≥ 80% |
**通用规则**
- 测试文件与源文件同目录或 `tests/` 子目录
- 命名:`*.test.ts` / `*_test.go` / `test_*.py`
- 测试描述说明预期行为("should disable button while loading"
@@ -714,6 +739,7 @@ enum UserStatus {
```
**规则**
- 字段编号禁止复用,删除字段必须 `reserved` 标记
- 枚举第一个值必须为 `*_UNSPECIFIED = 0`
- 时间使用 `google.protobuf.Timestamp`,不使用 string
@@ -770,12 +796,12 @@ buf generate
### 7.1 令牌分层(沿用 CICD 模型,迁移至微前端共享包)
| Layer | 位置 | 用途 |
|-------|------|------|
| Layer 1 Primitive | `packages/ui-tokens/primitive.css` | 原始色板/字号/间距/阴影,业务代码不直接引用 |
| Layer 2 Semantic | `packages/ui-tokens/semantic-light.css` + `semantic-dark.css` | 语义令牌,业务代码唯一引用入口 |
| 模块命名空间 | `packages/ui-tokens/lesson-preparation.css` | `--lp-*` 令牌,明暗双份 |
| Tailwind 暴露 | `packages/ui-tokens/tailwind-theme.css` | `@theme inline` 暴露为 `bg-*`/`text-*`/`font-*` 类 |
| Layer | 位置 | 用途 |
| ----------------- | ------------------------------------------------------------- | -------------------------------------------------- |
| Layer 1 Primitive | `packages/ui-tokens/primitive.css` | 原始色板/字号/间距/阴影,业务代码不直接引用 |
| Layer 2 Semantic | `packages/ui-tokens/semantic-light.css` + `semantic-dark.css` | 语义令牌,业务代码唯一引用入口 |
| 模块命名空间 | `packages/ui-tokens/lesson-preparation.css` | `--lp-*` 令牌,明暗双份 |
| Tailwind 暴露 | `packages/ui-tokens/tailwind-theme.css` | `@theme inline` 暴露为 `bg-*`/`text-*`/`font-*` 类 |
### 7.2 强制规则
@@ -846,11 +872,11 @@ buf generate
### 8.6 依赖扫描
| 语言 | 工具 |
|------|------|
| TS | `npm audit` + Snyk + Trivy |
| Go | `govulncheck` |
| Python | `pip-audit` + `safety` |
| 语言 | 工具 |
| ------ | -------------------------- |
| TS | `npm audit` + Snyk + Trivy |
| Go | `govulncheck` |
| Python | `pip-audit` + `safety` |
高危漏洞阻断合并。
@@ -873,12 +899,12 @@ buf generate
### 9.1 测试分层
| 层级 | TS | Go | Python | 覆盖率 |
|------|-----|-----|--------|--------|
| 单元测试 | Vitest | testing + testify | pytest | ≥ 80% |
| 集成测试 | Vitest + Testcontainers | testing + Testcontainers | pytest + Testcontainers | 关键流程 |
| E2E 测试 | Playwright | - | - | 核心业务路径 |
| 契约测试 | pact + buf | pact + buf | pact + buf | 服务间契约 |
| 层级 | TS | Go | Python | 覆盖率 |
| -------- | ----------------------- | ------------------------ | ----------------------- | ------------ |
| 单元测试 | Vitest | testing + testify | pytest | ≥ 80% |
| 集成测试 | Vitest + Testcontainers | testing + Testcontainers | pytest + Testcontainers | 关键流程 |
| E2E 测试 | Playwright | - | - | 核心业务路径 |
| 契约测试 | pact + buf | pact + buf | pact + buf | 服务间契约 |
### 9.2 测试命令
@@ -965,15 +991,15 @@ pytest tests/integration -v
## 附录:与 CICD 单应用规范的差异
| 项目 | CICD 单应用 | Edu 微服务 | 原因 |
|------|-----------|-----------|------|
| 项目结构 | 单 Next.js 应用 | 多语言 monorepo | 微服务拆分 |
| 数据获取层 | `modules/[module]/data-access.ts` | NestJS Repository + Domain Entity | DDD 分层 |
| 中间件 | `proxy.ts`Next.js 16 | Go Gin Gateway | 网关独立 |
| 通信 | 函数调用 | gRPC + Kafka | 跨进程通信 |
| 状态管理 | Zustand + Context + nuqs | 沿用 | 团队熟悉 |
| 环境变量校验 | `@t3-oss/env-nextjs` + Zod | TS Zod / Go viper / Python pydantic-settings | 多语言 |
| 行数限制 | 单一规范 | 按语言分档 | 各语言惯例 |
| 契约 | 无(内部函数调用) | protobuf + buf | 跨服务通信需要 |
| 事件驱动 | 无 | Kafka + Outbox | 微服务最终一致 |
| 设计令牌 | `src/app/styles/tokens/` | `packages/ui-tokens/` | 微前端共享 |
| 项目 | CICD 单应用 | Edu 微服务 | 原因 |
| ------------ | --------------------------------- | -------------------------------------------- | -------------- |
| 项目结构 | 单 Next.js 应用 | 多语言 monorepo | 微服务拆分 |
| 数据获取层 | `modules/[module]/data-access.ts` | NestJS Repository + Domain Entity | DDD 分层 |
| 中间件 | `proxy.ts`Next.js 16 | Go Gin Gateway | 网关独立 |
| 通信 | 函数调用 | gRPC + Kafka | 跨进程通信 |
| 状态管理 | Zustand + Context + nuqs | 沿用 | 团队熟悉 |
| 环境变量校验 | `@t3-oss/env-nextjs` + Zod | TS Zod / Go viper / Python pydantic-settings | 多语言 |
| 行数限制 | 单一规范 | 按语言分档 | 各语言惯例 |
| 契约 | 无(内部函数调用) | protobuf + buf | 跨服务通信需要 |
| 事件驱动 | 无 | Kafka + Outbox | 微服务最终一致 |
| 设计令牌 | `src/app/styles/tokens/` | `packages/ui-tokens/` | 微前端共享 |