Chapter 02 · Backend

# 后端设计

Go 模块化单体，满足前端全部功能即可、不做过度设计。API 契约完整继承 `design/server/ARCHITECTURE.md` §3 蓝本，唯一语义修订：数据面从 WireGuard 换为 sing-box（REALITY / Hysteria2），`connect` 改为下发用户凭证 + 节点连接参数。

## §1服务形态与模块划分

单体起步、按模块分包（实现顺序即蓝本 §7）：

HTTP API chi · /v1
→
auth 验证码/注册/JWT
codes 激活码/兑换/webhook
devices 设备/订阅校验
nodes 目录/connect/调度
usage 用量/额度
admin 管理端（独立监听）
gRPC server（mTLS）节点 agent：注册 / 心跳 / 凭证下发与回收 / 用量上报
scheduler 探针汇聚 · 判封 · 节点生命周期驱动（见 04 章）
→
MySQL 主数据
Redis 验证码/限流/实时负载
一个二进制三个监听：公网 API（CDN 后）· agent gRPC（mTLS 双向认证）· 管理端（内网/白名单）

- 每模块完成定义（蓝本约定）：单测 + OpenAPI 同步 + 错误文案双语（`{code, message_zh, message_en}`）+ 符合脱敏口径。
- 管理端绝不与公网 API 共用监听端口；部署时仅绑内网地址，前置白名单 + 2FA（见 06 章）。

## §2API 契约 v1（继承蓝本 §3，正式版用 OpenAPI 定义）

    # 认证（无需 JWT）
    POST /v1/auth/code        {email}                  # 发 6 位验证码（Redis 10min，限 1/min）
    POST /v1/auth/register    {email, code, password}  # 建号 + 7 天试用 + JWT
    POST /v1/auth/login       {email, password}        # JWT（access 15min + refresh 30d）
    POST /v1/auth/refresh     {refresh_token}

    # 账户
    GET    /v1/me                                      # 账户 + 订阅 + 用量摘要
    GET    /v1/me/devices
    DELETE /v1/me/devices/:id                          # 移除设备（同步回收节点侧凭证）

    # 商业闭环（App 内无支付，只有兑换）
    POST /v1/redeem           {code}                   # 兑换激活码（幂等 + 审计）
    POST /v1/ads/unlock       {device_id, ad_token}    # 免费版激励视频解锁当日时长（验 SDK 回执）
    GET  /v1/plans                                     # 套餐目录（数字与 design/CLAUDE.md §7 一致）

    # 节点（数据面入口）
    GET  /v1/nodes            ?if_version=N            # 节点目录（按套餐过滤，304 支持）
    POST /v1/nodes/:id/connect    {device_id}          # 下发连接凭证（见 §3，语义已适配 sing-box）
    POST /v1/nodes/:id/disconnect {device_id}

    GET  /v1/usage            ?days=7                  # 用量曲线（统计页）
    GET  /v1/notices                                   # 公告（亦发布为多镜像签名静态 JSON）

- 除 auth 外全部接口要求 JWT；限流用 Redis 滑动窗口（按 IP + 用户双维度）。
- 错误体统一 `{code, message_zh, message_en}`；文案遵守铁律 13 脱敏口径。
- 节点目录响应带 `version`；客户端 `if_version` 命中返回 304——这是节点秒级灰度的基础（见 04 章）。

## §3connect 语义适配（相对蓝本 v0.1 的唯一修订）

修订理由蓝本 v0.1 的数据面是 WireGuard（connect = 下发 WG peer）。WG 协议特征明显，在目标网络环境会被快速识别阻断；plan/ phase-0~3 已选定 REALITY 主线 + Hysteria2 备线。API 形状不变，仅返回体语义调整。

### 3.1 连接流程

客户端 POST /nodes/:id/connect
→
API 校验订阅有效 · 设备数 ≤ 上限 · 免费版校验 ad_unlocked_at + 剩余分钟
→
gRPC → agent 确保该用户 UUID 已在节点 sing-box 用户表
→
返回连接参数 server, port, uuid, flow, reality pbk/sid/sni · hy2 备用
免费版凭证带 TTL = 当日剩余分钟，到时 agent 自动从用户表移除；付费版 TTL 24h 在线自动续期

### 3.2 凭证模型

- 每用户一个数据面 UUID（与账号 ID 解耦，可轮换）：节点侧只见 UUID，零账号信息——单节点被抄不泄露用户库。
- 凭证轮换：用户改密 / 管理端封禁 / 定期轮换时生成新 UUID，gRPC 广播到所有在册节点替换；旧 UUID 给 5 分钟宽限期平滑重连。
- 订阅过期、设备被移除、套餐降级 → scheduler 驱动 agent 即时回收对应 UUID。
- Hysteria2 凭证同源派生（同一 UUID 作 auth password），客户端按网络状况自动选协议。

## §4关键业务流程

### 4.1 注册与试用（对齐客户端 UI：邮箱 → 验证码 → 设密码）

- `POST /auth/code`：风控（IP + 邮箱限频、一次性邮箱域黑名单）→ 发码（Redis 10min）。
- `POST /auth/register`：验码 → 建号 → 自动写入 7 天 PRO 试用（`subscriptions(plan=pro, expires_at=now()+7d, source='trial')`，一邮箱仅一次）→ 返回 JWT。
- 试用到期回落 free：仅 tier=free 节点、每日 10 分钟、每日首连前需激励视频解锁。

### 4.2 激活码生命周期

发卡店售出 webhook（HMAC 签名）
人工渠道 TG/LINE/邮箱 · 管理端批量生成 batch
→
codes 入库 status=unused · 库存 hash
→
POST /redeem 事务：redeemed + 订阅顺延（叠加非覆盖）
→
audit_log 必记

- 码格式：Crockford Base32，16 位含校验位；明文只出现一次（生成响应 / 发卡店），库内存 hash。
- 兑换幂等：同一用户重复提交同一码返回首次结果；风控：单用户失败 5 次/小时锁 1 小时。

### 4.3 免费版广告解锁

- 客户端播完激励视频 → `POST /v1/ads/unlock` 携带广告 SDK 回执 → 服务端向 AdMob/Unity 校验回执真伪 → 记 `usage_daily.ad_unlocked_at`。
- `connect` 对 free 用户强制校验当日 `ad_unlocked_at` 与剩余分钟；分钟数由 agent 上报的会话时长累计，服务端为权威。

## §5后端安全要点（详见 06 章总纲）

- 密码 argon2id;JWT RS256，签名密钥定期轮换;全站 TLS 1.3。
- 验证码 / 兑换 / 登录接口限流 + 防一次性邮箱 + 失败锁定；所有敏感操作进 `audit_log`。
- 无日志口径（写进隐私政策并据实执行）：不记录目的地址 / DNS 查询 / 流量内容；仅 `usage_daily` 字节数与分钟数。
- agent gRPC 双向 mTLS：节点证书由内部 CA 签发、与节点 ID 绑定，节点被回收即吊销。
- CI：lint + 单测 + OpenAPI 校验 + 镜像构建；OpenAPI 即 API 文档与客户端 SDK 生成源。