# 代码规范

> **版本：** v1.0  
> **创建日期：** 2025-10-10  
> **适用范围：** 前端（React/TypeScript）+ 后端（Node.js/TypeScript）

---

## 📋 目录

1. [通用规范](#通用规范)
2. [TypeScript规范](#typescript规范)
3. [React规范](#react规范)
4. [Node.js后端规范](#nodejs后端规范)
5. [命名规范](#命名规范)
6. [注释规范](#注释规范)
7. [Git提交规范](#git提交规范)

---

## 🌟 平台能力使用规范（2025-11-16 新增）

> **⭐ 重要提示**：平台已提供完整的基础设施服务  
> **详细规范**：[云原生开发规范](./08-云原生开发规范.md)  
> **详细文档**：[平台基础设施规划](../09-架构实施/04-平台基础设施规划.md)

### 必须复用的平台服务

**业务模块（ASL/AIA/PKB/DC等）禁止重复实现以下功能：**

| 服务 | 导入方式 | 用途 |
|------|---------|------|
| **存储服务** | `import { storage } from '@/common/storage'` | 文件上传下载 |
| **日志系统** | `import { logger } from '@/common/logging'` | 标准化日志 |
| **异步任务** | `import { jobQueue } from '@/common/jobs'` | 长时间任务 |
| **缓存服务** | `import { cache } from '@/common/cache'` | 分布式缓存 |
| **数据库** | `import { prisma } from '@/config/database'` | 数据库操作 |
| **LLM能力** | `import { LLMFactory } from '@/common/llm'` | LLM调用 |

---

### ✅ 正确示例：使用平台服务

```typescript
// backend/src/modules/asl/services/literatureService.ts
import { storage } from '@/common/storage'
import { logger } from '@/common/logging'
import { jobQueue } from '@/common/jobs'
import { cache } from '@/common/cache'
import { prisma } from '@/config/database'

export class LiteratureService {
  async uploadPDF(projectId: string, pdfBuffer: Buffer) {
    // 1. 使用平台存储服务
    const key = `asl/projects/${projectId}/pdfs/${Date.now()}.pdf`
    const url = await storage.upload(key, pdfBuffer)
    
    // 2. 使用平台日志系统
    logger.info('PDF uploaded', { projectId, url })
    
    // 3. 使用平台数据库
    const literature = await prisma.aslLiterature.create({
      data: { projectId, pdfUrl: url, pdfFileSize: pdfBuffer.length }
    })
    
    // 4. 使用平台缓存
    await cache.set(`literature:${literature.id}`, literature, 3600)
    
    return literature
  }
  
  async startScreening(projectId: string, literatureIds: string[]) {
    // 5. 使用平台异步任务（长时间任务必须异步）
    const job = await jobQueue.push('asl:screening', {
      projectId,
      literatureIds
    })
    
    logger.info('Screening job created', { jobId: job.id })
    return { jobId: job.id }  // 立即返回
  }
}
```

---

### ❌ 错误示例：重复实现平台能力

```typescript
// ❌ 错误：在业务模块中自己实现存储
// backend/src/modules/asl/storage/LocalStorage.ts  ← 不应该存在！
import fs from 'fs'

export class LocalStorage {
  async upload(file: Buffer) {
    await fs.writeFile('./uploads/file.pdf', file)  // ❌ 重复实现
    return '/uploads/file.pdf'
  }
}

// ❌ 错误：在业务模块中自己实现日志
// backend/src/modules/asl/logger/logger.ts  ← 不应该存在！
import winston from 'winston'

export const logger = winston.createLogger({...})  // ❌ 重复实现

// ❌ 错误：每次新建数据库连接
import { PrismaClient } from '@prisma/client'

export function getUser() {
  const prisma = new PrismaClient()  // ❌ 连接泄漏
  return prisma.user.findMany()
}
```

**为什么错误？**
- ❌ 重复代码，难以维护
- ❌ 不同模块实现不一致
- ❌ 无法统一切换环境（本地/云端）
- ❌ 浪费开发时间
- ❌ 云端部署会失败（Serverless限制）

---

### 文件上传规范

```typescript
// ✅ 正确：使用存储抽象层
const url = await storage.upload('asl/pdf/123.pdf', buffer)

// ❌ 错误：直接操作文件系统
fs.writeFileSync('./uploads/123.pdf', buffer)  // Serverless容器重启会丢失

// ❌ 错误：硬编码存储路径
const filePath = 'D:/uploads/123.pdf'  // Windows路径，Linux无法运行
```

---

### 异步任务规范

```typescript
// ✅ 正确：长时间任务（>10秒）必须异步处理
app.post('/screening/start', async (req, res) => {
  const job = await jobQueue.push('asl:screening', data)
  res.send({ jobId: job.id })  // 立即返回，不等待完成
})

// 查询进度
app.get('/screening/jobs/:id', async (req, res) => {
  const job = await jobQueue.getJob(req.params.id)
  res.send({ status: job.status, progress: job.progress })
})

// ❌ 错误：同步等待长时间任务
app.post('/screening/start', async (req, res) => {
  const results = await processAllLiteratures(data)  // 可能需要10分钟
  res.send({ results })  // Serverless 30秒超时！
})
```

---

### 数据库连接规范

```typescript
// ✅ 正确：使用全局Prisma实例
import { prisma } from '@/config/database'

export async function getUsers() {
  return await prisma.user.findMany()
}

// ❌ 错误：每次新建实例
export async function getUsers() {
  const prisma = new PrismaClient()  // 连接数耗尽！
  return await prisma.user.findMany()
}
```

---

### 日志规范

```typescript
// ✅ 正确：使用平台日志系统
import { logger } from '@/common/logging'

logger.info('Operation successful', { userId, action: 'upload' })
logger.error('Operation failed', { error: err.message, userId })

// ❌ 错误：使用console.log
console.log('Operation successful')  // 无法集中收集，难以查询

// ❌ 错误：写本地日志文件
fs.appendFileSync('./app.log', 'Operation successful')  // Serverless不支持
```

---

## 通用规范

### 代码风格
- ✅ 使用ESLint和Prettier统一代码风格
- ✅ 缩进：2个空格
- ✅ 字符串：优先使用单引号 `'`
- ✅ 行尾：不加分号（除非必要）
- ✅ 单行最大长度：100字符
- ✅ 使用尾随逗号（对象、数组）

### 文件组织
- ✅ 一个文件一个组件/类
- ✅ 相关文件放在同一目录
- ✅ 使用barrel exports（index.ts）
- ✅ 测试文件与源文件同目录

```
src/
├── components/
│   ├── Button/
│   │   ├── Button.tsx
│   │   ├── Button.test.tsx
│   │   ├── Button.styles.ts
│   │   └── index.ts          # export { Button } from './Button'
```

### 代码注释
- ✅ 复杂逻辑必须注释
- ✅ 公共API必须注释
- ✅ 避免无用注释
- ✅ 使用JSDoc格式

---

## TypeScript规范

### 类型定义

**✅ 推荐：**
```typescript
// 使用interface定义对象结构
interface User {
  id: string
  email: string
  name?: string
}

// 使用type定义联合类型
type Status = 'active' | 'inactive' | 'suspended'

// 使用enum定义常量集合
enum UserRole {
  USER = 'user',
  ADMIN = 'admin',
}
```

**❌ 避免：**
```typescript
// 不要使用any
function process(data: any) {  // ❌
  // ...
}

// 应该明确类型
function process(data: ProcessData) {  // ✅
  // ...
}
```

### 类型导入导出

```typescript
// types.ts
export interface Project {
  id: string
  name: string
  description: string
}

export type ProjectStatus = 'active' | 'archived'

// project.service.ts
import type { Project, ProjectStatus } from './types'
```

### 严格模式
```json
// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true
  }
}
```

---

## React规范

### 组件定义

**✅ 推荐：函数组件 + Hooks**
```tsx
import { useState } from 'react'

interface ButtonProps {
  label: string
  onClick: () => void
  variant?: 'primary' | 'secondary'
  disabled?: boolean
}

export function Button({ 
  label, 
  onClick, 
  variant = 'primary',
  disabled = false 
}: ButtonProps) {
  const [isLoading, setIsLoading] = useState(false)

  const handleClick = async () => {
    setIsLoading(true)
    try {
      await onClick()
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <button
      onClick={handleClick}
      disabled={disabled || isLoading}
      className={`btn btn-${variant}`}
    >
      {isLoading ? 'Loading...' : label}
    </button>
  )
}
```

**❌ 避免：类组件**
```tsx
// 除非有特殊需求，否则不使用类组件
class Button extends React.Component { ... }  // ❌
```

### Hooks规范

**✅ 推荐：自定义Hooks**
```typescript
// useProjects.ts
import { useState, useEffect } from 'react'
import { projectService } from '@/services'
import type { Project } from '@/types'

export function useProjects() {
  const [projects, setProjects] = useState<Project[]>([])
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState<Error | null>(null)

  useEffect(() => {
    loadProjects()
  }, [])

  const loadProjects = async () => {
    setLoading(true)
    setError(null)
    try {
      const data = await projectService.getProjects()
      setProjects(data)
    } catch (err) {
      setError(err as Error)
    } finally {
      setLoading(false)
    }
  }

  return { projects, loading, error, reload: loadProjects }
}

// 使用
function ProjectList() {
  const { projects, loading, error } = useProjects()
  
  if (loading) return <Loading />
  if (error) return <Error message={error.message} />
  
  return (
    <ul>
      {projects.map(project => (
        <li key={project.id}>{project.name}</li>
      ))}
    </ul>
  )
}
```

### 组件组织

```tsx
// ✅ 推荐的组件结构
import { useState, useEffect, useMemo, useCallback } from 'react'
import { useNavigate } from 'react-router-dom'
import { SomeComponent } from '@/components'
import { useCustomHook } from '@/hooks'
import type { SomeType } from '@/types'

interface ComponentProps {
  // props定义
}

export function Component({ prop1, prop2 }: ComponentProps) {
  // 1. Hooks
  const navigate = useNavigate()
  const [state, setState] = useState()
  const { data } = useCustomHook()

  // 2. 派生状态（useMemo）
  const computedValue = useMemo(() => {
    return heavyComputation(data)
  }, [data])

  // 3. 事件处理（useCallback）
  const handleClick = useCallback(() => {
    // 处理逻辑
  }, [])

  // 4. Effects
  useEffect(() => {
    // 副作用
  }, [])

  // 5. 早期返回（Loading/Error）
  if (!data) return <Loading />

  // 6. 渲染
  return (
    <div>
      {/* JSX */}
    </div>
  )
}
```

### 条件渲染

**✅ 推荐：**
```tsx
// 简单条件：使用 &&
{isLoggedIn && <UserMenu />}

// if-else：使用三元运算符
{isLoggedIn ? <UserMenu /> : <LoginButton />}

// 多条件：提取为函数或组件
function renderContent() {
  if (loading) return <Loading />
  if (error) return <Error />
  if (data.length === 0) return <Empty />
  return <DataList data={data} />
}

return <div>{renderContent()}</div>
```

**❌ 避免：**
```tsx
// 避免复杂的嵌套三元运算符
{condition1 ? (
  condition2 ? <A /> : <B />
) : (
  condition3 ? <C /> : <D />
)}  // ❌ 难以理解
```

---

## Node.js后端规范

### 文件组织

```
backend/src/
├── routes/           # 路由定义
│   ├── auth.routes.ts
│   └── project.routes.ts
├── services/         # 业务逻辑
│   ├── auth.service.ts
│   └── project.service.ts
├── controllers/      # 控制器（可选）
├── models/           # Prisma模型
├── utils/            # 工具函数
├── config/           # 配置加载
├── types/            # 类型定义
└── server.ts         # 入口文件
```

### 路由定义

```typescript
// routes/project.routes.ts
import { FastifyInstance } from 'fastify'
import { projectService } from '../services/project.service'
import { authMiddleware } from '../middleware/auth'

export async function projectRoutes(server: FastifyInstance) {
  // 获取项目列表
  server.get(
    '/api/v1/projects',
    { 
      preHandler: [authMiddleware],
      schema: {
        querystring: {
          type: 'object',
          properties: {
            page: { type: 'number' },
            pageSize: { type: 'number' },
          },
        },
      },
    },
    async (request, reply) => {
      const { page = 1, pageSize = 20 } = request.query as any
      const userId = request.user.id

      const result = await projectService.getProjects(userId, {
        page,
        pageSize,
      })

      return reply.send({
        success: true,
        data: result,
      })
    }
  )

  // 创建项目
  server.post(
    '/api/v1/projects',
    {
      preHandler: [authMiddleware],
      schema: {
        body: {
          type: 'object',
          required: ['name', 'description'],
          properties: {
            name: { type: 'string', minLength: 1, maxLength: 200 },
            description: { type: 'string', minLength: 1 },
          },
        },
      },
    },
    async (request, reply) => {
      const userId = request.user.id
      const data = request.body as CreateProjectDto

      const project = await projectService.createProject(userId, data)

      return reply.code(201).send({
        success: true,
        data: project,
      })
    }
  )
}
```

### Service层

```typescript
// services/project.service.ts
import { prisma } from '../lib/prisma'
import type { CreateProjectDto, UpdateProjectDto } from '../types'

export class ProjectService {
  /**
   * 获取用户的项目列表
   */
  async getProjects(userId: string, options: PaginationOptions) {
    const { page, pageSize } = options

    const [items, total] = await Promise.all([
      prisma.project.findMany({
        where: { userId },
        skip: (page - 1) * pageSize,
        take: pageSize,
        orderBy: { createdAt: 'desc' },
      }),
      prisma.project.count({ where: { userId } }),
    ])

    return {
      items,
      pagination: {
        page,
        pageSize,
        total,
        totalPages: Math.ceil(total / pageSize),
        hasNext: page * pageSize < total,
        hasPrev: page > 1,
      },
    }
  }

  /**
   * 创建项目
   */
  async createProject(userId: string, data: CreateProjectDto) {
    return prisma.project.create({
      data: {
        userId,
        name: data.name,
        description: data.description,
      },
    })
  }

  /**
   * 更新项目
   */
  async updateProject(
    userId: string,
    projectId: string,
    data: UpdateProjectDto
  ) {
    // 验证权限
    const project = await prisma.project.findFirst({
      where: { id: projectId, userId },
    })

    if (!project) {
      throw new Error('Project not found or unauthorized')
    }

    return prisma.project.update({
      where: { id: projectId },
      data,
    })
  }

  /**
   * 删除项目
   */
  async deleteProject(userId: string, projectId: string) {
    // 验证权限
    const project = await prisma.project.findFirst({
      where: { id: projectId, userId },
    })

    if (!project) {
      throw new Error('Project not found or unauthorized')
    }

    await prisma.project.delete({
      where: { id: projectId },
    })
  }
}

export const projectService = new ProjectService()
```

### 错误处理

```typescript
// utils/errors.ts
export class AppError extends Error {
  constructor(
    public code: string,
    public message: string,
    public statusCode: number = 400,
    public details?: any
  ) {
    super(message)
    this.name = 'AppError'
  }
}

export class ValidationError extends AppError {
  constructor(message: string, details?: any) {
    super('VALIDATION_ERROR', message, 422, details)
  }
}

export class UnauthorizedError extends AppError {
  constructor(message: string = 'Unauthorized') {
    super('UNAUTHORIZED', message, 401)
  }
}

export class NotFoundError extends AppError {
  constructor(resource: string) {
    super('NOT_FOUND', `${resource} not found`, 404)
  }
}

// 使用
async function getProject(id: string) {
  const project = await prisma.project.findUnique({ where: { id } })
  
  if (!project) {
    throw new NotFoundError('Project')
  }
  
  return project
}
```

### 错误处理中间件

```typescript
// middleware/error-handler.ts
import { FastifyError, FastifyReply, FastifyRequest } from 'fastify'
import { AppError } from '../utils/errors'

export async function errorHandler(
  error: FastifyError | AppError,
  request: FastifyRequest,
  reply: FastifyReply
) {
  // 记录错误
  request.log.error(error)

  // 自定义错误
  if (error instanceof AppError) {
    return reply.code(error.statusCode).send({
      success: false,
      error: {
        code: error.code,
        message: error.message,
        details: error.details,
      },
      timestamp: new Date().toISOString(),
    })
  }

  // Prisma错误
  if (error.name === 'PrismaClientKnownRequestError') {
    // 处理Prisma特定错误
    return reply.code(400).send({
      success: false,
      error: {
        code: 'DATABASE_ERROR',
        message: 'Database operation failed',
      },
      timestamp: new Date().toISOString(),
    })
  }

  // 默认错误
  return reply.code(500).send({
    success: false,
    error: {
      code: 'INTERNAL_ERROR',
      message: 'Internal server error',
    },
    timestamp: new Date().toISOString(),
  })
}
```

---

## 命名规范

### 文件命名

| 类型 | 命名方式 | 示例 |
|------|---------|------|
| React组件 | PascalCase | `Button.tsx`, `ProjectList.tsx` |
| Hooks | camelCase + use前缀 | `useProjects.ts`, `useAuth.ts` |
| 工具函数 | camelCase | `formatDate.ts`, `api.ts` |
| 类型定义 | camelCase + .types | `user.types.ts`, `api.types.ts` |
| 常量 | camelCase + .constants | `routes.constants.ts` |
| 测试文件 | 同源文件 + .test | `Button.test.tsx` |

### 变量命名

```typescript
// ✅ 推荐
const userName = 'John'                    // camelCase
const USER_ROLE = 'admin'                  // 常量用UPPER_SNAKE_CASE
const isLoading = false                    // 布尔值用is/has/can前缀
const hasPermission = true
const canEdit = false

// ❌ 避免
const user_name = 'John'                   // 不用snake_case
const loading = false                      // 布尔值缺少is前缀
const x = 10                               // 无意义的变量名
```

### 函数命名

```typescript
// ✅ 推荐
function getUser() { }                     // get: 获取数据
function fetchProjects() { }               // fetch: 异步获取
function createProject() { }               // create: 创建
function updateProject() { }               // update: 更新
function deleteProject() { }               // delete: 删除
function handleClick() { }                 // handle: 事件处理
function validateEmail() { }               // validate: 验证
function formatDate() { }                  // format: 格式化

// ❌ 避免
function data() { }                        // 不清楚功能
function doSomething() { }                 // 太模糊
function process() { }                     // 不明确
```

### 组件命名

```typescript
// ✅ 推荐
<Button />                                 // 基础组件
<UserProfile />                            // 业务组件
<ProjectList />                            // 列表组件
<CreateProjectModal />                     // 弹窗组件

// ❌ 避免
<button />                                 // 不用小写
<user_profile />                           // 不用snake_case
<ListProjects />                           // 动词不要在前
```

---

## 注释规范

### JSDoc注释

```typescript
/**
 * 创建新项目
 * @param userId - 用户ID
 * @param data - 项目数据
 * @returns 创建的项目对象
 * @throws {ValidationError} 当数据验证失败时
 */
async function createProject(
  userId: string,
  data: CreateProjectDto
): Promise<Project> {
  // 实现...
}
```

### 代码注释

```typescript
// ✅ 好的注释：解释为什么
// 使用setTimeout避免阻塞UI渲染
setTimeout(() => {
  processLargeData()
}, 0)

// 等待Dify处理文档，最多重试10次
for (let i = 0; i < 10; i++) {
  const status = await checkStatus()
  if (status === 'completed') break
  await sleep(2000)
}

// ❌ 坏的注释：重复代码
// 设置loading为true
setLoading(true)

// 调用API
await api.getData()
```

---

## Git提交规范

### Commit Message格式

```
<type>(<scope>): <subject>

<body>

<footer>
```

### Type类型

| 类型 | 说明 |
|------|------|
| feat | 新功能 |
| fix | Bug修复 |
| docs | 文档更新 |
| style | 代码格式（不影响功能） |
| refactor | 重构 |
| perf | 性能优化 |
| test | 测试相关 |
| chore | 构建/工具变动 |

### 示例

```bash
# 好的提交
git commit -m "feat(auth): 实现用户登录功能"
git commit -m "fix(project): 修复项目删除权限问题"
git commit -m "docs(api): 更新API文档"
git commit -m "refactor(chat): 优化消息组件结构"

# 不好的提交
git commit -m "update"           # ❌ 太模糊
git commit -m "fix bug"          # ❌ 没有说明是什么bug
git commit -m "完成功能"          # ❌ 没有说明是什么功能
```

---

## ESLint配置

```javascript
// .eslintrc.js
module.exports = {
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
    'prettier',
  ],
  rules: {
    '@typescript-eslint/no-explicit-any': 'error',
    '@typescript-eslint/explicit-function-return-type': 'off',
    '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
    'react/react-in-jsx-scope': 'off',
    'react/prop-types': 'off',
  },
}
```

---

## Prettier配置

```json
{
  "semi": false,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "printWidth": 100,
  "arrowParens": "avoid"
}
```

---

## 代码Review检查清单

### 功能
- [ ] 功能是否完整实现
- [ ] 是否有遗漏的边界情况
- [ ] 错误处理是否完善

### 代码质量
- [ ] 代码是否易读易理解
- [ ] 是否有重复代码
- [ ] 函数是否过长（建议<50行）
- [ ] 是否遵守命名规范

### 性能
- [ ] 是否有性能问题
- [ ] 是否有不必要的重渲染
- [ ] 数据库查询是否优化

### 安全
- [ ] 是否有SQL注入风险
- [ ] 是否有XSS风险
- [ ] 权限验证是否完善

### 测试
- [ ] 是否有单元测试
- [ ] 测试覆盖率是否足够

---

**文档维护：** 规范更新需同步此文档  
**最后更新：** 2025-10-10  
**维护者：** 技术负责人














