# 关键配置补充说明 - 部署文档勘误与增强 > **文档版本:** v1.0 > **创建日期:** 2025-12-14 > **文档性质:** 对5个独立部署文档的关键补充 > **优先级:** ⭐⭐⭐⭐⭐ 必读(包含3个P0/P1致命问题) --- ## 📋 文档说明 本文档基于对5个独立部署文档的深度审查,补充了**3个致命问题**和**若干最佳实践**。这些内容在原文档中遗漏或未充分强调,但对生产环境部署至关重要。 **请在部署前务必阅读本文档!** --- ## 🚨 致命问题修正(P0/P1) ### 1. SAE孤岛效应 - NAT网关配置 ⭐⭐⭐⭐⭐ **问题严重度:P0(致命)** #### 问题描述 ``` SAE部署在VPC内,默认没有公网出口! 影响场景: ❌ 后端调用 DeepSeek/OpenAI API → 超时 ❌ Python下载公网PDF → 超时 ❌ npm install公网依赖(构建时)→ 失败 结果:所有AI功能不可用,系统基本瘫痪! ``` #### 解决方案 **方案A:NAT网关(推荐,生产环境)** ```bash # 步骤1:创建NAT网关 阿里云控制台 > VPC > NAT网关 > 创建NAT网关 ├─ VPC:选择SAE所在的VPC ├─ 交换机:选择SAE所在的交换机 ├─ 规格:小型(够用) └─ 计费方式:按使用量计费 # 步骤2:创建并绑定EIP NAT网关详情 > 弹性公网IP > 绑定弹性公网IP ├─ 创建新EIP或选择已有EIP ├─ 带宽:按使用流量(成本低) └─ 确认绑定 # 步骤3:配置SNAT条目 NAT网关详情 > SNAT管理 > 创建SNAT条目 ├─ 选择交换机:SAE所在的交换机(如 vsw-xxxxx) ├─ 选择公网IP:刚才绑定的EIP └─ 确认创建 成本:NAT网关¥60/月 + EIP流量费¥30-50/月 = ¥90-110/月 ``` **方案B:SAE绑定公网IP(部分地域支持)** ```bash SAE控制台 > 应用配置 > 网络配置 └─ 查看是否有"公网访问"或"绑定EIP"选项 ⚠️ 注意: - 并非所有地域都支持 - 优先使用方案A(更稳定) ``` #### 验证NAT网关是否生效 ```bash # 方法1:在SAE应用日志中查看 # 应用启动后,查看是否有DeepSeek API调用成功的日志 # 方法2:通过云助手执行命令(SAE控制台 > 实例列表 > 登录实例) curl -I https://api.deepseek.com # 应该返回 200 OK,而不是超时 # 方法3:测试Python下载公网PDF curl -I https://arxiv.org/pdf/2301.00001.pdf # 应该返回 200 OK ``` #### 更新的文档 - ✅ `00-部署架构总览.md`:物理架构图已增加NAT网关 - ✅ `00-部署架构总览.md`:成本估算已更新(¥1,200-1,250/月) - ⚠️ `05-Node.js后端-SAE容器部署指南.md`:需要在"SAE应用配置"章节增加网络配置说明 - ⚠️ `04-Python微服务-SAE容器部署指南.md`:同上 --- ### 2. 部署依赖死锁 - Dify API Key鸡生蛋问题 ⭐⭐⭐⭐⭐ **问题严重度:P1(严重)** #### 问题描述 ``` 死锁链: 1. 后端启动需要 DIFY_API_KEY 2. DIFY_API_KEY 需要 Dify 启动并人工登录后才能生成 3. 后端如果健康检查失败,会无限重启 结果:后端无法启动,或启动后PKB模块不可用 ``` #### 解决方案(分阶段部署) **阶段1:首次部署后端(临时配置)** ```bash # SAE环境变量配置 DIFY_API_KEY=temp_placeholder_will_update_later # ⚠️ 重要:后端代码需要容错处理 # backend/src/common/rag/DifyClient.ts constructor() { const apiKey = process.env.DIFY_API_KEY if (!apiKey || apiKey === 'temp' || apiKey.startsWith('temp_')) { console.warn('⚠️ Dify API Key未配置,PKB模块将不可用') this.enabled = false return } this.client = new DifySDK(apiKey) this.enabled = true } // 所有Dify调用前检查 async createDataset(name: string) { if (!this.enabled) { throw new Error('Dify服务未配置,请先配置DIFY_API_KEY环境变量') } // ... 正常逻辑 } ``` **阶段2:部署Dify并获取真实Key** ```bash # 1. 部署Dify到ECS(参考 03-Dify-ECS部署完全指南.md) cd /opt/dify docker-compose up -d # 2. 等待服务启动(约2-3分钟) docker-compose logs -f api # 3. 浏览器访问 http://ECS公网IP # 4. 注册管理员账号(首次访问会提示) # 5. 创建API Key # 设置 > API密钥 > 创建密钥 > 复制 # 格式:app-xxxxxxxxxxxxxxxxxxxxx # 6. 记录API Key(妥善保存) DIFY_API_KEY=app-xxxxxxxxxxxxxxxxxxxxx ``` **阶段3:更新后端配置** ```bash # SAE控制台 > 应用详情 > 环境变量 # 找到 DIFY_API_KEY,修改为真实值 DIFY_API_KEY=app-xxxxxxxxxxxxxxxxxxxxx # 保存 > 重启应用 # SAE会执行滚动重启(零停机) ``` **阶段4:验证PKB功能** ```bash # 测试知识库创建 curl -X POST https://your-api.com/api/v1/pkb/knowledge-bases \ -H "Authorization: Bearer YOUR_USER_TOKEN" \ -H "Content-Type: application/json" \ -d '{"name":"测试知识库","description":"测试"}' # 应该返回 200 OK,而不是 "Dify服务未配置" 错误 ``` #### 更新的文档 - ✅ `00-部署架构总览.md`:部署顺序已更新,明确分阶段部署 - ⚠️ `05-Node.js后端-SAE容器部署指南.md`:需要在"环境变量配置"章节增加临时配置说明 - ⚠️ `03-Dify-ECS部署完全指南.md`:需要在"首次访问"章节增加API Key生成步骤 --- ### 3. HTTP Client超时配置 - 防止连接泄漏 ⭐⭐⭐⭐ **问题严重度:P1(严重)** #### 问题描述 ``` Python服务处理PDF/OCR可能需要60-120秒 如果后端HTTP Client没有设置超时,会导致: ❌ 连接数堆积 ❌ 后端实例内存耗尽 ❌ 数据库连接池耗尽 ``` #### 解决方案 **后端HTTP Client配置** ```typescript // backend/src/common/http/httpClient.ts import axios from 'axios' export const pythonServiceClient = axios.create({ baseURL: process.env.EXTRACTION_SERVICE_URL || 'http://localhost:8000', timeout: 120000, // ⚠️ 120秒(2分钟) timeoutErrorMessage: 'Python微服务响应超时(>2分钟)', headers: { 'Content-Type': 'application/json' } }) // 请求拦截器(可选,用于日志) pythonServiceClient.interceptors.request.use( (config) => { console.log(`[HTTP] 调用Python服务: ${config.method?.toUpperCase()} ${config.url}`) return config }, (error) => Promise.reject(error) ) // 响应拦截器(错误处理) pythonServiceClient.interceptors.response.use( (response) => response, (error) => { if (error.code === 'ECONNABORTED') { console.error('[HTTP] Python服务超时:', error.message) } return Promise.reject(error) } ) ``` **Dify Client配置** ```typescript // backend/src/common/rag/DifyClient.ts import axios from 'axios' const difyHttpClient = axios.create({ baseURL: process.env.DIFY_API_URL || 'http://localhost/v1', timeout: 60000, // ⚠️ 60秒(Dify响应较快) headers: { 'Authorization': `Bearer ${process.env.DIFY_API_KEY}`, 'Content-Type': 'application/json' } }) ``` #### 超时时间建议 | 服务 | 超时时间 | 理由 | |------|---------|------| | **Python微服务** | 120秒 | PDF解析(Nougat OCR)可能需要60-120秒 | | **Dify API** | 60秒 | RAG检索通常<10秒,60秒足够 | | **外部LLM API** | 60秒 | DeepSeek/OpenAI流式响应,60秒足够 | | **数据库查询** | 30秒 | Prisma默认,复杂查询可能需要10-20秒 | #### 更新的文档 - ⚠️ `05-Node.js后端-SAE容器部署指南.md`:需要在"代码准备"章节增加HTTP Client配置 --- ## ⚠️ 重要安全配置 ### 4. ECS端口安全 - Redis/Weaviate不对外开放 ⭐⭐⭐⭐⭐ **问题严重度:P0(致命安全风险)** #### 问题描述 ``` Dify的Redis(6379)和Weaviate(8080)如果对公网开放: ❌ Redis无密码,可被攻击者直接访问 ❌ Weaviate包含敏感的向量数据 ❌ 可能被用于DDoS攻击的跳板 ``` #### 正确配置 **docker-compose.yaml 端口配置** ```yaml services: # ❌ 错误示例(危险) redis: ports: - "6379:6379" # 对所有网卡开放,包括公网! # ✅ 正确配置 redis: image: redis:6-alpine ports: - "127.0.0.1:6379:6379" # 只监听 localhost restart: always volumes: - ./volumes/redis/data:/data command: redis-server --save 60 1 --loglevel warning # ✅ 正确配置 weaviate: image: semitechnologies/weaviate:1.19.0 ports: - "127.0.0.1:8080:8080" # 只监听 localhost restart: always environment: - QUERY_DEFAULTS_LIMIT=25 - AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED=true - PERSISTENCE_DATA_PATH=/var/lib/weaviate # ✅ 只有Nginx需要对外(VPC内网) nginx: image: nginx:latest ports: - "80:80" # 对VPC内网开放(不是公网) restart: always volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf depends_on: - api - web ``` #### ECS安全组配置 ```bash # 安全组规则(ECS控制台 > 安全组 > 配置规则) 入方向规则: ├─ 允许 80/TCP 来源:VPC网段(172.16.0.0/12) # Nginx ├─ 允许 22/TCP 来源:您的办公室IP # SSH管理 └─ 拒绝 所有 来源:0.0.0.0/0 # 默认拒绝 出方向规则: └─ 允许 所有 目标:0.0.0.0/0 # 允许访问公网 ``` #### 验证安全配置 ```bash # 从公网测试(应该失败) telnet ECS公网IP 6379 # 应该超时或拒绝连接 telnet ECS公网IP 8080 # 应该超时或拒绝连接 # 从VPC内测试(应该成功) # 在SAE应用中执行 curl http://172.16.x.x # Dify内网地址 # 应该返回 Dify 的响应 ``` #### 更新的文档 - ⚠️ `03-Dify-ECS部署完全指南.md`:需要在"docker-compose.yaml配置"章节强调端口安全 --- ### 5. Nginx client_max_body_size - 支持大文件上传 ⭐⭐⭐⭐ **问题严重度:P2(一般)** #### 问题描述 ``` 医疗PDF可能很大(10-50MB) Nginx默认限制:1MB 结果:用户上传大文件时返回 413 Request Entity Too Large ``` #### 解决方案 **前端Nginx配置** ```nginx # frontend-v2/nginx.conf.template http { # ⚠️ 新增:支持大文件上传 client_max_body_size 50M; # ⚠️ 新增:开启gzip(React大体积JS) gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; gzip_min_length 1000; gzip_comp_level 6; server { listen 8080; server_name _; # 根目录 root /usr/share/nginx/html; index index.html; # API反向代理 location /api/ { proxy_pass http://${BACKEND_SERVICE_HOST}:${BACKEND_SERVICE_PORT}/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # ⚠️ 新增:代理超时配置 proxy_connect_timeout 120s; proxy_send_timeout 120s; proxy_read_timeout 120s; } # SPA路由 location / { try_files $uri $uri/ /index.html; } # 健康检查 location /health { access_log off; return 200 "healthy\n"; add_header Content-Type text/plain; } } } ``` #### 更新的文档 - ⚠️ `06-前端Nginx-SAE容器部署指南.md`:需要在"nginx.conf.template"章节增加配置 --- ### 6. Python Workers限制 - 防止OOM ⭐⭐⭐⭐⭐ **问题严重度:P1(严重)** #### 问题描述 ``` PyMuPDF/Nougat OCR非常吃内存(单个请求可能占用500MB-1GB) SAE配置:2GB内存 如果Gunicorn workers过多,会导致OOM(Out of Memory) ``` #### 解决方案 **Dockerfile配置** ```dockerfile # extraction_service/Dockerfile # 运行阶段 FROM python:3.11-slim # ... 其他配置 ... # ⚠️ 关键:限制workers防止OOM CMD ["gunicorn", "app.main:app", \ "--workers", "2", \ "--worker-class", "uvicorn.workers.UvicornWorker", \ "--bind", "0.0.0.0:8000", \ "--timeout", "120", \ "--max-requests", "100", \ "--max-requests-jitter", "10", \ "--access-logfile", "-", \ "--error-logfile", "-"] # workers=2: 最多2个worker(2GB内存限制) # timeout=120: 单个请求最多120秒(OCR可能很慢) # max-requests=100: 100个请求后重启worker(防止内存泄漏) ``` **SAE配置** ```bash # SAE控制台 > 应用配置 > 实例规格 CPU: 1核 内存: 2GB # ⚠️ 不要低于2GB # 实例数量 最小实例数: 1 最大实例数: 3(根据流量自动扩容) ``` #### Workers数量计算公式 ```python # 经验公式 workers = (CPU核数 × 2) + 1 # 但对于内存密集型应用(如PDF解析) workers = min((内存GB / 单worker内存GB), (CPU核数 × 2) + 1) # 示例:SAE 1核2GB 单worker内存 ≈ 800MB(PyMuPDF + Nougat) workers = min(2GB / 0.8GB, 1×2+1) = min(2.5, 3) = 2 # 结论:workers=2 是安全值 ``` #### 监控OOM ```bash # SAE控制台 > 监控 > 内存使用率 # 如果经常达到90%+,说明需要: # 1. 减少workers(从2降到1) # 2. 增加内存(从2GB升到4GB) # 3. 优化代码(减少内存占用) ``` #### 更新的文档 - ⚠️ `04-Python微服务-SAE容器部署指南.md`:需要在"Dockerfile"章节强调workers限制 --- ## 📖 开发调试最佳实践 ### 7. SSH隧道 - 本地直连RDS数据库 ⭐⭐⭐⭐ **用途:开发便利性(非必需,但强烈推荐)** #### 场景 ``` 开发人员需要用Navicat/DBeaver查看RDS数据 但RDS只允许VPC内网访问 解决:通过ECS作为跳板机,建立SSH隧道 ``` #### 操作步骤 **步骤1:确保ECS有SSH访问权限** ```bash # 本地生成SSH密钥(如果还没有) ssh-keygen -t rsa -b 4096 -C "your_email@example.com" # 将公钥添加到ECS # ECS控制台 > 实例 > 远程连接 > 重置密钥对 # 或者手动添加到 ~/.ssh/authorized_keys ``` **步骤2:建立SSH隧道** ```bash # 格式 ssh -N -L 本地端口:RDS内网地址:RDS端口 root@ECS公网IP -i 密钥文件 # 示例 ssh -N -L 5433:rm-bp1xxxxx.pg.rds.aliyuncs.com:5432 \ root@120.55.xx.xx \ -i ~/.ssh/dify-ecs.pem # 参数说明: # -N: 不执行远程命令,只建立隧道 # -L: 本地端口转发 # 5433: 本地监听端口(避免与本地PostgreSQL 5432冲突) # rm-bp1xxxxx...: RDS内网地址 # 5432: RDS端口 ``` **步骤3:Navicat连接** ``` 连接类型:PostgreSQL 主机:localhost 端口:5433 用户名:aiclinical_rw 密码:(RDS密码) 数据库:ai_clinical_research 测试连接 → 成功! ``` **步骤4:后台运行隧道(可选)** ```bash # 方法1:nohup后台运行 nohup ssh -N -L 5433:rm-xxxxx.pg.rds.aliyuncs.com:5432 \ root@ECS-IP -i key.pem > /dev/null 2>&1 & # 方法2:创建systemd服务(Linux) # /etc/systemd/system/rds-tunnel.service [Unit] Description=SSH Tunnel to RDS After=network.target [Service] Type=simple User=your-user ExecStart=/usr/bin/ssh -N -L 5433:rm-xxxxx.pg.rds.aliyuncs.com:5432 root@ECS-IP -i /home/your-user/.ssh/key.pem Restart=always [Install] WantedBy=multi-user.target # 启动服务 sudo systemctl start rds-tunnel sudo systemctl enable rds-tunnel ``` #### 安全注意事项 ``` ⚠️ 不要将SSH密钥提交到Git ⚠️ 定期轮换ECS的SSH密钥 ⚠️ 只在开发环境使用,生产环境通过VPN访问 ``` --- ### 8. 时区统一配置 - 防止日志时间混乱 ⭐⭐⭐⭐⭐ **问题严重度:P2(重要)** #### 问题描述 ``` 不同服务的时区不一致会导致: ❌ 日志时间对不上(前端14:00,后端06:00) ❌ pg-boss定时任务在错误时间触发 ❌ 用户看到的时间戳错误 ❌ 排查故障极为痛苦 ``` #### 解决方案 **所有服务统一使用 `Asia/Shanghai` 时区** ```dockerfile # backend/Dockerfile - Node.js后端 FROM node:22-alpine RUN apk add --no-cache tzdata ENV TZ=Asia/Shanghai # ... 其他配置 # extraction_service/Dockerfile - Python微服务 FROM python:3.11-slim RUN apt-get update && apt-get install -y tzdata ENV TZ=Asia/Shanghai RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone # ... 其他配置 # frontend-v2/Dockerfile - 前端(已配置) FROM nginx:1.25-alpine RUN apk add --no-cache tzdata ENV TZ=Asia/Shanghai # ... 其他配置 ``` ```sql -- RDS PostgreSQL 时区配置 -- RDS控制台 > 参数设置 > timezone timezone = Asia/Shanghai ``` **验证时区:** ```bash # 查看容器时区 docker exec date # 应该显示:Sat Dec 14 14:30:00 CST 2024 # 查看RDS时区 psql -c "SHOW timezone;" # 应该显示:Asia/Shanghai ``` #### 影响范围 - ✅ Node.js后端:需要更新Dockerfile - ✅ Python微服务:需要更新Dockerfile - ✅ 前端Nginx:已正确配置 - ✅ RDS PostgreSQL:需要修改参数 #### 修复步骤 ```bash # 1. 修改Node.js后端Dockerfile cd backend # 在Dockerfile中添加时区配置(见上方示例) # 2. 修改Python微服务Dockerfile cd extraction_service # 在Dockerfile中添加时区配置(见上方示例) # 3. 修改RDS时区 # RDS控制台 > 参数设置 > timezone > Asia/Shanghai # 需要重启RDS实例 # 4. 验证 docker exec backend-container date docker exec python-container date psql -h rds-host -c "SHOW timezone;" ``` --- ### 9. 镜像拉取策略 - 防止代码不更新 ⭐⭐⭐⭐⭐ **问题严重度:P2(重要)** #### 问题描述 ``` 场景: 开发者修改代码 → 构建镜像 → 推送到ACR(覆盖v1.0.0) → SAE部署 → 发现代码没更新??? 原因: SAE默认镜像拉取策略可能是 IfNotPresent 如果本地已有 v1.0.0,不会重新拉取 ``` #### 解决方案 **方案A:每次部署使用新版本号(强烈推荐)** ```bash # 使用语义化版本号 v1.0.0 → v1.0.1 → v1.0.2 ... # 或使用时间戳 v20251214-1430 → v20251214-1530 ... # 或使用Git SHA v-a1b2c3d → v-b2c3d4e ... # 构建示例 docker build -t backend:v1.0.1 . docker tag backend:v1.0.1 registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:v1.0.1 docker push registry.cn-hangzhou.aliyuncs.com/aiclinical/backend:v1.0.1 # SAE部署时选择新版本号 ``` **方案B:配置SAE镜像拉取策略(测试环境)** ```bash # SAE控制台 > 应用配置 > 镜像设置 镜像拉取策略:Always # ⚠️ 注意: # - 每次重启都会拉取镜像(启动稍慢) # - 适合测试环境,不推荐生产环境 ``` #### 最佳实践 | 环境 | 推荐方案 | 理由 | |------|---------|------| | **生产环境** | 方案A(版本号管理) | 版本可追溯,稳定 | | **测试环境** | 方案B(Always拉取) | 始终最新,方便 | | **开发环境** | 方案A | 避免混乱 | **❌ 不要:** ```bash # 始终使用 latest 标签(无法追溯版本) docker tag backend:latest ... ``` --- ### 10. Python服务内存管理 - 防止OOM ⭐⭐⭐⭐ **问题严重度:P2(重要)** #### 问题描述 ``` Python服务(PyMuPDF/Nougat)内存密集,容易OOM ❌ 单个PDF OCR可能占用500MB-1GB内存 ❌ 多个并发请求会导致内存溢出 ❌ SAE默认2GB内存可能不够 ``` #### 解决方案 **规格建议:** | 场景 | CPU | 内存 | Workers | 适用情况 | |------|-----|------|---------|---------| | **基础版** | 1核 | 2GB | 2 | 简单PDF解析 | | **标准版** | 2核 | 4GB | 3 | 包含OCR(Nougat) | | **增强版** | 2核 | 8GB | 4 | 大量OCR + 高并发 | **Dockerfile优化(已应用):** ```dockerfile # extraction_service/Dockerfile CMD ["gunicorn", "main:app", \ "--bind", "0.0.0.0:8000", \ "--workers", "2", \ # ⚠️ 限制并发 "--timeout", "120", \ # ⚠️ 2分钟超时 "--max-requests", "100", \ # ⚠️ 处理100个请求后重启worker "--max-requests-jitter", "10"] # ⚠️ 随机抖动,避免同时重启 ``` **监控与告警:** ```bash # SAE控制台 > 监控告警 > 创建告警规则 指标:内存使用率 阈值:> 80% 动作:发送通知 + 自动扩容(可选) ``` #### 如果遇到OOM **方案1:升级内存(推荐)** ```bash # SAE控制台 > 应用配置 > 规格调整 1核2GB → 2核4GB(增加¥100/月) ``` **方案2:限制并发(临时)** ```dockerfile # 修改Dockerfile CMD ["gunicorn", "main:app", \ "--workers", "1", \ # 降低并发 "--threads", "2"] # 使用线程而非进程 ``` --- ### 11. OSS签名URL - 安全的文件访问 ⭐⭐⭐⭐ **用途:安全最佳实践** #### 问题 ``` 如果OSS Bucket设置为Public: ❌ 任何人都可以访问所有文件 ❌ 无法追踪谁访问了哪些文件 ❌ 无法控制访问时效 ``` #### 解决方案 **OSS Bucket配置** ```bash # OSS控制台 > Bucket列表 > aiclinical-data-prod # 读写权限:私有(Private) ``` **后端生成签名URL** ```typescript // backend/src/common/storage/OSSAdapter.ts import OSS from 'ali-oss' export class OSSAdapter { private client: OSS constructor() { this.client = new OSS({ region: process.env.OSS_REGION!, accessKeyId: process.env.OSS_ACCESS_KEY_ID!, accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET!, bucket: process.env.OSS_BUCKET! }) } /** * 生成签名URL(1小时有效期) */ async getSignedUrl(objectKey: string, expiresIn: number = 3600): Promise { try { const url = this.client.signatureUrl(objectKey, { expires: expiresIn, // 秒,默认1小时 method: 'GET' }) return url } catch (error) { console.error('[OSS] 生成签名URL失败:', error) throw error } } /** * 上传文件 */ async uploadFile(objectKey: string, filePath: string): Promise { try { const result = await this.client.put(objectKey, filePath) console.log(`[OSS] 文件上传成功: ${objectKey}`) // 返回签名URL(不是公开URL) return this.getSignedUrl(objectKey) } catch (error) { console.error('[OSS] 文件上传失败:', error) throw error } } } ``` **API返回签名URL** ```typescript // backend/src/modules/pkb/documentController.ts router.get('/documents/:id/download', async (req, res) => { const { id } = req.params // 1. 查询文档元数据 const document = await prisma.document.findUnique({ where: { id } }) if (!document) { return res.status(404).json({ error: '文档不存在' }) } // 2. 权限校验(确保用户有权访问) if (document.userId !== req.user.id) { return res.status(403).json({ error: '无权访问此文档' }) } // 3. 生成签名URL(1小时有效) const ossAdapter = new OSSAdapter() const signedUrl = await ossAdapter.getSignedUrl(document.ossKey, 3600) // 4. 返回签名URL res.json({ url: signedUrl, expiresIn: 3600, filename: document.filename }) }) ``` **前端使用签名URL** ```typescript // frontend-v2/src/api/documents.ts export async function downloadDocument(documentId: string) { // 1. 调用后端API获取签名URL const response = await fetch(`/api/v1/pkb/documents/${documentId}/download`, { headers: { 'Authorization': `Bearer ${getToken()}` } }) const { url, filename } = await response.json() // 2. 使用签名URL下载文件 const link = document.createElement('a') link.href = url // 签名URL,1小时有效 link.download = filename link.click() } ``` #### 优势 ``` ✅ 安全:只有授权用户才能获取签名URL ✅ 时效:URL自动过期(1小时后失效) ✅ 审计:可以记录谁访问了哪些文件 ✅ 灵活:可以动态调整过期时间 ``` --- ## 📝 总结 ### 必须立即修复的问题 | # | 问题 | 严重度 | 影响 | 修复时间 | |---|------|--------|------|---------| | 1 | **NAT网关缺失** | P0 | 所有AI功能不可用 | 15分钟 | | 2 | **Dify API Key死锁** | P1 | PKB模块不可用 | 10分钟(分阶段部署) | | 3 | **HTTP超时未配置** | P1 | 连接泄漏,系统崩溃 | 5分钟(代码修改) | | 4 | **ECS端口对外开放** | P0 | 安全风险,可被攻击 | 5分钟(docker-compose修改) | | 5 | **Python Workers过多** | P1 | OOM,服务崩溃 | 2分钟(Dockerfile修改) | | 6 | **Nginx文件大小限制** | P2 | 大文件上传失败 | 2分钟(nginx.conf修改) | ### 推荐但非必需的优化 | # | 优化 | 价值 | 实施时间 | |---|------|------|---------| | 7 | **SSH隧道** | 开发便利性 | 10分钟 | | 8 | **OSS签名URL** | 安全最佳实践 | 30分钟(代码修改) | ### 下一步行动 ``` ☐ 1. 创建NAT网关(必需,15分钟)⭐⭐⭐⭐⭐ ☐ 2. 修改docker-compose.yaml(ECS端口安全,5分钟)⭐⭐⭐⭐⭐ ☐ 3. 修改Dockerfile(Python workers限制,2分钟)⭐⭐⭐⭐ ☐ 4. 修改nginx.conf(文件大小限制,2分钟)⭐⭐⭐⭐ ☐ 5. 修改后端代码(HTTP超时,5分钟)⭐⭐⭐⭐ ☐ 6. 修改后端代码(Dify容错,5分钟)⭐⭐⭐⭐ ☐ 7. 更新部署流程(分阶段部署,文档更新)⭐⭐⭐⭐ ☐ 8. 统一时区配置(必需,15分钟)⭐⭐⭐⭐⭐ ☐ 9. 配置镜像拉取策略(必需,5分钟)⭐⭐⭐⭐⭐ ☐ 10. Python内存管理(必需,10分钟)⭐⭐⭐⭐ ☐ 11. (可选)配置SSH隧道(开发便利,10分钟) ☐ 12. (可选)实现OSS签名URL(安全,30分钟) 总计:必需修复约70分钟,可选优化约40分钟 ``` --- **文档创建人:** AI助手 **最后更新:** 2025-12-14 **版本:** v1.0 **核心理念:安全第一、稳定第二、便利第三** ⭐⭐⭐