# 代码质量规范（Code Quality Standards）

> **模块**: 01_Architecture - 代码质量与 ESLint 规范  
> **更新日期**: 2026-03-20  
> **适用范围**: 全项目（dashboard、server、extension、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)
