Files
lingxi/docs/工程规范/02-代码风格.md
T
Stev_Wang 00ae12435e docs(specs): add AGENTS.md and engineering guidelines
项目尚无统一工程规范,AI 助手与研发各自发挥导致返工风险。建立
总纲 + 细则两层结构:AGENTS.md 只写硬约束与索引(14 条红线、技术栈
基线、仓库结构、命令契约),细则拆为 10 份文档覆盖 Git、代码风格、
格式化、API 与路由、数据库、测试与 CI、Review、日志、密钥、资产命名。

影响面:仅文档。约束力从本轮起对新建代码生效;与现状冲突处以文档为准。
回滚方式:git revert 本提交。
2026-11-02 15:09:51 +08:00

132 lines
7.1 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 02 · 代码风格
| 项 | 内容 |
| --- | --- |
| 文档版本 | V1.0(2026-11-02) |
| 依据 | 技术架构 §2.2 / §2.3 / §10.1 |
| 适用范围 | `apps/` `packages/` `prisma/` `infra/` 全部 TypeScript / JavaScript |
| 强制级别 | 强制(标注「建议」的除外) |
> 格式问题交给 Prettier(见 [03](03-格式化与Lint.md)),本文只约定**格式化管不到**的部分。
---
## 1 命名
| 对象 | 规则 | 示例 |
| --- | --- | --- |
| 目录 / 文件 | kebab-case | `ai-routes/`、`script-task.service.ts` |
| React 组件文件 | PascalCase.tsx | `ScriptCard.tsx` |
| 变量 / 函数 | camelCase | `nextCursor`、`resolveScope()` |
| 类型 / 接口 / 类 | PascalCase | `MediaTask`、`StorageProvider` |
| 常量(模块级不可变) | UPPER_SNAKE | `MAX_UPLOAD_MB` |
| 布尔 | `is/has/can/should` 前缀 | `isArchived`、`hasPermission` |
| 事件回调 | 属性 `onXxx`,处理器 `handleXxx` | `onSelect` / `handleSelect` |
| 异步动作 | 动词开头,返回 Promise | `createBatch()` |
| 数据库表 / 列 | snake_case(Prisma `@@map` / `@map`) | `media_tasks`、`customer_id` |
| 权限点常量 | `UPPER_SNAKE`,与库内 code 一致 | `SCRIPT_APPROVE` |
- `CS-01` 禁止拼音命名(`shangpin`);业务术语用 PRD 术语表英文(`briefCard`、`videoDirection`、`shot`)。
- `CS-02` 单字母变量只允许循环下标 `i/j` 与泛型 `T/K/V`。
- `CS-03` 缩写词按单词大小写:`s3Key`、`httpClient`、`ulid`,不写 `S3Key` / `HttpClient` 混排。
---
## 2 类型
- `CS-04` `strict: true`,禁止 `any`、隐式 `any`、`@ts-ignore`;确需忽略用 `@ts-expect-error` 并写原因。
- `CS-05` 禁止用 `as` 做类型断言绕过检查;只允许「收窄断言」(`as const`、`as unknown as X` 需注释理由)。
- `CS-06` 对外导出的函数 / 方法**必须显式标注返回类型**,内部函数可推断。
- `CS-07` 对象契约用 `interface`,联合 / 工具 / 映射类型用 `type`;二者不混用于同一实体。
- `CS-08` 业务枚举用 `as const` 对象 + 派生联合类型,**禁止 TS `enum`**;库内枚举由字典表提供(Prisma enum 仅用于永不扩展的封闭集合,需迁移评审说明)。
- `CS-09` 泛型参数 ≥ 2 个或含义不清时必须命名(`<TInput, TOutput>`),禁止全用 `T`。
- `CS-10` 禁止在业务代码中出现 `object`、`Function`、`{}` 作为类型;用具体形状或 `Record<string, unknown>`。
---
## 3 函数与结构
- `CS-11` 函数单一职责,建议 ≤ 50 行;超过则拆分。
- `CS-12` 参数 > 3 个必须用对象参数;可选参数不用位置占位。
- `CS-13` 嵌套 ≤ 3 层,用**早返回**替代 `else` 链。
- `CS-14` 禁止魔法值:数字 / 字符串常量抽到 `constants` 或读取系统配置(PRD R-26)。
- `CS-15` 单文件 ≤ 400 行;超出按职责拆分(Controller / Service / Repository 各自成文件)。
- `CS-16` 禁止循环依赖;跨包引用只从包 `index.ts` 出口导入。
---
## 4 错误处理
- `CS-17` 统一抛 `AppError(code, message, details?)`,`code` 取错误码表(见 [04](04-API与路由规范.md) §4);**禁止 `throw new Error('...')` 直接面向用户**。
- `CS-18` 禁止 `catch` 后空吞;要么处理、要么包装后重抛,并保留 `cause`。
- `CS-19` `async` 函数必须有 `await` 或显式 `void`;禁止 floating promise。
- `CS-20` 外部调用(AI / 存储 / ffmpeg)必须设置超时 + 分类重试;**内容违规类错误不重试**。
- `CS-21` 任务(BullMQ)处理器必须**幂等**:以 `taskItemId` 为幂等键,重复执行结果一致。
- `CS-22` 前端错误:路由级 ErrorBoundary + 请求层统一 toast,禁止页面内零散 `alert`。
---
## 5 注释与文档
- `CS-23` 注释解释**为什么**,不复述代码;业务规则必须引用 PRD 编号:`// R-04 查询必须带 customer_id`。
- `CS-24` 导出 API 写 JSDoc(用途、参数、错误码、副作用);内部实现按需。
- `CS-25` TODO 格式:`// TODO(@owner, 2026-12-01): 原因与后续`;禁止无主 TODO。
- `CS-26` 禁止提交注释掉的整段代码;历史查 git。
- `CS-27` 不写「本次改动」「新增」这类时间性注释,注释描述当下状态。
---
## 6 导入顺序
按组排序,组间空行,组内按字母序(由 ESLint `simple-import-sort` 自动修复):
```
1. Node 内置(node:fs)
2. 第三方(@nestjs/*、zod)
3. workspace 包(@lingxi/shared、@lingxi/ui)
4. 应用内绝对路径(~/ 或 @/)
5. 相对路径(./、../)
6. 样式 / 资源(*.css、*.svg?raw)
```
- `CS-28` 禁止 `import * as X` 整包导入(图标包、UI 包尤甚,会破坏 tree-shaking)。
- `CS-29` 图标只从 `packages/ui/icons.ts` 导入语义名(`import { IconScript } from '@lingxi/ui/icons'`),禁直接引 `remixicon` 具体路径。
- `CS-30` 禁 `export *` 聚合无审阅的公共 API;`index.ts` 显式列出导出。
---
## 7 React / 前端
- `CS-31` 只用函数组件 + Hooks;禁 class 组件、禁 `React.FC`(返回类型写 `ReactNode` / `JSX.Element`)。
- `CS-32` 服务端数据只用 TanStack Query(`useQuery` / `useMutation`);**禁止**在 `useEffect` 里手写 fetch + 自建缓存。
- `CS-33` 全局状态只放跨页共享且非服务端数据(Zustand);页面内状态用 `useState` / `useReducer`,禁止为省 props 上全局。
- `CS-34` 列表 `key` 用业务 ID,禁止 `index`。
- `CS-35` 组件文件导出一个主组件;子组件同文件时用内部函数,跨文件复用必须提为独立组件。
- `CS-36` 样式只用 Semi design token(`cssVar`)/ CSS Modules;**禁止散落十六进制色值**、禁止行内 style 做主题。
- `CS-37` 文案集中在 `packages/ui/locale`(zh-CN),组件内不写死中文用户可见文案(日志与注释除外)。
- `CS-38` 表单走 `packages/form-engine` 的 schema 渲染;**禁止**在业务页面手写动态字段映射(会绕过白名单校验)。
---
## 8 后端
- `CS-39` 分层职责:Controller 只解析入参 / 组装响应;Service 承载业务规则与状态机;Repository 是唯一访问 Prisma 的层。
- `CS-40` Repository 方法**必须接收 `ScopeContext`** 作为首个参数,禁止在 Repository 内部从全局取当前用户。
- `CS-41` 模块间只通过 Service 接口或领域事件通信;禁止跨模块注入 `PrismaService` 直接查表。
- `CS-42` 状态迁移集中在 Service 的状态机函数内,禁止 Controller 直接改 `status` 字段。
- `CS-43` 事务边界在 Service;事务内**禁止**外部 IO(AI 调用、HTTP、对象存储)。
- `CS-44` 队列任务只做「可重试的纯执行」,业务校验与成本预占在 API 层完成。
---
## 9 检查清单
- [ ] 命名符合 §1,无拼音、无缩写混排
- [ ] 无 `any` / `@ts-ignore` / 滥用 `as`
- [ ] 函数 ≤ 50 行、文件 ≤ 400 行、嵌套 ≤ 3 层
- [ ] 错误统一 `AppError`,无空 catch
- [ ] 队列任务幂等,外部调用有超时
- [ ] 图标 / UI 从统一出口导入
- [ ] 业务规则注释带 R-xx
- [ ] 无魔数、无硬编码阈值