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%)
648 lines
18 KiB
Markdown
648 lines
18 KiB
Markdown
# 鈭穃<E988AD><E7A983>笔<EFBFBD><E7AC94>𤏸<EFBFBD><F0A48FB8>?
|
||
> **<2A><>﹝<EFBFBD><EFB99D>𧋦嚗?* V1.1
|
||
> **<2A>𥕦遣<F0A595A6>交<EFBFBD>嚗?* 2025-11-16
|
||
> **<2A><><EFBFBD>擧凒<E693A7>堆<EFBFBD>** 2025-12-13 <20><> **Postgres-Only <20>嗆<EFBFBD>閫<EFBFBD><E996AB><EFBFBD>啣<EFBFBD>**
|
||
> **<2A><>鍂撖寡情嚗?* <20><><EFBFBD>匧<EFBFBD><E58CA7>睲犖<E79DB2>?
|
||
> **撘箏<E69298><E7AE8F>改<EFBFBD>** <20>?敹<>◆<EFBFBD>萄<EFBFBD>
|
||
> **蝏湔擪<E6B994><E693AA><EFBFBD>** <20>嗆<EFBFBD><E59786>a<EFBFBD>
|
||
|
||
---
|
||
|
||
## <20><> <20><>﹝霂湔<E99C82>
|
||
|
||
<EFBFBD>祆<EFBFBD>獢<EFBFBD><EFBFBD>銋劐<EFBFBD><EFBFBD>毺<EFBFBD><EFBFBD>臬<EFBFBD>嚗𠄎erverless SAE + RDS + OSS嚗劐<E59A97><E58A90>?*隞<><E99A9E>閫<EFBFBD><E996AB>**嚗峕<E59A97><E5B395>劐<EFBFBD><E58A90>⊥芋<E28AA5>梹<EFBFBD>ASL<53><4C>IA<49><41>KB蝑㚁<E89D91>敹<EFBFBD>◆<EFBFBD>萄<EFBFBD><E89084>?
|
||
**<EFBFBD><EFBFBD>粉<EFBFBD>園𡢿**嚗?0 <20><><EFBFBD>
|
||
**璉<><E79289>仿<EFBFBD><E4BBBF>?*嚗𡁏<E59A97>甈∩誨<E288A9><E8AAA8><EFBFBD>鈭文<E988AD>
|
||
|
||
---
|
||
|
||
## <20><> <20>詨<EFBFBD><E8A9A8>笔<EFBFBD>嚗𡁜<E59A97><F0A1819C>典像<E585B8>啗<EFBFBD><E59597>?
|
||
> **潃?<3F>滩<EFBFBD><E6BBA9>鞟內嚗?025-11-16 <20>湔鰵嚗?*嚗𡁜像<F0A1819C>啣歇<E595A3>𣂷<EFBFBD>摰峕㟲<E5B395><E39FB2>抅蝖<E68A85>霈暹鴌<E69AB9>滚𦛚
|
||
> **霂衣<E99C82><E8A1A3><EFBFBD>﹝**嚗靀撟喳蝱<E596B3>箇<EFBFBD>霈暹鴌閫<E9B48C><E996AB>](../09-<2D>嗆<EFBFBD>摰墧鴌/04-撟喳蝱<E596B3>箇<EFBFBD>霈暹鴌閫<E9B48C><E996AB>.md)
|
||
|
||
### 撟喳蝱撌脫<E6928C>靘𤤿<E99D98><F0A4A4BF>滚𦛚
|
||
|
||
**銝𡁜𦛚璅∪<E79285>嚗㇁SL/AIA/PKB/DC蝑㚁<E89D91>摨磰砲憭滨鍂隞乩<E99A9E>撟喳蝱<E596B3>賢<EFBFBD>嚗𣬚<E59A97>甇a<E79487>憭滚<E686AD><E6BB9A>堆<EFBFBD>**
|
||
|
||
| <20>滚𦛚 | 撖澆<E69296><E6BE86>孵<EFBFBD> | <20>券<EFBFBD>?| <20><>﹝ |
|
||
|------|---------|------|------|
|
||
| **摮睃<E691AE><E79D83>滚𦛚** | `import { storage } from '@/common/storage'` | <20><>辣銝𠹺<E98A9D>銝贝蝸 | <20>?撟喳蝱蝥?|
|
||
| **<EFBFBD>亙<EFBFBD>蝟餌<EFBFBD>** | `import { logger } from '@/common/logging'` | <20><><EFBFBD><EFBFBD>𡝗𠯫敹?| <20>?撟喳蝱蝥?|
|
||
| **撘<>郊隞餃𦛚** | `import { jobQueue } from '@/common/jobs'` | <20>踵𧒄<E8B8B5>港遙<E6B8AF>?| <20>?撟喳蝱蝥?|
|
||
| **蝻枏<E89DBB><E69E8F>滚𦛚** | `import { cache } from '@/common/cache'` | <20><><EFBFBD>撘讐<E69298>摮?| <20>?撟喳蝱蝥?|
|
||
| **<EFBFBD><EFBFBD> <20>剔<EFBFBD>蝏凋<E89D8F>** | `import { CheckpointService } from '@/common/jobs'` | 隞餃𦛚<E9A483>剔<EFBFBD>蝞∠<E89D9E> | <20>?撟喳蝱蝥改<E89DA5><E694B9>堆<EFBFBD> |
|
||
| **<EFBFBD>唳旿摨?* | `import { prisma } from '@/config/database'` | <20>唳旿摨𤘪<E691A8>雿?| <20>?撟喳蝱蝥?|
|
||
| **LLM<4C>賢<EFBFBD>** | `import { LLMFactory } from '@/common/llm'` | LLM靚<4D>鍂 | <20>?撟喳蝱蝥?|
|
||
|
||
### 蝷箔<E89DB7>嚗𡁏迤蝖桐蝙<E6A190>典像<E585B8>唳<EFBFBD><E594B3>?
|
||
```typescript
|
||
// <20>?甇<>&嚗𡁶凒<F0A181B6>亙紡<E4BA99>亙像<E4BA99>唳<EFBFBD><E594B3>?import { storage } from '@/common/storage'
|
||
import { logger } from '@/common/logging'
|
||
import { jobQueue } from '@/common/jobs'
|
||
import { prisma } from '@/config/database'
|
||
|
||
export class LiteratureService {
|
||
async uploadPDF(projectId: string, pdfBuffer: Buffer) {
|
||
// 1. 雿輻鍂撟喳蝱摮睃<E691AE><E79D83>滚𦛚
|
||
const key = `asl/projects/${projectId}/pdfs/${Date.now()}.pdf`
|
||
const url = await storage.upload(key, pdfBuffer)
|
||
|
||
// 2. 雿輻鍂撟喳蝱<E596B3>亙<EFBFBD>蝟餌<E89D9F>
|
||
logger.info('PDF uploaded', { projectId, url })
|
||
|
||
// 3. 雿輻鍂撟喳蝱<E596B3>唳旿摨? const literature = await prisma.aslLiterature.create({
|
||
data: { projectId, pdfUrl: url }
|
||
})
|
||
|
||
return literature
|
||
}
|
||
}
|
||
```
|
||
|
||
### <20>?<3F>躰秤嚗𡁻<E59A97>憭滚<E686AD><E6BB9A>啣像<E595A3>啗<EFBFBD><E59597>?
|
||
```typescript
|
||
// <20>?<3F>躰秤嚗𡁜銁銝𡁜𦛚璅∪<E79285>銝剛䌊撌勗<E6928C><E58B97>啣<EFBFBD><E595A3>?// backend/src/modules/asl/storage/LocalStorage.ts <20>?銝滚<E98A9D>霂亙<E99C82><E4BA99>剁<EFBFBD>
|
||
export class LocalStorage {
|
||
async upload(file: Buffer) {
|
||
await fs.writeFile('./uploads/file.pdf', file) // <20>?<3F>滚<EFBFBD>摰䂿緵
|
||
}
|
||
}
|
||
|
||
// <20>?<3F>躰秤嚗𡁜銁銝𡁜𦛚璅∪<E79285>銝剛䌊撌勗<E6928C><E58B97>唳𠯫敹?// backend/src/modules/asl/logger/logger.ts <20>?銝滚<E98A9D>霂亙<E99C82><E4BA99>剁<EFBFBD>
|
||
export const logger = winston.createLogger({...}) // <20>?<3F>滚<EFBFBD>摰䂿緵
|
||
```
|
||
|
||
**<EFBFBD>笔<EFBFBD>**嚗?- <20>?<3F>滚<EFBFBD>隞<EFBFBD><E99A9E>嚗屸𠗕隞亦輕<E4BAA6>?- <20>?銝滚<E98A9D>璅∪<E79285>摰䂿緵銝滢<E98A9D><E6BBA2>?- <20>?<3F>䭾<EFBFBD>蝏煺<E89D8F><E785BA><EFBFBD>揢<EFBFBD>臬<EFBFBD>嚗<EFBFBD>𧋦<EFBFBD>?鈭𤑳垢嚗?- <20>?瘚芾晶撘<E699B6><E69298>烐𧒄<E78390>?
|
||
---
|
||
|
||
## <20>?<3F>刻<EFBFBD><E588BB>𡁏<EFBFBD>嚗㇄O嚗?
|
||
### 1. <20><>辣摮睃<E691AE> <20>?
|
||
```typescript
|
||
// <20>?甇<>&嚗帋蝙<E5B88B>典<EFBFBD><E585B8>冽𡂝鞊∪<E99E8A>
|
||
import { storage } from '@/common/storage/StorageFactory'
|
||
|
||
export async function uploadFile(file: Buffer, filename: string) {
|
||
const key = `asl/pdfs/${Date.now()}-${filename}`
|
||
const url = await storage.upload(key, file)
|
||
return url
|
||
}
|
||
|
||
// <20>?甇<>&嚗鍃xcel <20>湔𦻖隞𤾸<E99A9E>摮䁅圾<E48185>?import * as xlsx from 'xlsx'
|
||
|
||
export async function importExcel(buffer: Buffer) {
|
||
const workbook = xlsx.read(buffer, { type: 'buffer' }) // <20><><EFBFBD>閫<EFBFBD><E996AB>
|
||
const data = xlsx.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]])
|
||
return data
|
||
}
|
||
```
|
||
|
||
**<EFBFBD><EFBFBD>眏**嚗?- 摰孵膥<E5ADB5>滚鍳銝滢<E98A9D>銝W仃<EFBCB7><E4BB83>辣
|
||
- <20>砍𧑐撘<F0A79190><E69298>穃<EFBFBD><E7A983>煺漣<E785BA>臬<EFBFBD>隞<EFBFBD><E99A9E>銝<EFBFBD><E98A9D>?- <20>芸𢆡<E88AB8>寞旿<E5AF9E>臬<EFBFBD><E887AC>㗛<EFBFBD><E3979B><EFBFBD>揢摮睃<E691AE><E79D83>孵<EFBFBD>
|
||
|
||
---
|
||
|
||
### 2. <20>唳旿摨栞<E691A8><E6A09E>?<3F>?
|
||
```typescript
|
||
// <20>?甇<>&嚗帋蝙<E5B88B>典<EFBFBD>撅<EFBFBD> Prisma Client
|
||
import { prisma } from '@/config/database'
|
||
|
||
export async function createProject(data: any) {
|
||
return await prisma.aslScreeningProject.create({ data })
|
||
}
|
||
|
||
// <20>?甇<>&嚗𡁏鸌<F0A1818F>𤩺<EFBFBD>雿靝蝙<E99D9D>其<EFBFBD><E585B6>?export async function importLiteratures(literatures: any[]) {
|
||
return await prisma.$transaction(async (tx) => {
|
||
return await tx.aslLiterature.createMany({ data: literatures })
|
||
})
|
||
}
|
||
```
|
||
|
||
**<EFBFBD><EFBFBD>眏**嚗?- <20>典<EFBFBD>摰硺<E691B0>憭滨鍂餈墧𦻖
|
||
- <20>踹<EFBFBD>餈墧𦻖<E5A2A7>啗<EFBFBD>堒偷
|
||
- 鈭见𦛚靽肽<E99DBD><E882BD>唳旿銝<E697BF><E98A9D>湔<EFBFBD>?
|
||
---
|
||
|
||
### 3. <20>臬<EFBFBD><E887AC>㗛<EFBFBD><E3979B>滨蔭 <20>?
|
||
```typescript
|
||
// <20>?甇<>&嚗𡁶<E59A97>銝<EFBFBD><E98A9D>滨蔭蝞∠<E89D9E>
|
||
// backend/src/config/env.ts
|
||
export const config = {
|
||
llm: {
|
||
apiKey: process.env.LLM_API_KEY!,
|
||
baseUrl: process.env.LLM_BASE_URL!,
|
||
},
|
||
oss: {
|
||
region: process.env.OSS_REGION!,
|
||
bucket: process.env.OSS_BUCKET!,
|
||
},
|
||
database: {
|
||
url: process.env.DATABASE_URL!,
|
||
}
|
||
}
|
||
|
||
// <20>?甇<>&嚗帋蝙<E5B88B>券<EFBFBD>蝵桀笆鞊?import { config } from '@/config/env'
|
||
const apiKey = config.llm.apiKey
|
||
```
|
||
|
||
**<EFBFBD><EFBFBD>眏**嚗?- <20>滨蔭<E6BBA8><E894AD>葉蝞∠<E89D9E>
|
||
- 蝐餃<E89D90>摰匧<E691B0>
|
||
- 靘蹂<E99D98><E8B982><EFBFBD>揢<EFBFBD>臬<EFBFBD>
|
||
|
||
---
|
||
|
||
### 4. <20>踵𧒄<E8B8B5>港遙<E6B8AF>∪<EFBFBD><E288AA>?<3F>?
|
||
```typescript
|
||
// <20>?甇<>&嚗𡁜<E59A97>甇乩遙<E4B9A9>?+ 餈𥕦漲頧株砭
|
||
export async function startScreening(req, res) {
|
||
// 1. <20>𥕦遣隞餃𦛚霈啣<E99C88>
|
||
const task = await prisma.aslScreeningTask.create({
|
||
data: {
|
||
projectId: req.params.projectId,
|
||
status: 'pending',
|
||
totalItems: 100,
|
||
}
|
||
})
|
||
|
||
// 2. 蝡见朖餈𥪜<E9A488>隞餃𦛚ID
|
||
res.send({ success: true, taskId: task.id })
|
||
|
||
// 3. <20>𤾸蝱撘<E89DB1>郊<EFBFBD>扯<EFBFBD>嚗<EFBFBD><E59A97><EFBFBD>餃<EFBFBD>霂瑟<E99C82>嚗? processScreeningAsync(task.id).catch(err => {
|
||
console.error('蝑偦<E89D91>劐遙<E58A90>∪仃韐?', err)
|
||
})
|
||
}
|
||
|
||
// <20>滨垢頧株砭餈𥕦漲
|
||
export async function getTaskProgress(req, res) {
|
||
const task = await prisma.aslScreeningTask.findUnique({
|
||
where: { id: req.params.taskId }
|
||
})
|
||
|
||
res.send({
|
||
status: task.status,
|
||
progress: Math.round((task.completedItems / task.totalItems) * 100)
|
||
})
|
||
}
|
||
```
|
||
|
||
**<EFBFBD><EFBFBD>眏**嚗?- <20>踹<EFBFBD>霂瑟<E99C82>頞<EFBFBD>𧒄嚗𠄎AE暺䁅恕30蝘𡜐<E89D98>
|
||
- <20>冽<EFBFBD>雿㯄<E99BBF><E3AF84>游末
|
||
- <20>舀<EFBFBD><E88880>寥<EFBFBD>隞餃𦛚
|
||
|
||
**<EFBFBD>?摰峕㟲摰噼殿<E599BC><E6AEBF><EFBFBD>?*嚗?霂西<E99C82> [Postgres-Only撘<79>郊隞餃𦛚憭<F0A69B9A><E686AD><EFBFBD><EFBFBD><EFBFBD>](../02-<2D>𡁶鍂<F0A181B6>賢<EFBFBD>撅?Postgres-Only撘<79>郊隞餃𦛚憭<F0A69B9A><E686AD><EFBFBD><EFBFBD><EFBFBD>.md)嚗<>抅鈭𥟠C Tool C摰峕㟲摰噼殿嚗?
|
||
---
|
||
|
||
### 5. <20>亙<EFBFBD>颲枏枂 <20>?
|
||
```typescript
|
||
// <20>?甇<>&嚗帋蝙<E5B88B>?logger 颲枏枂<E69E8F>?stdout
|
||
import pino from 'pino'
|
||
|
||
const logger = pino({
|
||
level: process.env.LOG_LEVEL || 'info'
|
||
})
|
||
|
||
export async function uploadFile(req, res) {
|
||
logger.info({
|
||
userId: req.userId,
|
||
filename: req.file.filename
|
||
}, 'File uploaded')
|
||
|
||
// ... 銝𠹺<E98A9D><F0A0B9BA>餉<EFBFBD>
|
||
}
|
||
|
||
// <20>?甇<>&嚗𡁶<E59A97><F0A181B6><EFBFBD><EFBFBD><EFBFBD>亙<EFBFBD>
|
||
logger.error({
|
||
error: err.message,
|
||
stack: err.stack,
|
||
userId: req.userId
|
||
}, 'Upload failed')
|
||
```
|
||
|
||
**<EFBFBD><EFBFBD>眏**嚗?- SAE <20>芸𢆡<E88AB8><F0A286A1><EFBFBD> stdout <20>亙<EFBFBD>
|
||
- 蝏𤘪<E89D8F><F0A498AA>碶噶鈭擧䰻霂W<E99C82><EFBCB7>?- <20><>葉<EFBFBD>亦<EFBFBD>嚗䔶<E59A97>隡帋腺憭?
|
||
---
|
||
|
||
### 6. <20>躰秤憭<E7A7A4><E686AD> <20>?
|
||
```typescript
|
||
// <20>?甇<>&嚗𡁶<E59A97>銝<EFBFBD><E98A9D>躰秤憭<E7A7A4><E686AD>
|
||
export async function uploadPdf(req, res) {
|
||
try {
|
||
const file = await req.file()
|
||
const buffer = await file.toBuffer()
|
||
|
||
const url = await storage.upload(key, buffer)
|
||
|
||
res.send({ success: true, url })
|
||
} catch (error) {
|
||
logger.error({ error }, 'PDF upload failed')
|
||
|
||
res.status(500).send({
|
||
success: false,
|
||
error: {
|
||
code: 'UPLOAD_FAILED',
|
||
message: '<27><>辣銝𠹺<E98A9D>憭梯揖嚗諹窈<E8ABB9>滩<EFBFBD>'
|
||
}
|
||
})
|
||
}
|
||
}
|
||
```
|
||
|
||
**<EFBFBD><EFBFBD>眏**嚗?- <20>冽<EFBFBD><E586BD>见<EFBFBD><E8A781>见末<E8A781>躰秤靽⊥<E99DBD>
|
||
- <20>亙<EFBFBD>霈啣<E99C88>霂衣<E99C82><E8A1A3>躰秤
|
||
- 銝齿𠂔<E9BDBF>脣<EFBFBD><E884A3>典<EFBFBD><E585B8>?
|
||
---
|
||
|
||
### 7. 銝湔𧒄<E6B994><F0A79284>辣憭<E8BEA3><E686AD> <20>?
|
||
```typescript
|
||
// <20>?甇<>&嚗?tmp <20>桀<EFBFBD><E6A180>典<EFBFBD>蝡见朖<E8A781>𣳇膄
|
||
import fs from 'fs/promises'
|
||
import path from 'path'
|
||
|
||
export async function extractPdfText(ossKey: string): Promise<string> {
|
||
const tmpPath = path.join('/tmp', `${Date.now()}.pdf`)
|
||
|
||
try {
|
||
// 1. 隞?OSS 銝贝蝸<E8B49D>?/tmp
|
||
await storage.download(ossKey, tmpPath)
|
||
|
||
// 2. <20>𣂼<EFBFBD><F0A382BC><EFBFBD>𧋦
|
||
const text = await extractWithNougat(tmpPath)
|
||
|
||
return text
|
||
} finally {
|
||
// 3. 蝡见朖<E8A781>𣳇膄銝湔𧒄<E6B994><F0A79284>辣嚗<E8BEA3><E59A97>霈箸<E99C88><E7AEB8>笔仃韐伐<E99F90>
|
||
try {
|
||
await fs.unlink(tmpPath)
|
||
} catch (err) {
|
||
logger.warn({ tmpPath }, 'Failed to delete temp file')
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**<EFBFBD><EFBFBD>眏**嚗?- /tmp 摰寥<E691B0><E5AFA5>厰<EFBFBD>嚗?12MB嚗?- 摰孵膥<E5ADB5>滚鍳隡𡁏<E99AA1>蝛?- <20>踹<EFBFBD>蝤<EFBFBD><E89DA4><EFBFBD>䭾說
|
||
|
||
---
|
||
|
||
## <20><> Postgres-Only <20>嗆<EFBFBD>閫<EFBFBD><E996AB>嚗?025-12-13 <20>啣<EFBFBD>嚗?
|
||
### <20>詨<EFBFBD><E8A9A8><EFBFBD>艙
|
||
|
||
**Platform-Only 璅∪<E79285>**嚗𡁏<E59A97><F0A1818F>劐遙<E58A90>∠恣<E288A0><E681A3>縑<EFBFBD>舐<EFBFBD>銝<EFBFBD>摮睃<E691AE><E79D83>?`platform_schema.job.data`嚗䔶<EFBFBD><EFBFBD>∟”<EFBFBD>芸<EFBFBD><EFBFBD>其<EFBFBD><EFBFBD>∩縑<EFBFBD>胯<EFBFBD>?
|
||
### 隞餃𦛚蝞∠<E89D9E><E288A0><EFBFBD>迤蝖桀<E89D96>瘜?
|
||
#### <20>?DO: 雿輻鍂 job.data 摮睃<E691AE>隞餃𦛚蝞∠<E89D9E>靽⊥<E99DBD>
|
||
|
||
```typescript
|
||
// <20>?甇<>&嚗帋遙<E5B88B>⊥<EFBFBD><E28AA5><EFBFBD><EFBFBD><EFBFBD>剔<EFBFBD>靽⊥<E99DBD>摮睃<E691AE><E79D83>?job.data
|
||
import { jobQueue } from '@/common/jobs';
|
||
import { CheckpointService } from '@/common/jobs/CheckpointService';
|
||
|
||
// <20>券<EFBFBD><E588B8>遙<EFBFBD>⊥𧒄嚗<F0A79284><E59A97><EFBFBD>怠<EFBFBD><E680A0>港縑<E6B8AF>?await jobQueue.push('asl:screening:batch', {
|
||
// 銝𡁜𦛚靽⊥<E99DBD>
|
||
taskId: 'xxx',
|
||
projectId: 'yyy',
|
||
literatureIds: [...],
|
||
|
||
// <20>?隞餃𦛚<E9A483><F0A69B9A><EFBFBD>靽⊥<E99DBD>嚗<EFBFBD><E59A97><EFBFBD>典銁 job.data嚗? batchIndex: 3,
|
||
totalBatches: 20,
|
||
startIndex: 150,
|
||
endIndex: 200,
|
||
|
||
// <20>?餈𥕦漲餈質葵
|
||
processedCount: 0,
|
||
successCount: 0,
|
||
failedCount: 0,
|
||
});
|
||
|
||
// Worker 銝凋蝙<E5878B>?CheckpointService
|
||
const checkpointService = new CheckpointService(prisma);
|
||
|
||
// 靽嘥<E99DBD><E598A5>剔<EFBFBD><E58994>?job.data
|
||
await checkpointService.saveCheckpoint(job.id, {
|
||
currentBatchIndex: 5,
|
||
currentIndex: 250,
|
||
processedBatches: 5,
|
||
totalBatches: 20
|
||
});
|
||
|
||
// <20>㰘蝸<E3B098>剔<EFBFBD>隞?job.data
|
||
const checkpoint = await checkpointService.loadCheckpoint(job.id);
|
||
if (checkpoint) {
|
||
resumeFrom = checkpoint.currentIndex;
|
||
}
|
||
```
|
||
|
||
#### <20>?DON'T: <20>其<EFBFBD><E585B6>∟”銝剖<E98A9D><E58996>其遙<E585B6>∠恣<E288A0><E681A3>縑<EFBFBD>?
|
||
```typescript
|
||
// <20>?<3F>躰秤嚗𡁜銁銝𡁜𦛚銵函<E98AB5> Schema 銝剜溶<E5899C>牐遙<E78990>∠恣<E288A0><E681A3><EFBFBD>畾?model AslScreeningTask {
|
||
id String @id
|
||
projectId String
|
||
|
||
// <20>?銝滩<E98A9D>瘛餃<E7989B>餈嗘<E9A488>摮埈挾嚗? totalBatches Int // <20>?摨磰砲<E7A3B0>?job.data 銝? processedBatches Int // <20>?摨磰砲<E7A3B0>?job.data 銝? currentIndex Int // <20>?摨磰砲<E7A3B0>?job.data 銝? checkpointData Json? // <20>?摨磰砲<E7A3B0>?job.data 銝?}
|
||
|
||
// <20>?<3F>躰秤嚗朞䌊撌勗<E6928C><E58B97>唳鱏<E594B3>寞<EFBFBD><E5AF9E>?class MyCheckpointService {
|
||
async save(taskId: string) {
|
||
await prisma.aslScreeningTask.update({
|
||
where: { id: taskId },
|
||
data: { checkpointData: {...} } // <20>?銝滩<E98A9D>餈蹱甅<E8B9B1>𡄯<EFBFBD>
|
||
});
|
||
}
|
||
}
|
||
```
|
||
|
||
**銝箔<E98A9D>銋<EFBFBD><E98A8B>撖對<E69296>**
|
||
- <20>?瘥譍葵璅∪<E79285><E288AA>質<EFBFBD>瘛餃<E7989B><E9A483>詨<EFBFBD><E8A9A8><EFBFBD><EFBFBD>畾蛛<E795BE>隞<EFBFBD><E99A9E><EFBFBD>滚<EFBFBD>嚗?- <20>?餈嘥<E9A488> DRY <20>笔<EFBFBD>
|
||
- <20>?餈嘥<E9A488> 3 撅<>沲<EFBFBD><E6B2B2><EFBFBD><EFBFBD>?- <20>?蝏湔擪<E6B994>圈𠗕嚗<F0A09795>耨<EFBFBD>寥<EFBFBD>餉<EFBFBD><E9A489><EFBFBD>閬<EFBFBD>㺿憭𡁜<E686AD>嚗?
|
||
### <20>箄<EFBFBD><E7AE84><EFBFBD><EFBFBD>澆ế<E6BE86>剔<EFBFBD>閫<EFBFBD><E996AB>
|
||
|
||
#### <20>?DO: 摰䂿緵<E482BF>箄<EFBFBD><E7AE84>峕芋撘誩<E69298><E8AAA9>?
|
||
```typescript
|
||
const QUEUE_THRESHOLD = 50; // <20>刻<EFBFBD><E588BB><EFBFBD><EFBFBD>?
|
||
export async function startTask(items: any[]) {
|
||
const useQueue = items.length >= QUEUE_THRESHOLD;
|
||
|
||
if (useQueue) {
|
||
// <20>笔<EFBFBD>璅∪<E79285>嚗𡁜之隞餃𦛚嚗<F0A69B9A>竉50<35>∴<EFBFBD>
|
||
const chunks = splitIntoChunks(items, 50);
|
||
for (const chunk of chunks) {
|
||
await jobQueue.push('task:batch', {...});
|
||
}
|
||
} else {
|
||
// <20>湔𦻖璅∪<E79285>嚗𡁜<E59A97>隞餃𦛚嚗?50<35>∴<EFBFBD>
|
||
processDirectly(items); // 敹恍<E695B9>笔<EFBFBD>摨? }
|
||
}
|
||
```
|
||
|
||
**銝箔<E98A9D>銋<EFBFBD><E98A8B><EFBFBD>瑕<EFBFBD>嚗?*
|
||
- <20>?撠譍遙<E8AD8D>∪翰<E288AA>笔<EFBFBD>摨䈑<E691A8><E48891>𣳇<EFBFBD><F0A3B387>堒辣餈<E8BEA3><E9A488>
|
||
- <20>?憭找遙<E689BE>⊿<EFBFBD><E28ABF>舫<EFBFBD>嚗<EFBFBD>𣈲<EFBFBD><F0A388B2>鱏<EFBFBD>寧賒隡𩤃<E99AA1>
|
||
- <20>?<3F>扯<EFBFBD>銝𤾸虾<F0A4BEB8>䭾<EFBFBD>批像銵?
|
||
#### <20>?DON'T: <20><><EFBFBD>劐遙<E58A90>⊿<EFBFBD>韏圈<E99F8F><E59C88>?
|
||
```typescript
|
||
// <20>?<3F>躰秤嚗𡁜朖雿?<3F>∟扇敶蓥<E695B6>雿輻鍂<E8BCBB>笔<EFBFBD>
|
||
export async function startTask(items: any[]) {
|
||
// <20>㰘捏憭𡁜<E686AD><F0A1819C>唳旿嚗屸<E59A97><E5B1B8>券<EFBFBD><E588B8><EFBFBD><EFBFBD>笔<EFBFBD>
|
||
await jobQueue.push('task:batch', items); // <20>?撠譍遙<E8AD8D>∩<EFBFBD><E288A9>匧辣餈?}
|
||
```
|
||
|
||
**銝箔<E98A9D>銋<EFBFBD><E98A8B>撖對<E69296>**
|
||
- <20>?撠譍遙<E8AD8D>∪<EFBFBD>摨娍<E691A8>嚗<EFBFBD><E59A97><EFBFBD>埈<EFBFBD>頧株砭<E6A0AA>湧<EFBFBD>嚗?- <20>?瘚芾晶<E88ABE>笔<EFBFBD>韏<EFBFBD><E99F8F>
|
||
- <20>?<3F>冽<EFBFBD>雿㯄<E99BBF>撌?
|
||
### <20><><EFBFBD>潭綫<E6BDAD>𣂼<EFBFBD>?
|
||
| 隞餃𦛚蝐餃<E89D90> | <20>刻<EFBFBD><E588BB><EFBFBD><EFBFBD>?| <20><>眏 |
|
||
|---------|---------|------|
|
||
| <20><>讃蝑偦<E89D91>?| 50蝭?| <20>閧<EFBFBD>~7蝘𡜐<E89D98>50蝭㻬5<E3BBAC><35><EFBFBD> |
|
||
| <20>唳旿<E594B3>𣂼<EFBFBD> | 50<35>?| <20>閙辺~5-10蝘𡜐<E89D98>50<35>﹚5<EFB99A><35><EFBFBD> |
|
||
| 蝏蠘恣璅∪<E79285> | 30銝?| <20>蓥葵~10蝘𡜐<E89D98>30銝泠5<E6B3A0><35><EFBFBD> |
|
||
| 暺䁅恕 | 50<35>?| <20>𡁶鍂<F0A181B6>刻<EFBFBD><E588BB>?|
|
||
|
||
---
|
||
|
||
## <20>?蝳<>迫<EFBFBD>𡁏<EFBFBD>嚗㇄ON'T嚗?
|
||
### 1. <20>砍𧑐<E7A08D><F0A79190>辣摮睃<E691AE> <20>?
|
||
```typescript
|
||
// <20>?蝳<>迫嚗𡁏𧋦<F0A1818F>唳<EFBFBD>隞嗥頂蝏笔<E89D8F><E7AC94>?import fs from 'fs'
|
||
|
||
export async function uploadFile(req, res) {
|
||
const file = await req.file()
|
||
const buffer = await file.toBuffer()
|
||
|
||
// <20>?<3F>躰秤嚗𡁜捆<F0A1819C>券<EFBFBD><E588B8>臭腺憭? fs.writeFileSync('./uploads/file.pdf', buffer)
|
||
|
||
res.send({ url: '/uploads/file.pdf' })
|
||
}
|
||
|
||
// <20>?蝳<>迫嚗帋<E59A97>韏𡝗𧋦<F0A19D97>啗楝敺?const uploadDir = '/var/app/uploads'
|
||
const filePath = path.join(uploadDir, filename)
|
||
```
|
||
|
||
**<EFBFBD>桅<EFBFBD>**嚗?- 摰孵膥<E5ADB5>滚鍳<E6BB9A>𡝗<EFBFBD>摰孵<E691B0><E5ADB5><EFBFBD>辣銝W仃
|
||
- 憭𡁜<E686AD>靘钅𡢿<E99285>䭾<EFBFBD><E4ADBE>曹澈<E69BB9><E6BE88>辣
|
||
- 蝤<><E89DA4>蝛粹𡢿<E7B2B9>厰<EFBFBD>
|
||
|
||
**甇<>&<EFBFBD>𡁏<EFBFBD>**嚗帋蝙<E5B88B>?`storage.upload()` 銝𠹺<E98A9D><F0A0B9BA>?OSS
|
||
|
||
---
|
||
|
||
### 2. <20><><EFBFBD>蝻枏<E89DBB> <20>?
|
||
```typescript
|
||
// <20>?蝳<>迫嚗𡁜<E59A97>摮条<E691AE>摮矋<E691AE>憭𡁜<E686AD>靘衤<E99D98><E8A1A4>曹澈嚗?const cache = new Map<string, any>()
|
||
|
||
export async function getProject(id: string) {
|
||
// <20>?<3F>躰秤嚗𡁏<E59A97>摰孵<E691B0><E5ADB5>嗡<EFBFBD>摰硺<E691B0>霂颱<E99C82><E9A2B1>啁<EFBFBD>摮? if (cache.has(id)) {
|
||
return cache.get(id)
|
||
}
|
||
|
||
const project = await prisma.aslScreeningProject.findUnique({ where: { id } })
|
||
cache.set(id, project)
|
||
return project
|
||
}
|
||
|
||
// <20>?蝳<>迫嚗𡁜<E59A97>撅<EFBFBD><E69285>㗛<EFBFBD>摮睃<E691AE><E79D83>嗆<EFBFBD>?let taskStatus = {} // 憭𡁜<E686AD>靘衤<E99D98><E8A1A4>峕郊
|
||
```
|
||
|
||
**<EFBFBD>桅<EFBFBD>**嚗?- 憭𡁜<E686AD>靘钅𡢿<E99285>唳旿銝滚<E98A9D>甇?- <20>拙捆<E68B99>𡒊<EFBFBD>摮睃仃<E79D83>?- <20><><EFBFBD><EFBFBD>删鍂銝滚虾<E6BB9A>?
|
||
**甇<>&<EFBFBD>𡁏<EFBFBD>**嚗帋蝙<E5B88B>?Redis <20>𡝗㺭<F0A19D97>桀<EFBFBD>
|
||
|
||
---
|
||
|
||
### 3. 蝖祉<E89D96><E7A589><EFBFBD><EFBFBD>蝵?<3F>?
|
||
```typescript
|
||
// <20>?蝳<>迫嚗𡁶′蝻𣇉<E89DBB>
|
||
const LLM_API_KEY = 'sk-xxx'
|
||
const DB_HOST = '192.168.1.100'
|
||
const OSS_BUCKET = 'my-bucket'
|
||
|
||
// <20>?蝳<>迫嚗𡁜<E59A97>甇餌垢<E9A48C>?app.listen(3001)
|
||
|
||
// <20>?蝳<>迫嚗𡁜<E59A97>甇餃<E79487><E9A483>?const baseUrl = 'https://api.example.com'
|
||
```
|
||
|
||
**<EFBFBD>桅<EFBFBD>**嚗?- <20>䭾<EFBFBD><E4ADBE><EFBFBD>揢<EFBFBD>臬<EFBFBD>
|
||
- 摰匧<E691B0>憌𡡞埯嚗<E59FAF><E59A97><EFBFBD>交<EFBFBD><E4BAA4>莎<EFBFBD>
|
||
- <20>函蔡<E587BD>圈𠗕
|
||
|
||
**甇<>&<EFBFBD>𡁏<EFBFBD>**嚗?```typescript
|
||
// <20>?雿輻鍂<E8BCBB>臬<EFBFBD><E887AC>㗛<EFBFBD>
|
||
const apiKey = process.env.LLM_API_KEY
|
||
const port = process.env.PORT || 3001
|
||
app.listen(port)
|
||
```
|
||
|
||
---
|
||
|
||
### 4. <20>峕郊<E5B395>蹂遙<E8B982>?<3F>?
|
||
```typescript
|
||
// <20>?蝳<>迫嚗𡁜<E59A97>甇亙<E79487><E4BA99><EFBFBD>鵭隞餃𦛚
|
||
export async function screenLiteratures(req, res) {
|
||
const literatures = await prisma.aslLiterature.findMany({...})
|
||
|
||
// <20>?<3F>躰秤嚗?00蝭<30>虾<EFBFBD>質<EFBFBD>餈?0蝘? for (const lit of literatures) {
|
||
await llmScreening(lit) // 瘥讐<E798A5>2-3蝘? }
|
||
|
||
res.send({ success: true }) // <20>航<EFBFBD>撌脩<E6928C>頞<EFBFBD>𧒄
|
||
}
|
||
|
||
// <20>?蝳<>迫嚗𡁏瓷<F0A1818F>㕑<EFBFBD><E39591>嗡<EFBFBD><E597A1>?const result = await axios.get(url) // <20>航<EFBFBD>瘞訾<E7989E>蝑匧<E89D91>
|
||
```
|
||
|
||
**<2A>桅<EFBFBD>**嚗?- SAE 霂瑟<E99C82>頞<EFBFBD>𧒄 30 蝘?- <20>滨垢蝑匧<E89D91><E58CA7>園𡢿餈<F0A1A2BF>鵭
|
||
- <20>䭾<EFBFBD><E4ADBE>曄內餈𥕦漲
|
||
|
||
**甇<>&<EFBFBD>𡁏<EFBFBD>**嚗𡁜<E59A97>甇乩遙<E4B9A9>?+ 餈𥕦漲頧株砭嚗<E7A0AD><E59A97> DO 蝚?<3F>∴<EFBFBD>
|
||
|
||
---
|
||
|
||
### 5. <20>砍𧑐<E7A08D>亙<EFBFBD><E4BA99><EFBFBD>辣 <20>?
|
||
```typescript
|
||
// <20>?蝳<>迫嚗𡁜<E59A97><F0A1819C>交𧋦<E4BAA4>唳<EFBFBD>隞?import fs from 'fs'
|
||
|
||
export function logError(error: Error) {
|
||
// <20>?<3F>躰秤嚗𡁜捆<F0A1819C>券<EFBFBD><E588B8>臭腺憭梧<E686AD><E6A2A7>䭾<EFBFBD><E4ADBE><EFBFBD>葉<EFBFBD>亦<EFBFBD>
|
||
fs.appendFileSync('/var/log/app.log', error.message + '\n')
|
||
}
|
||
|
||
// <20>?蝳<>迫嚗帋蝙<E5B88B>?console.log <20>䔶<EFBFBD><E494B6>?logger
|
||
console.log('User logged in') // <20>删<EFBFBD><E588A0><EFBFBD><EFBFBD>嚗屸𠗕隞交䰻霂?```
|
||
|
||
**<2A>桅<EFBFBD>**嚗?- 摰孵膥<E5ADB5>滚鍳<E6BB9A>亙<EFBFBD>銝W仃
|
||
- 憭𡁜<E686AD>靘𧢲𠯫敹堒<E695B9><E5A092>?- <20>䭾<EFBFBD><E4ADBE><EFBFBD>葉<EFBFBD><E89189><EFBFBD>
|
||
|
||
**甇<>&<EFBFBD>𡁏<EFBFBD>**嚗?```typescript
|
||
// <20>?颲枏枂<E69E8F>?stdout嚗䔶蝙<E494B6>?logger
|
||
logger.info({ userId, action: 'login' }, 'User logged in')
|
||
```
|
||
|
||
---
|
||
|
||
### 6. <20>啣遣<E595A3>唳旿摨栞<E691A8><E6A09E>?<3F>?
|
||
```typescript
|
||
// <20>?蝳<>迫嚗𡁏<E59A97>甈∟窈瘙<E7AA88>鰵撱箄<E692B1><E7AE84>?import { PrismaClient } from '@prisma/client'
|
||
|
||
export async function getProjects(req, res) {
|
||
// <20>?<3F>躰秤嚗𡁏<E59A97>甈⊥鰵撱綽<E692B1>餈墧𦻖<E5A2A7>唳𠂔憓? const prisma = new PrismaClient()
|
||
const projects = await prisma.aslScreeningProject.findMany()
|
||
await prisma.$disconnect()
|
||
|
||
res.send(projects)
|
||
}
|
||
|
||
// <20>?蝳<>迫嚗𡁶凒<F0A181B6>乩蝙<E4B9A9>?pg <20>硋<EFBFBD>隞㚚店<E39A9A>?import { Pool } from 'pg'
|
||
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
|
||
```
|
||
|
||
**<2A>桅<EFBFBD>**嚗?- 餈墧𦻖<E5A2A7>啣翰<E595A3>蠘<EFBFBD>堒偷嚗㇌DS<44>𣂼<EFBFBD> 400 餈墧𦻖嚗?- <20>扯<EFBFBD>雿𦒘<E99BBF>嚗<EFBFBD><E59A97><EFBFBD>亙遣蝡贝<E89DA1>埈𧒄嚗?- 韏<><E99F8F>瘚芾晶
|
||
|
||
**甇<>&<EFBFBD>𡁏<EFBFBD>**嚗?```typescript
|
||
// <20>?雿輻鍂<E8BCBB>典<EFBFBD> Prisma Client
|
||
import { prisma } from '@/config/database'
|
||
const projects = await prisma.aslScreeningProject.findMany()
|
||
```
|
||
|
||
---
|
||
|
||
### 7. 敹賜裦<E8B39C>躰秤 <20>?
|
||
```typescript
|
||
// <20>?蝳<>迫嚗𡁶征<F0A181B6>?catch
|
||
try {
|
||
await storage.upload(key, buffer)
|
||
} catch (error) {
|
||
// <20>?<3F>躰秤鋡怠<E98BA1><E680A0>㚁<EFBFBD><E39A81>䭾<EFBFBD><E4ADBE>埝䰻
|
||
}
|
||
|
||
// <20>?蝳<>迫嚗帋<E59A97>憭<EFBFBD><E686AD> Promise rejection
|
||
processAsync(taskId) // 瘝⊥<E7989D> .catch()
|
||
|
||
// <20>?蝳<>迫嚗朞<E59A97><E69C9E>墧芋蝟𢠃<E89D9F>霂?catch (error) {
|
||
res.status(500).send({ error: 'Something went wrong' })
|
||
// <20>冽<EFBFBD>銝滨䰻<E6BBA8>㮖<EFBFBD>銋<EFBFBD><E98A8B>鈭<EFBFBD><E988AD>憒<EFBFBD><E68692>閫<EFBFBD><E996AB>
|
||
}
|
||
```
|
||
|
||
**<2A>桅<EFBFBD>**嚗?- <20>躰秤<E8BAB0>䭾<EFBFBD>餈質葵
|
||
- <20>冽<EFBFBD>雿㯄<E99BBF>撌?- <20>埝䰻<E59F9D>圈𠗕
|
||
|
||
**甇<>&<EFBFBD>𡁏<EFBFBD>**嚗?```typescript
|
||
// <20>?霈啣<E99C88><E595A3>亙<EFBFBD> + <20>见末<E8A781>躰秤靽⊥<E99DBD>
|
||
try {
|
||
await storage.upload(key, buffer)
|
||
} catch (error) {
|
||
logger.error({ error, key }, 'Upload failed')
|
||
res.status(500).send({
|
||
success: false,
|
||
error: {
|
||
code: 'UPLOAD_FAILED',
|
||
message: '<27><>辣銝𠹺<E98A9D>憭梯揖嚗諹窈璉<E7AA88><E79289>亦<EFBFBD>蝏𨅯<E89D8F><F0A885AF>滩<EFBFBD>'
|
||
}
|
||
})
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## <20><> 隞<><E99A9E>摰⊥䰻璉<E4B0BB><E79289>交<EFBFBD><E4BAA4>?
|
||
<EFBFBD>?*<2A>𣂷漱隞<E6BCB1><E99A9E><EFBFBD>?*嚗諹窈<E8ABB9>鞾★璉<E29885><E79289>伐<EFBFBD>
|
||
|
||
### <20><>辣摮睃<E691AE>
|
||
- [ ] <20>臬炏雿輻鍂 `storage.upload()` <20>屸<EFBFBD> `fs.writeFile()`嚗?- [ ] Excel <20>臬炏隞𤾸<E99A9E>摮䁅圾<E48185>琜<EFBFBD><E7909C>屸<EFBFBD>靽嘥<E99DBD><E598A5>唳𧋦<E594B3>堆<EFBFBD>
|
||
- [ ] PDF <20>𣂼<EFBFBD><F0A382BC>擧糓<E693A7>衣<EFBFBD><E8A1A3>喳<EFBFBD><E596B3>支葩<E694AF>嗆<EFBFBD>隞塚<E99A9E>
|
||
|
||
### <20>唳旿摨?- [ ] <20>臬炏雿輻鍂<E8BCBB>典<EFBFBD> `prisma` 摰硺<E691B0>嚗?- [ ] <20>臬炏<E887AC>踹<EFBFBD><E8B8B9>典儐<E585B8>臭葉<E887AD>扯<EFBFBD><E689AF>閙辺<E99699>亥砭嚗<E7A0AD><E59A97>摨磰砲<E7A3B0>寥<EFBFBD><E5AFA5>滢<EFBFBD>嚗?- [ ] <20>寥<EFBFBD><E5AFA5>滢<EFBFBD><E6BBA2>臬炏雿輻鍂鈭见𦛚嚗?
|
||
### <20>滨蔭蝞∠<E89D9E>
|
||
- [ ] <20>臬炏<E887AC><E7828F><EFBFBD>厰<EFBFBD>蝵桅<E89DB5>隞?`process.env` 霂餃<E99C82>嚗?- [ ] <20>臬炏瘝⊥<E7989D>蝖祉<E89D96><E7A589><EFBFBD><EFBFBD> IP<49><50><EFBFBD><EFBFBD>溻<EFBFBD><E6BABB><EFBFBD><EFBFBD>伐<EFBFBD>
|
||
- [ ] `.env.example` <20>臬炏撌脫凒<E884AB>堆<EFBFBD>
|
||
|
||
### <20>踵𧒄<E8B8B5>港遙<E6B8AF>?- [ ] 頞<><E9A09E> 10 蝘垍<E89D98>隞餃𦛚<E9A483>臬炏<E887AC>嫣蛹撘<E89BB9>郊嚗?- [ ] <20>臬炏<E887AC>𣂷<EFBFBD>鈭<EFBFBD><E988AD>摨行䰻霂X𦻖<EFBCB8><F0A6BB96><EFBFBD>
|
||
- [ ] <20>滨垢<E6BBA8>臬炏<E887AC>㕑蔭霂X<E99C82> WebSocket <20>瑕<EFBFBD>餈𥕦漲嚗?
|
||
### <20>亙<EFBFBD>
|
||
- [ ] <20>臬炏雿輻鍂 `logger` <20>屸<EFBFBD> `console.log`嚗?- [ ] <20>亙<EFBFBD><E4BA99>臬炏蝏𤘪<E89D8F><F0A498AA>吔<EFBFBD>JSON<4F>澆<EFBFBD>嚗㚁<E59A97>
|
||
- [ ] <20>臬炏霈啣<E99C88>鈭<EFBFBD><E988AD><EFBFBD>格<EFBFBD>雿頣<E99BBF>userId<49><64>ction嚗㚁<E59A97>
|
||
|
||
### <20>躰秤憭<E7A7A4><E686AD>
|
||
- [ ] <20><><EFBFBD>?async <20>賣㺭<E8B3A3>臬炏<E887AC>?try-catch嚗?- [ ] <20>臬炏霈啣<E99C88>鈭<EFBFBD>祕蝏<E7A595><E89D8F>霂舀𠯫敹梹<E695B9>
|
||
- [ ] <20>臬炏餈𥪜<E9A488>鈭<EFBFBD><E988AD>憟賜<E6869F><E8B39C>躰秤靽⊥<E99DBD>嚗?
|
||
### 銝湔𧒄<E6B994><F0A79284>辣
|
||
- [ ] `/tmp` <20>桀<EFBFBD>雿輻鍂<E8BCBB>擧糓<E693A7>衣<EFBFBD><E8A1A3>喳<EFBFBD><E596B3>歹<EFBFBD>
|
||
- [ ] <20>臬炏<E887AC>?`finally` <20>𦯀葉皜<E89189><E79A9C>嚗?- [ ] <20>臬炏<E887AC>踹<EFBFBD><E8B8B9>踵<EFBFBD>靘肽<E99D98> `/tmp`嚗?
|
||
---
|
||
|
||
## <20>㴓 敹恍<E695B9>蠘䌊璉<E48C8A>嚗?<3F><><EFBFBD>嚗?
|
||
**餈鞱<E9A488>隞乩<E99A9E><E4B9A9>賭誘嚗峕<E59A97><E5B395>乩誨<E4B9A9><E8AAA8>葉<EFBFBD>臬炏<E887AC>㕑<EFBFBD>閫?*嚗?
|
||
```bash
|
||
# 璉<><E79289>交糓<E4BAA4>行<EFBFBD><E8A18C>砍𧑐<E7A08D><F0A79190>辣摮睃<E691AE>
|
||
grep -r "fs.writeFile\|fs.appendFile" backend/src/modules/
|
||
|
||
# 璉<><E79289>交糓<E4BAA4>行<EFBFBD>蝖祉<E89D96><E7A589><EFBFBD><EFBFBD>蝵?grep -r "sk-\|http://\|192.168" backend/src/modules/
|
||
|
||
# 璉<><E79289>交糓<E4BAA4>行<EFBFBD><E8A18C>啣遣 Prisma 餈墧𦻖
|
||
grep -r "new PrismaClient" backend/src/modules/
|
||
|
||
# 璉<><E79289>交糓<E4BAA4>行<EFBFBD> console.log
|
||
grep -r "console.log" backend/src/modules/
|
||
```
|
||
|
||
**憸<><E686B8>蝏𤘪<E89D8F>**嚗𡁏<E59A97><F0A1818F>㗇<EFBFBD><E39787>亙<EFBFBD>霂亥<E99C82><E4BAA5>?**0 銝芸龪<E88AB8>?*
|
||
|
||
---
|
||
|
||
## <20><> <20><><EFBFBD><EFBFBD><EFBFBD>獢?
|
||
- [鈭穃<E988AD><E7A983>罸<EFBFBD>蝵脫沲<E884AB><E6B2B2><EFBFBD><EFBFBD>㻩(../09-<2D>嗆<EFBFBD>摰墧鴌/03-鈭穃<E988AD><E7A983>罸<EFBFBD>蝵脫沲<E884AB><E6B2B2><EFBFBD><EFBFBD>?md) - <20><>鉄摰峕㟲隞<E39FB2><E99A9E>蝷箔<E89DB7>
|
||
- [<EFBFBD>滚<EFBFBD>蝡舀芋<EFBFBD>堒<EFBFBD><EFBFBD>嗆<EFBFBD>霈曇恣-V2](../00-蝟餌<E89D9F><E9A48C>颱<EFBFBD>霈曇恣/<2F>滚<EFBFBD>蝡舀芋<E88880>堒<EFBFBD><E5A092>嗆<EFBFBD>霈曇恣-V2.md) - <20>嗆<EFBFBD><E59786>餌熔
|
||
- [<5B>唳旿摨栞挽霈∟<E99C88><E2889F><EFBFBD>(./01-<2D>唳旿摨栞挽霈∟<E99C88><E2889F>?md)
|
||
- [API霈曇恣閫<EFBFBD><EFBFBD>](./02-API霈曇恣閫<E681A3><E996AB>.md)
|
||
- [隞<EFBFBD><EFBFBD>閫<EFBFBD><EFBFBD>](./05-隞<><E99A9E>閫<EFBFBD><E996AB>.md)
|
||
- [Git<EFBFBD>𣂷漱閫<EFBFBD><EFBFBD>](./06-Git<69>𣂷漱閫<E6BCB1><E996AB>.md)
|
||
|
||
---
|
||
|
||
## <20><> <20>湔鰵<E6B994>亙<EFBFBD>
|
||
|
||
| <20>交<EFBFBD> | <20><>𧋦 | <20>䀹凒<E480B9><E58792>捆 | 蝏湔擪<E6B994>?|
|
||
|------|------|---------|--------|
|
||
| 2025-11-16 | V1.0 | <20>𥕦遣<F0A595A6><E981A3>﹝嚗<EFB99D><E59A97>銋劐<E98A8B><E58A90>毺<EFBFBD>撘<EFBFBD><E69298>𤏸<EFBFBD><F0A48FB8>?| <20>嗆<EFBFBD><E59786>a<EFBFBD> |
|
||
|
||
---
|
||
|
||
**<EFBFBD><EFBFBD>﹝蝏湔擪<EFBFBD><EFBFBD><EFBFBD>** <20>嗆<EFBFBD><E59786>a<EFBFBD>
|
||
**<EFBFBD><EFBFBD><EFBFBD>擧凒<EFBFBD>堆<EFBFBD>** 2025-11-16
|
||
**<EFBFBD><EFBFBD>﹝<EFBFBD>嗆<EFBFBD><EFBFBD><EFBFBD>** <20>?撌脣<E6928C><E884A3>?
|
||
**撘箏<E69298><E7AE8F>扯<EFBFBD>嚗?* <20>?<3F><><EFBFBD>劐誨<E58A90><E8AAA8><EFBFBD>鈭文<E988AD>敹<EFBFBD>◆璉<E29786><E79289>?
|