Files
makemd/docs/ARCHIVE/01_Architecture/14_Code_Quality_Standards.md
wurenzhi 2b86715c09 refactor: 优化代码结构并修复类型问题
- 移除未使用的TabPane组件
- 修复类型定义和导入方式
- 优化mock数据源的环境变量判断逻辑
- 更新文档结构并归档旧文件
- 添加新的UI组件和Memo组件
- 调整API路径和响应处理
2026-03-23 12:41:35 +08:00

22 KiB
Raw Permalink Blame 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 安装依赖

# 核心依赖
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

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前端

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后端

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 安装依赖

npm install -D prettier eslint-config-prettier eslint-plugin-prettier

3.2 配置文件(.prettierrc.js

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 注释

/**
 * 获取用户信息
 * 
 * @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
}

单行注释

// 计算订单总金额
const totalAmount = calculateTotalAmount(items)

// TODO: 需要优化性能
const result = heavyComputation(data)

多行注释

/*
 * 订单处理流程:
 * 1. 验证订单数据
 * 2. 检查库存
 * 3. 创建支付记录
 * 4. 更新订单状态
 */

4.3 代码组织

文件结构

// 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 }

导入顺序

// 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 类型定义

// ✅ 正确:使用 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 泛型使用

// ✅ 正确:使用泛型提高代码复用性
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 类型守卫

// ✅ 正确:使用类型守卫
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 可选链和空值合并

// ✅ 正确:使用可选链
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
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 使用

// ✅ 正确:使用自定义 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 类型定义

// ✅ 正确:明确的 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 层

// ✅ 正确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 层

// ✅ 正确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 层

// ✅ 正确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 测试结构

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 避免不必要的渲染

// ✅ 正确:使用 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 避免内存泄漏

// ✅ 正确:清理副作用
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 输入验证

// ✅ 正确:使用 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 注入防护

// ✅ 正确:使用参数化查询
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 防护

// ✅ 正确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 脚本

{
  "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 配置

{
  "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. 参考资源