Files
2026-05-02 10:49:38 +08:00

215 lines
8.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# huike-back 项目指南
## 服务器标识
| 环境 | 服务器 | SSH 用户 | 说明 |
|------|--------|----------|------|
| **正式服 (production)** | `alchemy-studio.cn` (152.136.103.69, CentOS 8) | `alchemy``sudo -u alchemy``sudo su - alchemy` | 线上生产环境 |
| **测试服 (test)** | `moicen.com` (101.43.244.164, Ubuntu 24.04) | `weli` | 测试验证环境 |
**正式服 = alchemy,测试服 = moicen,不要搞混。**
### 正式服 (alchemy) 部署要点
- 所有服务以 `alchemy` 用户运行(`sudo -u alchemy -H bash`
- 代码路径:`/mnt/huiwing/huike-back/`(新版本拆分仓库,非旧 monorepo)
- AuthCore 路径:`/mnt/huiwing/AuthCore/`
- 旧 monorepo (`/mnt/huiwing/huiwing/`) 已由拆分仓库替代,Java 服务已全部下线
### 测试服 (moicen) 部署要点
- 代码路径:`/home/weli/works/huike-back/`
- AuthCore 路径:`/home/weli/works/AuthCore/`
---
## E2E 测试流程(新功能开发标准流程)
为节约 CI 资源、加快迭代速度,新功能开发采用 **先本地验证,再推送 CI** 的流程:
1. **本地开发 & 编译**`cargo check` 确保无编译错误
2. **部署测试服 (moicen)**`git push` → SSH 登录 moicen → `git pull``cargo build --release`
3. **本地发起 e2e 测试** — 在本地运行 e2e 测试套件,请求指向 moicen 测试服
4. **验证通过后推送 CI**`git push origin master` → GitHub Actions 自动运行 `cargo check` + 全量 e2e
5. **部署正式服 (alchemy)** — SSH 登录 alchemy → `git pull``cargo build --release` → 重启服务
6. **正式服 CI smoke test** — 部署后立即触发 GitHub Actions 的 prod smoke test workflow,验证正式服 API 响应正常。若 smoke test 失败,立即回滚并排查
**Why:** 大部分问题在步骤 2-3 就能发现,避免反复触发 CI 浪费排队时间和 GitHub Actions 配额。
## E2E 调试原则
排查不了的 E2E 问题时,可以往前后端插入 debug 数据辅助排查。
### vConsole(小程序前端调试面板)
- **打开条件**:仅限有 `SYS_CAN_SUDO` tag 的 sudoer 用户 → 自动显示 vConsole 面板
- **实现位置**`huike-front/src/App.vue``watch(() => store.current)`
- **使用方式**:打开 vConsole 后,可在 Console 面板查看 `console.warn` / `console.log` 输出,Storage 面板查看 `localStorage` 数据
### 已有 debug 数据注入模式
项目中已存在以下 debug 注入模式,可直接复用:
| 模式 | 注入位置 | 查看方式 |
|------|----------|----------|
| `window.__cp_debug` | `pick.vue``add.vue`(课包相关) | vConsole Console 输入 `__cp_debug` |
| `localStorage.setItem("OrgSwitchDebug", ...)` | `store/org.ts` | vConsole Storage 面板 |
| `localStorage.setItem("ClazzPayloadDebug", ...)` | `store/clazz.ts` | vConsole Storage 面板 |
| `localStorage.setItem("CourseSectionPayloadDebug", ...)` | `store/qumu-section.ts` | vConsole Storage 面板 |
| `console.warn("[OrgSwitchDebug]", ...)` | `store/org.ts` | vConsole Console 面板 |
### 插入新 debug 数据的规范
**前端**:参考已有模式,用 `console.warn("[TagName]", data)` + `window.__tagName = data``localStorage.setItem("TagName", JSON.stringify(data))` 注入。vConsole 自动捕获 console 输出。
**后端(Rust**:用 `tracing::warn!("[TagName] {:?}", data)``tracing::info!(...)` 打印关键中间数据。日志会出现在服务启动的 nohup 输出(`/tmp/htykc.log` / `/tmp/htyuc.log`)以及 CI 的收尾日志步骤中。
**数据库**:可在 `huike-unit/ci/fixtures/` 下添加 SQL fixture 文件,插入特定测试数据。在 `e2e.yml` 中新增步骤运行即可。
## 部署原则
**禁止使用 scp/rsync 部署代码。** 所有部署必须走 GitHub push → 服务器 git pull 的流程。
```bash
# 本地
git push
# 服务器
ssh <server>
cd /path/to/repo
git pull --ff-only
cargo build --release # 后端
# 或 sh cp_dist_moicen.sh / cp_dist_huiwing.sh # 前端
```
如果服务器缺少对应仓库,先在服务器上用 `git clone` 克隆,而不是从本机传文件。
**Why:** 二进制/构建产物拷贝绕过构建系统,会引入静默的架构或链接不匹配问题。git 工作流确保二进制与代码一致、依赖已解析、构建可重复。
### 前端部署提醒:必须上传静态资源到又拍云 CDN
`cp_dist_huiwing.sh` / `cp_dist_moicen.sh` 已自动包含又拍云上传步骤。
**严禁**只手动 `sudo cp` 而不上传 CDN。HTML 中的 JS/CSS 链接指向 `static.*` CDN 域名(又拍云),而非 nginx 本地目录。若只拷贝到 nginx,JS 会返回 404,页面为空白白屏。
```bash
# 正确:执行完整部署脚本
sh cp_dist_huiwing.sh
# 错误(从仓库复制到 nginx 但跳过 CDN,会导致白屏)
sudo cp -r dist/* /usr/local/openresty/nginx/html/music-room/
```
手动修复白屏:
```bash
cd /mnt/huiwing/huike-front/cdn
sh upload_assets.sh .huiwing_upyun_pass 1
```
---
## htyproc (Task Processor)
### 启动流程
```bash
cd /path/to/huike-back/htyproc
bash start.sh
```
启动后 proc server 默认处于 **PENDING** 状态,不会处理任务。需要调用 API 切换为 RUNNING
```bash
# proc server 默认 PENDING,必须手动 start
curl -s 'http://127.0.0.1:3004/api/v1/proc/start'
# 检查状态
curl -s 'http://127.0.0.1:3004/api/v1/proc/status'
# → {"r":true,"d":"RUNNING",...}
# 停止
curl -s 'http://127.0.0.1:3004/api/v1/proc/stop'
```
**注意**`/api/v1/proc/start` 是 GET 方法(不是 POST),路由定义在 `htyproc/src/proc_api.rs`
重启后必须手动调用 start 才能恢复任务处理。
### 重置 FAILED 任务为 PENDING
若任务因临时故障(如 AI API 不可用)标记为 FAILED,可手动重置重新处理:
```bash
ssh moicen
sudo -u postgres psql -d htytask_moicen -c "UPDATE dbtack SET task_status='PENDING', updated_at=NOW(), updated_by='manual_reset' WHERE task_id='<task_id>';"
```
确保 proc 为 RUNNING 状态后,等待 WAIT_SEC(默认 5s)即可自动拾取。
### NGX_URL 配置陷阱
`.env``NGX_URL` 不要包含 `/api/ngx` 后缀 — 代码 `clients.rs:98` 会拼接 `/api/ngx/audio/convert`,两者叠加会变成双 `/api/ngx/api/ngx/` 路径,导致 OpenResty 返回 405。
```bash
# 错误
NGX_URL=https://admin.huiwings.cn/api/ngx
# 正确
NGX_URL=https://admin.huiwings.cn
```
### 日志
日志文件:`htyproc/htyproc.nohup.log`,受 logrotate 管理。
---
## AI API 网络架构
### 架构说明
AI APIFlask,端口 5000)部署在独立的 AI 服务器上(Mac mini 192.168.0.115),Flask 运行在 **Podman 容器** 内,通过 NPS + SSH 隧道穿透访问:
```
AI Server Podman container(port 5000)
→ Podman gvproxy(port 5000)
→ Mac host(port 5000)
→ NPS client → NPS server alchemy:10001
→ alchemy(port 5000 via SSH -L)
→ moicen(port 5000 via SSH -L)
```
- **穿透链路**:本机 → `ssh weli@alchemy-studio.cn``ssh -p 10001 weli@localhost`(经 NPS 隧道)→ Mac → `/usr/local/bin/podman exec ai-api`
- **容器信息**`localhost/ai-api:latest`,端口映射 `5000→5000/tcp``5909→5901/tcp``2222→22/tcp`
- **便捷工具**`plan_skills/tools/huiwing-ai-api`(一键执行容器命令,无需逐层 SSH)
- **详细文档**:见 `plan_skills/moicen/moicen-access-ai-api-container.md`
### SSH 隧道命令
```bash
# alchemy 上的隧道(由 tmux ai-tunnel 守护,自动重连)
tmux new-session -d -s ai-tunnel 'while true; do sleep 3; ssh -L 5909:localhost:5909 -L 5000:localhost:5000 weli@localhost -p 10001; done'
# moicen 到 alchemy 转发
ssh -L 5000:localhost:5000 weli@alchemy-studio.cn
```
### OpenResty 反向代理
两台服务器的 OpenResty 都配置了 `ai.conf`,将 `https://ai.<domain>/api/v1/ai/*` 代理到 `http://127.0.0.1:5000/`(注意尾部 / 会剥离 `/api/v1/ai` 前缀)。
Flask 路由实际路径(无前缀):
- `/form_image_compress_audit` — 图片压缩审计
- `/compare` — AI 评分对比
-
### 验证 AI API 可用性
```bash
curl -s 'https://ai.moicen.com/api/v1/ai/form_image_compress_audit' \
-X POST -H 'Content-Type: application/json' \
-H 'HtySudoerToken: test' -H 'HtyHost: ts.moicen.com' \
-d '{"url":"https://test.com/test.jpg"}'
# → {"d":{"acknowledged":true},"e":null,"r":true}
```