Files
makemd/docs/ARCHIVE/01_Architecture/14_Code_Quality_Standards.md

1022 lines
22 KiB
Markdown
Raw Normal View History

# 代码质量规范Code Quality Standards
> **模块**: 01_Architecture - 代码质量与 ESLint 规范
> **更新日期**: 2026-03-20
> **适用范围**: 全项目dashboard、server、node-agent
---
## 1. 概述Overview
### 1.1 目标
- **代码一致性**:统一代码风格和结构
- **类型安全**:通过 ESLint + TypeScript 确保类型正确
- **可维护性**:提高代码可读性和可维护性
- **自动化检查**:通过工具链自动检查代码质量
### 1.2 工具链
| 工具 | 用途 | 优先级 |
|------|------|--------|
| **ESLint** | 代码质量检查 | P0 |
| **Prettier** | 代码格式化 | P0 |
| **TypeScript** | 类型检查 | P0 |
| **Husky** | Git 钩子 | P1 |
| **lint-staged** | 提交前检查 | P1 |
---
## 2. ESLint 配置ESLint Configuration
### 2.1 安装依赖
```bash
# 核心依赖
npm install -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin
# 额外插件
npm install -D eslint-plugin-prettier eslint-config-prettier
npm install -D eslint-plugin-import eslint-plugin-boundaries
```
### 2.2 基础配置(.eslintrc.js
```javascript
module.exports = {
env: {
node: true,
es2021: true,
browser: true
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:import/recommended',
'plugin:import/typescript',
'plugin:prettier/recommended'
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
project: './tsconfig.json',
tsconfigRootDir: __dirname
},
plugins: [
'@typescript-eslint',
'import',
'boundaries'
],
settings: {
'import/resolver': {
typescript: {
alwaysTryTypes: true,
project: './tsconfig.json'
}
},
'boundaries/elements': [
{
type: 'controller',
pattern: 'src/api/controllers/*'
},
{
type: 'service',
pattern: 'src/services/*'
},
{
type: 'repository',
pattern: 'src/repositories/*'
},
{
type: 'model',
pattern: 'src/models/*'
},
{
type: 'dto',
pattern: 'src/dto/*'
}
]
},
rules: {
// TypeScript 核心规则
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/explicit-function-return-type': 'error',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-unused-vars': ['error', {
argsIgnorePattern: '^_',
varsIgnorePattern: '^_'
}],
'@typescript-eslint/no-floating-promises': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/strict-boolean-expressions': 'warn',
'@typescript-eslint/no-unnecessary-type-assertion': 'error',
'@typescript-eslint/consistent-type-imports': ['error', {
prefer: 'type-imports',
disallowTypeAnnotations: false
}],
'@typescript-eslint/no-non-null-assertion': 'warn',
// Import 规则
'import/order': ['error', {
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index'
],
'newlines-between': 'always',
alphabetize: {
order: 'asc',
caseInsensitive: true
}
}],
'import/no-duplicates': 'error',
'import/no-cycle': 'error',
'import/no-unresolved': 'off',
// Boundaries 规则(架构分层)
'boundaries/element-types': ['error', {
default: 'disallow',
rules: [
{
from: 'controller',
allow: ['service', 'dto']
},
{
from: 'service',
allow: ['repository', 'model', 'dto']
},
{
from: 'repository',
allow: ['model']
},
{
from: 'model',
allow: []
}
]
}],
// 通用规则
'no-console': ['warn', { allow: ['warn', 'error'] }],
'no-debugger': 'error',
'no-alert': 'error',
'no-var': 'error',
'prefer-const': 'error',
'prefer-arrow-callback': 'error',
'no-const-assign': 'error',
'no-dupe-keys': 'error',
'no-duplicate-case': 'error',
'no-empty': 'error',
'no-eval': 'error',
'no-implied-eval': 'error',
'no-irregular-whitespace': 'error',
'no-iterator': 'error',
'no-multi-spaces': 'error',
'no-new-wrappers': 'error',
'no-return-await': 'error',
'no-sequences': 'error',
'no-shadow-restricted-names': 'error',
'no-sparse-arrays': 'error',
'no-throw-literal': 'error',
'no-unreachable': 'error',
'no-unsafe-finally': 'error',
'no-useless-concat': 'error',
'no-useless-return': 'error',
'no-with': 'error',
'radix': 'error',
'yoda': 'error',
// Prettier 集成
'prettier/prettier': 'error'
},
ignorePatterns: [
'dist',
'node_modules',
'*.config.js',
'*.config.ts',
'.umi',
'.umi-production'
]
}
```
### 2.3 模块特定配置
#### Dashboard前端
```javascript
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:jsx-a11y/recommended',
'plugin:prettier/recommended'
],
plugins: ['react', 'react-hooks', 'jsx-a11y'],
settings: {
react: {
version: 'detect'
}
},
rules: {
'react/react-in-jsx-scope': 'off',
'react/prop-types': 'off',
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': 'warn'
}
}
```
#### Server后端
```javascript
module.exports = {
env: {
node: true,
es2021: true
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended'
],
rules: {
'no-console': ['warn', { allow: ['warn', 'error'] }]
}
}
```
---
## 3. Prettier 配置Prettier Configuration
### 3.1 安装依赖
```bash
npm install -D prettier eslint-config-prettier eslint-plugin-prettier
```
### 3.2 配置文件(.prettierrc.js
```javascript
module.exports = {
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: true,
singleQuote: true,
quoteProps: 'as-needed',
jsxSingleQuote: false,
trailingComma: 'es5',
bracketSpacing: true,
bracketSameLine: false,
arrowParens: 'always',
proseWrap: 'preserve',
htmlWhitespaceSensitivity: 'css',
endOfLine: 'lf',
embeddedLanguageFormatting: 'auto'
}
```
### 3.3 忽略文件(.prettierignore
```
node_modules
dist
build
coverage
*.min.js
*.min.css
package-lock.json
yarn.lock
pnpm-lock.yaml
.umi
.umi-production
```
---
## 4. 代码风格规范Code Style Standards
### 4.1 命名规范
| 类型 | 命名规则 | 示例 |
|------|----------|------|
| **变量/函数** | camelCase | `getUserData`, `userName` |
| **类/接口/类型** | PascalCase | `UserService`, `Product` |
| **常量** | UPPER_SNAKE_CASE | `MAX_RETRY_COUNT`, `API_BASE_URL` |
| **私有成员** | camelCase + `_` 前缀 | `_privateMethod`, `_internalState` |
| **枚举** | PascalCase | `Status`, `OrderStatus` |
| **文件名** | PascalCase (组件) / kebab-case (工具) | `UserList.tsx`, `api-utils.ts` |
### 4.2 注释规范
#### JSDoc 注释
```typescript
/**
* 获取用户信息
*
* @param userId - 用户ID
* @param includeProfile - 是否包含个人资料
* @returns 用户信息对象
* @throws {Error} 当用户不存在时抛出错误
*
* @example
* ```typescript
* const user = await getUser('123', true)
* console.log(user.name)
* ```
*/
async function getUser(
userId: string,
includeProfile: boolean = false
): Promise<User> {
const user = await userRepository.findById(userId)
if (!user) {
throw new Error(`User not found: ${userId}`)
}
if (includeProfile) {
return {
...user,
profile: await profileRepository.findByUserId(userId)
}
}
return user
}
```
#### 单行注释
```typescript
// 计算订单总金额
const totalAmount = calculateTotalAmount(items)
// TODO: 需要优化性能
const result = heavyComputation(data)
```
#### 多行注释
```typescript
/*
* 订单处理流程:
* 1. 验证订单数据
* 2. 检查库存
* 3. 创建支付记录
* 4. 更新订单状态
*/
```
### 4.3 代码组织
#### 文件结构
```typescript
// 1. 导入按顺序Node.js 内置、第三方库、内部模块)
import { promises as fs } from 'fs'
import axios from 'axios'
import { UserService } from '@/services/UserService'
import type { User } from '@/types/domain/User'
// 2. 类型定义
interface UserDTO {
id: string
name: string
email: string
}
// 3. 常量定义
const MAX_RETRY_COUNT = 3
const API_TIMEOUT = 5000
// 4. 类/函数定义
export class UserController {
// ...
}
// 5. 导出
export { UserDTO }
```
#### 导入顺序
```typescript
// 1. Node.js 内置模块
import { promises as fs } from 'fs'
import path from 'path'
// 2. 第三方库
import axios from 'axios'
import { z } from 'zod'
// 3. 内部模块(按路径层级)
import { UserService } from '@/services/UserService'
import { UserRepository } from '@/repositories/UserRepository'
import type { User } from '@/types/domain/User'
import { validateUser } from '@/utils/validators'
// 4. 相对路径导入
import { formatDate } from './utils/date'
```
---
## 5. TypeScript 特定规范TypeScript Specific Standards
### 5.1 类型定义
```typescript
// ✅ 正确:使用 interface 定义对象类型
interface User {
id: string
name: string
email: string
}
// ✅ 正确:使用 type 定义联合类型、交叉类型
type Status = 'active' | 'inactive' | 'pending'
type UserWithProfile = User & { profile: Profile }
// ❌ 错误:使用 type 定义简单对象类型
type User = {
id: string
name: string
email: string
}
```
### 5.2 泛型使用
```typescript
// ✅ 正确:使用泛型提高代码复用性
function fetchData<T>(url: string): Promise<T> {
return axios.get<T>(url).then(res => res.data)
}
// ✅ 正确:使用泛型约束
function processItem<T extends { id: string }>(item: T): void {
console.log(item.id)
}
// ❌ 错误:过度使用泛型
function process<T, U, V, W>(a: T, b: U, c: V, d: W): void {
// ...
}
```
### 5.3 类型守卫
```typescript
// ✅ 正确:使用类型守卫
function isProduct(data: unknown): data is Product {
return (
typeof data === 'object' &&
data !== null &&
'id' in data &&
'name' in data &&
'price' in data
)
}
function handleData(data: unknown) {
if (isProduct(data)) {
// TypeScript 知道 data 是 Product 类型
console.log(data.name)
}
}
```
### 5.4 可选链和空值合并
```typescript
// ✅ 正确:使用可选链
const userName = user?.profile?.name
// ✅ 正确:使用空值合并
const displayName = userName ?? 'Anonymous'
// ✅ 正确:结合使用
const value = obj?.prop ?? defaultValue
// ❌ 错误:过度使用可选链
const value = obj?.prop?.nested?.value?.toString()
```
---
## 6. React 特定规范React Specific Standards
### 6.1 组件定义
```typescript
// ✅ 正确:使用函数组件 + TypeScript
interface Props {
title: string
count: number
onAction: () => void
}
export function ProductCard({ title, count, onAction }: Props) {
return (
<div className="product-card">
<h2>{title}</h2>
<p>Count: {count}</p>
<button onClick={onAction}>Action</button>
</div>
)
}
// ✅ 正确:使用 React.FC不推荐但可用
export const ProductCard: React.FC<Props> = ({ title, count, onAction }) => {
return (
<div className="product-card">
<h2>{title}</h2>
<p>Count: {count}</p>
<button onClick={onAction}>Action</button>
</div>
)
}
```
### 6.2 Hooks 使用
```typescript
// ✅ 正确:使用自定义 Hook
function useUserData(userId: string) {
const [user, setUser] = useState<User | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<Error | null>(null)
useEffect(() => {
async function fetchUser() {
try {
setLoading(true)
const data = await userService.getUser(userId)
setUser(data)
} catch (err) {
setError(err as Error)
} finally {
setLoading(false)
}
}
fetchUser()
}, [userId])
return { user, loading, error }
}
// ✅ 正确:在组件中使用
function UserProfile({ userId }: { userId: string }) {
const { user, loading, error } = useUserData(userId)
if (loading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
if (!user) return <div>User not found</div>
return <div>{user.name}</div>
}
```
### 6.3 Props 类型定义
```typescript
// ✅ 正确:明确的 Props 类型
interface ProductListProps {
products: Product[]
loading?: boolean
onProductClick: (product: Product) => void
className?: string
}
export function ProductList({
products,
loading = false,
onProductClick,
className
}: ProductListProps) {
if (loading) return <div>Loading...</div>
return (
<div className={className}>
{products.map(product => (
<ProductCard
key={product.id}
product={product}
onClick={() => onProductClick(product)}
/>
))}
</div>
)
}
```
---
## 7. 后端特定规范Backend Specific Standards
### 7.1 Controller 层
```typescript
// ✅ 正确Controller 只负责请求/响应
export class UserController {
constructor(private readonly userService: UserService) {}
async getUser(req: Request, res: Response): Promise<void> {
try {
const { userId } = req.params
const user = await this.userService.getUser(userId)
res.json({ success: true, data: user })
} catch (error) {
res.status(500).json({
success: false,
error: error instanceof Error ? error.message : 'Unknown error'
})
}
}
}
```
### 7.2 Service 层
```typescript
// ✅ 正确Service 负责业务逻辑
export class UserService {
constructor(
private readonly userRepository: UserRepository,
private readonly profileRepository: ProfileRepository
) {}
async getUser(userId: string): Promise<UserWithProfile> {
const user = await this.userRepository.findById(userId)
if (!user) {
throw new Error(`User not found: ${userId}`)
}
const profile = await this.profileRepository.findByUserId(userId)
return {
...user,
profile
}
}
}
```
### 7.3 Repository 层
```typescript
// ✅ 正确Repository 只负责数据访问
export class UserRepository {
constructor(private readonly db: Knex) {}
async findById(id: string): Promise<User | null> {
const row = await this.db('users').where({ id }).first()
return row ? this.mapToEntity(row) : null
}
private mapToEntity(row: any): User {
return {
id: row.id,
name: row.name,
email: row.email,
createdAt: new Date(row.created_at),
updatedAt: new Date(row.updated_at)
}
}
}
```
---
## 8. 测试规范Testing Standards
### 8.1 测试文件命名
```
src/
services/
UserService.ts
UserService.test.ts # 单元测试
UserService.integration.test.ts # 集成测试
```
### 8.2 测试结构
```typescript
describe('UserService', () => {
let userService: UserService
let mockUserRepository: jest.Mocked<UserRepository>
beforeEach(() => {
mockUserRepository = {
findById: jest.fn(),
create: jest.fn(),
update: jest.fn(),
delete: jest.fn()
} as any
userService = new UserService(mockUserRepository)
})
describe('getUser', () => {
it('should return user when user exists', async () => {
const mockUser = {
id: '123',
name: 'John Doe',
email: 'john@example.com'
}
mockUserRepository.findById.mockResolvedValue(mockUser)
const result = await userService.getUser('123')
expect(result).toEqual(mockUser)
expect(mockUserRepository.findById).toHaveBeenCalledWith('123')
})
it('should throw error when user does not exist', async () => {
mockUserRepository.findById.mockResolvedValue(null)
await expect(userService.getUser('123')).rejects.toThrow('User not found: 123')
})
})
})
```
---
## 9. 性能优化规范Performance Standards
### 9.1 避免不必要的渲染
```typescript
// ✅ 正确:使用 React.memo
export const ProductCard = React.memo(function ProductCard({ product }: { product: Product }) {
return <div>{product.name}</div>
})
// ✅ 正确:使用 useMemo
function ProductList({ products }: { products: Product[] }) {
const sortedProducts = useMemo(
() => [...products].sort((a, b) => a.name.localeCompare(b.name)),
[products]
)
return (
<div>
{sortedProducts.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
)
}
// ✅ 正确:使用 useCallback
function ProductList({ products, onProductClick }: { products: Product[], onProductClick: (product: Product) => void }) {
const handleClick = useCallback(
(product: Product) => {
onProductClick(product)
},
[onProductClick]
)
return (
<div>
{products.map(product => (
<ProductCard key={product.id} product={product} onClick={handleClick} />
))}
</div>
)
}
```
### 9.2 避免内存泄漏
```typescript
// ✅ 正确:清理副作用
function useInterval(callback: () => void, delay: number) {
useEffect(() => {
const intervalId = setInterval(callback, delay)
return () => {
clearInterval(intervalId)
}
}, [callback, delay])
}
// ✅ 正确:取消请求
function useUserData(userId: string) {
const [user, setUser] = useState<User | null>(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
const abortController = new AbortController()
async function fetchUser() {
try {
setLoading(true)
const data = await userService.getUser(userId, { signal: abortController.signal })
setUser(data)
} catch (error) {
if (error.name !== 'AbortError') {
console.error(error)
}
} finally {
setLoading(false)
}
}
fetchUser()
return () => {
abortController.abort()
}
}, [userId])
return { user, loading }
}
```
---
## 10. 安全规范Security Standards
### 10.1 输入验证
```typescript
// ✅ 正确:使用 zod 验证输入
const CreateUserSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
password: z.string().min(8).regex(/[A-Z]/).regex(/[0-9]/)
})
async function createUser(data: unknown): Promise<User> {
const validatedData = CreateUserSchema.parse(data)
return userRepository.create(validatedData)
}
```
### 10.2 SQL 注入防护
```typescript
// ✅ 正确:使用参数化查询
async function getUserById(id: string): Promise<User | null> {
const row = await this.db('users').where({ id }).first()
return row ? this.mapToEntity(row) : null
}
// ❌ 错误:字符串拼接 SQL
async function getUserByIdBad(id: string): Promise<User | null> {
const row = await this.db.raw(`SELECT * FROM users WHERE id = '${id}'`)
return row ? this.mapToEntity(row) : null
}
```
### 10.3 XSS 防护
```typescript
// ✅ 正确React 自动转义
function UserDisplay({ name }: { name: string }) {
return <div>{name}</div>
}
// ✅ 正确:使用 DOMPurify 清理 HTML
import DOMPurify from 'dompurify'
function RichTextContent({ html }: { html: string }) {
const cleanHtml = DOMPurify.sanitize(html)
return <div dangerouslySetInnerHTML={{ __html: cleanHtml }} />
}
```
---
## 11. Git 提交规范Git Commit Standards
### 11.1 提交信息格式
```
<type>(<scope>): <subject>
<body>
<footer>
```
### 11.2 Type 类型
| Type | 说明 | 示例 |
|------|------|------|
| `feat` | 新功能 | `feat(user): add user registration` |
| `fix` | 修复 bug | `fix(auth): resolve login issue` |
| `docs` | 文档更新 | `docs(readme): update installation guide` |
| `style` | 代码格式 | `style: format code with prettier` |
| `refactor` | 重构 | `refactor(service): simplify user service` |
| `perf` | 性能优化 | `perf(api): optimize database query` |
| `test` | 测试 | `test(user): add user service tests` |
| `chore` | 构建/工具 | `chore(deps): update dependencies` |
### 11.3 示例
```
feat(user): add user registration feature
- Add user registration endpoint
- Implement email verification
- Add user profile creation
Closes #123
```
---
## 12. 自动化检查流程Automated Check Process
### 12.1 开发流程
```
写代码
ESLint 实时报错IDE 插件)
Prettier 自动格式化(保存时)
提交前 lint-staged 拦截
CI 执行完整检查
代码审查
合并
```
### 12.2 package.json 脚本
```json
{
"scripts": {
"lint": "eslint . --ext .ts,.tsx,.js,.jsx",
"lint:fix": "eslint . --ext .ts,.tsx,.js,.jsx --fix",
"format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\"",
"typecheck": "tsc --noEmit",
"test": "jest",
"test:coverage": "jest --coverage",
"validate": "npm run lint && npm run format:check && npm run typecheck && npm run test",
"prepare": "husky install"
}
}
```
### 12.3 lint-staged 配置
```json
{
"lint-staged": {
"*.{ts,tsx}": [
"eslint --fix",
"prettier --write"
],
"*.{js,jsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md}": [
"prettier --write"
]
}
}
```
---
## 13. 版本管理
| 版本 | 变更内容 | 日期 |
|------|----------|------|
| 1.0.0 | 初始版本 | 2026-03-20 |
---
## 14. 参考资源
- [ESLint 官方文档](https://eslint.org/docs/latest/)
- [Prettier 官方文档](https://prettier.io/docs/en/)
- [TypeScript ESLint 规则](https://typescript-eslint.io/rules/)
- [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript)
- [Google TypeScript Style Guide](https://google.github.io/styleguide/tsguide.html)