Files
AIclinicalresearch/docs/09-架构实施/03-云原生部署架构指南.md
HaHafeng 1b53ab9d52 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%)
2026-01-14 19:15:01 +08:00

1067 lines
25 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.
# 鈭穃<E988AD><E7A983><EFBFBD>蝵脫沲<E884AB><E6B2B2><EFBFBD><EFBFBD>?
> **<2A><><EFBFBD><EFB99D>𧋦嚗?* V1.0
> **<2A>𥕦遣<F0A595A6><EFBFBD>嚗?* 2025-11-16
> **<2A><>鍂撖寡情嚗?* <20>𡒊垢撘<E59EA2><E69298><EFBFBD><E3BBAB><EFBFBD><E6B2B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>蝏?
> **蝏湔擪<E6B994><E693AA><EFBFBD>** <20><EFBFBD><E59786><EFBFBD>
> **<2A><EFBFBD><E59786><EFBFBD>** <20>?撌脣<E6928C><E884A3>?
---
## <20><> <20><>﹝霂湔<E99C82>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD>靘?**AI銝游<E98A9D><E6B8B8>𠉛弦撟喳蝱** <20>函蔡<E587BD>圈燵<E59C88><EFBFBD> Serverless <20><EFBFBD><E59786><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B994>𨰜<EFBFBD>?
> **潃?<3F><EFBFBD><E6BBA9>湔鰵嚗?025-11-16嚗?*嚗?
> 撟喳蝱<EFBFBD><EFBFBD>霈暹鴌<EFBFBD><EFBFBD>祕蝏<EFBFBD><EFBFBD><EFBFBD>質恣<EFBFBD><EFBFBD><EFBFBD><EFBFBD>摰䂿緵撌脰<EFBFBD>蝘餃<EFBFBD>嚗?
> **[撟喳蝱<E596B3><EFBFBD>霈暹鴌閫<E9B48C><E996AB>](./04-撟喳蝱<E596B3><EFBFBD>霈暹鴌閫<E9B48C><E996AB>.md)**
>
> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗?> - 鈭穃<E988AD><E7A983><EFBFBD><EFBFBD><E6B2B2><EFBFBD><EFBFBD>霈曇恣
> - <20><EFBFBD>鈭烐<E988AD><E78390><EFBFBD><EFBFBD><E58CA7><EFBFBD>蝵?> - Docker摰孵膥<E5ADB5><EFBFBD><E7A18B>函蔡瘚<E894A1><E7989A>
> - <20>鞉𧋦隡啁<E99AA1><E59581>𣬚<EFBFBD><F0A3AC9A><EFBFBD>霅?
**<2A><>﹝摰帋<E691B0>**嚗?- <20><EFBFBD><EFBFBD><E78DA2>03嚗㚁<E59A97>**鈭穃<E988AD><E7A983><EFBFBD>蝵脫沲<E884AB><E6B2B2><EFBFBD><EFBFBD>** - 靘折<E99D98>鈭烐<E988AD><E78390><EFBFBD><E288AA>函蔡瘚<E894A1><E7989A>
- 04<30><34>﹝嚗?*撟喳蝱<E596B3><EFBFBD>霈暹鴌閫<E9B48C><E996AB>** - 靘折<E99D98><EFBFBD><E99A9E>摰䂿緵<E482BF><E7B7B5><EFBFBD><EFBFBD><EFBFBD><E78390>?
**<2A><><EFBFBD>園𡢿**嚗?0 <20><><EFBFBD>
**摰墧鴌<E5A2A7>園𡢿**嚗𡁜<E59A97>閫?[撟喳蝱<EFBFBD><EFBFBD>霈暹鴌閫<EFBFBD><EFBFBD>](./04-撟喳蝱<E596B3><EFBFBD>霈暹鴌閫<E9B48C><E996AB>.md) <20>?.5憭拙<EFBFBD><EFBFBD>質恣<EFBFBD>?
---
## <20><>儭?<3F><EFBFBD>霂西圾
### 1. Serverless 摨𠉛鍂撘閙<E69298> (SAE)
#### **鈭批<E988AD><E689B9><EFBFBD>?*
| <20><EFBFBD>?| 霂湔<E99C82> | 隡睃飵 |
|------|------|------|
| **<EFBFBD>芸𢆡<EFBFBD>拍憬摰?* | <20>寞旿瘚<E697BF><E7989A><EFBFBD>芸𢆡靚<F0A286A1>㟲摰硺<E691B0><E7A1BA><EFBFBD>0-100嚗?| 擃睃陸<E79D83><EFBFBD>摰閙㦤嚗䔶<E59A97>靚瑟<E99D9A><E7919F><EFBFBD><EFBFBD><EFBFBD>?|
| **<EFBFBD><EFBFBD>隞䁅晶** | 瞼0.000110592/霂瑟<E99C82>甈?+ 摰硺<E691B0>韐?| <20><EFBFBD><E884B2><EFBFBD>晶蝥?瞼200-500 |
| **摰孵膥<E5ADB5><EFBFBD>蝵?* | <20><EFBFBD> Docker <20>𨅯<EFBFBD> | <20><EFBFBD><EFBFBD><E98A9D><EFBFBD><EFBFBD>敹恍<E695B9><EFBFBD>皛?|
| **<EFBFBD><EFBFBD>蔭韐蠘蝸<EFBFBD><EFBFBD>﹛** | <20>芸𢆡<E88AB8><F0A286A1><EFBFBD><EFBFBD><E7989A> | <20>𣳇<EFBFBD><F0A3B387>閧𡠺韐凋僭 SLB |
| **<EFBFBD>亙熒璉<EFBFBD><EFBFBD>?* | <20>芸𢆡<E88AB8>滚鍳撘<E98DB3>虜摰硺<E691B0> | <20><EFBFBD><E99EBE>舐鍂<E88890>?|
#### **摰硺<E691B0><EFBFBD><EFBFBD>㗇𥋘**
| <20>嗆挾 | 閫<>聢 | vCPU | <20><><EFBFBD> | <20><><EFBFBD>箸艶 |
|------|------|------|------|---------|
| **撘<><E69298>?瘚贝<E7989A>** | 0.5C1G | 0.5<EFBFBD>?| 1GB | <20>亥窈瘙?< 1000 |
| **<EFBFBD><EFBFBD>** | 1C2G | 1<>?| 2GB | <20>亥窈瘙?1000-5000 |
| **<EFBFBD>鞾鵭<EFBFBD>?* | 2C4G | 2<>?| 4GB | <20>亥窈瘙?5000-20000 |
| **<EFBFBD><EFBFBD><EFBFBD>?* | 4C8G | 4<>?| 8GB | <20>亥窈瘙?> 20000 |
**撱箄悅<E7AE84>滨蔭**嚗?```yaml
# SAE 摨𠉛鍂<F0A0899B>滨蔭
摰硺<EFBFBD><EFBFBD>聢: 1C2G
<EFBFBD><EFBFBD>撠誩<EFBFBD>靘𧢲㺭: 1 # <20><EFBFBD><E8B8B9>瑕鍳<E79195>?<3F><>憭批<E686AD>靘𧢲㺭: 10
CPU 閫血<E996AB><E8A180>拙捆<E68B99><E68D86><EFBFBD>? 70%
<EFBFBD><EFBFBD><EFBFBD>閫血<EFBFBD><EFBFBD>拙捆<EFBFBD><EFBFBD><EFBFBD>? 80%
```
---
### 2. 鈭烐㺭<E78390><EFBFBD> RDS (PostgreSQL 15)
#### **閫<><EFBFBD><EFBFBD>**
| <20>嗆挾 | 閫<>聢 | vCPU | <20><><EFBFBD> | <20><>憭扯<E686AD><E689AF>交㺭 | <20><>晶 |
|------|------|------|------|-----------|------|
| **撘<><E69298>?瘚贝<E7989A>** | <20><EFBFBD><E7AE87>?1C1G | 1<>?| 1GB | 100 | 瞼120 |
| **<2A><EFBFBD>** | <20>𡁶鍂<F0A181B6>?2C4G | 2<>?| 4GB | 400 | 瞼300 |
| **<2A>鞾鵭<E99EBE>?* | <20>𡁶鍂<F0A181B6>?4C8G | 4<>?| 8GB | 800 | 瞼600 |
| **<2A><EFBFBD><E99E9F>?* | <20>砌澈<E7A08C>?8C16G | 8<>?| 16GB | 1600 | 瞼1200 |
#### **<2A>喲睸<E596B2>滨蔭**
```sql
-- <20><EFBFBD>敶枏<E695B6><E69E8F><EFBFBD>憭扯<E686AD><E689AF>交㺭
SHOW max_connections;
-- <20><EFBFBD>敶枏<E695B6>瘣餉<E798A3>餈墧𦻖
SELECT count(*) FROM pg_stat_activity;
-- <20>㗇㺭<E39787><EFBFBD><E6A180><EFBFBD><EFBFBD>蝏蠘恣餈墧𦻖
SELECT datname, count(*)
FROM pg_stat_activity
GROUP BY datname;
```
**餈墧𦻖瘙㰘恣蝞堒<E89D9E>撘?*嚗?```
瘥誩<EFBFBD>靘贝<EFBFBD><EFBFBD>交㺭 = RDS<44><53>憭扯<E686AD><E689AF>交㺭 / SAE<41><45>憭批<E686AD>靘𧢲㺭 <20> 0.8嚗<EFBFBD><EFBFBD><EFBFBD>函頂<EFBFBD><EFBFBD>
蝷箔<EFBFBD>嚗?RDS: 400餈墧𦻖
SAE: <20><>憭?0摰硺<E691B0>
瘥誩<EFBFBD>靘? 400 / 10 <20> 0.8 = 32餈墧𦻖
```
---
### 3. 撖寡情摮睃<E691AE> OSS
#### **Bucket <20>滨蔭**
```yaml
Bucket<EFBFBD>滨妍: aiclinical-prod
<EFBFBD><EFBFBD>: <20>𦒘<EFBFBD>1嚗<31>㜺撌痹<E6928C>oss-cn-hangzhou
摮睃<EFBFBD>蝐餃<EFBFBD>: <20><><EFBFBD>摮睃<E691AE>
霈輸䔮<EFBFBD><EFBFBD><EFBFBD>: 蝘<><E89D98>嚗㇊rivate嚗?<3F><>𧋦<EFBFBD><EFBFBD>: 撘<><E69298>?頝典<E9A09D>霈曄蔭: <20><><EFBFBD>滨垢<E6BBA8><EFBFBD>
```
#### **<2A><EFBFBD>蝏𤘪<E89D8F><EFBFBD><E996AB>**
```
aiclinical-prod/
<EFBFBD><EFBFBD><EFBFBD><EFBFBD> asl/
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> pdfs/ # PDF<44><46>
<EFBFBD>? <20><EFBFBD><E98EBF><EFBFBD> excel/ # Excel<65><6C>
<EFBFBD>? <20><EFBFBD><E5A999><EFBFBD> exports/ # 撖澆枂<E6BE86><E69E82>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD> avatars/ # <20><EFBFBD>憭游<E686AD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD> documents/ # <20><EFBFBD>摨𤘪<E691A8>獢?<3F><EFBFBD><E5A999><EFBFBD> temp/ # 銝湔𧒄<E6B994><F0A79284>辣嚗?憭拙<E686AD><E68B99>芸𢆡<E88AB8>𣳇膄嚗?```
#### **<2A>笔𦶢<E7AC94><EFBFBD>蝞∠<E89D9E>**
```json
{
"Rules": [
{
"ID": "delete-temp-files",
"Prefix": "temp/",
"Status": "Enabled",
"Expiration": {
"Days": 1
}
},
{
"ID": "archive-old-pdfs",
"Prefix": "asl/pdfs/",
"Status": "Enabled",
"Transitions": [
{
"Days": 90,
"StorageClass": "IA" // 頧砌蛹雿𡡞<E99BBF>霈輸䔮
}
]
}
]
}
```
---
## <20>凃 摮睃<E691AE><E79D83>質情撅<E68385>挽霈∴<E99C88><E288B4><EFBFBD>嚗?
### <20>亙藁摰帋<E691B0>
**<2A><>辣**嚗䫤backend/src/common/storage/StorageAdapter.ts`
```typescript
/**
* 摮睃<E691AE><E79D83>質情撅<E68385>𦻖<EFBFBD>? *
* @description
* - <20><EFBFBD><E88880>砍𧑐<E7A08D><F0A79190>辣蝟餌<E89D9F> + <20><EFBFBD>鈭?OSS <20><EFBFBD><E588A0><EFBFBD>
* - <20><EFBFBD><E69C9E><EFBFBD><E887AC><EFBFBD><E3979B><EFBFBD>摰䂿緵蝐? *
* @example
* const storage = StorageFactory.create()
* const url = await storage.upload('files/doc.pdf', buffer)
*/
export interface StorageAdapter {
/**
* 銝𠹺<E98A9D><F0A0B9BA><EFBFBD>
* @param key 摮睃<E691AE><E79D83><EFBFBD>頝臬<E9A09D>嚗㚁<E59A97>憒?'asl/pdfs/xxx.pdf'
* @param buffer <20><><EFBFBD><E8BEA3>
* @returns 霈輸䔮URL
*/
upload(key: string, buffer: Buffer): Promise<string>
/**
* 銝贝蝸<E8B49D><E89DB8>
* @param key 摮睃<E691AE><E79D83>? * @returns <20><><EFBFBD><E8BEA3>
*/
download(key: string): Promise<Buffer>
/**
* <20>𣳇膄<F0A3B387><E88684>
* @param key 摮睃<E691AE><E79D83>? */
delete(key: string): Promise<void>
/**
* <20><EFBFBD>霈輸䔮URL
* @param key 摮睃<E691AE><E79D83>? * @returns 摰峕㟲霈輸䔮URL
*/
getUrl(key: string): string
/**
* <20><EFBFBD>銝𠹺<E98A9D>
* @param files <20><><EFBFBD>𡑒”
* @returns URL<52>𡑒”
*/
uploadMany(files: Array<{ key: string; buffer: Buffer }>): Promise<string[]>
}
```
---
### LocalAdapter 摰䂿緵嚗<E7B7B5>𧋦<EFBFBD><EFBFBD><E595A3>𡢅<EFBFBD>
**<EFBFBD><EFBFBD>辣**嚗䫤backend/src/common/storage/LocalAdapter.ts`
```typescript
import fs from 'fs/promises'
import path from 'path'
import { StorageAdapter } from './StorageAdapter.js'
/**
* <20>砍𧑐<E7A08D><F0A79190>辣蝟餌<E89D9F>摮睃<E691AE><E79D83><EFBFBD><EFBFBD><EFBFBD>? *
* @description
* - <20><EFBFBD><E585B6>砍𧑐撘<F0A79190><E69298>𤑳㴓憓? * - <20><>辣摮睃<E691AE><E79D83>?./uploads <20><EFBFBD>
* - <20><EFBFBD> HTTP 霈輸䔮嚗冴ttp://localhost:3001/uploads/xxx
*/
export class LocalAdapter implements StorageAdapter {
private uploadDir: string
private baseUrl: string
constructor() {
this.uploadDir = path.resolve(process.cwd(), 'uploads')
this.baseUrl = process.env.BASE_URL || 'http://localhost:3001'
// 蝖桐<E89D96><E6A190><EFBFBD>摮睃銁
this.ensureUploadDir()
}
private async ensureUploadDir() {
try {
await fs.mkdir(this.uploadDir, { recursive: true })
} catch (error) {
console.error('<27>𥕦遣銝𠹺<E98A9D><F0A0B9BA><EFBFBD>憭梯揖:', error)
}
}
async upload(key: string, buffer: Buffer): Promise<string> {
const filePath = path.join(this.uploadDir, key)
// 蝖桐<E89D96><E6A190>嗥𤌍敶訫<E695B6><E8A8AB>? await fs.mkdir(path.dirname(filePath), { recursive: true })
// <20><EFBFBD><E59D94><EFBFBD>
await fs.writeFile(filePath, buffer)
// 餈𥪜<E9A488>霈輸䔮URL
return this.getUrl(key)
}
async download(key: string): Promise<Buffer> {
const filePath = path.join(this.uploadDir, key)
return await fs.readFile(filePath)
}
async delete(key: string): Promise<void> {
const filePath = path.join(this.uploadDir, key)
try {
await fs.unlink(filePath)
} catch (error) {
// <20><>辣銝滚<E98A9D><E6BB9A>冽𧒄敹賜裦<E8B39C>躰秤
if ((error as any).code !== 'ENOENT') {
throw error
}
}
}
getUrl(key: string): string {
return `${this.baseUrl}/uploads/${key}`
}
async uploadMany(files: Array<{ key: string; buffer: Buffer }>): Promise<string[]> {
const urls = await Promise.all(
files.map(file => this.upload(file.key, file.buffer))
)
return urls
}
}
```
---
### OSSAdapter 摰䂿緵嚗<E7B7B5><E59A97>鈭抒㴓憓<E3B493><E68693>
**<2A><>辣**嚗䫤backend/src/common/storage/OSSAdapter.ts`
```typescript
import OSS from 'ali-oss'
import { StorageAdapter } from './StorageAdapter.js'
/**
* <20><EFBFBD>鈭?OSS 摮睃<E691AE><E79D83><EFBFBD><EFBFBD><EFBFBD>? *
* @description
* - <20><EFBFBD><E585B6>煺漣<E785BA><EFBFBD>
* - <20><>辣摮睃<E691AE><E79D83>券燵<E588B8><EFBFBD> OSS
* - <20><EFBFBD><E88880><EFBFBD><EFBFBD>/憭𣇉<E686AD>霈輸䔮
*/
export class OSSAdapter implements StorageAdapter {
private client: OSS
private bucket: string
private region: string
constructor() {
this.region = process.env.OSS_REGION!
this.bucket = process.env.OSS_BUCKET!
if (!this.region || !this.bucket) {
throw new Error('OSS<53>滨蔭蝻箏仃嚗鐾SS_REGION <20>?OSS_BUCKET <20>芾挽蝵?)
}
this.client = new OSS({
region: this.region,
accessKeyId: process.env.OSS_ACCESS_KEY_ID!,
accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET!,
bucket: this.bucket,
// 雿輻鍂<E8BCBB><E98D82><EFBFBD>endpoint嚗𠄎AE霈輸䔮OSS<53>齿<EFBFBD><E9BDBF>讛晶嚗? internal: process.env.NODE_ENV === 'production',
})
}
async upload(key: string, buffer: Buffer): Promise<string> {
try {
const result = await this.client.put(key, buffer)
return result.url
} catch (error) {
console.error('OSS銝𠹺<EFBFBD>:', error)
throw new Error(`OSS銝𠹺<E98A9D>憭梯揖: ${key}`)
}
}
async download(key: string): Promise<Buffer> {
try {
const result = await this.client.get(key)
return result.content as Buffer
} catch (error) {
console.error('OSS銝贝蝸憭梯揖:', error)
throw new Error(`OSS銝贝蝸憭梯揖: ${key}`)
}
}
async delete(key: string): Promise<void> {
try {
await this.client.delete(key)
} catch (error) {
console.error('OSS<EFBFBD>𣳇:', error)
// <20><>辣銝滚<E98A9D><E6BB9A>冽𧒄銝齿<E98A9D><E9BDBF><EFBFBD>霂? }
}
getUrl(key: string): string {
// 餈𥪜<E9A488>憭𣇉<E686AD>霈輸䔮URL
return `https://${this.bucket}.${this.region}.aliyuncs.com/${key}`
}
async uploadMany(files: Array<{ key: string; buffer: Buffer }>): Promise<string[]> {
// 撟嗉<E6929F>銝𠹺<E98A9D><EFBFBD><E59A97>憭?0銝芸僎<E88AB8>𡢅<EFBFBD>
const chunks = []
for (let i = 0; i < files.length; i += 10) {
chunks.push(files.slice(i, i + 10))
}
const urls: string[] = []
for (const chunk of chunks) {
const chunkUrls = await Promise.all(
chunk.map(file => this.upload(file.key, file.buffer))
)
urls.push(...chunkUrls)
}
return urls
}
/**
* <20><><EFBFBD>蝑曉<E89D91>URL嚗<4C><EFBFBD>嗉挪<E59789><EFBFBD>
* @param key 摮睃<E691AE><E79D83>? * @param expires 餈<><E9A488><EFBFBD>園𡢿嚗<F0A1A2BF><E59A97>嚗㚁<E59A97>暺䁅恕1撠𤩺𧒄
*/
async getSignedUrl(key: string, expires: number = 3600): Promise<string> {
return this.client.signatureUrl(key, { expires })
}
}
```
---
### StorageFactory 撌亙<E6928C>蝐?
**<2A><>辣**嚗䫤backend/src/common/storage/StorageFactory.ts`
```typescript
import { StorageAdapter } from './StorageAdapter.js'
import { LocalAdapter } from './LocalAdapter.js'
import { OSSAdapter } from './OSSAdapter.js'
/**
* 摮睃<E691AE>撌亙<E6928C>蝐? *
* @description
* - <20>寞旿<E5AF9E><EFBFBD><E887AC><EFBFBD><E3979B>芸𢆡<E88AB8>㗇𥋘摮睃<E691AE>摰䂿緵
* - STORAGE_TYPE=local <20>?LocalAdapter
* - STORAGE_TYPE=oss <20>?OSSAdapter
*/
export class StorageFactory {
private static instance: StorageAdapter | null = null
/**
* <20>𥕦遣摮睃<E691AE>摰硺<E691B0><EFBFBD><E59A97>靘𧢲芋撘𧶏<E69298>
*/
static create(): StorageAdapter {
if (this.instance) {
return this.instance
}
const storageType = process.env.STORAGE_TYPE || 'local'
switch (storageType) {
case 'oss':
console.log('<27>𣑐 雿輻鍂<E8BCBB><EFBFBD>鈭?OSS 摮睃<E691AE>')
this.instance = new OSSAdapter()
break
case 'local':
console.log('<27><> 雿輻鍂<E8BCBB>砍𧑐<E7A08D><F0A79190>辣摮睃<E691AE>')
this.instance = new LocalAdapter()
break
default:
throw new Error(`<EFBFBD>芰䰻<EFBFBD><EFBFBD><EFBFBD><EFBFBD>函掩<EFBFBD>? ${storageType}`)
}
return this.instance
}
/**
* <20>滨蔭摰硺<E691B0><EFBFBD>鍂鈭擧<E988AD>霂𤏪<E99C82>
*/
static reset() {
this.instance = null
}
}
// 撖澆枂<E6BE86><EFBFBD>
export const storage = StorageFactory.create()
```
---
### 雿輻鍂蝷箔<E89DB7>
```typescript
// backend/src/modules/asl/controllers/literatureController.ts
import { storage } from '../../../common/storage/StorageFactory.js'
import { prisma } from '../../../config/database.js'
/**
* 銝𠹺<E98A9D>PDF<44><46>
*/
export async function uploadPdf(req, res) {
try {
const { literatureId } = req.params
const file = await req.file()
if (!file) {
return res.status(400).send({ error: 'No file uploaded' })
}
// 霂餃<E99C82><E9A483><EFBFBD><EFBFBD><E8BEA3>
const buffer = await file.toBuffer()
// <20><><EFBFBD>摮睃<E691AE><E79D83>? const key = `asl/pdfs/${Date.now()}-${file.filename}`
// <20>?銝𠹺<E98A9D><F0A0B9BA><EFBFBD><E595A3><EFBFBD><E58981>芸𢆡<E88AB8>寞旿<E5AF9E><EFBFBD><E887AC>㗇𥋘Local<61>𤈛SS嚗? const url = await storage.upload(key, buffer)
// 靽嘥<E99DBD><E598A5>唳㺭<E594B3><EFBFBD>
await prisma.aslLiterature.update({
where: { id: literatureId },
data: {
pdfUrl: url,
pdfOssKey: key,
pdfFileSize: buffer.length,
}
})
res.send({
success: true,
url,
size: buffer.length
})
} catch (error) {
console.error('PDF銝𠹺<E98A9D>憭梯揖:', error)
res.status(500).send({ error: 'Upload failed' })
}
}
/**
* Excel銝𠹺<E98A9D><EFBFBD><E59A97><EFBFBD><EFBFBD><EFBFBD><E996AC><EFBFBD><EFBFBD><E58981>湔𦻖閫<F0A6BB96><E996AB>嚗? */
export async function importExcel(req, res) {
const file = await req.file()
const buffer = await file.toBuffer()
// <20>?<3F>湔𦻖隞𤾸<E99A9E>摮䁅圾<E48185><EFBFBD>銝滩氜<E6BBA9>? const workbook = xlsx.read(buffer, { type: 'buffer' })
const data = xlsx.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]])
// <20><EFBFBD><E5AFA5><EFBFBD>
await prisma.aslLiterature.createMany({ data })
res.send({ success: true, count: data.length })
}
```
---
## <20><20>唳旿摨栞<E691A8><E6A09E><EFBFBD><E4BAA4>滨蔭
### Prisma<6D>滨蔭
**<2A><>辣**嚗䫤backend/src/config/database.ts`
```typescript
import { PrismaClient } from '@prisma/client'
// <20><EFBFBD><E887AC>斗鱏
const isProduction = process.env.NODE_ENV === 'production'
// 霈∠<E99C88>餈墧𦻖瘙惩之撠?// <20>煺漣<E785BA><EFBFBD>嚗鑹DS 400餈墧𦻖 / SAE 10摰硺<E691B0> <20> 0.8 = 32餈墧𦻖/摰硺<E691B0>
// 撘<><E69298>𤑳㴓憓<E3B493><E68693><EFBFBD>砍𧑐 PostgreSQL嚗?餈墧𦻖頞喳<E9A09E>
const connectionLimit = isProduction ? 32 : 5
/**
* Prisma 摰<E691B0>蝡舫<E89DA1>蝵? */
export const prisma = new PrismaClient({
log: isProduction
? ['error', 'warn'] // <20>煺漣<E785BA><EFBFBD><E887AC>芾扇敶閖<E695B6>霂臬<E99C82>霅血<E99C85>
: ['query', 'error', 'warn'], // 撘<><E69298>𤑳㴓憓<E3B493>扇敶閙<E695B6><E99699>㗇䰻霂?
datasources: {
db: {
url: process.env.DATABASE_URL
}
},
// <20>喲睸<E596B2>滨蔭嚗朞<E59A97><E69C9E><EFBFBD>
...(isProduction && {
// 隞<><EFBFBD>煺漣<E785BA><EFBFBD><E887AC>滨蔭餈墧𦻖瘙𣳇<E79899><F0A3B387>? datasources: {
db: {
url: process.env.DATABASE_URL,
// 餈墧𦻖瘙𣳇<E79899>蝵? pool: {
timeout: 5, // <20><EFBFBD>餈墧𦻖頞<F0A6BB96>𧒄嚗<F0A79284><E59A97>嚗? maxsize: connectionLimit, // <20><>憭扯<E686AD><E689AF>交㺭
min: 2, // <20><>撠譍<E692A0><E8AD8D><EFBFBD><EFBFBD><EFBFBD>? }
}
}
})
})
// 隡㗛<E99AA1><E3979B>喲𡡒
process.on('SIGINT', async () => {
console.log('<27>𣑐 甇<><EFBFBD>喲𡡒<E596B2>唳旿摨栞<E691A8><E6A09E>?..')
await prisma.$disconnect()
process.exit(0)
})
process.on('SIGTERM', async () => {
console.log('<27>𣑐 <20><EFBFBD>SIGTERM嚗峕迤<E5B395><EFBFBD><E585B8>剜㺭<E5899C><EFBFBD>餈墧𦻖...')
await prisma.$disconnect()
process.exit(0)
})
// 餈墧𦻖<E5A2A7><EFBFBD><E59581><EFBFBD><EFBFBD><E99A9E>鈭抒㴓憓<E3B493><E68693>
if (isProduction) {
setInterval(async () => {
try {
const result = await prisma.$queryRaw<Array<{ count: bigint }>>`
SELECT count(*) as count
FROM pg_stat_activity
WHERE datname = current_database()
`
const connectionCount = Number(result[0].count)
console.log(`<EFBFBD><EFBFBD> 敶枏<E695B6><E69E8F>唳旿摨栞<E691A8><E6A09E>交㺭: ${connectionCount}`)
// <20>𡃏郎<F0A1838F><E9838E><EFBFBD><EFBFBD>80%
const maxConnections = 400 // <20>寞旿RDS閫<53>聢霈曄蔭
if (connectionCount > maxConnections * 0.8) {
console.error(`<EFBFBD>𩤃<EFBFBD> <20>唳旿摨栞<E691A8><E6A09E>交㺭餈<E3BAAD><E9A488>: ${connectionCount}/${maxConnections}`)
}
} catch (error) {
console.error('餈墧𦻖<E5A2A7><EFBFBD><E59581>批仃韐?', error)
}
}, 60000) // 瘥?0蝘埝<E89D98><E59F9D><EFBFBD>甈?}
```
---
## <20><> <20><EFBFBD><E887AC><EFBFBD>蝞∠<E89D9E>
### <20>砍𧑐撘<F0A79190><E69298>𤑳㴓憓?
**<2A><>辣**嚗䫤backend/.env.development`
```bash
# <20><EFBFBD>
NODE_ENV=development
# 摮睃<E691AE><E79D83>滨蔭
STORAGE_TYPE=local
BASE_URL=http://localhost:3001
# <20>唳旿摨?DATABASE_URL=postgresql://postgres:postgres@localhost:5432/aiclinical_dev
# LLM<4C>滨蔭
LLM_API_KEY=sk-xxx
LLM_BASE_URL=https://api.deepseek.com
# <20><EFBFBD><E597A1>滚𦛚嚗<F0A69B9A>𧋦<EFBFBD><EFBFBD><E594B3><EFBFBD><EFBFBD>滨蔭嚗?OSS_REGION=
OSS_ACCESS_KEY_ID=
OSS_ACCESS_KEY_SECRET=
OSS_BUCKET=
```
---
### <20>煺漣<E785BA><EFBFBD><E887AC>滨蔭
**<2A>沒AE<41><EFBFBD><E689B9><EFBFBD>蝵殷<E89DB5>銝滩<E98A9D><E6BBA9><EFBFBD><E59D94><EFBFBD>辣**嚗?
```bash
# <20><EFBFBD>
NODE_ENV=production
# 摮睃<E691AE><E79D83>滨蔭
STORAGE_TYPE=oss
OSS_REGION=oss-cn-hangzhou
OSS_ACCESS_KEY_ID=LTAI5t***嚗<><E59A97>RAM<41><EFBFBD><E586BD><EFBFBD>嚗?OSS_ACCESS_KEY_SECRET=***
OSS_BUCKET=aiclinical-prod
# <20>唳旿摨?DATABASE_URL=postgresql://aiclinical:***@rm-xxx.mysql.rds.aliyuncs.com:5432/aiclinical_prod
# LLM<4C>滨蔭
LLM_API_KEY=sk-***
LLM_BASE_URL=https://api.deepseek.com
# <20><EFBFBD>蝥批<E89DA5>
LOG_LEVEL=info
```
---
## <20>閦 Docker <20>滨蔭
### Dockerfile
**<2A><>辣**嚗䫤backend/Dockerfile`
```dockerfile
# ==================== <20><><EFBFBD>嗆挾 ====================
FROM node:20-alpine AS builder
WORKDIR /app
# 憭滚<E686AD>靘肽<E99D98><E882BD><EFBFBD>
COPY package*.json ./
COPY prisma ./prisma/
# 摰㕑<E691B0>靘肽<E99D98><EFBFBD><E59A97><EFBFBD>查ev靘肽<E99D98>嚗𣬚鍂鈭擧<E988AD>撱綽<E692B1>
RUN npm ci
# 憭滚<E686AD>皞𣂷誨<F0A382B7>?COPY . .
# <20><><EFBFBD> Prisma Client
RUN npx prisma generate
# <20><>遣 TypeScript
RUN npm run build
# ==================== <20>煺漣<E785BA>嗆挾 ====================
FROM node:20-alpine
WORKDIR /app
# 憭滚<E686AD>靘肽<E99D98><E882BD><EFBFBD>
COPY package*.json ./
COPY prisma ./prisma/
# 隞<><E99A9E><EFBFBD><E98B86>鈭找<E988AD>韏?RUN npm ci --only=production
# <20><><EFBFBD> Prisma Client
RUN npx prisma generate
# 隞擧<E99A9E>撱粹𧫴畾萄<E795BE><E89084><EFBFBD>霂穃<E99C82><E7A983><EFBFBD><EFBFBD>?COPY --from=builder /app/dist ./dist
# 憭滚<E686AD><E6BB9A><EFBFBD><E8B9B1><EFBFBD>隞塚<E99A9E><EFBFBD>rompts嚗?COPY prompts ./prompts
COPY config ./config
# <20>𥕦遣<F0A595A6>𡇙oot<6F><EFBFBD>
RUN addgroup -g 1001 -S nodejs && \
adduser -S nodejs -u 1001
# <20><><EFBFBD><EFBFBD>root<6F><EFBFBD>
USER nodejs
# <20>湧蠧蝡臬藁
EXPOSE 3001
# <20>亙熒璉<E78692><E79289>?HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD node -e "require('http').get('http://localhost:3001/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
# <20>臬𢆡摨𠉛鍂
CMD ["node", "dist/index.js"]
```
---
### docker-compose.yml嚗<6C>𧋦<EFBFBD><EFBFBD>霂𤏪<E99C82>
**<EFBFBD><EFBFBD>辣**嚗䫤docker-compose.yml`
```yaml
version: '3.8'
services:
# PostgreSQL<51>唳旿摨? postgres:
image: postgres:15-alpine
container_name: aiclinical-postgres
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: aiclinical_dev
ports:
- "5432:5432"
volumes:
- postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
# Redis嚗<73><EFBFBD><EFBFBD>
redis:
image: redis:7-alpine
container_name: aiclinical-redis
ports:
- "6379:6379"
volumes:
- redis-data:/data
# <20>𡒊垢摨𠉛鍂
backend:
build:
context: ./backend
dockerfile: Dockerfile
container_name: aiclinical-backend
ports:
- "3001:3001"
environment:
NODE_ENV: development
STORAGE_TYPE: local
DATABASE_URL: postgresql://postgres:postgres@postgres:5432/aiclinical_dev
depends_on:
postgres:
condition: service_healthy
volumes:
- ./backend/uploads:/app/uploads # <20>砍𧑐摮睃<E691AE><E79D83><EFBFBD>
volumes:
postgres-data:
redis-data:
```
**雿輻鍂<E8BCBB><EFBFBD>**嚗?```bash
# <20>臬𢆡<E887AC><F0A286A1><EFBFBD><EFBFBD><E39787>?docker-compose up -d
# <20><EFBFBD><E4BAA6><EFBFBD>
docker-compose logs -f backend
# <20>𨀣迫<F0A880A3>滚𦛚
docker-compose down
```
---
## <20><> <20>函蔡瘚<E894A1><E7989A>
### Step 1: <20><><EFBFBD><EFBFBD><EFBFBD>鈭烐<E988AD><E78390>?
#### 1.1 撘<><E69298><EFBFBD><E5B88B>唳旿摨?RDS
```bash
# 1. <20><EFBFBD><E9A483><EFBFBD>鈭烐綉<E78390>嗅蝱
# 2. <20>𦦵揣"鈭烐㺭<E78390><EFBFBD> RDS"
# 3. <20>𥕦遣摰硺<E691B0>
# - <20>唳旿摨梶掩<E6A2B6><EFBFBD>PostgreSQL 15
# - 閫<>聢嚗?<3F>?GB嚗<42><E59A97>𡁶鍂<F0A181B6><EFBFBD>
# - 摮睃<E691AE>蝛粹𡢿嚗?0GB嚗𠄎SD嚗?# - 蝵𤑳<E89DB5>嚗间PC嚗<43><E59A97>SAE<41><45><EFBFBD><E8BAB9><EFBFBD>
# 4. 霈曄蔭<E69B84><EFBFBD><E8B3A2>𤏪<EFBFBD>瘛餃<E7989B> SAE 摨𠉛鍂<F0A0899B>?VPC蝵烐挾嚗?# 5. <20>𥕦遣<F0A595A6>唳旿摨梶鍂<E6A2B6>?# 6. <20>𥕦遣<F0A595A6>唳旿摨橒<E691A8>aiclinical_prod
```
#### 1.2 撘<><E69298>𡁜笆鞊<E99E8A><E288AA>?OSS
```bash
# 1. <20>𦦵揣"撖寡情摮睃<E691AE> OSS"
# 2. <20>𥕦遣 Bucket
# - Bucket<65>滨妍嚗惨iclinical-prod
# - <20><EFBFBD>嚗𡁜<E59A97>銝?嚗<>㜺撌痹<E6928C>
# - 摮睃<E691AE>蝐餃<E89D90>嚗𡁏<E59A97><F0A1818F><EFBFBD><EFBFBD><EFBFBD>?# - 霈輸䔮<E8BCB8><E494AE><EFBFBD>嚗𡁶<E59A97><F0A181B6>?# 3. <20>𥕦遣 RAM <20><EFBFBD><EFBFBD>鍂鈭垾PI霈輸䔮嚗?# - <20><><EFBFBD>嚗鋫liyunOSSFullAccess
# - <20><EFBFBD> AccessKeyId <20>?AccessKeySecret
```
#### 1.3 撘<><E69298>𡁜捆<F0A1819C><EFBFBD><E588B8>𤩺<EFBFBD><F0A4A9BA>?
```bash
# 1. <20>𦦵揣"摰孵膥<E5ADB5>𨅯<EFBFBD><F0A885AF>滚𦛚 ACR"
# 2. <20>𥕦遣<F0A595A6><EFBFBD>蝛粹𡢿嚗惨iclinical
# 3. <20>𥕦遣<F0A595A6>𨅯<EFBFBD>隞枏<E99A9E>嚗颹ackend
# - 蝐餃<E89D90>嚗𡁶<E59A97><F0A181B6>?# - <20><EFBFBD>嚗𡁜<E59A97>銝?嚗<>㜺撌痹<E6928C>
```
---
### Step 2: <20><><EFBFBD>峕綫<E5B395><E7B6AB><EFBFBD><EFBFBD>?
```bash
# 1. <20><EFBFBD><E9A483><EFBFBD>鈭穃捆<E7A983><EFBFBD><E588B8>𤩺<EFBFBD><F0A4A9BA>?docker login --username=<雿删<E99BBF><E588A0><EFBFBD>鈭𤏸揭<F0A48FB8>? registry.cn-hangzhou.aliyuncs.com
# 2. <20><><EFBFBD>𨅯<EFBFBD>
cd backend
docker build -t aiclinical-backend:v1.0.0 .
# 3. <20>𤘪<EFBFBD>蝑?docker tag aiclinical-backend:v1.0.0 \
registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:v1.0.0
# 4. <20><EFBFBD><E588B8><EFBFBD><EFBFBD><EFBFBD>鈭?docker push registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:v1.0.0
```
**<2A>𡁏𧋦<F0A1818F>芸𢆡<E88AB8>?*嚗?
**<2A><>辣**嚗䫤backend/scripts/build-and-push.sh`
```bash
#!/bin/bash
VERSION=$1
if [ -z "$VERSION" ]; then
echo "Usage: ./build-and-push.sh <version>"
echo "Example: ./build-and-push.sh v1.0.0"
exit 1
fi
echo "<EFBFBD><20><><EFBFBD>𨅯<EFBFBD>: $VERSION"
docker build -t aiclinical-backend:$VERSION .
echo "<EFBFBD>噡儭?<3F>𤘪<EFBFBD>蝑?
docker tag aiclinical-backend:$VERSION \
registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:$VERSION
echo "<EFBFBD><20><EFBFBD><E588B8><EFBFBD><EFBFBD><EFBFBD>鈭?
docker push registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:$VERSION
echo "<22>?摰峕<E691B0>嚗?
echo "<EFBFBD>𨅯<EFBFBD><EFBFBD><EFBFBD>: registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:$VERSION"
```
---
### Step 3: <20>𥕦遣 SAE 摨𠉛鍂
#### 3.1 <20>箸𧋦<E7AEB8>滨蔭
```yaml
摨𠉛鍂<EFBFBD>滨妍: aiclinical-backend
摨𠉛鍂蝐餃<EFBFBD>: 摰孵膥<EFBFBD>𨅯<EFBFBD>
<EFBFBD>𨅯<EFBFBD><EFBFBD><EFBFBD>: registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:v1.0.0
蝡臬藁: 3001
<EFBFBD>亙熒璉<EFBFBD><EFBFBD>亥楝敺? /health
```
#### 3.2 摰硺<E691B0><E7A1BA>滨蔭
```yaml
摰硺<EFBFBD><EFBFBD>: 1C2G
<EFBFBD><EFBFBD>撠誩<EFBFBD>靘𧢲㺭: 1
<EFBFBD><EFBFBD>憭批<EFBFBD>靘𧢲㺭: 10
CPU閫血<EFBFBD><EFBFBD>拙捆: 70%
<EFBFBD><EFBFBD><EFBFBD>閫血<EFBFBD><EFBFBD>拙捆: 80%
```
#### 3.3 <20><EFBFBD><E887AC><EFBFBD><E3979B>滨蔭
<EFBFBD>?SAE <20><EFBFBD><E689B9><EFBFBD>蝵桃㴓憓<E3B493><E68693><EFBFBD>𧶏<EFBFBD>**<2A><EFBFBD>嚗?*嚗㚁<E59A97>
```bash
NODE_ENV=production
STORAGE_TYPE=oss
DATABASE_URL=postgresql://aiclinical:***@rm-xxx.mysql.rds.aliyuncs.com:5432/aiclinical_prod
OSS_REGION=oss-cn-hangzhou
OSS_ACCESS_KEY_ID=LTAI5t***
OSS_ACCESS_KEY_SECRET=***
OSS_BUCKET=aiclinical-prod
LLM_API_KEY=sk-***
LOG_LEVEL=info
```
#### 3.4 VPC蝵𤑳<E89DB5><F0A491B3>滨蔭
```yaml
VPC: <EFBFBD>㗇𥋘銝竃DS<EFBFBD><EFBFBD><EFBFBD><EFBFBD>PC
摰匧<EFBFBD>蝏? <20><>捂3001蝡臬藁<E887AC><EFBFBD>
<EFBFBD><EFBFBD>霈輸䔮: <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>SLB嚗?```
---
### Step 4: <20>函蔡摨𠉛鍂
```bash
# 1. <20>?SAE <20><EFBFBD><E689B9><EFBFBD><E59581>?<3F>函蔡摨𠉛鍂"
# 2. <20>㗇𥋘<E39787>𨅯<EFBFBD><F0A885AF><EFBFBD>𧋦
# 3. 蝖株恕<E6A0AA>滨蔭<E6BBA8>㰘秤
# 4. <20>孵稬"蝖桀<E89D96>"撘<>憪钅<E686AA>蝵?
# 蝑匧<E89D91>3-5<><35><EFBFBD>嚗峕䰻<E5B395><EFBFBD>蝵脫𠯫敹?```
**<2A>函蔡<E587BD>𣂼<EFBFBD><F0A382BC><EFBFBD><EFBFBD>**嚗?- <EFBFBD>?摰硺<E691B0><E7A1BA><EFBFBD><E59786><EFBFBD>餈鞱<E9A488>銝?- <20>?<3F>亙熒璉<E78692><E79289><EFBFBD><E4BC90><EFBFBD>
- <EFBFBD>?霈輸䔮 `http://<SAE<41><EFBFBD><E7A589><EFBFBD>>/health` 餈𥪜<E9A488> 200
---
### Step 5: 撉諹<E69289><E8ABB9>函蔡
```bash
# 1. <20>亙熒璉<E78692><E79289>?curl http://<SAE<41><EFBFBD><E7A589><EFBFBD>>/health
# 憸<><E686B8><EFBFBD><EFBFBD>嚗?{
"status": "ok",
"database": "connected",
"timestamp": "2025-11-16T10:30:00.000Z"
}
# 2. API瘚贝<E7989A>
curl http://<SAE<41><EFBFBD><E7A589><EFBFBD>>/api/v1/health
# 3. <20><EFBFBD><E4BAA6><EFBFBD>
# <20>?SAE <20><EFBFBD><E689B9>?<3F>?摨𠉛鍂霂行<E99C82> <20>?摰墧𧒄<E5A2A7><EFBFBD>
```
---
## <20><> <20>鞉𧋦隡啁<E99AA1>
### <20><EFBFBD><E884B2>嗆挾嚗?00<30><EFBFBD>嚗峕𠯫瘣?0嚗?
| <20>滚𦛚 | 閫<>聢 | <20><>晶 |
|------|------|------|
| SAE | 1C2G <20> 1摰硺<E691B0> | 瞼200 |
| RDS | 2C4G <20>𡁶鍂<F0A181B6>?| 瞼300 |
| OSS | 100GB<47><42><EFBFBD>摮睃<E691AE> + 10GB瘚<42><E7989A> | 瞼15 |
| ACR | 蝘<><E89D98>隞枏<E99A9E> | 瞼0嚗<30><E59A97>韐寥<E99F90>摨佗<E691A8> |
| **<EFBFBD><EFBFBD>恣** | | **瞼515/<2F>?* |
### <20>鞾鵭<E99EBE><E9B5AD><EFBFBD>1000<30><EFBFBD>嚗峕𠯫瘣?00嚗?
| <20>滚𦛚 | 閫<>聢 | <20><>晶 |
|------|------|------|
| SAE | 2C4G <20> 撟喳<E6929F>3摰硺<E691B0> | 瞼600 |
| RDS | 4C8G <20>𡁶鍂<F0A181B6>?| 瞼600 |
| OSS | 500GB<47><42><EFBFBD>摮睃<E691AE> + 50GB瘚<42><E7989A> | 瞼70 |
| CDN | 100GB瘚<42><E7989A><EFBFBD><EFBFBD><EFBFBD> | 瞼20 |
| **<EFBFBD><EFBFBD>恣** | | **瞼1290/<2F>?* |
### <20><EFBFBD><E99E9F><EFBFBD><EFBFBD>10000<30><EFBFBD>嚗峕𠯫瘣?000嚗?
| <20>滚𦛚 | 閫<>聢 | <20><>晶 |
|------|------|------|
| SAE | 4C8G <20> 撟喳<E6929F>10摰硺<E691B0> | 瞼2000 |
| RDS | 8C16G <20>砌澈<E7A08C>?+ 霂餃<E99C82><E9A483><EFBFBD>氖 | 瞼2000 |
| OSS | 2TB<54><42><EFBFBD>摮睃<E691AE> + 500GB瘚<42><E7989A> | 瞼300 |
| CDN | 1TB瘚<42><E7989A> | 瞼200 |
| Redis | 2GB<47><42><EFBFBD><EFBFBD>?| 瞼200 |
| **<EFBFBD><EFBFBD>恣** | | **瞼4700/<2F>?* |
---
## <20><> <20>烐綉銝𤾸<E98A9D>霅?
### 摨𠉛鍂<F0A0899B><EFBFBD><E689AF>烐綉嚗㇁RMS嚗?
```yaml
# <20>?SAE <20><EFBFBD><E689B9><EFBFBD><E595A3>?ARMS
<EFBFBD>烐綉<EFBFBD><EFBFBD><EFBFBD>:
- RT嚗<EFBFBD><EFBFBD>摨娍𧒄<EFBFBD><EFBFBD>
- QPS嚗<EFBFBD><EFBFBD>蝘坿窈瘙<EFBFBD>㺭嚗? - <20>躰秤<E8BAB0>? - JVM<56><4D><EFBFBD>
<EFBFBD>𡃏郎閫<EFBFBD><EFBFBD>:
- RT > 3蝘? - <20>躰秤<E8BAB0>?> 5%
- <EFBFBD>舐鍂<EFBFBD>?< 99%
```
### <20>唳旿摨梶<E691A8><E6A2B6>?
```yaml
# RDS <20><EFBFBD><E689B9>?<3F>?<3F>烐綉銝擧𥁒霅?<3F>烐綉<E78390><E7B689><EFBFBD>:
- CPU<EFBFBD>拍鍂<EFBFBD>? - <20><><EFBFBD><EFBFBD>拍鍂<E68B8D>? - 餈墧𦻖<E5A2A7>? - IOPS
<EFBFBD>𡃏郎閫<EFBFBD><EFBFBD>:
- CPU > 80%
- 餈墧𦻖<EFBFBD>?> 320嚗?0%嚗? - 蝤<><E89DA4>雿輻鍂<E8BCBB>?> 80%
```
### <20>鞉𧋦<E99E89>𡃏郎
```bash
# 韐寧鍂銝剖<E98A9D> <20>?韐寧鍂憸<E98D82>
霈曄蔭憸<EFBFBD><EFBFBD>:
- 瘥𤩺<E798A5><EFBFBD><E686B8>: 瞼1000
- 80%憸<>郎: 瞼800
- <20>𡁶䰻<F0A181B6><EFBFBD>: <20>桐辣 + <20>凋縑
```
---
## <20><> <20><><EFBFBD><EFBFBD>埝䰻
### 撣貉<E692A3><E8B289><EFBFBD>
#### <20><EFBFBD>1嚗𡁏㺭<F0A1818F><EFBFBD>餈墧𦻖憭梯揖
**<EFBFBD><EFBFBD>𠶖**嚗𡁜<E59A97><F0A1819C>典鍳<E585B8>典仃韐伐<E99F90><E4BC90><EFBFBD><E4BA99>曄內 `Connection refused`
**閫<><E996AB><EFBFBD><EFBFBD>**嚗?```bash
# 1. 璉<><E79289>?RDS <20><EFBFBD><E8B3A2>?# 蝖桐<E89D96>瘛餃<E7989B>鈭?SAE 摨𠉛鍂<F0A0899B>?VPC蝵烐挾
# 2. 璉<><E79289>?DATABASE_URL <20><EFBFBD>
# 甇<><EFBFBD><EFBFBD>: postgresql://user:pass@host:port/db
# 3. 璉<><E79289>?RDS 摰硺<E691B0><E7A1BA><EFBFBD>?# 蝖桐<E89D96>摰硺<E691B0>餈鞱<E9A488>銝?```
---
#### <20><EFBFBD>2嚗鐾SS 銝𠹺<E98A9D>憭梯揖
**<2A><>𠶖**嚗𡁏<E59A97>隞嗡<E99A9E>隡㰘<E99AA1><E3B098>?403 Forbidden
**閫<><E996AB><EFBFBD><EFBFBD>**嚗?```bash
# 1. 璉<><E79289>?RAM <20><EFBFBD><E586BD><EFBFBD><EFBFBD>
# 蝖桐<E89D96><E6A190>?AliyunOSSFullAccess
# 2. 璉<><E79289>?Bucket <20><><EFBFBD>
# 蝖桐<E89D96>摨𠉛鍂<F0A0899B><EFBFBD><E58CA7><EFBFBD><E4BAA4>?
# 3. 璉<><E79289>亦㴓憓<E3B493><E68693><EFBFBD>?# OSS_ACCESS_KEY_ID <20>?OSS_ACCESS_KEY_SECRET <20>臬炏甇<E7828F>
```
---
#### <20><EFBFBD>3嚗朞<E59A97><E69C9E>交㺭<E4BAA4>堒偷
**<2A><>𠶖**嚗䫤Connection pool exhausted`
**閫<><E996AB><EFBFBD><EFBFBD>**嚗?```typescript
// 1. 璉<><E79289><EFBFBD><E4BA99><EFBFBD><E6BBA9>交㺭
const result = await prisma.$queryRaw`
SELECT count(*) FROM pg_stat_activity
`
// 2. 靚<>㟲餈墧𦻖瘙𣳇<E79899>蝵?// <20><EFBFBD>瘥誩<E798A5>靘贝<E99D98><E8B49D>交㺭嚗峕<E59A97>憓𧼮<E68693> RDS 閫<>
// 3. 璉<><E79289>交糓<E4BAA4><EFBFBD>餈墧𦻖瘜<F0A6BB96><E7989C>
// 蝖桐<E89D96><E6A190><EFBFBD><EFBFBD>㗇䰻霂<E99C82><EFBFBD><EFBFBD>喲𡡒
```
---
## <20><> <20>湔鰵<E6B994><EFBFBD>
| <20><EFBFBD> | <20><>𧋦 | <20>䀹凒<E480B9><E58792>捆 | 蝏湔擪<E6B994>?|
|------|------|---------|--------|
| 2025-11-16 | V1.0 | <20>𥕦遣<F0A595A6><E981A3>﹝嚗<EFB99D><E59A97>銋劐<E98A8B><E58A90><EFBFBD><E6AFBA>函蔡<E587BD><EFBFBD> | <20><EFBFBD><E59786><EFBFBD> |
---
## <20><> <20><EFBFBD><E8A9A8><EFBFBD>
- [<EFBFBD><EFBFBD>蝡舀芋<EFBFBD><EFBFBD><EFBFBD><EFBFBD>霈曇恣-V2](../00-蝟餌<E89D9F><E9A48C><EFBFBD>霈曇恣/<2F><EFBFBD>蝡舀芋<E88880><EFBFBD><E5A092><EFBFBD>霈曇恣-V2.md) - <20><EFBFBD><E59786>餌熔
- [鈭穃<EFBFBD><EFBFBD><EFBFBD><EFBFBD>𤏸<EFBFBD><EFBFBD><EFBFBD>(../04-撘<><E69298>𤏸<EFBFBD><F0A48FB8>?08-鈭穃<E988AD><E7A983><EFBFBD><E7AC94>𤏸<EFBFBD><F0A48FB8>?md) - DO/DON'T 璉<><E79289><EFBFBD><E4BAA4>?- [Schema<6D>𠉛氖<F0A0899B><EFBFBD>霈曇恣](./01-Schema<6D>𠉛氖<F0A0899B><EFBFBD>霈曇恣嚗?0銝迎<E98A9D>.md)
- [<5B>唳旿摨栞<E691A8><E6A09E>仿<EFBFBD>蝵孫(./02-<2D>唳旿摨栞<E691A8><E6A09E>仿<EFBFBD>蝵?md)
---
**<EFBFBD><EFBFBD>﹝蝏湔擪<EFBFBD><EFBFBD><EFBFBD>** <20><EFBFBD><E59786><EFBFBD>
**<EFBFBD><EFBFBD><EFBFBD>擧凒<EFBFBD><EFBFBD>** 2025-11-16
**<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>** <20>?撌脣<E6928C><E884A3>?