feat(aia): Complete AIA V2.0 with universal streaming capabilities
Major Changes: - Add StreamingService with OpenAI Compatible format - Upgrade Chat component V2 with Ant Design X integration - Implement AIA module with 12 intelligent agents - Update API routes to unified /api/v1 prefix - Update system documentation Backend (~1300 lines): - common/streaming: OpenAI Compatible adapter - modules/aia: 12 agents, conversation service, streaming integration - Update route versions (RVW, PKB to v1) Frontend (~3500 lines): - modules/aia: AgentHub + ChatWorkspace (100% prototype restoration) - shared/Chat: AIStreamChat, ThinkingBlock, useAIStream Hook - Update API endpoints to v1 Documentation: - AIA module status guide - Universal capabilities catalog - System overview updates - All module documentation sync Tested: Stream response verified, authentication working Status: AIA V2.0 core completed (85%)
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
# 浠g爜瑙勮寖
|
||||
|
||||
> **版本:** v1.0
|
||||
> **创建日期:** 2025-10-10
|
||||
> **适用范围:** 前端(React/TypeScript)+ 后端(Node.js/TypeScript)
|
||||
|
||||
> **鐗堟湰锛?* v1.0
|
||||
> **鍒涘缓鏃ユ湡锛?* 2025-10-10
|
||||
> **閫傜敤鑼冨洿锛?* 鍓嶇<E98D93>锛圧eact/TypeScript锛? 鍚庣<E98D9A>锛圢ode.js/TypeScript锛?
|
||||
---
|
||||
|
||||
## 馃搵 鐩<>綍
|
||||
@@ -18,29 +17,26 @@
|
||||
|
||||
---
|
||||
|
||||
## 🌟 平台能力使用规范(2025-11-16 新增)
|
||||
|
||||
> **⭐ 重要提示**:平台已提供完整的基础设施服务
|
||||
> **详细规范**:[云原生开发规范](./08-云原生开发规范.md)
|
||||
## 馃専 骞冲彴鑳藉姏浣跨敤瑙勮寖锛?025-11-16 鏂板<EFBFBD>锛?
|
||||
> **猸?閲嶈<E996B2>鎻愮ず**锛氬钩鍙板凡鎻愪緵瀹屾暣鐨勫熀纭€璁炬柦鏈嶅姟
|
||||
> **璇︾粏瑙勮寖**锛歔浜戝師鐢熷紑鍙戣<E98D99>鑼僝(./08-浜戝師鐢熷紑鍙戣<E98D99>鑼?md)
|
||||
> **璇︾粏鏂囨。**锛歔骞冲彴鍩虹<E98DA9>璁炬柦瑙勫垝](../09-鏋舵瀯瀹炴柦/04-骞冲彴鍩虹<E98DA9>璁炬柦瑙勫垝.md)
|
||||
|
||||
### 必须复用的平台服务
|
||||
### 蹇呴』澶嶇敤鐨勫钩鍙版湇鍔?
|
||||
**涓氬姟妯″潡锛圓SL/AIA/PKB/DC绛夛級绂佹<E7BB82>閲嶅<E996B2>瀹炵幇浠ヤ笅鍔熻兘锛?*
|
||||
|
||||
**业务模块(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'` | 数据库操作 |
|
||||
| **鏃ュ織绯荤粺** | `import { logger } from '@/common/logging'` | 鏍囧噯鍖栨棩蹇?|
|
||||
| **寮傛<EFBFBD>浠诲姟** | `import { jobQueue } from '@/common/jobs'` | 闀挎椂闂翠换鍔?|
|
||||
| **缂撳瓨鏈嶅姟** | `import { cache } from '@/common/cache'` | 鍒嗗竷寮忕紦瀛?|
|
||||
| **鏁版嵁搴?* | `import { prisma } from '@/config/database'` | 鏁版嵁搴撴搷浣?|
|
||||
| **LLM鑳藉姏** | `import { LLMFactory } from '@/common/llm'` | LLM璋冪敤 |
|
||||
|
||||
---
|
||||
|
||||
### ✅ 正确示例:使用平台服务
|
||||
|
||||
### 鉁?姝g‘绀轰緥锛氫娇鐢ㄥ钩鍙版湇鍔?
|
||||
```typescript
|
||||
// backend/src/modules/asl/services/literatureService.ts
|
||||
import { storage } from '@/common/storage'
|
||||
@@ -58,8 +54,7 @@ export class LiteratureService {
|
||||
// 2. 浣跨敤骞冲彴鏃ュ織绯荤粺
|
||||
logger.info('PDF uploaded', { projectId, url })
|
||||
|
||||
// 3. 使用平台数据库
|
||||
const literature = await prisma.aslLiterature.create({
|
||||
// 3. 浣跨敤骞冲彴鏁版嵁搴? const literature = await prisma.aslLiterature.create({
|
||||
data: { projectId, pdfUrl: url, pdfFileSize: pdfBuffer.length }
|
||||
})
|
||||
|
||||
@@ -70,8 +65,7 @@ export class LiteratureService {
|
||||
}
|
||||
|
||||
async startScreening(projectId: string, literatureIds: string[]) {
|
||||
// 5. 使用平台异步任务(长时间任务必须异步)
|
||||
const job = await jobQueue.push('asl:screening', {
|
||||
// 5. 浣跨敤骞冲彴寮傛<EFBFBD>浠诲姟锛堥暱鏃堕棿浠诲姟蹇呴』寮傛<EFBFBD>锛? const job = await jobQueue.push('asl:screening', {
|
||||
projectId,
|
||||
literatureIds
|
||||
})
|
||||
@@ -84,54 +78,44 @@ export class LiteratureService {
|
||||
|
||||
---
|
||||
|
||||
### ❌ 错误示例:重复实现平台能力
|
||||
|
||||
### 鉂?閿欒<E996BF>绀轰緥锛氶噸澶嶅疄鐜板钩鍙拌兘鍔?
|
||||
```typescript
|
||||
// ❌ 错误:在业务模块中自己实现存储
|
||||
// backend/src/modules/asl/storage/LocalStorage.ts ← 不应该存在!
|
||||
// 鉂?閿欒<E996BF>锛氬湪涓氬姟妯″潡涓<E6BDA1>嚜宸卞疄鐜板瓨鍌?// backend/src/modules/asl/storage/LocalStorage.ts 鈫?涓嶅簲璇ュ瓨鍦<E793A8>紒
|
||||
import fs from 'fs'
|
||||
|
||||
export class LocalStorage {
|
||||
async upload(file: Buffer) {
|
||||
await fs.writeFile('./uploads/file.pdf', file) // ❌ 重复实现
|
||||
await fs.writeFile('./uploads/file.pdf', file) // 鉂?閲嶅<E996B2>瀹炵幇
|
||||
return '/uploads/file.pdf'
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ 错误:在业务模块中自己实现日志
|
||||
// backend/src/modules/asl/logger/logger.ts ← 不应该存在!
|
||||
// 鉂?閿欒<E996BF>锛氬湪涓氬姟妯″潡涓<E6BDA1>嚜宸卞疄鐜版棩蹇?// backend/src/modules/asl/logger/logger.ts 鈫?涓嶅簲璇ュ瓨鍦<E793A8>紒
|
||||
import winston from 'winston'
|
||||
|
||||
export const logger = winston.createLogger({...}) // ❌ 重复实现
|
||||
export const logger = winston.createLogger({...}) // 鉂?閲嶅<E996B2>瀹炵幇
|
||||
|
||||
// ❌ 错误:每次新建数据库连接
|
||||
// 鉂?閿欒<E996BF>锛氭瘡娆℃柊寤烘暟鎹<E69A9F>簱杩炴帴
|
||||
import { PrismaClient } from '@prisma/client'
|
||||
|
||||
export function getUser() {
|
||||
const prisma = new PrismaClient() // ❌ 连接泄漏
|
||||
const prisma = new PrismaClient() // 鉂?杩炴帴娉勬紡
|
||||
return prisma.user.findMany()
|
||||
}
|
||||
```
|
||||
|
||||
**涓轰粈涔堥敊璇<E6958A>紵**
|
||||
- ❌ 重复代码,难以维护
|
||||
- ❌ 不同模块实现不一致
|
||||
- ❌ 无法统一切换环境(本地/云端)
|
||||
- ❌ 浪费开发时间
|
||||
- ❌ 云端部署会失败(Serverless限制)
|
||||
|
||||
- 鉂?閲嶅<E996B2>浠g爜锛岄毦浠ョ淮鎶?- 鉂?涓嶅悓妯″潡瀹炵幇涓嶄竴鑷?- 鉂?鏃犳硶缁熶竴鍒囨崲鐜<E5B4B2><E9909C>锛堟湰鍦?浜戠<E6B59C>锛?- 鉂?娴<>垂寮€鍙戞椂闂?- 鉂?浜戠<E6B59C>閮ㄧ讲浼氬け璐ワ紙Serverless闄愬埗锛?
|
||||
---
|
||||
|
||||
### 鏂囦欢涓婁紶瑙勮寖
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:使用存储抽象层
|
||||
// 鉁?姝g‘锛氫娇鐢ㄥ瓨鍌ㄦ娊璞″眰
|
||||
const url = await storage.upload('asl/pdf/123.pdf', buffer)
|
||||
|
||||
// ❌ 错误:直接操作文件系统
|
||||
fs.writeFileSync('./uploads/123.pdf', buffer) // Serverless容器重启会丢失
|
||||
|
||||
// ❌ 错误:硬编码存储路径
|
||||
// 鉂?閿欒<E996BF>锛氱洿鎺ユ搷浣滄枃浠剁郴缁?fs.writeFileSync('./uploads/123.pdf', buffer) // Serverless瀹瑰櫒閲嶅惎浼氫涪澶?
|
||||
// 鉂?閿欒<E996BF>锛氱‖缂栫爜瀛樺偍璺<E5818D>緞
|
||||
const filePath = 'D:/uploads/123.pdf' // Windows璺<73>緞锛孡inux鏃犳硶杩愯<E69DA9>
|
||||
```
|
||||
|
||||
@@ -140,7 +124,7 @@ const filePath = 'D:/uploads/123.pdf' // Windows路径,Linux无法运行
|
||||
### 寮傛<E5AFAE>浠诲姟瑙勮寖
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:长时间任务(>10秒)必须异步处理
|
||||
// 鉁?姝g‘锛氶暱鏃堕棿浠诲姟锛?10绉掞級蹇呴』寮傛<E5AFAE>澶勭悊
|
||||
app.post('/screening/start', async (req, res) => {
|
||||
const job = await jobQueue.push('asl:screening', data)
|
||||
res.send({ jobId: job.id }) // 绔嬪嵆杩斿洖锛屼笉绛夊緟瀹屾垚
|
||||
@@ -152,29 +136,26 @@ app.get('/screening/jobs/:id', async (req, res) => {
|
||||
res.send({ status: job.status, progress: job.progress })
|
||||
})
|
||||
|
||||
// ❌ 错误:同步等待长时间任务
|
||||
// 鉂?閿欒<E996BF>锛氬悓姝ョ瓑寰呴暱鏃堕棿浠诲姟
|
||||
app.post('/screening/start', async (req, res) => {
|
||||
const results = await processAllLiteratures(data) // 可能需要10分钟
|
||||
const results = await processAllLiteratures(data) // 鍙<EFBFBD>兘闇€瑕?0鍒嗛挓
|
||||
res.send({ results }) // Serverless 30绉掕秴鏃讹紒
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 数据库连接规范
|
||||
|
||||
### 鏁版嵁搴撹繛鎺ヨ<EFBFBD>鑼?
|
||||
```typescript
|
||||
// ✅ 正确:使用全局Prisma实例
|
||||
// 鉁?姝g‘锛氫娇鐢ㄥ叏灞€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()
|
||||
// 鉂?閿欒<E996BF>锛氭瘡娆℃柊寤哄疄渚?export async function getUsers() {
|
||||
const prisma = new PrismaClient() // 杩炴帴鏁拌€楀敖锛? return await prisma.user.findMany()
|
||||
}
|
||||
```
|
||||
|
||||
@@ -183,52 +164,44 @@ export async function getUsers() {
|
||||
### 鏃ュ織瑙勮寖
|
||||
|
||||
```typescript
|
||||
// ✅ 正确:使用平台日志系统
|
||||
import { logger } from '@/common/logging'
|
||||
// 鉁?姝g‘锛氫娇鐢ㄥ钩鍙版棩蹇楃郴缁?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不支持
|
||||
```
|
||||
// 鉂?閿欒<E996BF>锛氫娇鐢╟onsole.log
|
||||
console.log('Operation successful') // 鏃犳硶闆嗕腑鏀堕泦锛岄毦浠ユ煡璇?
|
||||
// 鉂?閿欒<E996BF>锛氬啓鏈<E59593>湴鏃ュ織鏂囦欢
|
||||
fs.appendFileSync('./app.log', 'Operation successful') // Serverless涓嶆敮鎸?```
|
||||
|
||||
---
|
||||
|
||||
## 閫氱敤瑙勮寖
|
||||
|
||||
### 浠g爜椋庢牸
|
||||
- ✅ 使用ESLint和Prettier统一代码风格
|
||||
- ✅ 缩进:2个空格
|
||||
- ✅ 字符串:优先使用单引号 `'`
|
||||
- ✅ 行尾:不加分号(除非必要)
|
||||
- ✅ 单行最大长度:100字符
|
||||
- ✅ 使用尾随逗号(对象、数组)
|
||||
- 鉁?浣跨敤ESLint鍜孭rettier缁熶竴浠g爜椋庢牸
|
||||
- 鉁?缂╄繘锛?涓<EFBFBD>┖鏍?- 鉁?瀛楃<EFBFBD>涓诧細浼樺厛浣跨敤鍗曞紩鍙?`'`
|
||||
- 鉁?琛屽熬锛氫笉鍔犲垎鍙凤紙闄ら潪蹇呰<EFBFBD>锛?- 鉁?鍗曡<EFBFBD>鏈€澶ч暱搴︼細100瀛楃<EFBFBD>
|
||||
- 鉁?浣跨敤灏鹃殢閫楀彿锛堝<EFBFBD>璞°€佹暟缁勶級
|
||||
|
||||
### 鏂囦欢缁勭粐
|
||||
- ✅ 一个文件一个组件/类
|
||||
- ✅ 相关文件放在同一目录
|
||||
- ✅ 使用barrel exports(index.ts)
|
||||
- ✅ 测试文件与源文件同目录
|
||||
|
||||
- 鉁?涓€涓<EFBFBD>枃浠朵竴涓<EFBFBD>粍浠?绫?- 鉁?鐩稿叧鏂囦欢鏀惧湪鍚屼竴鐩<EFBFBD>綍
|
||||
- 鉁?浣跨敤barrel exports锛坕ndex.ts锛?- 鉁?娴嬭瘯鏂囦欢涓庢簮鏂囦欢鍚岀洰褰?
|
||||
```
|
||||
src/
|
||||
鈹溾攢鈹€ components/
|
||||
│ ├── Button/
|
||||
│ │ ├── Button.tsx
|
||||
│ │ ├── Button.test.tsx
|
||||
│ │ ├── Button.styles.ts
|
||||
│ │ └── index.ts # export { Button } from './Button'
|
||||
鈹? 鈹溾攢鈹€ Button/
|
||||
鈹? 鈹? 鈹溾攢鈹€ Button.tsx
|
||||
鈹? 鈹? 鈹溾攢鈹€ Button.test.tsx
|
||||
鈹? 鈹? 鈹溾攢鈹€ Button.styles.ts
|
||||
鈹? 鈹? 鈹斺攢鈹€ index.ts # export { Button } from './Button'
|
||||
```
|
||||
|
||||
### 浠g爜娉ㄩ噴
|
||||
- ✅ 复杂逻辑必须注释
|
||||
- ✅ 公共API必须注释
|
||||
- ✅ 避免无用注释
|
||||
- ✅ 使用JSDoc格式
|
||||
- 鉁?澶嶆潅閫昏緫蹇呴』娉ㄩ噴
|
||||
- 鉁?鍏<>叡API蹇呴』娉ㄩ噴
|
||||
- 鉁?閬垮厤鏃犵敤娉ㄩ噴
|
||||
- 鉁?浣跨敤JSDoc鏍煎紡
|
||||
|
||||
---
|
||||
|
||||
@@ -236,7 +209,7 @@ src/
|
||||
|
||||
### 绫诲瀷瀹氫箟
|
||||
|
||||
**✅ 推荐:**
|
||||
**鉁?鎺ㄨ崘锛?*
|
||||
```typescript
|
||||
// 浣跨敤interface瀹氫箟瀵硅薄缁撴瀯
|
||||
interface User {
|
||||
@@ -255,16 +228,14 @@ enum UserRole {
|
||||
}
|
||||
```
|
||||
|
||||
**❌ 避免:**
|
||||
**鉂?閬垮厤锛?*
|
||||
```typescript
|
||||
// 涓嶈<E6B693>浣跨敤any
|
||||
function process(data: any) { // ❌
|
||||
// ...
|
||||
function process(data: any) { // 鉂? // ...
|
||||
}
|
||||
|
||||
// 搴旇<E690B4>鏄庣‘绫诲瀷
|
||||
function process(data: ProcessData) { // ✅
|
||||
// ...
|
||||
function process(data: ProcessData) { // 鉁? // ...
|
||||
}
|
||||
```
|
||||
|
||||
@@ -303,7 +274,7 @@ import type { Project, ProjectStatus } from './types'
|
||||
|
||||
### 缁勪欢瀹氫箟
|
||||
|
||||
**✅ 推荐:函数组件 + Hooks**
|
||||
**鉁?鎺ㄨ崘锛氬嚱鏁扮粍浠?+ Hooks**
|
||||
```tsx
|
||||
import { useState } from 'react'
|
||||
|
||||
@@ -343,15 +314,14 @@ export function Button({
|
||||
}
|
||||
```
|
||||
|
||||
**❌ 避免:类组件**
|
||||
**鉂?閬垮厤锛氱被缁勪欢**
|
||||
```tsx
|
||||
// 闄ら潪鏈夌壒娈婇渶姹傦紝鍚﹀垯涓嶄娇鐢ㄧ被缁勪欢
|
||||
class Button extends React.Component { ... } // ❌
|
||||
```
|
||||
class Button extends React.Component { ... } // 鉂?```
|
||||
|
||||
### Hooks瑙勮寖
|
||||
|
||||
**✅ 推荐:自定义Hooks**
|
||||
**鉁?鎺ㄨ崘锛氳嚜瀹氫箟Hooks**
|
||||
```typescript
|
||||
// useProjects.ts
|
||||
import { useState, useEffect } from 'react'
|
||||
@@ -403,8 +373,7 @@ function ProjectList() {
|
||||
### 缁勪欢缁勭粐
|
||||
|
||||
```tsx
|
||||
// ✅ 推荐的组件结构
|
||||
import { useState, useEffect, useMemo, useCallback } from 'react'
|
||||
// 鉁?鎺ㄨ崘鐨勭粍浠剁粨鏋?import { useState, useEffect, useMemo, useCallback } from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { SomeComponent } from '@/components'
|
||||
import { useCustomHook } from '@/hooks'
|
||||
@@ -420,23 +389,19 @@ export function Component({ prop1, prop2 }: ComponentProps) {
|
||||
const [state, setState] = useState()
|
||||
const { data } = useCustomHook()
|
||||
|
||||
// 2. 派生状态(useMemo)
|
||||
const computedValue = useMemo(() => {
|
||||
// 2. 娲剧敓鐘舵€侊紙useMemo锛? const computedValue = useMemo(() => {
|
||||
return heavyComputation(data)
|
||||
}, [data])
|
||||
|
||||
// 3. 事件处理(useCallback)
|
||||
const handleClick = useCallback(() => {
|
||||
// 3. 浜嬩欢澶勭悊锛坲seCallback锛? const handleClick = useCallback(() => {
|
||||
// 澶勭悊閫昏緫
|
||||
}, [])
|
||||
|
||||
// 4. Effects
|
||||
useEffect(() => {
|
||||
// 副作用
|
||||
}, [])
|
||||
// 鍓<EFBFBD>綔鐢? }, [])
|
||||
|
||||
// 5. 早期返回(Loading/Error)
|
||||
if (!data) return <Loading />
|
||||
// 5. 鏃╂湡杩斿洖锛圠oading/Error锛? if (!data) return <Loading />
|
||||
|
||||
// 6. 娓叉煋
|
||||
return (
|
||||
@@ -449,7 +414,7 @@ export function Component({ prop1, prop2 }: ComponentProps) {
|
||||
|
||||
### 鏉′欢娓叉煋
|
||||
|
||||
**✅ 推荐:**
|
||||
**鉁?鎺ㄨ崘锛?*
|
||||
```tsx
|
||||
// 绠€鍗曟潯浠讹細浣跨敤 &&
|
||||
{isLoggedIn && <UserMenu />}
|
||||
@@ -468,14 +433,14 @@ function renderContent() {
|
||||
return <div>{renderContent()}</div>
|
||||
```
|
||||
|
||||
**❌ 避免:**
|
||||
**鉂?閬垮厤锛?*
|
||||
```tsx
|
||||
// 閬垮厤澶嶆潅鐨勫祵濂椾笁鍏冭繍绠楃<E7BBA0>
|
||||
{condition1 ? (
|
||||
condition2 ? <A /> : <B />
|
||||
) : (
|
||||
condition3 ? <C /> : <D />
|
||||
)} // ❌ 难以理解
|
||||
)} // 鉂?闅句互鐞嗚В
|
||||
```
|
||||
|
||||
---
|
||||
@@ -487,11 +452,11 @@ return <div>{renderContent()}</div>
|
||||
```
|
||||
backend/src/
|
||||
鈹溾攢鈹€ routes/ # 璺<>敱瀹氫箟
|
||||
│ ├── auth.routes.ts
|
||||
│ └── project.routes.ts
|
||||
鈹? 鈹溾攢鈹€ auth.routes.ts
|
||||
鈹? 鈹斺攢鈹€ project.routes.ts
|
||||
鈹溾攢鈹€ services/ # 涓氬姟閫昏緫
|
||||
│ ├── auth.service.ts
|
||||
│ └── project.service.ts
|
||||
鈹? 鈹溾攢鈹€ auth.service.ts
|
||||
鈹? 鈹斺攢鈹€ project.service.ts
|
||||
鈹溾攢鈹€ controllers/ # 鎺у埗鍣<E59F97>紙鍙<E7B499>€夛級
|
||||
鈹溾攢鈹€ models/ # Prisma妯″瀷
|
||||
鈹溾攢鈹€ utils/ # 宸ュ叿鍑芥暟
|
||||
@@ -571,8 +536,7 @@ export async function projectRoutes(server: FastifyInstance) {
|
||||
}
|
||||
```
|
||||
|
||||
### Service层
|
||||
|
||||
### Service灞?
|
||||
```typescript
|
||||
// services/project.service.ts
|
||||
import { prisma } from '../lib/prisma'
|
||||
@@ -580,8 +544,7 @@ import type { CreateProjectDto, UpdateProjectDto } from '../types'
|
||||
|
||||
export class ProjectService {
|
||||
/**
|
||||
* 获取用户的项目列表
|
||||
*/
|
||||
* 鑾峰彇鐢ㄦ埛鐨勯」鐩<EFBFBD>垪琛? */
|
||||
async getProjects(userId: string, options: PaginationOptions) {
|
||||
const { page, pageSize } = options
|
||||
|
||||
@@ -712,8 +675,7 @@ async function getProject(id: string) {
|
||||
}
|
||||
```
|
||||
|
||||
### 错误处理中间件
|
||||
|
||||
### 閿欒<EFBFBD>澶勭悊涓<EFBFBD>棿浠?
|
||||
```typescript
|
||||
// middleware/error-handler.ts
|
||||
import { FastifyError, FastifyReply, FastifyRequest } from 'fastify'
|
||||
@@ -727,8 +689,7 @@ export async function errorHandler(
|
||||
// 璁板綍閿欒<E996BF>
|
||||
request.log.error(error)
|
||||
|
||||
// 自定义错误
|
||||
if (error instanceof AppError) {
|
||||
// 鑷<EFBFBD>畾涔夐敊璇? if (error instanceof AppError) {
|
||||
return reply.code(error.statusCode).send({
|
||||
success: false,
|
||||
error: {
|
||||
@@ -783,23 +744,22 @@ export async function errorHandler(
|
||||
### 鍙橀噺鍛藉悕
|
||||
|
||||
```typescript
|
||||
// ✅ 推荐
|
||||
// 鉁?鎺ㄨ崘
|
||||
const userName = 'John' // camelCase
|
||||
const USER_ROLE = 'admin' // 甯搁噺鐢║PPER_SNAKE_CASE
|
||||
const isLoading = false // 甯冨皵鍊肩敤is/has/can鍓嶇紑
|
||||
const hasPermission = true
|
||||
const canEdit = false
|
||||
|
||||
// ❌ 避免
|
||||
// 鉂?閬垮厤
|
||||
const user_name = 'John' // 涓嶇敤snake_case
|
||||
const loading = false // 甯冨皵鍊肩己灏慽s鍓嶇紑
|
||||
const x = 10 // 无意义的变量名
|
||||
```
|
||||
const x = 10 // 鏃犳剰涔夌殑鍙橀噺鍚?```
|
||||
|
||||
### 鍑芥暟鍛藉悕
|
||||
|
||||
```typescript
|
||||
// ✅ 推荐
|
||||
// 鉁?鎺ㄨ崘
|
||||
function getUser() { } // get: 鑾峰彇鏁版嵁
|
||||
function fetchProjects() { } // fetch: 寮傛<E5AFAE>鑾峰彇
|
||||
function createProject() { } // create: 鍒涘缓
|
||||
@@ -807,24 +767,20 @@ function updateProject() { } // update: 更新
|
||||
function deleteProject() { } // delete: 鍒犻櫎
|
||||
function handleClick() { } // handle: 浜嬩欢澶勭悊
|
||||
function validateEmail() { } // validate: 楠岃瘉
|
||||
function formatDate() { } // format: 格式化
|
||||
|
||||
// ❌ 避免
|
||||
function data() { } // 不清楚功能
|
||||
function doSomething() { } // 太模糊
|
||||
function process() { } // 不明确
|
||||
```
|
||||
function formatDate() { } // format: 鏍煎紡鍖?
|
||||
// 鉂?閬垮厤
|
||||
function data() { } // 涓嶆竻妤氬姛鑳?function doSomething() { } // 澶<>ā绯?function process() { } // 涓嶆槑纭?```
|
||||
|
||||
### 缁勪欢鍛藉悕
|
||||
|
||||
```typescript
|
||||
// ✅ 推荐
|
||||
// 鉁?鎺ㄨ崘
|
||||
<Button /> // 鍩虹<E98DA9>缁勪欢
|
||||
<UserProfile /> // 涓氬姟缁勪欢
|
||||
<ProjectList /> // 鍒楄〃缁勪欢
|
||||
<CreateProjectModal /> // 寮圭獥缁勪欢
|
||||
|
||||
// ❌ 避免
|
||||
// 鉂?閬垮厤
|
||||
<button /> // 涓嶇敤灏忓啓
|
||||
<user_profile /> // 涓嶇敤snake_case
|
||||
<ListProjects /> // 鍔ㄨ瘝涓嶈<E6B693>鍦ㄥ墠
|
||||
@@ -838,11 +794,9 @@ function process() { } // 不明确
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* 创建新项目
|
||||
* @param userId - 用户ID
|
||||
* 鍒涘缓鏂伴」鐩? * @param userId - 鐢ㄦ埛ID
|
||||
* @param data - 椤圭洰鏁版嵁
|
||||
* @returns 创建的项目对象
|
||||
* @throws {ValidationError} 当数据验证失败时
|
||||
* @returns 鍒涘缓鐨勯」鐩<EFBFBD><EFBFBD>璞? * @throws {ValidationError} 褰撴暟鎹<E69A9F>獙璇佸け璐ユ椂
|
||||
*/
|
||||
async function createProject(
|
||||
userId: string,
|
||||
@@ -855,21 +809,18 @@ async function createProject(
|
||||
### 浠g爜娉ㄩ噴
|
||||
|
||||
```typescript
|
||||
// ✅ 好的注释:解释为什么
|
||||
// 使用setTimeout避免阻塞UI渲染
|
||||
// 鉁?濂界殑娉ㄩ噴锛氳В閲婁负浠€涔?// 浣跨敤setTimeout閬垮厤闃诲<E99783>UI娓叉煋
|
||||
setTimeout(() => {
|
||||
processLargeData()
|
||||
}, 0)
|
||||
|
||||
// 等待Dify处理文档,最多重试10次
|
||||
for (let i = 0; i < 10; i++) {
|
||||
// 绛夊緟Dify澶勭悊鏂囨。锛屾渶澶氶噸璇?0娆?for (let i = 0; i < 10; i++) {
|
||||
const status = await checkStatus()
|
||||
if (status === 'completed') break
|
||||
await sleep(2000)
|
||||
}
|
||||
|
||||
// ❌ 坏的注释:重复代码
|
||||
// 设置loading为true
|
||||
// 鉂?鍧忕殑娉ㄩ噴锛氶噸澶嶄唬鐮?// 璁剧疆loading涓簍rue
|
||||
setLoading(true)
|
||||
|
||||
// 璋冪敤API
|
||||
@@ -894,10 +845,10 @@ await api.getData()
|
||||
|
||||
| 绫诲瀷 | 璇存槑 |
|
||||
|------|------|
|
||||
| feat | 新功能 |
|
||||
| feat | 鏂板姛鑳?|
|
||||
| fix | Bug淇<67><E6B787> |
|
||||
| docs | 鏂囨。鏇存柊 |
|
||||
| style | 代码格式(不影响功能) |
|
||||
| style | 浠g爜鏍煎紡锛堜笉褰卞搷鍔熻兘锛?|
|
||||
| refactor | 閲嶆瀯 |
|
||||
| perf | 鎬ц兘浼樺寲 |
|
||||
| test | 娴嬭瘯鐩稿叧 |
|
||||
@@ -912,11 +863,8 @@ 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 "完成功能" # ❌ 没有说明是什么功能
|
||||
```
|
||||
# 涓嶅ソ鐨勬彁浜?git commit -m "update" # 鉂?澶<>ā绯?git commit -m "fix bug" # 鉂?娌℃湁璇存槑鏄<E6A791>粈涔坆ug
|
||||
git commit -m "瀹屾垚鍔熻兘" # 鉂?娌℃湁璇存槑鏄<E6A791>粈涔堝姛鑳?```
|
||||
|
||||
---
|
||||
|
||||
@@ -959,36 +907,30 @@ module.exports = {
|
||||
|
||||
---
|
||||
|
||||
## 代码Review检查清单
|
||||
|
||||
## 浠g爜Review妫€鏌ユ竻鍗?
|
||||
### 鍔熻兘
|
||||
- [ ] 鍔熻兘鏄<E58598>惁瀹屾暣瀹炵幇
|
||||
- [ ] 鏄<>惁鏈夐仐婕忕殑杈圭晫鎯呭喌
|
||||
- [ ] 閿欒<E996BF>澶勭悊鏄<E6828A>惁瀹屽杽
|
||||
|
||||
### 浠g爜璐ㄩ噺
|
||||
- [ ] 代码是否易读易理解
|
||||
- [ ] 是否有重复代码
|
||||
- [ ] 函数是否过长(建议<50行)
|
||||
- [ ] 浠g爜鏄<EFBFBD>惁鏄撹<EFBFBD>鏄撶悊瑙?- [ ] 鏄<>惁鏈夐噸澶嶄唬鐮?- [ ] 鍑芥暟鏄<E69A9F>惁杩囬暱锛堝缓璁?50琛岋級
|
||||
- [ ] 鏄<>惁閬靛畧鍛藉悕瑙勮寖
|
||||
|
||||
### 鎬ц兘
|
||||
- [ ] 鏄<>惁鏈夋€ц兘闂<E58598><E99782>
|
||||
- [ ] 鏄<>惁鏈変笉蹇呰<E8B987>鐨勯噸娓叉煋
|
||||
- [ ] 数据库查询是否优化
|
||||
|
||||
- [ ] 鏁版嵁搴撴煡璇㈡槸鍚︿紭鍖?
|
||||
### 瀹夊叏
|
||||
- [ ] 鏄<>惁鏈塖QL娉ㄥ叆椋庨櫓
|
||||
- [ ] 鏄<>惁鏈塜SS椋庨櫓
|
||||
- [ ] 鏉冮檺楠岃瘉鏄<E79889>惁瀹屽杽
|
||||
|
||||
### 娴嬭瘯
|
||||
- [ ] 是否有单元测试
|
||||
- [ ] 测试覆盖率是否足够
|
||||
|
||||
- [ ] 鏄<EFBFBD>惁鏈夊崟鍏冩祴璇?- [ ] 娴嬭瘯瑕嗙洊鐜囨槸鍚﹁冻澶?
|
||||
---
|
||||
|
||||
**文档维护:** 规范更新需同步此文档
|
||||
**鏂囨。缁存姢锛?* 瑙勮寖鏇存柊闇€鍚屾<E98D9A>姝ゆ枃妗?
|
||||
**鏈€鍚庢洿鏂帮細** 2025-10-10
|
||||
**缁存姢鑰咃細** 鎶€鏈<E282AC>礋璐d汉
|
||||
|
||||
|
||||
Reference in New Issue
Block a user