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:
@@ -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/` | 微前端共享 |
|
||||
|
||||
Reference in New Issue
Block a user