Files
makemd/docs/01_Architecture/14_Code_Quality_Standards.md
wurenzhi 15ee1758f5 refactor: 重构项目结构并优化类型定义
- 移除extension模块,将功能迁移至node-agent
- 修复类型导出问题,使用export type明确类型导出
- 统一数据库连接方式,从直接导入改为使用config/database
- 更新文档中的项目结构描述
- 添加多个服务的实用方法,如getForecast、getBalances等
- 修复类型错误和TS1205警告
- 优化RedisService调用方式
- 添加新的实体类型定义
- 更新审计日志格式,统一字段命名
2026-03-21 15:04:06 +08:00

1022 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
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.
# 代码质量规范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)