# 线上部署与数据导入完整操作方案

> **适用环境**: Linux 生产服务器（当前: `iZwz92tgv6q223rdi4w8cdZ`）  
> **项目路径**: `/opt/pharmacopoeia-ai`  
> **药典数据源**: `/opt/2025`（6317 个 DOCX 文件）  
> **耗时**: 首次完整部署约 2-4 小时（主要在 DOCX 向量化）

---

## 一、环境检查与代码拉取

```bash
# ============================================================
# 1.1 基础环境确认
# ============================================================
java -version 2>&1
# 期望: 21.x.x（若未安装需先装 JDK 21）

python3 --version
# 期望: 3.10+

git --version
# 基本命令

# ============================================================
# 1.2 拉取最新代码
# ============================================================
cd /opt/pharmacopoeia-ai
git pull origin main
git status
# 期望: 无冲突，干净状态（或有新增文件）

# ============================================================
# 1.3 创建必要目录
# ============================================================
mkdir -p /var/log/pharmacopoeia
mkdir -p /opt/pharmacopoeia-ai/backend-java/target

# ============================================================
# 1.4 配置环境变量（如首次部署）
# ============================================================
cd /opt/pharmacopoeia-ai
if [ ! -f .env ]; then
    cp .env.example .env
    echo ">>> 请编辑 .env 文件，填入 QWEN_API_KEY 等真实值 <<<"
fi

# 确认关键配置已填写
grep -E "QWEN_API_KEY|POSTGRES_PASSWORD" .env
# QWEN_API_KEY=sk-your-real-key      ← 必须是有效值
# POSTGRES_PASSWORD=pharma2025
```

---

## 二、编译 Java 后端

```bash
cd /opt/pharmacopoeia-ai/backend-java

# 2.1 设置 JDK 21
export JAVA_HOME=/usr/lib/jvm/jdk-21
# 如果不知道 JDK 路径，用 update-alternatives 查找:
# update-alternatives --list java

# 2.2 编译打包
mvn clean package -DskipTests -q

# 确认 jar 包生成
ls -lh target/pharmacopoeia-ai-1.0.0.jar
# 期望: ~50MB jar 文件
```

---

## 三、启动基础设施（PostgreSQL + Redis）

```bash
cd /opt/pharmacopoeia-ai/deploy

# 3.1 拉取镜像并启动
docker-compose pull
docker-compose up -d

# 3.2 等待健康检查通过
echo "等待 PostgreSQL 就绪..."
sleep 15
docker-compose ps
# pharmacopoeia-pg     Up (healthy)
# pharmacopoeia-redis  Up (healthy)

# 3.3 验证 PG 连接
docker exec pharmacopoeia-pg pg_isready -U postgres
# /var/run/postgresql:5432 - accepting connections
```

---

## 四、初始化数据库

```bash
cd /opt/pharmacopoeia-ai

# ============================================================
# 4.1 【首次部署 或 Schema 变更时】完全重置数据库
# 警告：这会删除所有旧数据，之后必须重新导入
# ============================================================
docker exec -i pharmacopoeia-pg psql -U postgres -d pharmacopoeia < database/reset_schema.sql

# 4.2 导入最新表结构
docker exec -i pharmacopoeia-pg psql -U postgres -d pharmacopoeia < database/schema.sql

# ============================================================
# 4.3 【如果是旧库升级，不想丢数据】仅执行迁移
# ============================================================
# docker exec -i pharmacopoeia-pg psql -U postgres -d pharmacopoeia < database/migrate_json_to_jsonb.sql

# 4.4 验证表结构
docker exec pharmacopoeia-pg psql -U postgres -d pharmacopoeia -c "\dt"
# 期望 9 张表

---

## 五、安装 Python 依赖

```bash
cd /opt/pharmacopoeia-ai/data-pipeline

# 5.1 创建虚拟环境（只需一次）
python3 -m venv venv

# 5.2 激活虚拟环境并安装依赖
source venv/bin/activate
pip install httpx sqlalchemy asyncpg psycopg2-binary python-docx

# 5.3 验证
python -c "import httpx, sqlalchemy, docx; print('OK')"

# ⚠️ 之后每次执行 data-pipeline 脚本前，都需要先激活 venv：
# source /opt/pharmacopoeia-ai/data-pipeline/venv/bin/activate
```

---

## 六、导入 Wiki 临床数据（快速，~2 分钟）

**数据内容**: 13 个药品的维基百科临床专著（概述/适应症/药理/不良反应等）+ 4 个药典示例 + 4 个用药指导

```bash
cd /opt/pharmacopoeia-ai/data-pipeline

# 激活虚拟环境
source venv/bin/activate

# 6.1 加载环境变量
set -a
source ../.env
set +a

# 6.2 执行导入
python import_all.py
```

**预期输出**:
```
📂 扫描数据目录: .../data-pipeline/data
   ✅ sample_drugs.json — 兼容，将导入
   ✅ wiki_merged.json — 兼容，将导入
   ✅ drug_guides.json — 兼容，将导入
   ⏭️ drug_index.json — 跳过
   ⏭️ catalog_volume1.json — 跳过
   ...
📦 共 3 个文件待导入
🚀 正在导入: sample_drugs.json ...
🎉 全部导入完成！
```

**验证**:
```bash
docker exec pharmacopoeia-pg psql -U postgres -d pharmacopoeia -c "
SELECT COUNT(*) AS drugs FROM drugs WHERE is_active=TRUE;
SELECT COUNT(*) AS chunks FROM drug_chunks WHERE vec IS NOT NULL;
"
# drugs: ~21, chunks: ~200
```

---

## 七、导入药典 DOCX 原文（完整，~2-4 小时）

**数据内容**: `/opt/2025/` 下 6317 个 DOCX 文件（2025 年版中国药典四部全文）

```bash
cd /opt/pharmacopoeia-ai/data-pipeline

# 激活虚拟环境
source venv/bin/activate

# 7.1 加载环境变量（如果上面已做可跳过）
set -a
source ../.env
set +a

# 7.2 确认数据源
ls /opt/2025/output2/品种正文/*.docx | wc -l
# 期望: 2930（二部化学药最多）

# 7.3 在 screen 中运行（防止 SSH 断连中断）
screen -S docx-ingest

# screen 内执行以下命令：
cd /opt/pharmacopoeia-ai/data-pipeline
source venv/bin/activate
set -a
source ../.env
set +a

# 执行导入（默认路径 /opt/2025）
python docx_ingest.py

# Ctrl+A D 分离 screen
# screen -r docx-ingest 重新连接查看进度
```

**进度监控**（另开终端）:
```bash
# 每 60 秒查看入库进度
watch -n 60 "docker exec pharmacopoeia-pg psql -U postgres -d pharmacopoeia -t -c 'SELECT NOW() AS time, COUNT(*) AS chunks FROM drug_chunks;'"
```

**预期结果**:
```
📂 扫描 DOCX 文件...
   发现 6317 个 DOCX 文件
📦 解析完成: ~6300 个有效条目
   分类分布:
     化学药: 2930
     中药: 2276
     通则: 689
     药用辅料: 387
     ...
🎉 全部完成！新增药品 ~6300 个, chunks ~35000 条
```

---

## 八、启动 Java 后端服务

```bash
cd /opt/pharmacopoeia-ai/backend-java

# 8.1 设置环境变量
export POSTGRES_PASSWORD=$(grep POSTGRES_PASSWORD /opt/pharmacopoeia-ai/.env | cut -d= -f2)
export QWEN_API_KEY=$(grep QWEN_API_KEY /opt/pharmacopoeia-ai/.env | cut -d= -f2)

# 8.2 启动服务
nohup java -Xms512m -Xmx2g -Dfile.encoding=UTF-8 \
    -jar target/pharmacopoeia-ai-1.0.0.jar \
    > /var/log/pharmacopoeia/backend.log 2>&1 &

# 8.3 等待启动
sleep 10

# 8.4 验证
curl -s http://localhost:9000/api/v1/health
# {"status":"UP"}

curl -s http://localhost:9000/api/v1/admin/stats | python3 -m json.tool
# {"total_drugs": ~6300, "total_chunks": ~35000, ...}
```

---

## 九、验证搜索功能

```bash
# 9.1 药品搜索
curl -s "http://localhost:9000/api/v1/drug/search?keyword=布洛芬" | python3 -m json.tool | head -20

# 9.2 AI 问答
curl -s -X POST http://localhost:9000/api/v1/chat/ask \
  -H "Content-Type: application/json" \
  -d '{"message":"布洛芬缓释胶囊的用法用量"}' | python3 -m json.tool | head -30

# 9.3 分类树
curl -s http://localhost:9000/api/v1/drug/category/tree | python3 -m json.tool | head -15

# 9.4 Wiki 数据（临床专著）
curl -s "http://localhost:9000/api/v1/drug/search?keyword=甲硝唑" | python3 -m json.tool | head -15
```

---

## 十、设置开机自启（可选）

```bash
# 10.1 创建 systemd 服务
cat > /etc/systemd/system/pharmacopoeia.service << 'SERVICEOF'
[Unit]
Description=Pharmacopoeia AI Backend
After=network.target docker.service
Requires=docker.service

[Service]
Type=simple
User=root
WorkingDirectory=/opt/pharmacopoeia-ai/backend-java
EnvironmentFile=/opt/pharmacopoeia-ai/.env
ExecStart=/usr/lib/jvm/jdk-21/bin/java -Xms512m -Xmx2g -Dfile.encoding=UTF-8 -jar target/pharmacopoeia-ai-1.0.0.jar
Restart=always
RestartSec=10
StandardOutput=append:/var/log/pharmacopoeia/backend.log
StandardError=append:/var/log/pharmacopoeia/backend-error.log

[Install]
WantedBy=multi-user.target
SERVICEOF

# 10.2 启用
systemctl daemon-reload
systemctl enable pharmacopoeia
systemctl start pharmacopoeia
systemctl status pharmacopoeia

# 10.3 Docker 容器也设置自启
cd /opt/pharmacopoeia-ai/deploy
# 已在 docker-compose.yml 中默认 restart: (无)，
# 可加 restart: always 后 docker-compose up -d 生效
```

---

## 十一、故障排查

### PG 连接失败
```bash
docker logs pharmacopoeia-pg --tail 30
docker start pharmacopoeia-pg
```

### Java 启动失败
```bash
tail -50 /var/log/pharmacopoeia/backend-error.log
# 常见: PG 未启动 → 检查 Step 三
# 常见: API Key 无效 → 检查 .env 中的 QWEN_API_KEY
```

### DOCX 导入中断
```bash
# 直接重新运行即可（ON CONFLICT DO NOTHING，不重复写入）
screen -r docx-ingest
# 如果 screen 已丢失，重新执行 Step 七
```

### 搜索无结果
```bash
# 确认数据状态
docker exec pharmacopoeia-pg psql -U postgres -d pharmacopoeia -c "
SELECT 'drugs' AS tbl, COUNT(*) FROM drugs WHERE is_active=TRUE
UNION ALL
SELECT 'chunks', COUNT(*) FROM drug_chunks WHERE vec IS NOT NULL;
"
# drugs 和 chunks 都应为 >0
```

---

## 十二、完全重导数据（含图片提取）

> 适用：DOCX 解析逻辑更新、图片提取功能上线、数据清空重建

```bash
cd /opt/pharmacopoeia-ai

# 1. 停 Java
bash tools/java-deploy.sh stop

# 2. 清空旧数据
docker exec -i pharmacopoeia-pg psql -U postgres -d pharmacopoeia -c "
TRUNCATE drug_chunks CASCADE;
TRUNCATE drugs CASCADE;
"

# 3. 重建表结构
docker exec -i pharmacopoeia-pg psql -U postgres -d pharmacopoeia < database/schema.sql

# 4. 创建图片目录
mkdir -p /opt/static/images

# 5. 导入 Wiki（~2 分钟）
cd data-pipeline && source venv/bin/activate
python import_all.py

# 6. 导入 2025 药典 DOCX（~2-4 小时，含图片提取）
screen -S reimport
python docx_ingest.py

# 7. 跑完后验证
python verify_import.py

# 8. 修复 vec 列
docker exec -i pharmacopoeia-pg psql -U postgres -d pharmacopoeia -c "
UPDATE drug_chunks SET vec = (embedding::text)::vector WHERE vec IS NULL AND embedding IS NOT NULL;
"

# 9. 重启 Java
cd /opt/pharmacopoeia-ai && bash tools/java-deploy.sh restart && nginx -s reload
```

---

## 十三、增量更新图片（不改数据库，仅替换图片路径）

> 适用：图片提取逻辑或路径变更，但药品数据无需重建

```bash
cd /opt/pharmacopoeia-ai

# 1. 拉取最新代码
git pull origin main

# 2. 清理旧图片目录，提取新图片
rm -rf /opt/static/images/*
cd data-pipeline && source venv/bin/activate

# 3. 执行图片提取（生成 hash 文件名的图片 + drug_images.json）
python extract_images.py

# 4. 迁移 DB 中的旧图片 URL（中文前缀 → hash 前缀，同时去掉 /drugs/ 子目录）
python migrate_image_urls.py

# 5. 更新 nginx 配置（如未改过）并 reload
cd /opt/pharmacopoeia-ai
# 确认 deploy/nginx-pharmacopoeia.conf 中 /images/ alias 指向 /opt/static/images/
nginx -t && nginx -s reload

# 6. 验证
curl -s "http://localhost:9000/api/v1/drug/H2025-FA1237" | python3 -c "
import sys, json
d = json.load(sys.stdin)
for k, v in (d.get('sections') or {}).items():
    if '/images/' in v and 'img' in v:
        print(f'  [{k}]: {v[:120]}...')
"
```

---

## 快速参考

| 路径/命令 | 用途 |
|-----------|------|
| `/opt/pharmacopoeia-ai/` | 项目根目录 |
| `/opt/2025/` | DOCX 药典数据源 |
| `/opt/static/images/` | DOCX 提取的图片（新路径） |
| `/opt/pharmacopoeia-ai/static/images/drugs/` | DOCX 提取的图片（旧路径，已废弃） |
| `docker-compose ps` | 查看 PG/Redis 状态 |
| `screen -r reimport` | 重连重导任务 |
| `http://localhost:9000/api/v1/health` | 健康检查 |
| `/var/log/pharmacopoeia/backend.log` | Java 日志 |
