# Tencent IP Port Dev Deploy Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** 将当前 ForgeFlow ERP 前后端部署到腾讯云 1Panel Ubuntu 服务器，临时开启动态加载，通过 `公网 IP + 端口` 访问，数据库继续连接阿里云 MySQL。

**Architecture:** 临时测试阶段不走域名、HTTPS、Nginx/OpenResty 反代。浏览器访问 Vite dev server `http://腾讯云公网IP:5173`，前端请求 FastAPI `http://腾讯云公网IP:8000/api`，后端连接阿里云 MySQL `121.40.97.156:3306`。稳定后再切到正式生产架构：前端静态构建、OpenResty 反代 `/api`、HTTPS、后端仅监听本机。

**Tech Stack:** Ubuntu 22.04/1Panel, Python 3, FastAPI/Uvicorn, Vue 3/Vite, Node.js 20, MySQL on Aliyun.

---

## Phase 0: 部署边界确认

本计划只做临时测试部署，不代表最终生产配置。

临时测试访问方式：

```text
浏览器
  -> http://<腾讯云公网IP>:5173
       Vite dev server，支持前端热更新

前端 API 请求
  -> http://<腾讯云公网IP>:8000/api
       FastAPI/Uvicorn，使用 --reload 支持后端动态加载

后端数据库连接
  -> 121.40.97.156:3306
       阿里云 MySQL
```

需要提前准备的信息：

```bash
export TENCENT_IP="<腾讯云服务器公网IP>"
export ALIYUN_MYSQL_HOST="121.40.97.156"
export ALIYUN_MYSQL_PORT="3306"
export ERP_DB_NAME="jiaheng_erp"
export ERP_DB_USER="<数据库用户名>"
export ERP_DB_PASSWORD="<数据库密码>"
```

本阶段必须接受的临时风险：

- `5173` 和 `8000` 会临时暴露在公网，只适合测试期。
- Vite dev server 和 `uvicorn --reload` 不是最终生产运行方式。
- 如果直接连现有阿里云数据库，腾讯云上的操作会真实写入同一个库。
- 数据库账号不要长期使用 `root`，至少正式前要换成 ERP 专用账号。

---

## Phase 1: 腾讯云服务器基础准备

### Task 1: 登录服务器并确认系统

- [ ] **Step 1: SSH 登录腾讯云服务器**

```bash
ssh root@<腾讯云公网IP>
```

Expected:

```text
成功进入 Ubuntu shell
```

- [ ] **Step 2: 确认系统版本和架构**

```bash
uname -a
lsb_release -a || cat /etc/os-release
```

Expected:

```text
Linux VM-0-14-ubuntu ...
Ubuntu ...
x86_64
```

### Task 2: 安装基础依赖

- [ ] **Step 1: 更新 apt 包索引**

```bash
sudo apt update
```

Expected:

```text
Reading package lists... Done
```

- [ ] **Step 2: 安装 Python、构建工具、Git、MySQL 客户端**

```bash
sudo apt install -y git curl build-essential python3 python3-venv python3-pip mysql-client tmux
```

Expected:

```text
git, curl, python3, python3-venv, mysql-client, tmux 安装完成
```

- [ ] **Step 3: 安装 Node.js 20**

如果当前没有 Node，或者 `node -v` 低于 20，执行：

```bash
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
```

验证：

```bash
node -v
npm -v
```

Expected:

```text
v20.x.x
10.x.x 或更高
```

---

## Phase 2: 网络和防火墙放行

### Task 3: 腾讯云安全组放行临时端口

- [ ] **Step 1: 在腾讯云控制台安全组放行 TCP 端口**

放行入站规则：

```text
TCP 5173  来源：你的办公公网 IP/32
TCP 8000  来源：你的办公公网 IP/32
TCP 22    来源：你的办公公网 IP/32
```

如果测试人员公网 IP 不固定，临时可用：

```text
TCP 5173  来源：0.0.0.0/0
TCP 8000  来源：0.0.0.0/0
```

但这只能用于短期测试，稳定后必须收口。

Expected:

```text
外网可以访问 5173 和 8000，不再被腾讯云安全组拦截
```

### Task 4: 1Panel 或 Ubuntu 防火墙放行端口

- [ ] **Step 1: 如果使用 1Panel 防火墙，在 1Panel 面板中放行**

```text
端口：5173/tcp
端口：8000/tcp
```

- [ ] **Step 2: 如果服务器启用了 ufw，执行放行**

```bash
sudo ufw allow 5173/tcp
sudo ufw allow 8000/tcp
sudo ufw status
```

Expected:

```text
5173/tcp ALLOW
8000/tcp ALLOW
```

### Task 5: 阿里云 MySQL 放行腾讯云公网 IP

- [ ] **Step 1: 在阿里云服务器或 RDS 安全组中放行腾讯云公网 IP**

放行规则：

```text
入站 TCP 3306 来源：<腾讯云公网IP>/32
```

- [ ] **Step 2: 在腾讯云服务器测试 MySQL 连通性**

```bash
mysql -h 121.40.97.156 -P 3306 -u <数据库用户名> -p -e "SELECT NOW() AS db_time;"
```

Expected:

```text
+---------------------+
| db_time             |
+---------------------+
| 2026-...            |
+---------------------+
```

如果报错：

```text
ERROR 2003 Can't connect to MySQL server
```

处理方向：

```text
优先检查阿里云安全组、MySQL bind-address、防火墙、账号 Host 权限。
```

---

## Phase 3: 部署项目代码

### Task 6: 准备项目目录

- [ ] **Step 1: 创建部署目录**

```bash
sudo mkdir -p /opt/ForgeFlow-ERP
sudo chown -R $USER:$USER /opt/ForgeFlow-ERP
```

Expected:

```text
/opt/ForgeFlow-ERP 可由当前用户读写
```

### Task 7: 上传或拉取代码

推荐方式 A：服务器可以访问 Git 仓库时使用 `git clone`。

- [ ] **Step 1A: 克隆项目**

```bash
cd /opt
git clone <ForgeFlow-ERP仓库地址> ForgeFlow-ERP
cd /opt/ForgeFlow-ERP
git status
```

Expected:

```text
On branch ...
working tree clean
```

推荐方式 B：如果仓库暂时不方便从服务器拉取，就从 Mac 同步代码。

- [ ] **Step 1B: 在 Mac 上执行 rsync 上传**

```bash
rsync -av --delete \
  --exclude 'frontend/node_modules' \
  --exclude 'backend/.venv' \
  --exclude '.git' \
  /Users/souplearn/Gitlab/py/ForgeFlow-ERP/ \
  root@<腾讯云公网IP>:/opt/ForgeFlow-ERP/
```

Expected:

```text
代码同步到 /opt/ForgeFlow-ERP
```

- [ ] **Step 2: 在服务器确认目录结构**

```bash
cd /opt/ForgeFlow-ERP
ls -la
ls -la backend frontend
```

Expected:

```text
backend/
frontend/
README.md
```

---

## Phase 4: 后端动态加载部署

### Task 8: 配置后端 `.env`

- [ ] **Step 1: 创建后端环境变量文件**

```bash
cd /opt/ForgeFlow-ERP/backend
cp .env.example .env
```

- [ ] **Step 2: 编辑 `.env`**

```bash
nano /opt/ForgeFlow-ERP/backend/.env
```

推荐内容：

```env
APP_NAME=Jiaheng Hardware ERP API
APP_ENV=staging-dev
APP_HOST=0.0.0.0
APP_PORT=8000

MYSQL_HOST=121.40.97.156
MYSQL_PORT=3306
MYSQL_DATABASE=jiaheng_erp
MYSQL_USER=<数据库用户名>
MYSQL_PASSWORD=<数据库密码>

ALLOWED_ORIGINS=http://<腾讯云公网IP>:5173

AUTH_SECRET_KEY=<生成一串至少32位的随机字符串>
AUTH_TOKEN_EXPIRE_MINUTES=480
```

生成随机密钥：

```bash
python3 - <<'PY'
import secrets
print(secrets.token_urlsafe(48))
PY
```

Expected:

```text
输出一串随机字符串，将它填入 AUTH_SECRET_KEY
```

注意：

```text
不要把正式数据库密码、AUTH_SECRET_KEY 发到聊天或提交到 Git。
```

### Task 9: 安装后端依赖

- [ ] **Step 1: 创建虚拟环境**

```bash
cd /opt/ForgeFlow-ERP/backend
python3 -m venv .venv
```

- [ ] **Step 2: 安装依赖**

```bash
cd /opt/ForgeFlow-ERP/backend
. .venv/bin/activate
pip install -U pip
pip install -r requirements.txt
```

Expected:

```text
Successfully installed fastapi uvicorn sqlalchemy pymysql ...
```

### Task 10: 临时启动后端动态加载

- [ ] **Step 1: 用 tmux 启动后端**

```bash
tmux new -s forgeflow-backend
```

在 tmux 窗口内执行：

```bash
cd /opt/ForgeFlow-ERP/backend
. .venv/bin/activate
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
```

Expected:

```text
Uvicorn running on http://0.0.0.0:8000
Started reloader process
```

退出 tmux 但保留进程：

```text
按 Ctrl+B，然后按 D
```

- [ ] **Step 2: 服务器本机验证后端**

```bash
curl -s http://127.0.0.1:8000/ | python3 -m json.tool
curl -s http://127.0.0.1:8000/api/system/health | python3 -m json.tool
```

Expected:

```json
{
  "app": "ok",
  "database": {
    "connected": true
  }
}
```

- [ ] **Step 3: Mac 或浏览器外网验证后端**

```bash
curl -s http://<腾讯云公网IP>:8000/api/system/health
```

Expected:

```text
能返回 app ok，database connected true
```

---

## Phase 5: 前端动态加载部署

### Task 11: 配置前端 `.env`

- [ ] **Step 1: 创建前端环境变量文件**

```bash
cd /opt/ForgeFlow-ERP/frontend
cp .env.example .env
```

- [ ] **Step 2: 修改 API 地址**

```bash
nano /opt/ForgeFlow-ERP/frontend/.env
```

内容：

```env
VITE_API_BASE_URL=http://<腾讯云公网IP>:8000/api
```

Expected:

```text
前端所有 API 请求都会打到腾讯云后端 8000 端口
```

### Task 12: 安装前端依赖

- [ ] **Step 1: 安装 npm 依赖**

```bash
cd /opt/ForgeFlow-ERP/frontend
npm install
```

Expected:

```text
added ... packages
```

### Task 13: 临时启动前端动态加载

- [ ] **Step 1: 用 tmux 启动前端**

```bash
tmux new -s forgeflow-frontend
```

在 tmux 窗口内执行：

```bash
cd /opt/ForgeFlow-ERP/frontend
npm run dev -- --host 0.0.0.0 --port 5173
```

Expected:

```text
VITE v5.x.x ready
Local:   http://localhost:5173/
Network: http://<内网IP>:5173/
```

退出 tmux 但保留进程：

```text
按 Ctrl+B，然后按 D
```

- [ ] **Step 2: 服务器本机验证前端**

```bash
curl -I http://127.0.0.1:5173
```

Expected:

```text
HTTP/1.1 200 OK
```

- [ ] **Step 3: 浏览器访问**

```text
http://<腾讯云公网IP>:5173/login?redirect=/
```

Expected:

```text
出现 ERP 登录页
```

---

## Phase 6: 测试期进程守护

tmux 适合第一天快速验证。如果需要测试环境持续跑几天，建议改成 systemd 管理，但仍然保留动态加载。

### Task 14: 创建后端 dev systemd 服务

- [ ] **Step 1: 创建服务文件**

```bash
sudo nano /etc/systemd/system/forgeflow-backend-dev.service
```

内容：

```ini
[Unit]
Description=ForgeFlow ERP Backend Dev Reload
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/ForgeFlow-ERP/backend
ExecStart=/opt/ForgeFlow-ERP/backend/.venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
Restart=always
RestartSec=3
Environment=PYTHONUNBUFFERED=1

[Install]
WantedBy=multi-user.target
```

- [ ] **Step 2: 启动服务**

```bash
sudo systemctl daemon-reload
sudo systemctl enable forgeflow-backend-dev
sudo systemctl start forgeflow-backend-dev
sudo systemctl status forgeflow-backend-dev --no-pager
```

Expected:

```text
Active: active (running)
```

- [ ] **Step 3: 查看日志**

```bash
journalctl -u forgeflow-backend-dev -f
```

Expected:

```text
Uvicorn running on http://0.0.0.0:8000
```

### Task 15: 创建前端 dev systemd 服务

- [ ] **Step 1: 查出 npm 路径**

```bash
which npm
```

Expected:

```text
/usr/bin/npm
```

- [ ] **Step 2: 创建服务文件**

```bash
sudo nano /etc/systemd/system/forgeflow-frontend-dev.service
```

如果 `which npm` 输出 `/usr/bin/npm`，内容如下：

```ini
[Unit]
Description=ForgeFlow ERP Frontend Vite Dev Server
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/ForgeFlow-ERP/frontend
ExecStart=/usr/bin/npm run dev -- --host 0.0.0.0 --port 5173
Restart=always
RestartSec=3
Environment=NODE_ENV=development

[Install]
WantedBy=multi-user.target
```

- [ ] **Step 3: 启动服务**

```bash
sudo systemctl daemon-reload
sudo systemctl enable forgeflow-frontend-dev
sudo systemctl start forgeflow-frontend-dev
sudo systemctl status forgeflow-frontend-dev --no-pager
```

Expected:

```text
Active: active (running)
```

- [ ] **Step 4: 查看日志**

```bash
journalctl -u forgeflow-frontend-dev -f
```

Expected:

```text
VITE v5.x.x ready
```

---

## Phase 7: 动态加载更新流程

### Task 16: 代码更新后验证热加载

- [ ] **Step 1: 更新代码**

如果使用 Git：

```bash
cd /opt/ForgeFlow-ERP
git pull
```

如果使用 rsync，在 Mac 重新执行同步命令：

```bash
rsync -av --delete \
  --exclude 'frontend/node_modules' \
  --exclude 'backend/.venv' \
  --exclude '.git' \
  /Users/souplearn/Gitlab/py/ForgeFlow-ERP/ \
  root@<腾讯云公网IP>:/opt/ForgeFlow-ERP/
```

- [ ] **Step 2: 如果只改后端 Python 代码**

Expected:

```text
uvicorn --reload 自动重载，无需手动重启
```

验证：

```bash
journalctl -u forgeflow-backend-dev -n 80 --no-pager
```

- [ ] **Step 3: 如果改前端 Vue/CSS/JS**

Expected:

```text
Vite dev server 自动热更新浏览器页面
```

验证：

```bash
journalctl -u forgeflow-frontend-dev -n 80 --no-pager
```

- [ ] **Step 4: 如果改了依赖**

后端依赖变更：

```bash
cd /opt/ForgeFlow-ERP/backend
. .venv/bin/activate
pip install -r requirements.txt
sudo systemctl restart forgeflow-backend-dev
```

前端依赖变更：

```bash
cd /opt/ForgeFlow-ERP/frontend
npm install
sudo systemctl restart forgeflow-frontend-dev
```

Expected:

```text
依赖安装成功，服务重启成功
```

---

## Phase 8: 功能验收清单

### Task 17: 登录和基础页面验收

- [ ] **Step 1: 打开登录页**

```text
http://<腾讯云公网IP>:5173/login?redirect=/
```

Expected:

```text
登录页正常渲染，浏览器控制台无 CORS 报错
```

- [ ] **Step 2: 登录 ERP**

Expected:

```text
登录成功，进入首页
```

- [ ] **Step 3: 打开浏览器开发者工具 Network**

Expected:

```text
API 请求地址是 http://<腾讯云公网IP>:8000/api/...
状态码不是 CORS error
```

### Task 18: 核心业务验收

- [ ] **Step 1: 基础资料页面**

验证：

```text
供应商名录、客户名录、原材料名录、产品需规清单能打开
```

- [ ] **Step 2: 订单与采购页面**

验证：

```text
销售订单列表、采购订单、到货入库、质量校验能打开
```

- [ ] **Step 3: 仓库页面**

验证：

```text
嘉恒仓库六大库能打开，出入库弹窗能打开
```

- [ ] **Step 4: 生产执行页面**

验证：

```text
工单台账、工序报工能打开
```

- [ ] **Step 5: 发货概览页面**

验证：

```text
发货台账能打开
```

### Task 19: Excel 导入导出验收

- [ ] **Step 1: 测试导出**

验证：

```text
供应商名录、原材料名录、客户名录、仓库期初模板、盘库清单导出正常
```

- [ ] **Step 2: 测试导入**

验证：

```text
上传 Excel 时无 413、CORS、权限、临时目录报错
```

- [ ] **Step 3: 检查响应头**

Expected:

```text
浏览器能识别 Content-Disposition，下载文件名正常
```

说明：

```text
后端 CORS 已 expose Content-Disposition，只要 ALLOWED_ORIGINS 配对即可。
```

---

## Phase 9: 常见故障处理

### Task 20: 前端页面打不开

- [ ] **Step 1: 检查 Vite 服务**

```bash
sudo systemctl status forgeflow-frontend-dev --no-pager
journalctl -u forgeflow-frontend-dev -n 120 --no-pager
```

Expected:

```text
Active: active (running)
VITE ready
```

- [ ] **Step 2: 检查端口监听**

```bash
ss -lntp | grep 5173
```

Expected:

```text
0.0.0.0:5173
```

如果只看到 `127.0.0.1:5173`，说明没有绑定公网网卡，需要加：

```bash
npm run dev -- --host 0.0.0.0 --port 5173
```

### Task 21: 后端接口打不开

- [ ] **Step 1: 检查后端服务**

```bash
sudo systemctl status forgeflow-backend-dev --no-pager
journalctl -u forgeflow-backend-dev -n 120 --no-pager
```

- [ ] **Step 2: 检查端口监听**

```bash
ss -lntp | grep 8000
```

Expected:

```text
0.0.0.0:8000
```

- [ ] **Step 3: 本机 curl**

```bash
curl -s http://127.0.0.1:8000/api/system/health
```

Expected:

```text
本机通则进程正常；外网不通则优先查腾讯云安全组和 1Panel 防火墙。
```

### Task 22: 登录时报 CORS

- [ ] **Step 1: 确认前端访问地址**

```text
http://<腾讯云公网IP>:5173
```

- [ ] **Step 2: 确认后端 `.env`**

```bash
grep ALLOWED_ORIGINS /opt/ForgeFlow-ERP/backend/.env
```

Expected:

```env
ALLOWED_ORIGINS=http://<腾讯云公网IP>:5173
```

- [ ] **Step 3: 修改后重启后端**

```bash
sudo systemctl restart forgeflow-backend-dev
```

### Task 23: 数据库连接失败

- [ ] **Step 1: 用 mysql 客户端直连**

```bash
mysql -h 121.40.97.156 -P 3306 -u <数据库用户名> -p -e "SELECT 1;"
```

Expected:

```text
返回 1
```

- [ ] **Step 2: 如果 MySQL 客户端通，但后端 health 不通**

检查：

```bash
grep MYSQL /opt/ForgeFlow-ERP/backend/.env
journalctl -u forgeflow-backend-dev -n 120 --no-pager
```

处理方向：

```text
数据库名、账号、密码、账号 Host 权限、SQLAlchemy 连接字符串。
```

---

## Phase 10: 临时阶段转正式生产阶段

临时测试稳定后，再做以下切换。

### Task 24: 正式化前端

- [ ] **Step 1: 生产构建**

```bash
cd /opt/ForgeFlow-ERP/frontend
npm run build
```

Expected:

```text
dist/ 构建成功
```

- [ ] **Step 2: 用 1Panel OpenResty/Nginx 托管 dist**

站点根目录：

```text
/opt/ForgeFlow-ERP/frontend/dist
```

### Task 25: 正式化后端

- [ ] **Step 1: 后端改为只监听本机**

正式阶段后端不再暴露公网端口：

```text
127.0.0.1:8000
```

- [ ] **Step 2: systemd 去掉 `--reload`**

正式命令：

```bash
/opt/ForgeFlow-ERP/backend/.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000
```

### Task 26: 正式化反向代理

- [ ] **Step 1: 配置 OpenResty/Nginx**

核心规则：

```nginx
location /api/ {
    proxy_pass http://127.0.0.1:8000/api/;
    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;
}

location / {
    try_files $uri $uri/ /index.html;
}
```

- [ ] **Step 2: 前端正式环境 API 改为相对路径**

```env
VITE_API_BASE_URL=/api
```

### Task 27: 正式化安全策略

- [ ] **Step 1: 关闭临时公网端口**

腾讯云安全组和服务器防火墙关闭：

```text
5173/tcp
8000/tcp
```

只保留：

```text
80/tcp
443/tcp
22/tcp
```

- [ ] **Step 2: 配置域名和 HTTPS**

```text
ERP 正式访问地址：https://<正式域名>
```

- [ ] **Step 3: 数据库账号最小权限**

正式账号只给 ERP 所需库权限：

```sql
CREATE USER 'forgeflow_erp'@'<腾讯云公网IP>' IDENTIFIED BY '<强密码>';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER, INDEX, REFERENCES
ON jiaheng_erp.* TO 'forgeflow_erp'@'<腾讯云公网IP>';
FLUSH PRIVILEGES;
```

---

## Final Verification

- [ ] 腾讯云服务器能访问阿里云 MySQL。
- [ ] `http://<腾讯云公网IP>:8000/api/system/health` 返回后端和数据库正常。
- [ ] `http://<腾讯云公网IP>:5173/login?redirect=/` 能打开登录页。
- [ ] 登录后页面 API 请求没有 CORS 报错。
- [ ] Excel 导入导出正常。
- [ ] 基础资料、订单、采购、仓库、生产、发货页面都能打开。
- [ ] 修改前端代码后 Vite 能热更新。
- [ ] 修改后端代码后 Uvicorn 能自动 reload。
- [ ] 测试期结束后有明确计划关闭公网 `5173` 和 `8000`。

---

## Recommended Execution Mode

先用 `tmux` 跑通当天部署，再切 `systemd` 守护。

原因：

```text
tmux 启动最快，排错最直观；确认前后端、数据库、跨域都通后，再用 systemd 固化。
```

如果需要我来执行，建议按这个顺序：

```text
1. 先要腾讯云公网 IP、SSH 登录方式、数据库测试账号。
2. 执行 Phase 1 到 Phase 5，让系统先能访问。
3. 执行 Phase 8 做业务验收。
4. 验收没问题后执行 Phase 6，把进程挂 systemd。
5. 等测试稳定后，再执行 Phase 10 转正式生产配置。
```
