提灯喵 Rust 后端使用说明

环境要求

配置

复制 config/dev.toml,或通过环境变量覆盖:

变量 说明
DATABASE_URL MySQL 连接串
DB_USER / DB_PASSWORD 数据库账号
SECRET_ID 腾讯云 SecretId(默认 dev.toml,TDM OSS 用 manga-trans 密钥对)
SECRET_KEY 腾讯云 SecretKey(见 keychain_20260427/keys.txt,勿用 backup 密钥);写入 TdmServerRust/.env 即可,若 shell 残留 ci-placeholder 会自动被 .env 覆盖
CDN_KEY CDN 签名密钥(ossdev.yuriful.top)

启动测试数据库

cd TdmServerRust
docker compose up -d

迁移脚本位于 migrations/(自 Java Flyway 复制)。

CI/本地 Docker 基础库结构见 docker/mysql/00_schema.sql(同步自 WebBack-end local/tdm.sql)。

同步远程 dev 数据到本地

  1. Xshell 连接 dev,隧道:本地 13307127.0.0.1:3306
  2. 启动本地库:docker compose up -d mysql
  3. 同步:
cd TdmServerRust
$env:REMOTE_DATABASE_URL = "mysql://tdm:密码@127.0.0.1:13307/tdm?ssl-mode=disabled"
.\scripts\sync_remote_db.ps1
  1. .env 改本地连接:
DATABASE_URL=mysql://root:root@127.0.0.1:3307/tdm
参数 说明
-SkipImport 仅导出到 tmp/tdm_remote_dump.sql
LOCAL_DATABASE_URL 覆盖本地连接串,默认 root:root@127.0.0.1:3307/tdm

启动服务

cargo run -- --profile dev

默认端口 8090,与 Java 一致。

HTTP/2(本地 / 生产)

场景 做法
本地 dev cargo run -- --profile dev-h2(TLS + ALPN 协商 h2;首次自动生成 config/certs/dev/*.pem
RustRover Cargo 配置文件选 dev-h2程序实参--profile dev-h2(前者是编译 profile,后者是应用配置档)
前端 .env.local-development 改为 VITE_BASE_API=https://localhost:8090,浏览器信任自签证书
生产 nginx listen 443 ssl http2,示例见 deploy/nginx/back-end.http2.conf.example

Chrome Network 协议列应显示 h2;dev 控制台 [api-timing]queue 应明显下降。

OSS 下载

场景 接口 说明
单文件 GET /api/oss/downloadCredential + CDN 直链,或 GET /api/oss/downloadFile 302(非浏览器客户端)
ZIP/CORS 回退 GET /api/oss/downloadFile/proxy 服务端代理,需连接池
CDN CORS deploy/cdn-cors.md 配好后 ZIP 直拉 CDN

开发期双端对比:Java 8090 / Rust 8091(修改 config/dev.toml 中 port)。

dev 模式自动记录每个 HTTP 请求/响应(body 超 64KB 截断),并在控制台输出调用栈耗时树prof │ 前缀)。

调用栈耗时分析(仅 dev)

--profile dev 时,每个 API 请求结束后在 stderr 打印各层方法 self(自身)与 incl(含子调用)耗时。

环境变量 说明
RUST_LOG 默认 tdm_server_rust=debug
DEV_PROFILE_MIN_MS 隐藏 self 低于该阈值的 span(毫秒),默认 0.1
DEV_PROFILE_NO_LINKS 设为任意值则禁用源码链接
DEV_PROFILE_LINK_SCHEME jetbrains(默认)/cursor/vscode/file/none
DEV_PROFILE_JB_STYLE rustc(默认)/stack
DEV_PROFILE_JSONL 每请求追加一行 JSON profile(如 tmp/member_profile.jsonl
DEV_SERVER_TIMING 设为 0 关闭 Server-Timing 响应头(dev 默认开启)
MANGA_NAME_CACHE_SECS 译名/原名列表缓存 TTL(秒),默认 60
MEMBER_ALL_CACHE_SECS 全量组员列表缓存 TTL(秒),默认 60

RustRover Run 窗口:

  1. Run 配置 取消「在输出控制台中模拟终端」
  2. Ctrl+点击独立行 --> src/repository/member_repo.rs:75:1

输出示例:

prof │ ├─ repository::episode_repo::page_list  self  23.3ms ...
   --> src/repository/member_repo.rs:75:1

Server-Timing(前后端联调)

--profile dev 时响应头携带 Server-Timingtotal(墙钟)、hot(self 最大 span)、s1…(其余热点)。

JSON 性能

新增 service/repository/web handler 的 async fn 后:

python scripts/add_profile_instrument.py

dev 控制台(日志与错误检查)

dev / dev-h2 profile 注册,不经 /api 鉴权。

URL 说明
/dev/console.html Web 控制台(健康 / 错误 / 应用日志)
/dev/api/health JSON 健康检查
/dev/api/logs/errors?limit=100 内存错误列表
/dev/api/logs/app?lines=200 tail app.log

本地:http://localhost:8090/dev/console.html
远程 dev:https://back.dev.yuriful.top/dev/console.html

配置 config/dev.toml [dev_console];部署时 log_dir / app_log 指向 /data/TdmServerRust/。环境变量 DEV_LOG_DIRDEV_APP_LOG 可覆盖。

RSS 订阅(事件驱动,无轮询)

输出目录:{folder.base}/src(dev 部署常为 /data/WebBack-end/local/tdm/src),由 Caddy rss.dev.yuriful.top 静态托管。

Feed 内容
rssManga.xml 新开坑 + 漫画信息更新 + 最新 5 话
rssEpisode.xml 最新 5 话新单话提醒
rssEpisode_{post}.xml 各岗位交稿提醒(bot 订上一工序 feed)
rssMember.xml 三月未交稿组员
rssPublishLink.xml 发布链接

业务状态变更后异步写盘;rss_file_lock 按文件名互斥。

事件 刷新
增/删/改话、接稿 EpisodePipeline
交稿 对应岗位 + rssMember.xml
新发/更新漫画 rssManga.xml
发布链接 rssPublishLink.xml
启动 / 每日 12:00 全量 / rssMember.xml

dev 验证:curl -sI https://rss.dev.yuriful.top/rssManga.xml 交稿/改漫画后 Last-Modified 即时变化。

测试:cargo test rss_service;集成 cargo test --test rss_event_test(需 DB)。

单元测试

cargo test

集成测试(需数据库)

PowerShell:

$env:RUN_INTEGRATION_TESTS = "1"
$env:DATABASE_URL = "mysql://root:root@127.0.0.1:3307/tdm"
cargo test --test episode_api_test
cargo test --test member_api_test
cargo test --test manga_api_test
cargo test --test manga_card_repo_test
cargo test --test oss_api_test
cargo test --test task_tracking_api_test
cargo test --test cos_presign_test --test cos_sts_test --test oss_entity_test

/task-tracking 页面 API 冒烟(需后端 8090 + admin 账号):

powershell -File scripts/test_task_tracking_apis.ps1

Git Bash / WSL:

export RUN_INTEGRATION_TESTS=1
export DATABASE_URL=mysql://root:root@127.0.0.1:3307/tdm
cargo test --test episode_api_test
cargo test --test member_api_test
cargo test --test manga_api_test
cargo test --test station_episode_test
cargo test --test oss_api_test

exe 被占用导致 link 失败时:

$env:CARGO_TARGET_DIR = "target/test-build"
cargo test --test episode_api_test

常驻组员填充/清空单话

接口 行为
POST /api/mangas/station/admin fillEpisodes: true 时,将该漫画该岗位所有 空位且未交稿 的单话指派给新组员,并写入 *SetupTime
DELETE /api/mangas/station/:stationId 删除常驻前,清空该组员在该漫画、该岗位上 未交稿 的单话分配(已交稿不动)

岗位支持:翻译/校对/嵌字/审稿/时轴(post 1–4、6);图源(post 0)仅按空位填充,无 detail 交稿字段。

集成测试:cargo test --test station_episode_test

公共断言见 tests/common/mod.rsassert_manga_card_fieldsassert_episode_list_fields 等)。

curl 接口测试

依赖:curljq、Git Bash 或 WSL(Windows 原生 PowerShell 无 bash 时需 WSL)。

# 单模块 smoke
bash tests/curl/login/01_login.sh

# 字段契约(需 TOKEN)
TOKEN=your_jwt bash tests/curl/manga/02_manga_fields.sh
TOKEN=your_jwt bash tests/curl/episode/02_episode_fields.sh
TOKEN=your_jwt bash tests/curl/member/02_stationed.sh
TOKEN=your_jwt bash tests/curl/reward/02_reward_fields.sh
bash tests/curl/oss/02_oss_credential.sh

# 全量(01 + 02)
TOKEN=your_jwt bash tests/curl/run_all.sh

# Java vs Rust 双端 key 对比
JAVA_URL=http://127.0.0.1:8090 RUST_URL=http://127.0.0.1:8091 \
  TOKEN=xxx MANGA_ID=1 MEMBER_ID=1 bash tests/curl/compare_module.sh manga
bash tests/curl/compare_module.sh episode
bash tests/curl/compare_module.sh glossary

字段断言库:tests/curl/lib/assert_json.shassert_field_existsassert_no_field)。

OpenAPI 文档(dev / dev-h2)

路径 说明
/doc/openapi.json OpenAPI 3.1 契约,与 Java static/doc/openapi.json 一致
/swagger-ui.html Swagger UI,url=/doc/openapi.jsonoperationsSorter=method

契约源:assets/doc/openapi.json(由 Java smart-doc 同步)。pro 环境不暴露上述路由。

OpenAPI parity 全量验收

契约:tests/fixtures/openapi.json(与 https://back-docs.dev.yuriful.top/ 一致;Java 基准 API 为 https://back.dev.yuriful.top
用例:tests/fixtures/parity_cases.yaml(101 operation,含 json / multipart / form-urlencoded)

前置:数据库

默认读取 .env / 环境变量中的 DATABASE_URL。如需严格对比业务数据值,请先同步 dev 库:

cd TdmServerRust
$env:REMOTE_DATABASE_URL = "mysql://tdm:密码@127.0.0.1:13307/tdm?ssl-mode=disabled"
.\scripts\sync_remote_db.ps1
$env:DATABASE_URL = "mysql://root:root@127.0.0.1:3307/tdm"

启动 Rust(hotspot 采集必选)

$env:DEV_PROFILE_JSONL = "tmp/openapi_profile.jsonl"
cargo run -- --profile dev

run_openapi_parity.ps1 会在 benchmark 前清空 JSONL;Rust 服务端必须带 DEV_PROFILE_JSONL 启动,否则 hotspot 列显示 none

启动本地 Java(与 Rust 共用 DATABASE_URL)

$env:DATABASE_URL = "mysql://root:root@127.0.0.1:3307/tdm"
.\scripts\start_local_java_backend.ps1 -Port 8091

跑 Java vs Rust parity + 性能报告

$env:RUN_INTEGRATION_TESTS = "1"
$env:JAVA_BASE_URL = "http://127.0.0.1:8091"
.\scripts\run_openapi_parity.ps1
# 远程 Java 基准:$env:JAVA_BASE_URL = "https://back.dev.yuriful.top"
# 仅 Rust 单测(不调 Java):$env:SKIP_JAVA_PARITY = "1"
# 严格对比业务数据值:$env:STRICT_OPENAPI_PARITY = "1"

单独跑集成测试

cargo test --test openapi_parity_test -- --nocapture
cargo test --test evaluation_api_test
cargo test --test questionnaire_api_test
cargo test --test reward_api_test
cargo test --test task_tracking_api_test

重新生成 parity_cases.yaml

python scripts/generate_parity_cases.py

报告输出:docs/reports/api_performance_YYYY-MM-DD.md

含义
hotspot 最耗时 span 标签
self_ms hotspot 自身耗时(ms)
pct hotspot 占 wall 比例
source jsonl / server-timing / none

parity 集成测试覆盖全部 37 个 POST/PUT(default_body 自动补请求体);reg 为 Rust-only(skip_java)。

账号:gum979 / 123456(member);Gum979 / 123456(admin task-tracking)

Member 性能巡检

一键(自动启停 dev 服务 + curl + hotspot 表):

cd TdmServerRust
.\scripts\run_member_profile.ps1
# 保持服务运行:.\scripts\run_member_profile.ps1 -KeepServer

手动分步:

$env:DEV_PROFILE_JSONL = "tmp/member_profile.jsonl"
$env:DATABASE_URL = "mysql://root:root@127.0.0.1:3307/tdm"
cargo run -- --profile dev
.\scripts\profile_member_api.ps1
# 或 Git Bash:bash tests/curl/member/03_profile_all.sh
python scripts/parse_profile_report.py tmp/member_profile.jsonl

环境变量:DEV_USER/DEV_PWD(默认 gum979/123456,memberId=18)、MEMBER_IDBASE_URL

写接口默认不自动跑。

API 响应格式

{"code":200,"msg":"成功!","data":...}

分页:{"total":100,"rows":[...]}(仅一层 Result 包裹,勿再嵌套 code/data

列表项主键 JSON 字段为 id(小写,对齐 Java/OpenAPI)

鉴权头:tokenAuthorization(JWT,密钥 yuri.tdm

Query 与路由约定

CI/CD

工作流 触发 说明
CI push/PR → dev;PR → master check、clippy、单元测试;push dev 额外跑集成测试
Deploy push → dev / 手动 编译 release → SSH 部署到 /data/TdmServerRust → Flyway → 重启

分支:日常开发用 dev,不直接提交 master;任务完成后开 PR。

GitHub Environments(与 WebBack-end 同名):developmentproduction

类型 变量/密钥
Variables SSH_HOSTSSH_USERNAMEMYSQL_APP_USERSENTRY_DSNSECRET_ID
Secrets SSH_KEY(完整私钥,含 BEGIN/END 行,无 passphrase)、MYSQL_APP_PASSWORDSECRET_KEYCDN_KEY

SSH_KEY 配置(Windows 用文件导入,勿手粘贴):

gh secret set SSH_KEY --env development --repo Tideng-Cat/TdmServerRust < C:\path\to\deploy_key

服务器 ~/.ssh/authorized_keys 需有对应公钥。

手动部署:Actions → Deploy → Run workflow → 选择 development / production

模块迁移状态

模块 端点 状态
Login 5 已迁移
Member 12 已迁移
Author 7 已迁移
Magazine 7 已迁移
Evaluation 5 已迁移
Questionnaire 3 已迁移
Manga 27 已迁移
Episode 14 已迁移
MangaBenefit 4 已迁移
Reward 10 已迁移
OSS 4 已迁移
TaskTracking 3 已迁移