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

25 KiB
Raw Blame History

鈭穃<EFBFBD><EFBFBD><EFBFBD>蝵脫沲<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?

*<EFBFBD><EFBFBD><EFBFBD><EFBFBD>𧋦嚗? V1.0
*<EFBFBD>𥕦遣<EFBFBD><EFBFBD>嚗? 2025-11-16
*<EFBFBD><EFBFBD>鍂撖寡情嚗? <20>𡒊垢撘<E59EA2><E69298><EFBFBD><E3BBAB><EFBFBD><E6B2B2><EFBFBD><EFBFBD><EFBFBD><EFBFBD>蝏? 蝏湔擪<EFBFBD><EFBFBD><EFBFBD> <20><EFBFBD><E59786><EFBFBD>
<EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20>?撌脣<E6928C><E884A3>?


<EFBFBD><EFBFBD> <20><>﹝霂湔<E99C82>

<EFBFBD><EFBFBD><EFBFBD><EFBFBD>靘?AI銝游<EFBFBD><EFBFBD>𠉛弦撟喳蝱 <20>函蔡<E587BD>圈燵<E59C88><EFBFBD> Serverless <20><EFBFBD><E59786><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E6B994>𨰜<EFBFBD>?

**潃?<3F><EFBFBD><E6BBA9>湔鰵嚗?025-11-16嚗?*嚗? 撟喳蝱<E596B3><EFBFBD>霈暹鴌<E69AB9><E9B48C>祕蝏<E7A595><E89D8F><EFBFBD>質恣<E8B3AA><EFBFBD><EFBFBD><E99A9E>摰䂿緵撌脰<E6928C>蝘餃<E89D98>嚗? 撟喳蝱<EFBFBD><EFBFBD>霈暹鴌閫<EFBFBD><EFBFBD>

<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗?> - 鈭穃<E988AD><E7A983><EFBFBD><EFBFBD><E6B2B2><EFBFBD><EFBFBD>霈曇恣

  • <EFBFBD><EFBFBD>鈭烐<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>蝵?> - Docker摰孵膥<E5ADB5><EFBFBD><E7A18B>函蔡瘚<E894A1><E7989A>
  • <EFBFBD>鞉𧋦隡啁<EFBFBD><EFBFBD>𣬚<EFBFBD><EFBFBD><EFBFBD>霅? **<2A><>﹝摰帋<E691B0>**嚗?- <20><EFBFBD><EFBFBD><E78DA2>03嚗㚁<E59A97>鈭穃<EFBFBD><EFBFBD><EFBFBD>蝵脫沲<EFBFBD><EFBFBD><EFBFBD><EFBFBD> - 靘折<E99D98>鈭烐<E988AD><E78390><EFBFBD><E288AA>函蔡瘚<E894A1><E7989A>
  • 04<EFBFBD><EFBFBD>﹝嚗?撟喳蝱<EFBFBD><EFBFBD>霈暹鴌閫<EFBFBD><EFBFBD>* - 靘折<E99D98><EFBFBD><E99A9E>摰䂿緵<E482BF><E7B7B5><EFBFBD><EFBFBD><EFBFBD><E78390>? <EFBFBD><EFBFBD><EFBFBD>園𡢿嚗?0 <20><><EFBFBD>
    摰墧鴌<EFBFBD>園𡢿嚗𡁜<EFBFBD>閫?撟喳蝱<EFBFBD><EFBFBD>霈暹鴌閫<EFBFBD><EFBFBD> <20>?.5憭拙<EFBFBD><EFBFBD>質恣<EFBFBD>?

<EFBFBD><EFBFBD>儭?<3F><EFBFBD>霂西圾

1. Serverless 摨𠉛鍂撘閙<E69298> (SAE)

*鈭批<EFBFBD><EFBFBD><EFBFBD>?

<EFBFBD><EFBFBD>? 霂湔<EFBFBD> 隡睃飵
*<EFBFBD>芸𢆡<EFBFBD>拍憬摰? <EFBFBD>寞旿瘚<EFBFBD><EFBFBD><EFBFBD>芸𢆡靚<EFBFBD>㟲摰硺<EFBFBD><EFBFBD><EFBFBD>0-100嚗? 擃睃陸<EFBFBD><EFBFBD>摰閙㦤嚗䔶<EFBFBD>靚瑟<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
<EFBFBD><EFBFBD>隞䁅晶 瞼0.000110592/霂瑟<E99C82>甈?+ 摰硺<E691B0>韐? <EFBFBD><EFBFBD><EFBFBD><EFBFBD>晶蝥?瞼200-500
*摰孵膥<EFBFBD><EFBFBD>蝵? <EFBFBD><EFBFBD> Docker <20>𨅯<EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>敹恍<EFBFBD><EFBFBD>皛?
<EFBFBD><EFBFBD>蔭韐蠘蝸<EFBFBD><EFBFBD> <EFBFBD>芸𢆡<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <EFBFBD>𣳇<EFBFBD><EFBFBD>閧𡠺韐凋僭 SLB
*<EFBFBD>亙熒璉<EFBFBD><EFBFBD>? <EFBFBD>芸𢆡<EFBFBD>滚鍳撘<EFBFBD>虜摰硺<EFBFBD> <EFBFBD><EFBFBD><EFBFBD>舐鍂<EFBFBD>?

摰硺<EFBFBD><EFBFBD><EFBFBD>㗇𥋘

<EFBFBD>嗆挾 <EFBFBD> vCPU <EFBFBD><EFBFBD><EFBFBD> <EFBFBD><EFBFBD><EFBFBD>箸艶
<EFBFBD><EFBFBD>?瘚贝<E7989A> 0.5C1G 0.5<EFBFBD>? 1GB <EFBFBD>亥窈瘙?< 1000
<EFBFBD><EFBFBD> 1C2G 1<EFBFBD>? 2GB <EFBFBD>亥窈瘙?1000-5000
*<EFBFBD>鞾鵭<EFBFBD>? 2C4G 2<EFBFBD>? 4GB <EFBFBD>亥窈瘙?5000-20000
*<EFBFBD><EFBFBD><EFBFBD>? 4C8G 4<EFBFBD>? 8GB <EFBFBD>亥窈瘙?> 20000

撱箄悅<EFBFBD>滨蔭嚗?```yaml

SAE 摨𠉛鍂<F0A0899B>滨蔭

摰硺<EFBFBD><EFBFBD>聢: 1C2G <0A><>撠誩<E692A0>靘𧢲㺭: 1 # <20><EFBFBD><E8B8B9>瑕鍳<E79195>?<3F><>憭批<E686AD>靘𧢲㺭: 10 CPU 閫血<E996AB><E8A180>拙捆<E68B99><E68D86><EFBFBD>? 70% <0A><><EFBFBD>閫血<E996AB><E8A180>拙捆<E68B99><E68D86><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>撘?*嚗?``` 瘥誩<E798A5>靘贝<E99D98><E8B49D>交㺭 = RDS<44><53>憭扯<E686AD><E689AF>交㺭 / SAE<41><45>憭批<E686AD>靘𧢲㺭 <20> 0.8嚗<EFBFBD><EFBFBD><EFBFBD>函頂<EFBFBD><EFBFBD>

蝷箔<EFBFBD>嚗?RDS: 400餈墧𦻖 SAE: <20><>憭?0摰硺<E691B0> 瘥誩<E798A5>靘? 400 / 10 <20> 0.8 = 32餈墧𦻖


---

### 3. 撖寡情摮睃<E691AE> OSS

#### **Bucket <20>滨蔭**

```yaml
Bucket<65>滨妍: aiclinical-prod
<0A><EFBFBD>: <20>𦒘<EFBFBD>1嚗<31>㜺撌痹<E6928C>oss-cn-hangzhou
摮睃<E691AE>蝐餃<E89D90>: <20><><EFBFBD>摮睃<E691AE>
霈輸䔮<E8BCB8><E494AE><EFBFBD>: 蝘<><E89D98>嚗㇊rivate嚗?<3F><>𧋦<EFBFBD><EFBFBD>: 撘<><E69298>?頝典<E9A09D>霈曄蔭: <20><><EFBFBD>滨垢<E6BBA8><EFBFBD>

<EFBFBD><EFBFBD>蝏𤘪<EFBFBD><EFBFBD><EFBFBD>

aiclinical-prod/
<0A><EFBFBD><E98EBF><EFBFBD> asl/
<0A>?  <20><EFBFBD><E98EBF><EFBFBD> pdfs/               # PDF<44><46><0A>?  <20><EFBFBD><E98EBF><EFBFBD> excel/              # Excel<65><6C><0A>?  <20><EFBFBD><E5A999><EFBFBD> exports/            # 撖澆枂<E6BE86><E69E82><0A><EFBFBD><E98EBF><EFBFBD> avatars/                # <20><EFBFBD>憭游<E686AD>
<0A><EFBFBD><E98EBF><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>霈輸䔮
        }
      ]
    }
  ]
}

<EFBFBD>凃 摮睃<E691AE><E79D83>質情撅<E68385>挽霈∴<E99C88><E288B4><EFBFBD>嚗?

<EFBFBD>亙藁摰帋<EFBFBD>

<EFBFBD><EFBFBD>嚗䫤backend/src/common/storage/StorageAdapter.ts`

/**
 * 摮睃<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`

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>

<EFBFBD><EFBFBD>嚗䫤backend/src/common/storage/OSSAdapter.ts`

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>蝐?

<EFBFBD><EFBFBD>嚗䫤backend/src/common/storage/StorageFactory.ts`

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(`<60>芰䰻<E88AB0><E4B0BB><EFBFBD><EFBFBD>函掩<E587BD>? ${storageType}`)
    }
    
    return this.instance
  }
  
  /**
   * <20>滨蔭摰硺<E691B0><EFBFBD>鍂鈭擧<E988AD>霂𤏪<E99C82>
   */
  static reset() {
    this.instance = null
  }
}

// 撖澆枂<E6BE86><EFBFBD>
export const storage = StorageFactory.create()

雿輻鍂蝷箔<EFBFBD>

// 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 })
}

<EFBFBD><20>唳旿摨栞<E691A8><E6A09E><EFBFBD><E4BAA4>滨蔭

Prisma<EFBFBD>滨蔭

<EFBFBD><EFBFBD>嚗䫤backend/src/config/database.ts`

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(`<60><> 敶枏<E695B6><E69E8F>唳旿摨栞<E691A8><E6A09E>交㺭: ${connectionCount}`)
      
      // <20>𡃏郎<F0A1838F><E9838E><EFBFBD><EFBFBD>80%
      const maxConnections = 400  // <20>寞旿RDS閫<53>聢霈曄蔭
      if (connectionCount > maxConnections * 0.8) {
        console.error(`<60>𩤃<EFBFBD> <20>唳旿摨栞<E691A8><E6A09E>交㺭餈<E3BAAD><E9A488>: ${connectionCount}/${maxConnections}`)
      }
    } catch (error) {
      console.error('餈墧𦻖<E5A2A7><EFBFBD><E59581>批仃韐?', error)
    }
  }, 60000)  // 瘥?0蝘埝<E89D98><E59F9D><EFBFBD>甈?}

<EFBFBD><EFBFBD> <20><EFBFBD><E887AC><EFBFBD>蝞∠<E89D9E>

<EFBFBD>砍𧑐撘<EFBFBD><EFBFBD>𤑳㴓憓?

<EFBFBD><EFBFBD>嚗䫤backend/.env.development`

# <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=

<EFBFBD>煺漣<EFBFBD><EFBFBD><EFBFBD>滨蔭

<EFBFBD>沒AE<EFBFBD><EFBFBD><EFBFBD><EFBFBD>蝵殷<EFBFBD>銝滩<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗?

# <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

<EFBFBD>閦 Docker <20>滨蔭

Dockerfile

<EFBFBD><EFBFBD>嚗䫤backend/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`

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

<EFBFBD>臬𢆡<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?docker-compose up -d

<EFBFBD><EFBFBD><EFBFBD><EFBFBD>

docker-compose logs -f backend

<EFBFBD>𨀣迫<EFBFBD>滚𦛚

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

# 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>?

# 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>?

# 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>?*嚗? <EFBFBD><EFBFBD>嚗䫤backend/scripts/build-and-push.sh`

#!/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 "<22><20><><EFBFBD>𨅯<EFBFBD>: $VERSION"
docker build -t aiclinical-backend:$VERSION .

echo "<22>噡儭?<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>滨蔭

摨𠉛鍂<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>滨蔭

摰硺<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>

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>滨蔭

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>

<EFBFBD><EFBFBD> <20>鞉𧋦隡啁<E99AA1>

<EFBFBD><EFBFBD><EFBFBD>嗆挾嚗?00<30><EFBFBD>嚗峕𠯫瘣?0嚗?

<EFBFBD>滚𦛚 <EFBFBD> <EFBFBD><EFBFBD>
SAE 1C2G <20> 1摰硺<E691B0> 瞼200
RDS 2C4G <20>𡁶鍂<F0A181B6>? 瞼300
OSS 100GB<EFBFBD><EFBFBD><EFBFBD>摮睃<EFBFBD> + 10GB瘚<42><E7989A> 瞼15
ACR <EFBFBD><EFBFBD>隞枏<EFBFBD> 瞼0嚗<EFBFBD><EFBFBD>韐寥<EFBFBD>摨佗<EFBFBD>
<EFBFBD><EFBFBD> *瞼515/<2F>?

<EFBFBD>鞾鵭<EFBFBD><EFBFBD><EFBFBD>1000<EFBFBD><EFBFBD>嚗峕𠯫瘣?00嚗?

<EFBFBD>滚𦛚 <EFBFBD> <EFBFBD><EFBFBD>
SAE 2C4G <20> 撟喳<E6929F>3摰硺<E691B0> 瞼600
RDS 4C8G <20>𡁶鍂<F0A181B6>? 瞼600
OSS 500GB<EFBFBD><EFBFBD><EFBFBD>摮睃<EFBFBD> + 50GB瘚<42><E7989A> 瞼70
CDN 100GB瘚<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 瞼20
<EFBFBD><EFBFBD> *瞼1290/<2F>?

<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>10000<EFBFBD><EFBFBD>嚗峕𠯫瘣?000嚗?

<EFBFBD>滚𦛚 <EFBFBD> <EFBFBD><EFBFBD>
SAE 4C8G <20> 撟喳<E6929F>10摰硺<E691B0> 瞼2000
RDS 8C16G <20>砌澈<E7A08C>?+ 霂餃<E99C82><E9A483><EFBFBD> 瞼2000
OSS 2TB<EFBFBD><EFBFBD><EFBFBD>摮睃<EFBFBD> + 500GB瘚<42><E7989A> 瞼300
CDN 1TB瘚<EFBFBD><EFBFBD> 瞼200
Redis 2GB<EFBFBD><EFBFBD><EFBFBD><EFBFBD>? 瞼200
<EFBFBD><EFBFBD> *瞼4700/<2F>?

<EFBFBD><EFBFBD> <20>烐綉銝𤾸<E98A9D>霅?

摨𠉛鍂<EFBFBD><EFBFBD><EFBFBD>烐綉嚗㇁RMS嚗?

# <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%

<EFBFBD>唳旿摨梶<EFBFBD><EFBFBD>?

# 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%

<EFBFBD>鞉𧋦<EFBFBD>𡃏郎

# 韐寧鍂銝剖<E98A9D> <20>?韐寧鍂憸<E98D82>
霈曄蔭憸<E894AD><E686B8>:
  - 瘥𤩺<E798A5><EFBFBD><E686B8>: 瞼1000
  - 80%憸<>郎: 瞼800
  - <20>𡁶䰻<F0A181B6><EFBFBD>: <20>桐辣 + <20>凋縑

<EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>埝䰻

撣貉<EFBFBD><EFBFBD><EFBFBD>

<EFBFBD><EFBFBD>1嚗𡁏㺭<EFBFBD><EFBFBD>餈墧𦻖憭梯揖

<EFBFBD><EFBFBD>𠶖嚗𡁜<EFBFBD><EFBFBD>典鍳<EFBFBD>典仃韐伐<EFBFBD><EFBFBD><EFBFBD><EFBFBD>曄內 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><EFBFBD>: postgresql://user:pass@host:port/db

3. 璉<><E79289>?RDS 摰硺<E691B0><E7A1BA><EFBFBD>?# 蝖桐<E89D96>摰硺<E691B0>餈鞱<E9A488>銝?```


<EFBFBD><EFBFBD>2嚗鐾SS 銝𠹺<E98A9D>憭梯揖

<EFBFBD><EFBFBD>𠶖嚗𡁏<EFBFBD>隞嗡<EFBFBD>隡㰘<EFBFBD><EFBFBD>?403 Forbidden

**閫<><E996AB><EFBFBD><EFBFBD>**嚗?```bash

1. 璉<><E79289>?RAM <20><EFBFBD><E586BD><EFBFBD><EFBFBD>

蝖桐<EFBFBD><EFBFBD>?AliyunOSSFullAccess

2. 璉<><E79289>?Bucket <20><><EFBFBD>

蝖桐<EFBFBD>摨𠉛鍂<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?

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>喲𡡒

<EFBFBD><EFBFBD> <20>湔鰵<E6B994><EFBFBD>

<EFBFBD><EFBFBD> <EFBFBD><EFBFBD>𧋦 <EFBFBD>䀹凒<EFBFBD><EFBFBD> 蝏湔擪<EFBFBD>?
2025-11-16 V1.0 <EFBFBD>𥕦遣<EFBFBD><EFBFBD>﹝嚗<EFBFBD><EFBFBD>銋劐<EFBFBD><EFBFBD><EFBFBD><EFBFBD>函蔡<EFBFBD><EFBFBD> <EFBFBD><EFBFBD><EFBFBD><EFBFBD>

<EFBFBD><EFBFBD> <20><EFBFBD><E8A9A8><EFBFBD>


<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>?