# 部署指南

本文档面向将 jw-beauty 项目部署到腾讯云生产环境的运维人员。

---

## 1. 部署架构

```
┌─────────────┐      HTTPS       ┌──────────────────┐
│  微信小程序  │ ───────────────► │  SCF HTTP 函数    │
└─────────────┘                  │  jw-beauty-api   │
                                 │  (Node.js 18)    │
┌─────────────┐      HTTPS       │                  │      VPC 内网      ┌──────────────┐
│  管理后台H5  │ ──► COS/CDN     │  scf_bootstrap   │ ──────────────────► │ TencentDB    │
│  (UniApp)   │                  │  → local-server  │                    │ MySQL 5.7+   │
└─────────────┘                  └──────────────────┘                    └──────────────┘
                                        │
                                        │ COS SDK
                                        ▼
                                 ┌──────────────────┐
                                 │  COS Bucket      │
                                 │  头像/固件/后台H5 │
                                 └──────────────────┘
```

| 组件 | 服务 | 说明 |
|------|------|------|
| 后端 API | 腾讯云 SCF (HTTP 函数) | Express 应用，通过 scf_bootstrap 启动 |
| 数据库 | TencentDB for MySQL | 同 VPC 内网连接，端口 3306 |
| 对象存储 | COS | 存放头像、固件包、管理后台静态文件 |
| API 域名 | api.vsai.net.cn | 指向 SCF 函数 URL / API 网关 |
| CDN 域名 | tx.vsai.net.cn | COS 自定义域名，用于头像和固件下载 |
| 小程序 | 微信小程序 | 通过微信开发者工具上传并提审 |

---

## 2. 后端部署（SCF）

### 2.1 打包

部署包以 `server/` 为根目录打包，包含以下文件和目录：

```
index.js              # SCF 入口 (exports.main_handler)
package.json
package-lock.json
scf_bootstrap         # HTTP 函数启动脚本
scripts/              # local-server.js 等
src/                  # 应用源码
sql/                  # schema.sql
node_modules/         # 依赖
```

**不要** 把 `.env` 文件打进部署包。

打包命令：

```bash
cd server
npm install --production
rm -f /tmp/hox-http-function.zip
zip -qry /tmp/hox-http-function.zip \
  index.js package.json package-lock.json \
  scripts scf_bootstrap src sql node_modules
```

### 2.2 scf_bootstrap 配置

当前使用 HTTP 函数模式，`scf_bootstrap` 内容：

```bash
#!/bin/bash
set -e
export PORT=9000
node scripts/local-server.js
```

`scf_bootstrap` 必须有可执行权限。SCF 平台会直接运行此文件启动 HTTP 服务，监听 `PORT` 端口。

> 备注：`index.js` 中的 `main_handler` 是事件函数入口，目前未使用。如果切换到事件函数触发模式，SCF 入口改为 `index.main_handler`。

### 2.3 环境变量设置

在 SCF 控制台的函数配置中添加以下环境变量：

| 变量名 | 必填 | 说明 |
|--------|------|------|
| `NODE_ENV` | 是 | 必须设为 `production` |
| `PORT` | 否 | 默认 `9000`，scf_bootstrap 已设置 |
| `TENCENT_SECRET_ID` | 是 | 腾讯云 API 密钥 ID（COS 签名用） |
| `TENCENT_SECRET_KEY` | 是 | 腾讯云 API 密钥 Key |
| `TENCENT_REGION` | 否 | 默认 `ap-guangzhou` |
| `DB_HOST` | 是 | 数据库内网地址 |
| `DB_PORT` | 否 | 默认 `3306` |
| `DB_USER` | 是 | 数据库用户名 |
| `DB_PASSWORD` | 是 | 数据库密码 |
| `DB_NAME` | 否 | 默认 `jw_beauty` |
| `COS_BUCKET` | 否 | 默认 `jw-bucket-1426323813` |
| `COS_REGION` | 否 | 默认 `ap-guangzhou` |
| `COS_CDN_DOMAIN` | 否 | 默认 `tx.vsai.net.cn` |
| `WECHAT_APPID` | 是 | 微信小程序 AppID |
| `WECHAT_SECRET` | 是 | 微信小程序 AppSecret |
| `JWT_SECRET` | 是 | 用户 JWT 签名密钥（随机长字符串） |
| `ADMIN_JWT_SECRET` | 是 | 管理员 JWT 签名密钥（随机长字符串） |
| `ADMIN_USERNAME` | 是 | 初始管理员用户名（不要用默认 admin） |
| `ADMIN_PASSWORD` | 是 | 初始管理员密码（不要用默认 admin） |

> 生产环境安全检查：代码在 `NODE_ENV=production` 时会校验 `JWT_SECRET` 和 `ADMIN_JWT_SECRET` 不是默认值，`ADMIN_USERNAME` / `ADMIN_PASSWORD` 不是 `admin/admin`，否则启动报错。

参考模板文件：`server/.env.production.example`

### 2.4 VPC 网络配置

SCF 函数必须配置与 TencentDB 相同的 VPC 和子网，才能通过内网地址访问数据库。

操作步骤：
1. 在 SCF 控制台 → 函数管理 → 选择函数 → 函数配置 → 网络配置
2. 选择与 TencentDB 实例相同的 VPC 和子网
3. 确认安全组允许 SCF 访问数据库端口（默认 3306）

### 2.5 注意事项

- **冷启动**：SCF 冷启动时需加载 Node.js 运行时和 `node_modules`，首次请求延迟较高（约 1-3 秒）。可配置预置并发实例减少冷启动。
- **超时设置**：建议函数超时设为 30 秒以上。
- **内存**：建议 256MB 或以上。
- **日志**：错误日志通过 `console.error` 输出，可在 SCF 日志服务中查看。

---

## 3. 数据库初始化

### 3.1 连接 TencentDB

本地连接 TencentDB 需要：
- 数据库已开启公网访问，或通过 VPN/跳板机连接内网
- 安全组白名单中已添加本机公网 IP
- `DB_PORT` 使用控制台显示的公网端口

在 `server/.env` 中配置连接信息后执行：

```bash
cd server
npm install
npm run db:init
```

### 3.2 执行 schema.sql

`db:init` 脚本会自动执行 `sql/schema.sql`，创建以下业务表：

| 表名 | 说明 |
|------|------|
| `users` | 用户信息 |
| `devices` | 设备（产品码） |
| `bindings` | 用户-设备绑定关系 |
| `subscriptions` | 订阅记录 |
| `treatment_records` | 护理记录 |
| `device_events` | 设备事件 |
| `device_commands` | 远程指令队列 |
| `operation_logs` | 操作日志 |
| `admin_accounts` | 管理员账户 |
| `system_settings` | 系统设置 |
| `firmware_files` | 固件版本 |

同时写入默认系统设置（月卡价格、年卡价格、试用天数等）。

### 3.3 初始管理员创建

`db:init` 会使用环境变量 `ADMIN_USERNAME` / `ADMIN_PASSWORD` 创建初始管理员账户。

首次登录后应立即在管理后台修改默认密码。

---

## 4. 管理后台部署（COS）

### 4.1 构建

确认 `admin-console/src/config/env.js` 中 `ENV` 已设为 `prod`（或 API 地址正确）：

```js
const ENV = 'prod'
```

然后构建：

```bash
cd admin-console
npm install
npm run build:h5
```

构建产物位于 `admin-console/dist/build/h5/`。

### 4.2 上传到 COS

方式一：使用部署脚本（推荐）

```bash
cd admin-console
npm run deploy:cos
```

脚本会读取 `server/.env` 中的 COS 凭证，将构建产物上传到 COS bucket 的 `admin/` 前缀下。

可通过环境变量自定义前缀：

```bash
ADMIN_COS_PREFIX=admin-test/ npm run deploy:cos
```

方式二：手动上传

通过 COS 控制台或 COSCMD 工具将 `dist/build/h5/` 目录下所有文件上传到 bucket 的 `admin/` 目录。

### 4.3 CDN/域名配置

管理后台通过 COS 的自定义域名（`tx.vsai.net.cn`）访问：

- 在 COS 控制台为 bucket 绑定自定义域名
- 配置 CDN 加速（可选）
- 访问地址：`https://tx.vsai.net.cn/admin/`

### 4.4 base path 设置

管理后台的路由 base path 为 `/admin/`，确保 COS 上传前缀与此一致。

如果需要配置单页应用（SPA）的 history 模式回退，可在 COS 静态网站配置中设置错误文档为 `/admin/index.html`。

---

## 5. 小程序发布

### 5.1 环境切换

发布前将 `miniprogram/config/env.js` 中的 `ENV` 改为 `prod`：

```js
var ENV = 'prod'
```

### 5.2 微信开发者工具上传

1. 打开微信开发者工具
2. 导入项目目录 `miniprogram/`
3. 确认 AppID 正确
4. 点击"上传"，填写版本号和备注
5. 在微信公众平台 → 版本管理 → 提交审核

### 5.3 审核注意事项

- **用户协议** 和 **隐私政策** 页面必须存在且内容完整，否则审核不通过
- 涉及蓝牙权限的小程序需要在 `app.json` 中声明并说明用途
- 隐私信息收集弹窗需符合微信规范
- 建议提交审核前在体验版充分测试

---

## 6. 域名和证书

### 6.1 API 域名

| 域名 | 用途 | 指向 |
|------|------|------|
| `api.vsai.net.cn` | 后端 API | SCF 函数 URL / API 网关 |

需要配置 HTTPS 证书（腾讯云可申请免费 SSL 证书）。

### 6.2 COS CDN 域名

| 域名 | 用途 | 指向 |
|------|------|------|
| `tx.vsai.net.cn` | 头像/固件下载/管理后台 | COS bucket CDN |

在 COS 控制台绑定自定义域名并配置 HTTPS。

### 6.3 小程序合法域名配置

在微信公众平台 → 开发管理 → 开发设置 → 服务器域名中配置：

| 类型 | 域名 |
|------|------|
| request 合法域名 | `https://api.vsai.net.cn` |
| downloadFile 合法域名 | `https://tx.vsai.net.cn` |
| uploadFile 合法域名 | `https://api.vsai.net.cn` |

---

## 7. 上线检查清单

### 环境配置

- [ ] `NODE_ENV=production` 已设置
- [ ] `JWT_SECRET` 和 `ADMIN_JWT_SECRET` 已更换为强随机字符串
- [ ] `ADMIN_USERNAME` / `ADMIN_PASSWORD` 已更换默认值
- [ ] 数据库连接使用 VPC 内网地址
- [ ] 安全组规则正确（SCF → MySQL 3306）
- [ ] COS 凭证配置正确

### 数据库

- [ ] `schema.sql` 已执行成功，所有表已创建
- [ ] 初始管理员已创建并更改了默认密码
- [ ] 默认系统设置已写入 `system_settings` 表

### 后端

- [ ] `GET /health` 返回 `{"code":0}`
- [ ] `POST /api/v1/admin/login` 可正常登录
- [ ] `GET /api/v1/admin/dashboard` 返回统计数据
- [ ] `GET /api/v1/admin/settings` 返回系统设置

### 管理后台

- [ ] 构建时 `ENV` 已设为 `prod`
- [ ] 构建产物已上传到 COS `admin/` 目录
- [ ] `https://tx.vsai.net.cn/admin/` 可正常访问
- [ ] 管理员可登录并查看仪表盘

### 小程序

- [ ] `miniprogram/config/env.js` 中 `ENV='prod'`
- [ ] 微信公众平台已配置 request/download/upload 合法域名
- [ ] 小程序登录流程正常
- [ ] 蓝牙扫码绑定流程正常
- [ ] 护理记录同步正常
- [ ] 用户协议和隐私政策页面内容完整

### 安全

- [ ] `.env` 文件未包含在部署包中
- [ ] 生产环境 `mock-bind` 和 `mock-purchase` 接口已自动禁用
- [ ] CORS 策略按需收紧（当前为 `*`）
- [ ] Rate limiting 已生效（登录 15min/10次，管理员 15min/5次）
