客户端exe技术方案.md 269 KB

游铛铛客户端exe技术方案

文档版本: v1.3.0 创建日期: 2026-02-01 最后更新: 2026-02-02 文档目标: 总结提炼数据库模型和客户端exe建设思路,形成完整技术方案

v1.3.0 更新内容:

  • 新增 4.4 接口日志记录规范
  • 记录入参密文、入参明文、出参明文、出参密文
  • 日志文件按天滚动,每天一个日志文件
  • 敏感字段自动脱敏
  • 支持文件日志、数据库日志、控制台日志三种输出方式

v1.2.1 更新内容:

  • 优化机器码生成方案,仅使用硬盘序列号和MAC地址
  • 移除主板序列号依赖,提高云环境兼容性
  • 解决云虚拟机主板序列号不可用的问题

v1.2.0 更新内容:

  • 新增 3.3.1 动态 HMAC 密钥方案
  • 解决静态密钥被逆向破解的问题
  • 支持密钥定期轮换,增强安全性

一、项目概述

1.1 产品定位

游铛铛是一款BUFF选品和Steam扫货工具,通过客户端exe形式提供给用户使用。

1.2 工程架构

游铛铛客户端exe项目
│
├── client-tauri/                    # 🎨 前端工程 (Vue 3 + Tauri)
│   ├── src-tauri/                   # Rust后端 (Tauri原生命令)
│   │   ├── src/                     # Rust源代码
│   │   │   ├── commands/            # Tauri命令实现
│   │   │   │   ├── crypto/          # 加解密命令
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   ├── aes.rs       # AES加密/解密
│   │   │   │   │   └── rsa.rs       # RSA加密
│   │   │   │   ├── auth/            # 认证命令
│   │   │   │   │   ├── mod.rs
│   │   │   │   │   ├── login.rs     # 安全登录
│   │   │   │   │   ├── register.rs  # 安全注册
│   │   │   │   │   └── unbind.rs    # 机器解绑
│   │   │   │   ├── heartbeat.rs     # 心跳检测
│   │   │   │   ├── machine.rs       # 机器信息获取
│   │   │   │   └── mod.rs           # 模块导出
│   │   │   ├── security.rs          # 安全检查
│   │   │   ├── machine.rs           # 机器码生成
│   │   │   └── main.rs              # 程序入口
│   │   └── Cargo.toml               # Rust依赖配置
│   ├── src/                         # Vue 3 前端代码
│   │   ├── components/              # Vue组件
│   │   ├── views/                   # 页面视图
│   │   ├── stores/                  # Pinia状态管理
│   │   └── ...
│   └── ...
│
├── backend-client/                  # ⚙️ 后端服务 (FastAPI)
│   ├── app/                         # 应用代码
│   │   ├── api/                     # API路由
│   │   │   ├── v1/                  # API版本
│   │   │   │   ├── keys.py          # 密钥相关
│   │   │   │   ├── auth.py          # 认证相关
│   │   │   │   ├── announcements.py # 公告相关
│   │   │   │   ├── heartbeat.py     # 心跳相关
│   │   │   │   ├── user.py          # 用户相关
│   │   │   │   └── steam.py         # Steam相关
│   │   │   └── deps.py              # 依赖注入
│   │   ├── core/                    # 核心模块
│   │   │   ├── config.py            # 配置管理
│   │   │   ├── encrypt.py           # 加解密工具
│   │   │   └── security.py          # 安全中间件
│   │   ├── models/                  # 数据模型
│   │   ├── services/                # 业务逻辑
│   │   ├── middleware/              # 中间件
│   │   └── utils/                   # 工具函数
│   ├── migrations/                  # 数据库迁移
│   └── requirements.txt             # 依赖管理
│
├── steam-client/                    # 🎮 Steam服务 (独立工程)
│   ├── steam_login/                 # Steam登录
│   ├── order_confirm/               # 订单确认
│   ├── captcha_handler/             # 验证码处理
│   └── ...                          # 其他Steam相关
│
└── scheduled-tasks/                 # ⏰ 定时任务服务 (独立工程)
    ├── heartbeat_check/             # 心跳检测任务
    ├── session_clean/               # 会话清理任务
    └── ...                          # 其他定时任务

说明:当前仓库中 未包含 scheduled-tasks/ 独立工程目录。原因可以是以下之一或组合:

  • 规划中:技术方案描述的是目标架构,scheduled-tasks 作为独立服务尚未立项或未在本仓库中创建。
  • 逻辑暂放在现有服务中:心跳检测、会话清理等能力目前由 backend-admin(如 app/services/heartbeat_monitor.py、app/services/session_service.py)或 backend-client 在应用内以定时/后台任务方式承担;若未做多实例部署,则暂不需要单独拆出定时任务服务。
  • 后续按需拆出:当客户端 API 或管理端需要多实例水平扩展时,再拆出 scheduled-tasks 独立进程,可避免多实例重复执行定时任务(见 1.3 划分目的)。

1.3 划分目的

  1. 面向客户的服务快速扩容 - 客户端服务可独立扩展
  2. 定时任务独立运行 - 避免多实例重复执行定时任务
  3. Steam服务隔离 - Steam相关逻辑独立,便于维护和扩展

1.4 与管理端的关系

┌─────────────────────────────────────────────────────────────────────────────┐
│                            系统整体架构                                       │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌─────────────────┐         ┌─────────────────┐                            │
│  │  backend-admin  │         │  backend-client │                            │
│  │  (管理端)        │         │  (客户端API)     │                            │
│  │                 │         │                 │                            │
│  │  • 管理员认证    │         │  • 用户认证      │                            │
│  │  • 密钥生成轮换  │ ───┬──>│  • 使用RSA公钥   │                            │
│  │  • 系统配置管理  │    │   │  • 配置从DB获取  │                            │
│  │  • API密钥管理   │    │   │                 │                            │
│  └────────┬────────┘    │   └────────┬────────┘                            │
│           │             │            │                                      │
│           │    RSA公钥   │            │                                      │
│           └─────────────┘            │                                      │
│           ┌─────────────────────────┘                                      │
│           │                                                                   │
│           ▼                                                                   │
│  ┌─────────────────────────────────────────────────────────────────────────┐│
│  │                         api_keys 表                                       ││
│  │  • public_key: RSA公钥 (客户端使用)                                       ││
│  │  • private_key: RSA私钥 (仅管理端可见,客户端不可访问)                     ││
│  │  • key_version: 密钥版本                                                 ││
│  │  • is_active: 是否激活                                                   ││
│  │  • expire_at: 过期时间                                                   ││
│  └─────────────────────────────────────────────────────────────────────────┘│
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

关键说明:

  • RSA密钥管理: 由backend-admin管理端完全控制生成、轮换、过期和历史记录
  • 客户端角色: 客户端exe只需要使用RSA公钥进行加密,不参与密钥的创建和管理
  • 密钥获取: 客户端通过GET /api/keys/public接口获取最新的RSA公钥

二、数据库模型分析

2.1 核心业务实体

┌─────────────────────────────────────────────────────────────────────────────┐
│                              用户与认证模块                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│  users (用户表)                                                              │
│  ├── id: 主键                                                                │
│  ├── username: 用户名 (唯一)                                                 │
│  ├── password: 密码 (bcrypt加密)                                             │
│  ├── machine_code: 机器码 (MD5, 绑定后不可更改)                              │
│  ├── card_id: 当前卡密ID                                                     │
│  ├── expire_time: 到期时间                                                   │
│  └── status: 状态 (1正常/0禁用)                                              │
│                                                                              │
│  sessions (会话表)                                                           │
│  ├── session_id: 会话ID (唯一)                                               │
│  ├── user_id: 用户ID                                                         │
│  ├── machine_code: 机器码                                                    │
│  ├── is_active: 是否激活                                                     │
│  └── expires_at: 过期时间                                                    │
└─────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│                              商业化模块                                      │
├─────────────────────────────────────────────────────────────────────────────┤
│  software (软件产品表)                                                       │
│  ├── code: 软件特征码 (如: buff-xp-steam-sh)                                 │
│  ├── name: 软件名称                                                          │
│  ├── daily_price: 每天价格基数                                                │
│  └── is_active: 是否启用                                                     │
│                                                                              │
│  cards (卡密表)                                                              │
│  ├── card_code: 卡密代码 (唯一)                                              │
│  ├── software_code: 所属软件                                                 │
│  ├── days: 天数                                                              │
│  ├── discount: 折扣                                                          │
│  ├── user_id: 激活用户ID                                                     │
│  ├── activated_at: 激活时间                                                  │
│  ├── expire_time: 到期时间                                                   │
│  └── status: 状态 (0未激活/1已激活/2已过期)                                  │
│                                                                              │
│  recharge_records (充值记录表)                                               │
│  ├── user_id: 用户ID                                                         │
│  ├── card_id: 卡密ID                                                         │
│  ├── software_code: 软件特征码                                               │
│  ├── days: 充值天数                                                          │
│  ├── expire_time_before/after: 充值前后到期时间                              │
└─────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│                              Steam账号模块                                   │
├─────────────────────────────────────────────────────────────────────────────┤
│  steam_accounts (Steam账号绑定表)                                            │
│  ├── user_id: 用户ID                                                         │
│  ├── steam_username: Steam用户名                                             │
│  ├── steam_id: Steam ID                                                      │
│  ├── shared_secret: Steam Guard共享密钥 (加密存储)                           │
│  ├── identity_secret: Steam Guard身份密钥 (加密存储)                         │
│  └── is_active: 是否启用                                                     │
└─────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│                              系统配置模块 (核心)                              │
├─────────────────────────────────────────────────────────────────────────────┤
│  system_config (系统配置表 - 纵表设计)                                       │
│  ├── config_key: 配置键名                                                    │
│  ├── config_value: 配置值                                                    │
│  ├── config_type: 类型 (int/string/bool/json)                               │
│  ├── app_name: 所属应用 (backend-admin/backend-client)                       │
│  ├── is_system: 是否系统配置                                                 │
│  └── is_editable: 是否可编辑                                                 │
│                                                                              │
│  【客户端核心配置项】                                                         │
│  • heartbeat_interval_seconds: 心跳间隔秒数                                  │
│  • max_heartbeat_failures: 心跳最大失败次数                                  │
│  • max_login_attempts: 最大登录失败次数                                      │
│  • login_lockout_minutes: 登录锁定分钟数                                     │
│  • timestamp_tolerance_seconds: 时间戳容忍秒数                               │
│  • prevent_duplicate_login: 是否防止重复登录                                 │
│  • announcement_cache_seconds: 公告缓存秒数                                  │
└─────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│                              安全通信模块                                    │
├─────────────────────────────────────────────────────────────────────────────┤
│  api_keys (API密钥表 - 仅backend-admin管理)                                  │
│  ├── public_key: RSA公钥                                                     │
│  ├── private_key: RSA私钥 (加密存储)                                         │
│  ├── key_version: 密钥版本                                                   │
│  ├── is_active: 是否激活                                                     │
│  └── expire_at: 过期时间 (创建+10天)                                         │
│                                                                              │
│  api_nonces (防重放表)                                                       │
│  ├── nonce: 随机字符串                                                       │
│  ├── endpoint: 接口路径                                                      │
│  ├── session_id: 会话ID                                                      │
│  └── expires_at: 过期时间                                                    │
└─────────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────────┐
│                              API日志模块                                     │
├─────────────────────────────────────────────────────────────────────────────┤
│  api_logs (API请求日志表)                                                    │
│  ├── user_id: 用户ID (可为NULL)                                              │
│  ├── endpoint: 接口路径                                                      │
│  ├── method: HTTP方法                                                        │
│  ├── request_data: 加密前的原始请求参数                                       │
│  ├── response_data: 解密后的原始响应数据                                      │
│  ├── ip_address: 客户端IP                                                    │
│  ├── response_code: 响应状态码                                               │
│  └── execution_time: 执行时间(毫秒)                                          │
└─────────────────────────────────────────────────────────────────────────────┘

注意:

  • 客户端exe认证不涉及admins管理员表
  • api_keys表的私钥对客户端完全不可见

2.2 历史记录机制

设计原则: 每个主表都有对应的 _his 历史表

主表操作流程:
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  INSERT操作  │ →  │ 复制到_his表 │ →  │  插入主表    │
└─────────────┘    └─────────────┘    └─────────────┘

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  UPDATE操作  │ →  │ 复制旧数据   │ →  │  更新主表    │
│              │    │ 到_his表    │    │             │
└─────────────┘    └─────────────┘    └─────────────┘

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│  DELETE操作  │ →  │ 复制到_his表 │ →  │  标记删除    │
└─────────────┘    └─────────────┘    └─────────────┘

历史表字段:

  • 原表所有字段
  • his_created_at: 历史记录创建时间
  • his_operation: 操作类型 (INSERT/UPDATE/DELETE)

2.3 关键索引设计

表名 索引字段 用途
users username 登录查询
users machine_code 机器码验证
users expire_time 到期检查
sessions session_id 会话验证
sessions user_id 用户会话查询
cards card_code 卡密验证
cards user_id 用户卡密查询
api_nonces expires_at 过期清理
api_logs user_id/created_at 日志查询

三、安全体系设计

3.1 客户端本地安全

┌─────────────────────────────────────────────────────────────────────────────┐
│                              客户端本地安全                                   │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  1. 进程唯一性检查                                                           │
│     ├── Windows: FindWindow/互斥体检测                                       │
│     └── 启动时检测,已运行则退出或激活已有窗口                               │
│                                                                              │
│  2. 机器码绑定机制                                                           │
│     ├── 客户端收集机器特征信息 (硬盘序列号、MAC地址)                          │
│     ├── 登录时将特征信息传递给后端                                            │
│     ├── 后端根据特征信息生成MD5机器码                                         │
│     ├── 后端比对生成的机器码与数据库存储的机器码                              │
│     │   ├── 数据库machine_code=NULL → 首次登录,绑定机器码                    │
│     │   ├── 机器码一致 → 同一机器,允许登录                                   │
│     │   └── 机器码不一致 → 非同一机器,拒绝登录                               │
│     ├── 登录后所有接口在报文body中传递session_id和machine_code               │
│     └── 解绑操作: 需要用户触发,解除机器码绑定                                 │
│                                                                              │
│  3. 防请求伪造 (session和机器码都在报文中传递)                                │
│     ├── 所有请求必须包含session_id                                            │
│     ├── 所有请求必须包含machine_code                                          │
│     ├── 所有请求必须包含时间戳                                                │
│     ├── 所有请求必须包含随机nonce                                             │
│     ├── 所有请求必须包含HMAC签名                                              │
│     └── 后端验证签名有效性                                                    │
└─────────────────────────────────────────────────────────────────────────────┘

3.2 传输安全 (AES+RSA混合加密)

┌─────────────────────────────────────────────────────────────────────────────┐
│                          AES+RSA混合加密流程                                 │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  【加密流程】                                                                │
│                                                                              │
│  1. 客户端生成随机AES密钥 (32字节)                                           │
│  2. 客户端使用AES密钥加密请求数据                                            │
│  3. 客户端使用RSA公钥加密AES密钥                                             │
│  4. 客户端发送: encrypted_params + encrypted_aes_key + signature            │
│                                                                              │
│  5. 后端使用RSA私钥解密AES密钥                                               │
│  6. 后端使用AES密钥解密请求数据                                              │
│  7. 后端处理业务逻辑                                                         │
│  8. 后端使用AES密钥加密响应数据                                              │
│  9. 后端返回: encrypted_data                                                 │
│                                                                              │
│  10. 客户端使用本地AES密钥解密响应数据                                       │
│                                                                              │
│  【加密报文结构】                                                            │
│                                                                              │
│  请求报文:                                                                   │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │ {                                                                   │    │
│  │   "encrypted_params": "base64(AES加密请求数据)",                   │    │
│  │   "aes_key": "base64(RSA加密AES密钥)",                              │    │
│  │   "key_version": 1,                                                │    │
│  │   "timestamp": 1704105600,                                         │    │
│  │   "nonce": "a1b2c3d4e5f6g7h8...",                                  │    │
│  │   "signature": "base64(HMAC-SHA256签名)"                            │    │
│  │ }                                                                   │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                                                              │
│  响应报文:                                                                   │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │ {                                                                   │    │
│  │   "code": 200,                                                     │    │
│  │   "msg": "操作成功",                                               │    │
│  │   "encrypted_data": "base64(AES加密响应数据)",                     │    │
│  │   "timestamp": 1704105600,                                         │    │
│  │   "nonce": "h8g7f6e5d4c3b2a1...",                                  │    │
│  │   "signature": "base64(HMAC-SHA256签名)"                            │    │
│  │ }                                                                   │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                                                              │
│  【签名生成算法】                                                            │
│                                                                              │
│  sign_string = f"{timestamp}:{nonce}:{encrypted_params}:{aes_key}:{key_version}"
│  signature = HMAC-SHA256(sign_string, secret_key)                            │
│                                                                              │
│  【响应签名生成算法】                                                        │
│                                                                              │
│  后端响应时,使用相同的算法生成响应签名:                                      │
│  response_sign_string = f"{response_timestamp}:{response_nonce}:{encrypted_data}:{key_version}"
│  response_signature = HMAC-SHA256(response_sign_string, secret_key)          │
│                                                                              │
│  【时间戳验证】                                                              │
│  • 容忍范围: ±5分钟 (可配置)                                                 │
│  • 防止重放攻击                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

3.3 双向签名验证体系

┌─────────────────────────────────────────────────────────────────────────────┐
│                          双向签名验证体系                                    │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌─────────────────────┐                     ┌─────────────────────┐       │
│  │  客户端 (Rust)       │                     │  服务端 (Python)     │       │
│  │  client-tauri       │                     │  backend-client     │       │
│  └──────────┬──────────┘                     └──────────┬──────────┘       │
│             │                                           │                   │
│             │  ┌───────────────────────────────────────┘                   │
│             │  │                                                           │
│             ▼  ▼                                                           │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  1. 客户端请求签名生成                                                 │   │
│  │     ├── sign_string = timestamp:nonce:encrypted_params:aes_key:kv   │   │
│  │     ├── signature = HMAC-SHA256(sign_string, CLIENT_SECRET)          │   │
│  │     └── 发送请求时携带 signature                                      │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│             │                                                           │
│             │  加密请求 (AES+RSA)                                        │
│             ▼                                                           │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  2. 服务端请求签名验证                                                 │   │
│  │     ├── 使用相同的 secret_key 重新计算签名                           │   │
│  │     ├── 对比客户端传入的 signature                                    │   │
│  │     ├── 验证失败 → 返回 400 错误                                      │   │
│  │     └── 验证成功 → 继续处理请求                                       │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│             │                                                           │
│             │  处理业务逻辑                                              │
│             ▼                                                           │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  3. 服务端响应签名生成                                                 │   │
│  │     ├── response_sign_string = rtimestamp:rnonce:encrypted_data:kv  │   │
│  │     ├── response_signature = HMAC-SHA256(response_sign_string,       │   │
│  │     │                              SERVER_SECRET)                    │   │
│  │     └── 响应报文携带 response_signature                              │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│             │                                                           │
│             │  加密响应 (AES)                                            │
│             ▼                                                           │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  4. 客户端响应签名验证                                                 │   │
│  │     ├── 使用相同的 secret_key 重新计算签名                           │   │
│  │     ├── 对比服务端返回的 response_signature                           │   │
│  │     ├── 验证失败 → 拒绝处理响应,弹出安全警告                         │   │
│  │     └── 验证成功 → 解密并处理业务数据                                 │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【密钥配置】                                                                │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  • CLIENT_SECRET: 客户端密钥 (硬编码在Rust中)                        │   │
│  │    - 值: "buff-client-secret-2024"                                   │   │
│  │    - 用途: 客户端生成请求签名,服务端验证                             │   │
│  │                                                                      │   │
│  │  • SERVER_SECRET: 服务端密钥 (存储在环境变量中)                      │   │
│  │    - 值: 从环境变量 BUFF_API_SERVER_SECRET 获取                      │   │
│  │    - 用途: 服务端生成响应签名,客户端验证                             │   │
│  │                                                                      │   │
│  │  • 注意: 两个密钥必须不同,实现双向认证                              │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【安全优势】                                                                │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  1. 请求防篡改: 客户端签名确保请求未被中间人修改                      │   │
│  │  2. 响应防篡改: 服务端签名确保响应未被中间人篡改                      │   │
│  │  3. 双向认证: 客户端和服务端互相验证对方身份                         │   │
│  │  4. 中间人防护: 同时防范请求和响应方向的中间人攻击                    │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

3.3.1 动态 HMAC 密钥方案

┌─────────────────────────────────────────────────────────────────────────────┐
│                          动态 HMAC 密钥方案                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  【设计背景】                                                                │
│  静态 HMAC 密钥 (CLIENT_SECRET) 硬编码在客户端中,存在被逆向破解的风险。      │
│  动态密钥方案通过服务端实时下发密钥,定期轮换,有效降低密钥泄露的影响范围。    │
│                                                                              │
│  【核心特性】                                                                │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  1. 密钥动态获取: 客户端启动时从服务端获取 HMAC 密钥                   │   │
│  │  2. 定期密钥轮换: 密钥定期自动更换 (默认每1小时)                       │   │
│  │  3. 用户+机器绑定: 密钥与 user_id 和 machine_code 绑定                │   │
│  │  4. 服务端存储: 密钥存储在 Redis,支持过期和主动失效                   │   │
│  │  5. 完整审计: 所有密钥操作记录到日志表                                 │   │
│  │  6. 零持久化: 客户端密钥只在内存中,不写入磁盘                          │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【静态密钥 vs 动态密钥】                                                    │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  对比项        │  静态密钥方案            │  动态密钥方案              │   │
│  │  ─────────────│────────────────────────│────────────────────────│   │
│  │  密钥存储      │  硬编码在客户端          │  服务端 Redis 存储         │   │
│  │  密钥更换      │  永不变                  │  自动定期轮换              │   │
│  │  泄露影响      │  全局泄露                │  影响范围有限              │   │
│  │  防逆向        │  易被找到                │  客户端无持久化密钥        │   │
│  │  实现复杂度    │  低                      │  中                       │   │
│  │  服务端压力    │  低                      │  略高                     │   │
│  │  首次请求延迟  │  无                      │  增加 1 RTT               │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【密钥绑定规则】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  • 绑定 user_id: 确定密钥归属的用户                                   │   │
│  │  • 绑定 machine_code: 确定密钥归属的设备                              │   │
│  │  • 不绑定 IP 地址: 允许用户在同设备的不同网络环境下使用               │   │
│  │  • 密钥与用户+设备组合唯一                                            │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【完整数据流转】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                                                                        │   │
│  │  1. 客户端首次启动                                                     │   │
│  │     ├── 检查本地是否有缓存的有效密钥                                  │   │
│  │     ├── 无有效密钥 → 向服务端请求动态密钥                            │   │
│  │     └── 有有效密钥 → 直接使用                                        │   │
│  │                                                                        │   │
│  │  2. 请求动态密钥 (RSA 加密)                                           │   │
│  │     ├── POST /api/keys/dynamic                                        │   │
│  │     ├── 使用 RSA 公钥加密 AES 密钥                                    │   │
│  │     ├── 请求体: { machine_code, timestamp }                           │   │
│  │     └── 服务端验证机器码后生成动态密钥                               │   │
│  │                                                                        │   │
│  │  3. 服务端处理                                                         │   │
│  │     ├── 验证机器码是否有效                                            │   │
│  │     ├── 生成随机动态密钥 (32字节)                                     │   │
│  │     ├── 存储到 Redis (key: hmac_key:{user_id}:{machine_code})        │   │
│  │     ├── 设置过期时间 (默认1小时)                                      │   │
│  │     └── RSA 加密后返回给客户端                                        │   │
│  │                                                                        │   │
│  │  4. 客户端处理响应                                                     │   │
│  │     ├── 解密获取动态密钥                                              │   │
│  │     ├── 存储到内存 (不持久化)                                         │   │
│  │     ├── 启动刷新定时器 (默认30分钟后刷新)                             │   │
│  │     └── 使用动态密钥进行 HMAC 签名                                    │   │
│  │                                                                        │   │
│  │  5. 密钥自动刷新                                                       │   │
│  │     ├── 定时器触发 (每30分钟)                                         │   │
│  │     ├── POST /api/keys/refresh                                        │   │
│  │     ├── 使用当前动态密钥签名                                          │   │
│  │     ├── 服务端生成新密钥并更新 Redis                                  │   │
│  │     └── 客户端替换使用新密钥                                          │   │
│  │                                                                        │   │
│  │  6. 心跳同步更新 (可选)                                                │   │
│  │     ├── 心跳响应中包含新密钥信息                                      │   │
│  │     ├── 减少额外网络请求                                              │   │
│  │     └── 客户端解析响应更新密钥                                        │   │
│  │                                                                        │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【Redis 存储设计】                                                          │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  Key: hmac_key:{user_id}:{machine_code}                             │   │
│  │  Value: {                                                           │   │
│  │    "key": "abc123...",              // 动态 HMAC 密钥               │   │
│  │    "key_id": "uuid-12345",          // 密钥标识符                   │   │
│  │    "created_at": 1704105600,        // 创建时间                     │   │
│  │    "expires_at": 1704109200,        // 过期时间                     │   │
│  │    "refresh_count": 5,              // 刷新次数                     │   │
│  │    "last_used_at": 1704108000       // 最后使用时间                 │   │
│  │  }                                                                 │   │
│  │  TTL: 3600  // 1小时过期                                             │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【密钥分配日志表】                                                          │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  hmac_key_logs (HMAC密钥日志表)                                      │   │
│  │  ├── id: 主键                                                        │   │
│  │  ├── user_id: 用户ID                                                 │   │
│  │  ├── machine_code: 机器码                                            │   │
│  │  ├── key_id: 密钥标识符                                              │   │
│  │  ├── action: 操作类型 (ISSUE/REFRESH/EXPIRE/REVOKE)                 │   │
│  │  ├── client_version: 客户端版本                                      │   │
│  │  ├── created_at: 创建时间                                            │   │
│  │  └── expires_at: 密钥过期时间                                        │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【接口设计】                                                                │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  🔑 POST /api/keys/dynamic                                           │   │
│  │  │  获取动态 HMAC 密钥 (未登录状态,RSA+AES加密)                     │   │
│  │  │  Request: { machine_code, timestamp }                            │   │
│  │  │  Response: { encrypted_key, key_id, expires_in }                 │   │
│  │  │                                                               │   │
│  │  🔑 POST /api/keys/refresh                                          │   │
│  │  │  刷新动态 HMAC 密钥 (已登录状态,动态密钥签名)                    │   │
│  │  │  Headers: { X-Dynamic-Key-Id: key_uuid }                        │   │
│  │  │  Request: { machine_code }                                       │   │
│  │  │  Response: { encrypted_key, key_id, expires_in }                │   │
│  │  │                                                               │   │
│  │  🔑 POST /api/heartbeat (心跳同步密钥)                              │   │
│  │  │  心跳响应中可包含新密钥信息 (减少额外请求)                        │   │
│  │  │  Response: { ..., new_key_info: { encrypted_key, key_id } }     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【安全性增强】                                                              │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  1. 密钥与用户+机器绑定                                               │   │
│  │     ├── 请求时验证 user_id 和 machine_code 匹配                      │   │
│  │     ├── 不匹配 → 记录安全日志,可能封禁账号                          │   │
│  │     └── 防止密钥被其他用户/设备使用                                  │   │
│  │                                                                        │   │
│  │  2. 异常检测                                                          │   │
│  │     ├── 高频密钥请求检测 (5分钟内 > 3次)                             │   │
│  │     ├── 同一 IP 多个用户请求检测 (1小时内 > 5个)                     │   │
│  │     ├── 机器码变更检测                                               │   │
│  │     └── 检测到异常 → 记录日志 + 可能触发验证码                       │   │
│  │                                                                        │   │
│  │  3. 密钥过期机制                                                      │   │
│  │     ├── 默认1小时过期                                                 │   │
│  │     ├── 过期后自动失效                                                │   │
│  │     ├── 客户端需重新获取                                              │   │
│  │     └── 减少泄露密钥的长期影响                                        │   │
│  │                                                                        │   │
│  │  4. 主动失效机制                                                      │   │
│  │     ├── 支持服务端主动使密钥失效                                      │   │
│  │     ├── 用于处理安全事件                                              │   │
│  │     └── 删除 Redis 记录即可                                          │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【代码示例】                                                                │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  后端 - 获取动态密钥接口:                                            │   │
│  │  ```python                                                          │   │
│  │  @router.post("/api/keys/dynamic")                                  │   │
│  │  async def get_dynamic_key(request: Request):                       │   │
│  │      # 1. 解密请求参数                                               │   │
│  │      decrypted = decrypt_request(await request.body())              │   │
│  │      machine_code = decrypted.get('machine_code')                   │   │
│  │      timestamp = decrypted.get('timestamp')                         │   │
│  │                                                                        │   │
│  │      # 2. 验证时间戳                                                 │   │
│  │      if not validate_timestamp(timestamp):                          │   │
│  │          raise HTTPException(status_code=400, detail="请求已过期")  │   │
│  │                                                                        │   │
│  │      # 3. 验证机器码 (检查用户是否已绑定机器)                        │   │
│  │      user = db.query(User).filter(                                  │   │
│  │          User.machine_code == machine_code,                         │   │
│  │          User.status == 1                                           │   │
│  │      ).first()                                                      │   │
│  │      if not user:                                                   │   │
│  │          # 新用户或未绑定机器,允许获取密钥(后续登录时会绑定)       │   │
│  │          user_id = None                                             │   │
│  │      else:                                                          │   │
│  │          user_id = user.id                                          │   │
│  │                                                                        │   │
│  │      # 4. 生成随机动态密钥 (32字节 = 256位)                          │   │
│  │      import secrets                                                 │   │
│  │      dynamic_key = secrets.token_hex(32)                            │   │
│  │      key_id = str(uuid.uuid4())                                     │   │
│  │                                                                        │   │
│  │      # 5. 构建 Redis 存储结构                                        │   │
│  │      key_data = {                                                    │   │
│  │          "key": dynamic_key,                                         │   │
│  │          "key_id": key_id,                                           │   │
│  │          "created_at": time.time(),                                  │   │
│  │          "expires_at": time.time() + 3600,  # 1小时后过期           │   │
│  │          "refresh_count": 0,                                         │   │
│  │          "last_used_at": time.time(),                                │   │
│  │          "user_id": user_id,                                         │   │
│  │          "machine_code": machine_code                                │   │
│  │      }                                                               │   │
│  │                                                                        │   │
│  │      # 6. 存储到 Redis                                               │   │
│  │      redis_key = f"hmac_key:{user_id or 'anonymous'}:{machine_code}"│   │
│  │      redis.setex(redis_key, 3600, json.dumps(key_data))             │   │
│  │                                                                        │   │
│  │      # 7. 记录日志                                                    │   │
│  │      log_key_assignment(user_id, machine_code, key_id, "ISSUE")     │   │
│  │                                                                        │   │
│  │      # 8. 使用 RSA 公钥加密动态密钥返回                               │   │
│  │      public_key = get_rsa_public_key()                               │   │
│  │      encrypted_key = rsa_encrypt(dynamic_key, public_key)           │   │
│  │                                                                        │   │
│  │      return {                                                        │   │
│  │          "code": 200,                                                │   │
│  │          "encrypted_key": base64.b64encode(encrypted_key),          │   │
│  │          "key_id": key_id,                                           │   │
│  │          "expires_in": 3600,                                         │   │
│  │          "refresh_interval": 1800  # 30分钟后刷新                    │   │
│  │      }                                                               │   │
│  │  ```                                                                 │   │
│  │                                                                        │   │
│  │  后端 - 刷新动态密钥接口:                                             │   │
│  │  ```python                                                          │   │
│  │  @router.post("/api/keys/refresh")                                  │   │
│  │  async def refresh_key(request: Request,                             │   │
│  │                        key_id: str = Header(None)):                 │   │
│  │      # 1. 获取请求参数                                               │   │
│  │      decrypted = decrypt_request(await request.body())              │   │
│  │      machine_code = decrypted.get('machine_code')                   │   │
│  │                                                                        │   │
│  │      # 2. 获取当前密钥                                               │   │
│  │      redis_key = f"hmac_key:*:{machine_code}"  # 查找匹配的key      │   │
│  │      current_key_data = redis.get(redis_key)                        │   │
│  │      if not current_key_data:                                       │   │
│  │          # 密钥不存在,需要重新获取                                   │   │
│  │          return {"code": 403, "msg": "密钥已过期,请重新获取"}       │   │
│  │      current_data = json.loads(current_key_data)                    │   │
│  │                                                                        │   │
│  │      # 3. 验证 key_id 匹配                                           │   │
│  │      if key_id and current_data.get("key_id") != key_id:            │   │
│  │          log_security_event("key_id_mismatch", machine_code)        │   │
│  │          return {"code": 403, "msg": "密钥验证失败"}                 │   │
│  │                                                                        │   │
│  │      # 4. 验证 HMAC 签名 (使用当前动态密钥)                           │   │
│  │      provided_signature = decrypted.get('signature')                │   │
│  │      sign_string = build_sign_string(decrypted)                     │   │
│  │      if not verify_hmac(sign_string, provided_signature,            │   │
│  │                         current_data["key"]):                       │   │
│  │          log_security_event("signature_verify_failed",              │   │
│  │                             machine_code)                           │   │
│  │          return {"code": 400, "msg": "签名验证失败"}                 │   │
│  │                                                                        │   │
│  │      # 5. 生成新密钥                                                  │   │
│  │      new_key = secrets.token_hex(32)                                │   │
│  │      new_key_id = str(uuid.uuid4())                                 │   │
│  │                                                                        │   │
│  │      # 6. 更新 Redis                                                  │   │
│  │      current_data["key"] = new_key                                   │   │
│  │      current_data["key_id"] = new_key_id                             │   │
│  │      current_data["refresh_count"] += 1                              │   │
│  │      current_data["last_used_at"] = time.time()                     │   │
│  │      redis.setex(redis_key, 3600, json.dumps(current_data))         │   │
│  │                                                                        │   │
│  │      # 7. 记录日志                                                    │   │
│  │      log_key_assignment(current_data.get("user_id"),                │   │
│  │                         machine_code, new_key_id, "REFRESH")        │   │
│  │                                                                        │   │
│  │      # 8. 返回新密钥                                                  │   │
│  │      encrypted_new_key = rsa_encrypt(new_key, get_rsa_public_key()) │   │
│  │      return {                                                        │   │
│  │          "code": 200,                                                │   │
│  │          "encrypted_key": base64.b64encode(encrypted_new_key),      │   │
│  │          "key_id": new_key_id,                                       │   │
│  │          "expires_in": 3600                                          │   │
│  │      }                                                               │   │
│  │  ```                                                                 │   │
│  │                                                                        │   │
│  │  客户端 - 获取动态密钥 (Rust):                                        │   │
│  │  ```rust                                                            │   │
│  │  #[tauri::command]                                                   │   │
│  │  async fn get_dynamic_hmac_key() -> Result<DynamicKeyResponse, String> {
│  │      // 1. 检查本地是否有有效密钥                                     │   │
│  │      if let Some(cached) = get_cached_key() {                       │   │
│  │          if cached.expires_at > current_time() {                    │   │
│  │              return Ok(cached);  // 使用缓存密钥                     │   │
│  │          }                                                           │   │
│  │      }                                                               │   │
│  │                                                                        │   │
│  │      // 2. 获取机器码                                                │   │
│  │      let machine_code = get_machine_code().await?;                  │   │
│  │                                                                        │   │
│  │      // 3. 构建请求参数                                               │   │
│  │      let request_data = json!({                                     │   │
│  │          "machine_code": machine_code,                               │   │
│  │          "timestamp": get_current_timestamp()                        │   │
│  │      });                                                             │   │
│  │                                                                        │   │
│  │      // 4. 使用 RSA 加密请求                                         │   │
│  │      let (encrypted_params, aes_key) = encrypt_with_rsa(&request_data).await?;
│  │                                                                        │   │
│  │      // 5. 发送请求                                                   │   │
│  │      let response = api_client.post("/api/keys/dynamic")            │   │
│  │          .json(&build_encrypted_request(encrypted_params, aes_key)) │   │
│  │          .send().await?;                                             │   │
│  │                                                                        │   │
│  │      // 6. 解密响应                                                   │   │
│  │      let encrypted_key = response.get("encrypted_key")              │   │
│  │          .and_then(|v| v.as_str())                                  │   │
│  │          .ok_or("No encrypted key")?;                               │   │
│  │                                                                        │   │
│  │      let dynamic_key = rsa_decrypt(encrypted_key)?;                 │   │
│  │                                                                        │   │
│  │      // 7. 缓存到内存                                                 │   │
│  │      let key_data = DynamicKeyData {                                 │   │
│  │          key: dynamic_key.clone(),                                   │   │
│  │          key_id: response.get("key_id")                              │   │
│  │              .and_then(|v| v.as_str())                              │   │
│  │              .unwrap_or("").to_string(),                             │   │
│  │          expires_at: current_time() + response.get("expires_in")    │   │
│  │              .and_then(|v| v.as_i64())                              │   │
│  │              .unwrap_or(3600),                                       │   │
│  │          refresh_interval: response.get("refresh_interval")         │   │
│  │              .and_then(|v| v.as_i64())                              │   │
│  │              .unwrap_or(1800)                                        │   │
│  │      };                                                              │   │
│  │      cache_key_to_memory(key_data);                                  │   │
│  │                                                                        │   │
│  │      // 8. 启动刷新定时器                                            │   │
│  │      start_refresh_timer(key_data.refresh_interval);                │   │
│  │                                                                        │   │
│  │      Ok(DynamicKeyResponse {                                         │   │
│  │          key: dynamic_key,                                           │   │
│  │          key_id: key_data.key_id                                     │   │
│  │      })                                                              │   │
│  │  }                                                                   │   │
│  │  ```                                                                 │   │
│  │                                                                        │   │
│  │  客户端 - 使用动态密钥签名 (Rust):                                    │   │
│  │  ```rust                                                            │   │
│  │  fn sign_with_dynamic_key(data: &str, dynamic_key: &str) -> String {│   │
│  │      let mut mac = HmacSha256::new_from_slice(dynamic_key.as_bytes())│   │
│  │          .expect("HMAC init failed");                                │   │
│  │                                                                        │   │
│  │      mac.update(data.as_bytes());                                    │   │
│  │      let signature = mac.finalize().into_bytes();                    │   │
│  │                                                                        │   │
│  │      general_purpose::STANDARD.encode(signature)                     │   │
│  │  }                                                                   │   │
│  │  ```                                                                 │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【方案选择建议】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  场景                              │  推荐方案                       │   │
│  │  ─────────────────────────────────│───────────────────────────────│   │
│  │  普通工具软件                       │  静态密钥 + 服务端防护          │   │
│  │  高价值应用 (如游戏、支付)           │  动态密钥                      │   │
│  │  极高安全要求 (如金融、政务)          │  动态密钥 + 设备绑定 + 二次验证 │   │
│  │  游铛铛客户端exe                     │  动态密钥 (推荐)               │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【方案对比总结】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  ✅ 动态密钥相比静态密钥的优势:                                      │   │
│  │     1. 即使泄露,影响范围有限 (仅当前密钥)                           │   │
│  │     2. 定期轮换,攻击窗口小                                         │   │
│  │     3. 完整审计日志,可追溯                                         │   │
│  │     4. 支持主动失效,应对安全事件                                    │   │
│  │     5. 增加破解成本 (需要持续获取新密钥)                             │   │
│  │                                                                        │   │
│  │  ⚠️ 需要考虑的代价:                                                  │   │
│  │     1. 实现复杂度增加                                                │   │
│  │     2. 首次请求增加网络延迟                                          │   │
│  │     3. 服务端 Redis 存储和查询压力                                   │   │
│  │     4. 密钥刷新失败的处理逻辑                                        │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

3.4 服务端签名验证与生成

┌─────────────────────────────────────────────────────────────────────────────┐
│                              服务端安全处理                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  【请求签名验证】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  def verify_client_signature(params: dict, provided_signature: str) │   │
│  │      # 1. 构建签名字符串                                              │   │
│  │      sign_string = f"{timestamp}:{nonce}:{encrypted_params}:"      │   │
│  │                      f"{aes_key}:{key_version}"                      │   │
│  │                                                                       │   │
│  │      # 2. 使用客户端密钥计算签名                                      │   │
│  │      expected = hmac_sha256(sign_string, CLIENT_SECRET)              │   │
│  │                                                                       │   │
│  │      # 3. 常数时间比较,防止时序攻击                                  │   │
│  │      return hmac.compare_digest(signature, expected)                 │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【响应签名生成】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  def generate_server_signature(encrypted_data: str,                  │   │
│  │                                 key_version: int) -> str:           │   │
│  │      # 1. 生成响应时间戳和nonce                                      │   │
│  │      response_timestamp = get_current_timestamp()                    │   │
│  │      response_nonce = generate_random_nonce()                        │   │
│  │                                                                       │   │
│  │      # 2. 构建响应签名字符串                                          │   │
│  │      sign_string = f"{response_timestamp}:{response_nonce}:"       │   │
│  │                      f"{encrypted_data}:{key_version}"                │   │
│  │                                                                       │   │
│  │      # 3. 使用服务端密钥生成签名                                      │   │
│  │      signature = hmac_sha256(sign_string, SERVER_SECRET)             │   │
│  │                                                                       │   │
│  │      return signature, response_timestamp, response_nonce            │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  1. 签名验证                                                                 │
│     ├── 验证客户端HMAC-SHA256签名                                           │
│     ├── 签名不匹配 → 返回400                                                │
│     └── 使用常数时间比较防止时序攻击                                        │
│                                                                              │
│  2. 响应签名                                                                 │
│     ├── 服务端生成响应签名并返回                                            │
│     ├── 签名使用独立的 SERVER_SECRET                                        │
│     └── 客户端使用相同密钥验证                                              │
│                                                                              │
│  3. 时间戳验证                                                               │
│     ├── 验证请求时间戳在容忍范围内                                          │
│     ├── 超时 → 返回400                                                      │
│     └── 响应时间戳也返回给客户端                                            │
│                                                                              │
│  4. Nonce防重放                                                              │
│     ├── 验证nonce是否已使用                                                │
│     ├── 已使用 → 返回400                                                    │
│     └── 自动清理过期nonce                                                   │
│                                                                              │
│  5. 登录验证 (仅私有接口)                                                    │
│     ├── 验证session_id有效性                                               │
│     ├── 验证session过期时间                                                │
│     ├── 验证机器码匹配                                                     │
│     ├── 验证卡密有效期                                                     │
│     └── 验证失败 → 自动退出登录                                            │
│                                                                              │
│  6. 暴力破解防护                                                             │
│     ├── 登录失败次数限制 (从system_config获取)                             │
│     ├── 超过限制 → 锁定账号                                                │
│     └── 无验证码要求                                                        │
└─────────────────────────────────────────────────────────────────────────────┘

3.5 通用验证机制 (所有登录后API接口)

┌─────────────────────────────────────────────────────────────────────────────┐
│                        所有登录后API接口通用验证                              │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  【设计原则】                                                                │
│  所有需要登录的API接口 (/api/user/*, /api/steam/*, /api/heartbeat等)        │
│  都需要进行以下4项验证,任一验证失败都会触发自动退出登录                      │
│                                                                              │
│  【验证流程】                                                                │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                                                                        │   │
│  │  1. 验证session_id有效性                                               │   │
│  │     ├── 检查session_id是否存在                                         │   │
│  │     ├── session不存在 → 返回401,客户端自动退出登录                   │   │
│  │     └── session存在 → 继续验证                                        │   │
│  │                                                                        │   │
│  │  2. 验证session过期时间                                               │
│  │     ├── 检查session.expires_at > 当前时间                             │   │
│  │     ├── 已过期 → 返回401,客户端自动退出登录                          │   │
│  │     └── 未过期 → 继续验证                                             │   │
│  │                                                                        │   │
│  │  3. 验证机器码匹配                                                    │
│  │     ├── 获取请求中的machine_code                                      │   │
│  │     ├── 比对session关联用户的machine_code                              │   │
│  │     ├── 不匹配 → 返回403,客户端自动退出登录                          │   │
│  │     └── 匹配 → 继续验证                                               │   │
│  │                                                                        │   │
│  │  4. 验证卡密有效期                                                    │
│  │     ├── 检查用户expire_time > 当前时间                                │   │
│  │     ├── 已过期 → 返回403,客户端自动退出登录                          │   │
│  │     └── 有效 → 验证通过                                               │   │
│  │                                                                        │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【验证失败处理】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  1. 后端返回特定错误码                                                │   │
│  │     ├── session无效/过期 → 返回 401 {code: 401, msg: "未登录"}       │   │
│  │     ├── 机器码不匹配 → 返回 403 {code: 403, msg: "设备已变更"}       │   │
│  │     └── 卡密过期 → 返回 403 {code: 403, msg: "卡密已过期"}           │   │
│  │                                                                        │   │
│  │  2. 客户端收到错误后自动处理:                                          │   │
│  │     ├── 停止心跳定时器                                                │   │
│  │     ├── 清除本地会话缓存                                              │   │
│  │     ├── 跳转到登录页面                                                │   │
│  │     └── 显示友好的错误提示                                            │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【错误响应示例】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  // Session过期或无效                                                │   │
│  │  {                                                                   │   │
│  │    "code": 401,                                                     │   │
│  │    "msg": "会话已过期,请重新登录"                                   │   │
│  │  }                                                                   │   │
│  │                                                                        │   │
│  │  // 机器码不匹配                                                     │   │
│  │  {                                                                   │   │
│  │    "code": 403,                                                     │   │
│  │    "msg": "账号已在其他设备登录,如非本人操作请联系客服"             │   │
│  │  }                                                                   │   │
│  │                                                                        │   │
│  │  // 卡密过期                                                         │   │
│  │  {                                                                   │   │
│  │    "code": 403,                                                     │   │
│  │    "msg": "您的卡密已到期,请前往续费或联系客户支持"                 │   │
│  │  }                                                                   │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

3.6 Session自动续期机制

┌─────────────────────────────────────────────────────────────────────────────┐
│                          Session自动续期流程                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  【设计原理】                                                                │
│  用户活跃使用时,session会自动续期,避免用户频繁重新登录                      │
│                                                                              │
│  【续期触发条件】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  每次心跳请求时,检查session剩余时间:                                 │   │
│  │                                                                        │   │
│  │  if (session.expires_at - current_time) < session_renewal_threshold:  │   │
│  │      # 剩余时间 < 续期阈值(默认30分钟)                                │   │
│  │      session.expires_at = current_time + session_extension_duration   │   │
│  │      # 延长session有效期 (默认延长1小时)                              │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【配置项】 (system_config表)                                                │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  配置键                           │ 默认值    │ 说明                │   │
│  │  --------------------------------│----------│-------------------│   │
│  │  session_renewal_threshold       │ 1800     │ 续期阈值(秒)=30分钟 │   │
│  │  session_extension_duration      │ 3600     │ 延长时间(秒)=1小时  │   │
│  │  session_default_expiry_seconds  │ 86400    │ 默认有效期(24小时)  │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【续期流程图】                                                              │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  客户端发起心跳请求                                                     │   │
│  │         │                                                              │   │
│  │         ▼                                                              │   │
│  │  ┌─────────────────────────┐                                          │   │
│  │  │ 获取session剩余时间:    │                                          │   │
│  │  │ remaining = expires_at -│                                          │   │
│  │  │          current_time   │                                          │   │
│  │  └───────────┬─────────────┘                                          │   │
│  │              │                                                        │   │
│  │              ▼                                                        │   │
│  │  ┌─────────────────────────────────────────┐                        │   │
│  │  │ remaining < 30分钟? (1800秒)            │                        │   │
│  │  └──────────────────┬──────────────────────┘                        │   │
│  │                     │                                                │   │
│  │         ┌───────────┴───────────┐                                    │   │
│  │         ▼                       ▼                                    │   │
│  │  ┌─────────────┐        ┌─────────────────────┐                    │   │
│  │  │ 是          │        │ 否                  │                    │   │
│  │  │ 延长session │        │ 不操作              │                    │   │
│  │  └──────┬──────┘        └─────────────────────┘                    │   │
│  │         │                                                              │   │
│  │         ▼                                                              │   │
│  │  ┌─────────────────────────────────────────┐                        │   │
│  │  │ session.expires_at = now + 1小时        │                        │   │
│  │  │ 生成历史记录 (UPDATE操作)                 │                        │   │
│  │  └─────────────────────────────────────────┘                        │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【续期历史记录】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  session续期操作会自动记录到sessions_his历史表:                       │   │
│  │                                                                        │   │
│  │  his_created_at: NOW()                                                │   │
│  │  his_operation: 'RENEW' (新增操作类型)                                │   │
│  │  expires_at_before: 续期前的过期时间                                  │   │
│  │  expires_at_after: 续期后的过期时间                                   │   │
│  │  reason: 'session_auto_renewal'                                      │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【心跳响应返回续期状态】                                                    │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  {                                                                   │   │
│  │    "code": 200,                                                     │   │
│  │    "msg": "成功",                                                   │   │
│  │    "data": {                                                        │   │
│  │      "config": {...},                                               │   │
│  │      "user_info": {                                                 │   │
│  │        "expire_time": "2026-03-01",                                 │   │
│  │        "days_remaining": 30                                         │   │
│  │      },                                                             │   │
│  │      "session_info": {                                              │   │
│  │        "expires_at": "2026-02-02T10:30:00",  // 续期后的过期时间    │   │
│  │        "remaining_seconds": 3600,           // 剩余秒数             │   │
│  │        "renewed": true                      // 是否进行了续期       │   │
│  │      }                                                              │   │
│  │    }                                                                │   │
│  │  }                                                                   │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【客户端处理续期状态】                                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  Rust后端解析心跳响应:                                               │   │
│  │                                                                        │   │
│  │  if response.session_info.renewed {                                  │   │
│  │      // Session进行了续期,更新本地session缓存                       │   │
│  │      updateLocalSessionCache(response.session_info);                 │   │
│  │  }                                                                   │   │
│  │                                                                        │   │
│  │  // 无论是否续期,都更新本地session_info                             │   │
│  │  updateLocalSessionInfo(response.session_info);                      │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

3.7 客户端签名验证

┌─────────────────────────────────────────────────────────────────────────────┐
│                              客户端签名验证                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  【请求签名生成】 (Rust后端)                                                 │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  fn generate_request_signature(timestamp: i64,                       │   │
│  │                               nonce: &str,                           │   │
│  │                               encrypted_params: &str,                │   │
│  │                               aes_key_base64: &str,                  │   │
│  │                               key_version: i32) -> String:          │   │
│  │      let secret_key = b"buff-client-secret-2024";                    │   │
│  │                                                                       │   │
│  │      let sign_string = format!("{}:{}:{}:{}:{}",                     │   │
│  │          timestamp, nonce, encrypted_params, aes_key_base64,         │   │
│  │          key_version);                                                │   │
│  │                                                                       │   │
│  │      // HMAC-SHA256签名                                               │   │
│  │      let signature = hmac_sha256(sign_string, secret_key);           │   │
│  │      base64_encode(signature)                                         │   │
│  │  }                                                                    │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【响应签名验证】 (Rust后端)                                                 │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  fn verify_server_signature(response: &Response,                     │   │
│  │                             server_signature: &str) -> bool:        │   │
│  │      // 1. 构建响应签名字符串                                         │   │
│  │      let sign_string = format!("{}:{}:{}:{}",                        │   │
│  │          response.timestamp, response.nonce,                         │   │
│  │          response.encrypted_data, response.key_version);             │   │
│  │                                                                       │   │
│  │      // 2. 使用服务端密钥计算签名                                     │   │
│  │      let expected = hmac_sha256(sign_string, SERVER_SECRET);         │   │
│  │                                                                       │   │
│  │      // 3. 常数时间比较                                               │   │
│  │      constant_time_eq(server_signature, &expected)                    │   │
│  │  }                                                                    │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【响应签名验证流程】                                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  async fn handle_api_response<T>(encrypted_response: &str)          │   │
│  │      -> Result<T, ApiError>:                                         │   │
│  │                                                                       │   │
│  │      // 1. 解析响应JSON                                              │   │
│  │      let response: ApiResponse = parse_json(encrypted_response);     │   │
│  │                                                                       │   │
│  │      // 2. 验证响应签名                                               │   │
│  │      let is_valid = verify_server_signature(                          │   │
│  │          &response,                                                   │   │
│  │          &response.signature                                          │   │
│  │      );                                                               │   │
│  │                                                                       │   │
│  │      if !is_valid {                                                   │   │
│  │          // 签名验证失败,可能是中间人攻击                            │   │
│  │          return Err(ApiError::SignatureVerificationFailed);          │   │
│  │      }                                                                │   │
│  │                                                                       │   │
│  │      // 3. 解密响应数据                                              │   │
│  │      let decrypted = aes_decrypt(&response.encrypted_data,           │   │
│  │                                   &local_aes_key);                   │   │
│  │                                                                       │   │
│  │      // 4. 返回解密后的数据                                           │   │
│  │      parse_json(&decrypted)                                           │   │
│  │  }                                                                    │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【签名验证失败处理】                                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  1. 记录安全日志                                                      │   │
│  │  2. 清除本地会话缓存                                                  │   │
│  │  3. 跳转到登录页面                                                    │   │
│  │  4. 显示安全警告弹窗:                                                 │   │
│  │     "检测到响应签名验证失败,可能存在中间人攻击,\n"                 │   │
│  │     "为保障您的账户安全,请重新登录。"                                │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

3.6 RSA密钥管理 (仅backend-admin)

┌─────────────────────────────────────────────────────────────────────────────┐
│                          RSA密钥管理架构                                     │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  backend-admin (管理端)                                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │ • 密钥生成: generate_key_pair()                                     │    │
│  │ • 密钥轮换: 自动/手动两种模式                                        │    │
│  │ • 密钥存储: api_keys表 (包含私钥)                                    │    │
│  │ • 密钥过期: 默认10天                                                 │    │
│  │ • 历史记录: 自动记录到历史表                                         │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                   │                                         │
│                                   ▼                                         │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │  GET /api/keys/public (客户端接口)                                   │    │
│  │  返回: { "public_key": "...", "key_version": 1 }                   │    │
│  │  加密方式: AES (密钥服务内置密钥)                                    │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                   │                                         │
│                                   ▼                                         │
│  backend-client (客户端API)                                                  │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │ • 使用公钥进行RSA加密                                                 │    │
│  │ • 验证密钥版本有效性                                                  │    │
│  │ • 使用私钥进行RSA解密 (仅管理端可见)                                 │    │
│  │ • 不参与密钥生成和轮换                                                │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                                                              │
│  client-tauri (客户端exe)                                                    │
│  ┌─────────────────────────────────────────────────────────────────────┐    │
│  │ • 获取并缓存RSA公钥                                                  │    │
│  │ • 使用公钥进行RSA加密                                                 │    │
│  │ • RSA公钥分段混淆存储 (内存中)                                        │    │
│  │ • 每次启动都重新获取最新公钥                                          │    │
│  │ • 不参与密钥生成和管理                                                │    │
│  └─────────────────────────────────────────────────────────────────────┘    │
│                                                                              │
│  【客户端获取密钥流程】                                                       │
│                                                                              │
│  打开exe → 立即调用 /api/keys/public → 获取最新公钥 → 缓存到内存             │
│                                                                              │
│  【密钥版本管理】                                                             │
│  • 每次请求携带key_version                                                   │
│  • 后端验证版本有效性                                                        │
│  • 支持多版本共存平滑过渡                                                    │
└─────────────────────────────────────────────────────────────────────────────┘

3.5 RSA公钥存储安全 (Rust后端)

// 分段混淆存储设计
static mut RSA_KEY_SEGMENTS: [Option<String>; 8] = [
    None, None, None, None, None, None, None, None
];

// 随机存储顺序
static RSA_KEY_SEGMENT_ORDER: [usize; 8] = [3, 7, 1, 5, 0, 6, 2, 4];

// 存储流程
fn store_rsa_key_segments(public_key: String) {
    // 1. Base64编码
    let encoded_key = base64_encode(public_key);

    // 2. 分拆为8段
    let segment_size = (encoded_key.len() + 7) / 8;

    // 3. 按随机顺序分散存储
    for &segment_index in RSA_KEY_SEGMENT_ORDER.iter() {
        let segment = extract_segment(&encoded_key, segment_size);
        let noisy_segment = add_noise_marker(segment);  // 添加噪声标记
        RSA_KEY_SEGMENTS[segment_index] = Some(noisy_segment);
    }
}

// 重组流程
fn retrieve_rsa_key_segments() -> Result<String, String> {
    // 1. 按正确顺序收集所有段
    // 2. 移除噪声标记
    // 3. 重新组合并Base64解码
    return base64_decode(combined_segments);
}

四、接口设计

4.1 接口分类与加密要求

┌─────────────────────────────────────────────────────────────────────────────┐
│                            接口分类设计                                      │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  【加密方式说明】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  • AES+RSA加密: 敏感数据传输,使用RSA公钥加密AES密钥                 │   │
│  │  • AES加密: 仅使用服务端内置AES密钥加密                              │   │
│  │  • 签名: HMAC-SHA256双向签名验证                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【核心规则】                                                                │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  1. 登录前接口 (无需session和machine_code)                          │   │
│  │     ├── 加密方式: AES+RSA                                            │   │
│  │     ├── 无需验证: session、machine_code                              │   │
│  │     └── 需要验证: 签名、时间戳、Nonce                                │   │
│  │                                                                        │   │
│  │  2. 登录后接口 (需要验证session、machine_code、卡密有效期)           │   │
│  │     ├── 加密方式: AES+RSA                                            │   │
│  │     ├── 需要验证: 签名、时间戳、Nonce、session、machine_code、卡密   │   │
│  │     └── 包括: 用户信息、Steam账号、心跳、密钥同步等                  │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  🔓 登录前接口 (无需登录, AES+RSA加密, 无需session验证)                      │
│  ├── GET  /api/announcements      - 获取公告列表                             │
│  ├── POST /api/auth/register      - 用户注册                                 │
│  ├── POST /api/auth/login         - 用户登录                                 │
│  ├── POST /api/auth/recharge      - 卡密充值 (需要machine_code验证)          │
│  └── POST /api/auth/unbind        - 机器解绑                                 │
│                                                                              │
│  🔐 登录后接口 (需要登录, AES+RSA加密, 需要完整验证)                          │
│  ├── POST /api/user/profile       - 获取用户信息                             │
│  ├── POST /api/steam/bind         - 绑定Steam账号                            │
│  ├── POST /api/steam/unbind       - 解绑Steam账号                            │
│  ├── GET  /api/steam/accounts     - 获取已绑定的Steam账号                    │
│  ├── POST /api/keys/sync          - 同步RSA公钥 (AES+RSA加密)                │
│  └── POST /api/heartbeat          - 心跳检测 (返回system_config配置)         │
│                                                                              │
│  🔑 RSA公钥获取接口 (特殊处理)                                               │
│  └── POST /api/keys/public        - 获取RSA公钥                              │
│      ├── 客户端启动时立即调用 (AES加密,服务端内置密钥)                      │
│      ├── 加密方式: AES (使用服务端内置AES密钥)                              │
│      ├── 登录前: 返回 { public_key, key_version }                          │
│      └── 登录后: 同样使用AES加密,但可携带session信息                        │
│                                                                              │
│  【配置获取规则】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  客户端所有配置信息必须通过心跳服务从system_config表获取:           │   │
│  │                                                                        │   │
│  │  • heartbeat_interval_seconds       - 心跳间隔                       │   │
│  │  • max_heartbeat_failures           - 心跳最大失败次数              │   │
│  │  • session_renewal_threshold        - Session续期阈值               │   │
│  │  • session_extension_duration       - Session延长时间               │   │
│  │  • 其他所有客户端相关配置...                                         │   │
│  │                                                                        │   │
│  │  客户端本地不存储任何配置,全部动态获取                               │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

4.2 多场景接口设计

根据客户端工程的建设思路提示词.md,整理出以下多种场景的接口设计:

场景一:客户端启动

【场景描述】
用户打开exe程序,系统进行初始化

【处理流程】
┌─────────────────────────────────────────────────────────────────┐
│  客户端启动流程                                                   │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 单例检查                                                     │
│     ├── FindWindow/互斥体检测                                    │
│     └── 已运行 → 激活窗口并退出当前                              │
│                                                                 │
│  2. 获取机器信息                                                 │
│     ├── 硬盘序列号                                               │
│     ├── MAC地址                                                  │
│     └── 生成MD5机器码                                            │
│                                                                 │
│  3. 获取RSA公钥 (未登录状态,使用AES加密)                                     │
│     ├── 生成随机AES密钥                                                      │
│     ├── 使用AES密钥加密机器特征信息                                           │
│     ├── 调用: POST /api/keys/public                                          │
│     ├── 请求: { encrypted_params, aes_key, timestamp, nonce, signature }    │
│     ├── 后端使用RSA私钥解密AES密钥                                           │
│     ├── 后端使用AES密钥解密获取机器特征信息                                   │
│     ├── 后端返回: { public_key, key_version }                                │
│     ├── 缓存公钥到Rust内存 (分段混淆存储)                                     │
│     └── 如果获取失败 → 显示错误提示,阻塞后续操作                             │
│                                                                 │
│  4. 获取公告列表                                                             │
│     ├── 使用已缓存的RSA公钥 (AES+RSA加密)                                    │
│     ├── 调用: GET /api/announcements                                         │
│     └── 显示在登录界面                                                       │
│                                                                 │
│  5. 启动定时任务                                                             │
│     ├── 密钥同步 (每60秒)                                                    │
│     │   - 仅已登录状态下执行,使用AES+RSA加密                               │
│     └── 如果未登录,不启动心跳                                               │
│                                                                 │
. 显示登录界面                                                 │
│     ├── 检查本地会话缓存                                         │
│     ├──│  6 如果有有效会话 → 自动完成登录                            │
│     └── 否则显示登录表单                                         │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

场景二:用户登录

【场景描述】
用户在登录界面输入用户名和密码,点击登录

【处理流程】
┌─────────────────────────────────────────────────────────────────┐
│  用户登录流程                                                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 本地验证                                                     │
│     ├── 用户名非空                                               │
│     ├── 密码非空                                                 │
│     └── 机器码已获取                                             │
│                                                                 │
│  2. 获取RSA公钥 (如果内存中没有)                                 │
│     └── 调用 GET /api/keys/public                                │
│                                                                 │
│  3. 生成随机AES密钥                                              │
│     └── 32字节随机密钥                                           │
│                                                                 │
│  4. 加密请求参数                                                 │
│     ├── 使用AES密钥加密: {username, password, machine_code}      │
│     ├── 使用RSA公钥加密AES密钥                                   │
│     ├── 生成时间戳和nonce                                        │
│     └── 生成HMAC签名                                             │
│                                                                 │
│  5. 发送登录请求                                                 │
│     └── POST /api/auth/login                                     │
│                                                                 │
│  6. 后端处理                                                     │
│     ├── 验证时间戳和nonce                                        │
│     ├── 验证签名                                                 │
│     ├── 解密请求参数                                             │
│     ├── 验证用户名密码                                           │
│     ├── 验证账户状态                                             │
│     ├── 验证卡密有效期                                           │
│     ├── 首次登录 → 生成机器码并绑定                              │
│     ├── 非首次登录 → 验证机器码匹配                              │
│     ├── 创建/更新会话                                            │
│     ├── 记录API日志                                              │
│     ├── 加密响应数据 (使用请求中的AES密钥)                       │
│     └── 返回响应                                                 │
│                                                                 │
│  7. 客户端处理响应                                               │
│     ├── 解密响应数据                                             │
│     ├── 登录成功 →                                              │
│     │   ├── 保存session_id                                       │
│     │   ├── 保存user_id                                          │
│     │   ├── 跳转到主界面                                         │
│     │   └── 调用心跳服务                                         │
│     └── 登录失败 → 显示错误提示                                  │
│                                                                 │
│  8. 登录失败处理                                                 │
│     ├── 失败次数+1                                               │
│     ├── 检查是否超过限制 (从system_config获取)                   │
│     ├── 超过限制 → 锁定账号                                      │
│     └── 无验证码要求                                             │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

场景三:登录成功后的心跳

【场景描述】
用户登录成功后,立即调用心跳服务获取配置,后续定时调用

【处理流程】
┌─────────────────────────────────────────────────────────────────┐
│  心跳检测流程                                                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 登录成功后立即调用                                           │
│     └── POST /api/heartbeat (AES+RSA加密)                       │
│     Request: { session_id, machine_code }                       │
│                                                                 │
│  2. 后端处理                                                     │
│     ├── 验证session有效性                                       │
│     ├── 验证机器码匹配                                           │
│     ├── 检查用户卡密有效期                                       │
│     ├── 从system_config表获取配置:                              │
│     │   ├── heartbeat_interval_seconds                          │
│     │   ├── max_heartbeat_failures                              │
│     │   ├── timestamp_tolerance_seconds                         │
│     │   └── ...                                                  │
│     ├── 获取用户状态:                                            │
│     │   ├── expire_time                                          │
│     │   └── days_remaining                                       │
│     ├── 记录API日志                                              │
│     └── 加密响应数据                                             │
│                                                                 │
│  3. 客户端处理响应                                               │
│     ├── 解密响应数据                                             │
│     ├── 更新配置缓存                                             │
│     ├── 更新用户状态                                             │
│     ├── 重置失败计数                                             │
│     └── 设置定时器 (根据新间隔)                                  │
│                                                                 │
│  4. 定时心跳调用                                                 │
│     ├── 按配置的间隔时间定时调用                                 │
│     ├── 每次心跳重置失败计数                                     │
│     └── 失败处理:                                                │
│         ├── 失败次数+1                                           │
│         ├── 检查是否超过阈值                                     │
│         ├── 未超过 → 顶部提示"心跳服务不可用"                    │
│         └── 超过 → 自动退出登录,弹窗提醒                        │
│                                                                 │
│  5. 心跳响应内容                                                 │
│     {                                                           │
│       "config": {                                               │
│         "heartbeat_interval_seconds": 10,                      │
│         "max_heartbeat_failures": 100,                         │
│         "timestamp_tolerance_seconds": 300,                    │
│         "prevent_duplicate_login": true                        │
│       },                                                       │
│       "user_info": {                                           │
│         "expire_time": "2026-03-01",                           │
│         "days_remaining": 30                                   │
│       }                                                        │
│     }                                                           │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

场景四:验证失败处理

【场景描述】
用户已登录,但接口验证失败(session过期、机器码不匹配、卡密过期等)

【处理流程】
┌─────────────────────────────────────────────────────────────────┐
│  验证失败处理流程                                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 触发条件                                                     │
│     ├── session过期/无效                                        │
│     ├── 机器码不匹配                                            │
│     ├── 卡密已过期                                              │
│     └── 账户被禁用                                              │
│                                                                 │
│  2. 后端处理                                                     │
│     ├── 验证失败                                                │
│     ├── 清除会话 (如果是重复登录)                               │
│     ├── 记录API日志                                             │
│     └── 返回错误响应                                            │
│                                                                 │
│  3. 客户端处理                                                   │
│     ├── 收到验证失败响应                                        │
│     ├── 停止心跳定时器                                          │
│     ├── 清除本地会话缓存                                        │
│     ├── 跳转到登录页面                                          │
│     ├── 显示错误提示                                            │
│     └── 停止所有子线程                                          │
│                                                                 │
│  4. 用户体验                                                     │
│     ├── 自动返回登录界面                                        │
│     ├── 显示友好的错误提示                                      │
│     └── 如卡密过期 → 提示续费                                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

场景五:卡密充值

【场景描述】
用户在登录界面或充值页面输入卡密进行充值

【处理流程】
┌─────────────────────────────────────────────────────────────────┐
│  卡密充值流程                                                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 用户输入卡密                                                 │
│     └── 提交充值请求 (AES+RSA加密)                               │
│     Request: { card_code, username, password, machine_code }    │
│                                                                 │
│  2. 后端处理逻辑                                                 │
│     ├── 验证卡密格式                                            │
│     ├── 查找卡密记录                                            │
│     ├── 验证卡密状态:                                           │
│     │   ├── status=0 (未激活)                                   │
│     │   └── 未过期                                              │
│     ├── 验证软件类型匹配                                        │
│     ├── 激活卡密:                                               │
│     │   ├── status=1                                            │
│     │   ├── user_id=当前用户                                    │
│     │   ├── activated_at=NOW()                                  │
│     │   └── expire_time=NOW()+days                              │
│     ├── 更新用户信息:                                           │
│     │   ├── card_id=卡密ID                                      │
│     │   └── expire_time=卡密到期时间                            │
│     ├── 创建充值记录                                            │
│     └── 加密响应数据                                            │
│                                                                 │
│  3. 客户端处理响应                                               │
│     ├── 解密响应数据                                            │
│     ├── 充值成功 →                                             │
│     │   ├── 更新本地用户信息                                    │
│     │   ├── 显示充值成功提示                                    │
│     │   └── 显示新的到期时间                                    │
│     └── 充值失败 → 显示错误提示                                 │
│                                                                 │
│  4. 特殊说明                                                     │
│     ├── 充值后立即生效,无需重新登录                            │
│     ├── 充值接口不需要登录session                               │
│     └── 需要机器码验证                                          │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

场景六:机器解绑

【场景描述】
用户需要在新机器上登录,需要先解绑原机器

【处理流程】
┌─────────────────────────────────────────────────────────────────┐
│  机器解绑流程                                                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  1. 用户触发解绑                                                 │
│     └── 点击"机器解绑"按钮                                      │
│                                                                 │
│  2. 验证密码                                                     │
│     └── 输入当前账户密码                                        │
│                                                                 │
│  3. 发送解绑请求 (AES+RSA加密)                                   │
│     └── POST /api/auth/unbind                                   │
│     Request: { username, password, machine_code }               │
│                                                                 │
│  4. 后端处理                                                     │
│     ├── 验证用户名密码                                          │
│     ├── users.machine_code = NULL                               │
│     ├── 清除用户所有会话 (强制下线)                              │
│     ├── 生成历史记录                                            │
│     └── 加密响应数据                                            │
│                                                                 │
│  5. 客户端处理响应                                               │
│     ├── 解密响应数据                                            │
│     ├── 解绑成功 →                                             │
│     │   ├── 清除本地会话缓存                                    │
│     │   ├── 跳转到登录页面                                      │
│     │   └── 提示"解绑成功,可在其他机器登录"                    │
│     └── 解绑失败 → 显示错误提示                                 │
│                                                                 │
│  6. 登录策略总结                                                 │
│     ┌─────────────────────────────────────────────────────────┐ │
│     │ machine_code = NULL      → 任意机器可登录               │ │
│     │ machine_code 有值         → 必须在绑定机器登录           │ │
│     │ 新机器尝试登录            → 拒绝+提示解绑                │ │
│     └─────────────────────────────────────────────────────────┘ │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

4.3 核心接口详情

4.3.1 获取RSA公钥 (未登录状态)

接口: POST /api/keys/public
加密: AES (密钥服务内置密钥)
无需登录

【请求流程】
1. 客户端生成随机AES密钥 (32字节)
2. 客户端收集机器特征信息 (硬盘序列号、MAC地址)
3. 客户端使用AES密钥加密机器特征信息
4. 客户端使用AES密钥加密空的请求体 {}
5. 客户端发送加密后的请求

【请求报文结构】
{
  "encrypted_params": "base64(AES加密 {machine_info})",
  "aes_key": "base64(AES密钥)",  // 使用服务端内置AES密钥加密
  "timestamp": 1704105600,
  "nonce": "a1b2c3d4e5f6g7h8...",
  "signature": "base64(HMAC-SHA256签名)"
}

【后端处理】
1. 使用服务端内置AES密钥解密获取aes_key
2. 使用aes_key解密encrypted_params获取机器特征信息
3. 生成MD5机器码 (用于日志记录,不绑定用户)
4. 返回当前激活的RSA公钥和密钥版本

【响应报文】
{
  "code": 200,
  "msg": "成功",
  "data": {
    "public_key": "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----",
    "key_version": 1
  }
}

异常处理:
- RSA公钥不存在: 返回错误,客户端显示"系统初始化中"

4.3.2 同步RSA公钥 (已登录状态)

接口: POST /api/keys/sync
加密: AES+RSA (使用已缓存的公钥)
需要登录

【请求流程】
1. 生成随机AES密钥 (32字节)
2. 使用AES密钥加密 {machine_code, session_id}
3. 使用已缓存的RSA公钥加密AES密钥 (传递key_version)
4. 发送请求

【请求报文结构】
{
  "encrypted_params": "base64(AES加密 {machine_code, session_id})",
  "aes_key": "base64(RSA加密AES密钥)",
  "key_version": 1,  // 当前使用的密钥版本
  "timestamp": 1704105600,
  "nonce": "a1b2c3d4e5f6g7h8...",
  "signature": "base64(HMAC-SHA256签名)"
}

【后端处理】
1. 使用RSA私钥解密获取aes_key
2. 使用aes_key解密encrypted_params获取machine_code和session_id
3. 验证session_id有效性
4. 验证machine_code与session关联的用户是否匹配
5. 返回当前激活的RSA公钥 (可能与请求中的key_version相同或更新)

【响应报文】
{
  "code": 200,
  "msg": "成功",
  "data": {
    "public_key": "-----BEGIN PUBLIC KEY-----...-----END PUBLIC KEY-----",
    "key_version": 2  // 可能更新
  }
}

异常处理:
- session无效: 返回401,客户端自动退出登录
- machine_code不匹配: 返回403,客户端自动退出登录

4.3.3 获取公告列表

接口: GET /api/announcements
加密: AES+RSA
无需登录

请求参数:
{
  "page": 1,
  "page_size": 10
}

响应数据:
{
  "code": 200,
  "msg": "成功",
  "data": {
    "announcements": [
      {
        "id": 1,
        "title": "系统维护通知",
        "content": "...",
        "created_at": "2026-02-01T10:00:00"
      }
    ],
    "total": 5,
    "page": 1,
    "page_size": 10
  }
}

4.3.3 用户登录

接口: POST /api/auth/login
加密: AES+RSA
无需登录

请求参数:
{
  "username": "test",
  "password": "password123",
  "machine_code": "MD5哈希值"
}

响应数据:
{
  "code": 200,
  "msg": "登录成功",
  "data": {
    "user_id": 1,
    "username": "test",
    "expire_time": "2026-03-01",
    "days_remaining": 30
  }
}

异常码:
- 400: 请求参数错误
- 401: 用户名或密码错误
- 403: 卡密已过期/机器码不匹配/账号被禁用
- 423: 账号被锁定

4.3.4 用户注册

接口: POST /api/auth/register
加密: AES+RSA
无需登录

请求参数:
{
  "username": "newuser",
  "password": "password123",
  "email": "user@example.com",
  "card_code": "XXXX-XXXX-XXXX"  // 可选,注册时激活卡密
}

响应数据:
{
  "code": 200,
  "msg": "注册成功",
  "data": {
    "user_id": 2,
    "username": "newuser"
  }
}

4.3.5 卡密充值

接口: POST /api/auth/recharge
加密: AES+RSA
无需登录

请求参数:
{
  "username": "test",
  "password": "password123",
  "card_code": "XXXX-XXXX-XXXX"
}

响应数据:
{
  "code": 200,
  "msg": "充值成功",
  "data": {
    "expire_time": "2026-04-01",
    "days_remaining": 60
  }
}

4.3.6 心跳检测

接口: POST /api/heartbeat
加密: AES+RSA
需要登录

请求参数:
{
  "session_id": "uuid-xxx",
  "machine_code": "MD5哈希值"
}

响应数据:
{
  "code": 200,
  "msg": "成功",
  "data": {
    "config": {
      "heartbeat_interval_seconds": 10,
      "max_heartbeat_failures": 100,
      "timestamp_tolerance_seconds": 300
    },
    "user_info": {
      "expire_time": "2026-03-01",
      "days_remaining": 30
    }
  }
}

异常处理:
- session过期/无效 → 自动退出登录
- 机器码不匹配 → 自动退出登录
- 卡密过期 → 自动退出登录

4.4 接口日志记录

┌─────────────────────────────────────────────────────────────────────────────┐
│                            接口日志记录规范                                   │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  【日志记录内容】                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  所有接口必须记录以下4类数据:                                         │   │
│  │                                                                       │   │
│  │  1. 入参密文                                                         │   │
│  │     ├── 来源: 原始请求body                                           │   │
│  │     ├── 格式: Base64编码的加密数据                                   │   │
│  │     └── 自动由中间件捕获                                            │   │
│  │                                                                       │   │
│  │  2. 入参明文                                                         │   │
│  │     ├── 来源: 解密后的请求参数                                       │   │
│  │     ├── 格式: JSON对象                                              │   │
│  │     └── 需要在业务代码中调用 log_decrypted_request() 记录           │   │
│  │                                                                       │   │
│  │  3. 出参明文                                                         │   │
│  │     ├── 来源: 业务响应数据                                           │   │
│  │     ├── 格式: JSON对象                                              │   │
│  │     └── 需要在业务代码中调用 log_decrypted_response() 记录          │   │
│  │                                                                       │   │
│  │  4. 出参密文                                                         │   │
│  │     ├── 来源: 加密后的响应数据                                       │   │
│  │     ├── 格式: Base64编码的加密数据                                   │   │
│  │     └── 自动由中间件捕获                                            │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【日志存储方式】                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  1. 文件日志 (主要)                                                  │   │
│  │     ├── 路径: logs/api/api_YYYY-MM-DD.log                           │   │
│  │     ├── 格式: JSON Lines (每行一条JSON)                             │   │
│  │     ├── 轮转: 每天午夜自动创建新文件                                 │   │
│  │     └── 保留: 30天                                                   │   │
│  │                                                                       │   │
│  │  2. 数据库日志 (补充)                                                │   │
│  │     ├── 表: api_logs                                                │   │
│  │     ├── 内容: 请求和响应的密文(截断后)                               │   │
│  │     └── 用于快速查询和统计分析                                       │   │
│  │                                                                       │   │
│  │  3. 控制台日志 (调试)                                                │   │
│  │     ├── 格式: 简化版日志                                             │   │
│  │     └── 目的: 方便开发和调试                                         │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【日志文件格式】                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  {                                                                   │   │
│  │    "timestamp": "2026-02-02 18:30:45.123",   // 毫秒级时间戳        │   │
│  │    "endpoint": "/api/v1/auth/login",         // 接口路径           │   │
│  │    "method": "POST",                          // HTTP方法           │   │
│  │    "user_id": 1,                              // 用户ID            │   │
│  │    "client_ip": "192.168.1.100",              // 客户端IP          │   │
│  │    "processing_time_ms": 105,                 // 处理时间(ms)      │   │
│  │    "status_code": 200,                        // HTTP状态码        │   │
│  │    "error": null,                             // 错误信息          │   │
│  │    "request": {                               // 请求数据          │   │
│  │      "encrypted": "...",                      // 密文              │   │
│  │      "decrypted": {                           // 明文(已脱敏)      │   │
│  │        "username": "test",                    //                  │   │
│  │        "password": "****"                     // 已脱敏            │   │
│  │      }                                                                │   │
│  │    },                                                                 │   │
│  │    "response": {                              // 响应数据          │   │
│  │      "encrypted": "...",                      // 密文              │   │
│  │      "decrypted": {                           // 明文(已脱敏)      │   │
│  │        "code": 200,                           //                  │   │
│  │        "msg": "成功"                          //                  │   │
│  │      }                                                                │   │
│  │    }                                                                   │   │
│  │  }                                                                     │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【敏感字段脱敏】                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  以下字段会被自动脱敏 (仅显示首尾字符):                              │   │
│  │                                                                       │   │
│  │  • password      → "****"                                           │   │
│  │  • card_code     → "****"                                           │   │
│  │  • machine_code  → "****"                                           │   │
│  │  • shared_secret → "****"                                           │   │
│  │  • identity_secret → "****"                                         │   │
│  │  • aes_key       → "****"                                           │   │
│  │  • signature     → "****"                                           │   │
│  │  • access_token  → "****"                                           │   │
│  │  • refresh_token → "****"                                           │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【业务代码记录明文示例】                                                      │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  from app.utils.api_context import (                                │   │
│  │      get_decrypted_request,     # 获取解密后的请求                  │   │
│  │      log_decrypted_request,     # 记录入参明文                      │   │
│  │      log_decrypted_response,    # 记录出参明文                      │   │
│  │  )                                                                    │   │
│  │                                                                        │   │
│  │  @router.post("/auth/login")                                         │   │
│  │  async def login(request_data: LoginRequest):                        │   │
│  │      # 记录入参明文                                                   │   │
│  │      decrypted_request = get_decrypted_request()                     │   │
│  │      if decrypted_request:                                           │   │
│  │          log_decrypted_request(decrypted_request)                    │   │
│  │      else:                                                           │   │
│  │          log_decrypted_request(request_data.model_dump())           │   │
│  │                                                                        │   │
│  │      # 业务处理...                                                    │   │
│  │                                                                        │   │
│  │      # 记录出参明文                                                   │   │
│  │      response_data = {"code": 200, "msg": "成功", "data": {...}}     │   │
│  │      log_decrypted_response(response_data)                           │   │
│  │                                                                        │   │
│  │      return response_data                                            │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【日志文件位置】                                                             │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  backend-client/                                                      │   │
│  │  └── logs/                                                           │   │
│  │      └── api/                                                        │   │
│  │          ├── api_2026-02-01.log                                      │   │
│  │          ├── api_2026-02-02.log  ← 当天日志                          │   │
│  │          └── api_2026-02-03.log                                      │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

4.5 数据库日志记录

# api_logs表记录内容
{
  "user_id": 1,                    # 用户ID (可为NULL)
  "endpoint": "/api/auth/login",   # 接口路径
  "method": "POST",                # HTTP方法
  "request_data": "...",           # 加密前的原始请求参数
  "response_data": "{...}",        # 解密后的原始响应数据
  "ip_address": "192.168.1.100",  # 客户端IP
  "user_agent": "...",             # 用户代理
  "response_code": 200,            # 响应状态码
  "execution_time": 45             # 执行时间(毫秒)
}

五、核心业务流程

5.1 客户端启动流程

┌─────────────────────────────────────────────────────────────────┐
│                        客户端启动流程                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────┐                                                │
│  │ 程序启动    │                                                │
│  └──────┬──────┘                                                │
│         │                                                        │
│         ▼                                                        │
│  ┌─────────────────────────────────────────┐                    │
│  │ 检查是否已有实例运行 (单例检查)          │                    │
│  │ FindWindow/互斥体检测                    │                    │
│  └──────────────────┬──────────────────────┘                    │
│                     │ 检测到已运行                                │
│         ┌───────────┴───────────┐                                │
│         ▼                       ▼                                │
│  ┌─────────────┐        ┌─────────────────┐                    │
│  │ 激活已有窗口 │        │ 退出程序        │                    │
│  │ 并退出当前  │        └─────────────────┘                    │
│  └─────────────┘                                                │
│                                                                 │
│         │ 未检测到运行                                           │
│         ▼                                                        │
│  ┌─────────────────────────────────────────┐                    │
│  │ 获取本地机器信息                        │                    │
│  │ • 硬盘序列号                            │                    │
│  │ • MAC地址                              │                    │
│  │ • 生成MD5机器码                        │                    │
│  └──────────────────┬──────────────────────┘                    │
│                     │                                                │
│                     ▼                                                │
│  ┌─────────────────────────────────────────┐                    │
│  │ 【关键】获取RSA公钥                      │                    │
│  │ • 无论是否有缓存                        │                    │
│  │ • 调用 GET /api/keys/public             │                    │
│  │ • 缓存到Rust内存 (分段混淆存储)          │                    │
│  │ • 获取失败 → 阻塞并提示                 │                    │
│  └──────────────────┬──────────────────────┘                    │
│                     │                                                │
│                     ▼                                                │
│  ┌─────────────────────────────────────────┐                    │
│  │ 获取公告列表 (AES+RSA加密)              │                    │
│  │ • 显示在登录界面                        │                    │
│  └──────────────────┬──────────────────────┘                    │
│                     │                                                │
│                     ▼                                                │
│  ┌─────────────────────────────────────────┐                    │
│  │ 启动定时任务                            │                    │
│  │ • 密钥同步 (每60秒)                     │                    │
│  │   - 每次都调用密钥服务获取最新          │                    │
│  │   - 更新本地缓存                        │                    │
│  │ • 如果已登录 → 启动心跳                 │                    │
│  │ • 如果未登录 → 不启动心跳               │                    │
│  └──────────────────┬──────────────────────┘                    │
│                     │                                                │
│                     ▼                                                │
│  ┌─────────────────────────────────────────┐                    │
│  │ 检查本地会话缓存                        │                    │
│  │                                         │                    │
│  │ 有有效会话 + 卡密有效期内?              │                    │
│  │         ┌────────────┐                  │                    │
│  │         │ 是         │ 否               │                    │
│  │         ▼            ▼                  │                    │
│  │  ┌─────────────┐ ┌─────────────────┐   │                    │
│  │  │ 自动登录    │ │ 显示登录表单    │   │                    │
│  │  │ 调用心跳    │ └─────────────────┘   │                    │
│  │  └─────────────┘                       │                    │
│  └─────────────────────────────────────────┘                    │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.2 用户登录流程

┌─────────────────────────────────────────────────────────────────┐
│                        用户登录流程                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  用户输入用户名/密码 → 点击登录                                  │
│                                                                 │
│         │                                                        │
│         ▼                                                        │
│  ┌─────────────────────────────────────────┐                    │
│  │ 1. 验证输入参数                          │                    │
│  │    • 用户名非空                          │                    │
│  │    • 密码非空                            │                    │
│  │    • 机器码已获取                        │                    │
│  └──────────────────┬──────────────────────┘                    │
│                     │ 参数无效                                    │
│         ┌───────────┴───────────┐                                │
│         ▼                       ▼                                │
│  ┌─────────────┐        ┌─────────────────────┐                │
│  │ 显示错误提示 │        │ 继续处理            │                │
│  └─────────────┘        └─────────────────────┘                │
│                                                                 │
│         │ 参数有效                                                │
│         ▼                                                        │
│  ┌─────────────────────────────────────────┐                    │
│  │ 2. 获取RSA公钥 (如果内存中没有)          │                    │
│  │    调用 GET /api/keys/public             │                    │
│  └──────────────────┬──────────────────────┘                    │
│                     │                                                │
│                     ▼                                                │
│  ┌─────────────────────────────────────────┐                    │
│  │ 3. 生成随机AES密钥                       │                    │
│  │    • 32字节随机密钥                      │                    │
│  │    • 用于加密请求和响应                  │                    │
│  └──────────────────┬──────────────────────┘                    │
│                     │                                                │
│                     ▼                                                │
│  ┌─────────────────────────────────────────┐                    │
│  │ 4. 发送登录请求 (AES+RSA加密)            │                    │
│  │    • encrypted_params: 请求参数AES加密   │                    │
│  │    • aes_key: RSA加密的AES密钥           │                    │
│  │    • key_version                        │                    │
│  │    • timestamp, nonce, signature        │                    │
│  └──────────────────┬──────────────────────┘                    │
│                     │                                                │
│         ┌───────────┴───────────┐                                │
│         ▼                       ▼                                │
│  ┌─────────────────┐    ┌─────────────────────┐                │
│  │ 登录成功        │    │ 登录失败            │                │
│  │ (code=200)      │    │ (code=400/401/403)  │                │
│  └────────┬────────┘    └─────────────────────┘                │
│           │                                                        │
│           ▼                                                        │
│  ┌─────────────────────────────────────────┐                    │
│  │ 5. 处理登录成功响应                      │                    │
│  │    • 解密响应数据 (使用本地AES密钥)      │                    │
│  │    • 保存session_id                     │                    │
│  │    • 保存user_id                        │                    │
│  │    • 保存machine_code                   │                    │
│  │    • 跳转到主界面                        │                    │
│  │    • 启动心跳检测                        │                    │
│  └──────────────────┬──────────────────────┘                    │
│                     │                                                │
│                     ▼                                            │
│  ┌─────────────────────────────────────────┐                    │
│  │ 6. 调用心跳服务 (AES+RSA加密)            │                    │
│  │    • 获取系统配置 (从system_config)      │                    │
│  │    • 启动定时心跳任务                    │                    │
│  │    • 心跳间隔: 从配置获取                │                    │
│  │    • 最大失败次数: 从配置获取            │                    │
│  └─────────────────────────────────────────┘                    │
│                                                                 │
│  登录失败处理:                                                    │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ • 失败次数+1                                             │   │
│  │ • 检查是否超过 max_login_attempts (从system_config)      │   │
│  │ • 超过限制 → 锁定账号 (login_lockout_minutes)            │   │
│  │ • 显示具体错误提示                                        │   │
│  │ • 无验证码要求                                            │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

5.3 心跳检测流程

┌─────────────────────────────────────────────────────────────────┐
│                        心跳检测流程                               │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  登录成功 → 立即调用心跳服务 → 启动定时器                        │
│                                                                 │
│         │                                                        │
│         ▼                                                        │
│  ┌─────────────────────────────────────────┐                    │
│  │ 心跳请求 (AES+RSA加密)                   │                    │
│  │ • session_id                            │                    │
│  │ • machine_code                          │                    │
│  │ • timestamp, nonce, signature           │                    │
│  └──────────────────┬──────────────────────┘                    │
│                     │                                                │
│         ┌───────────┴───────────┐                                │
│         ▼                       ▼                                │
│  ┌─────────────────┐    ┌─────────────────────┐                │
│  │ 心跳成功        │    │ 心跳失败            │                │
│  │ (code=200)      │    │ (网络/验证失败)      │                │
│  └────────┬────────┘    └─────────────────────┘                │
│           │                   │                                    │
│           │                   ▼                                    │
│           │        ┌─────────────────────┐    ┌────────────────┐│
│           │        │ 失败次数+1          │    │ 失败次数<100   ││
│           │        └─────────────────────┘    └───────┬────────┘│
│           │                   │                      │         │
│           │                   │              ┌───────┴────────┐ │
│           │                   │              ▼                ▼ │
│           │                   │    ┌─────────────────┐ ┌─────────────┐
│           │                   │    │ 继续尝试心跳    │ │ 超过100次   │
│           │                   │    └────────┬────────┘ └────────┬─────┘
│           │                   │             │                   │
│           │                   │             │                   ▼
│           │                   │             │    ┌─────────────────────────┐
│           │                   │             │    │ 自动退出登录            │
│           │                   │             │    │ • 停止心跳任务          │
│           │                   │             │    │ • 清除会话缓存          │
│           │                   │             │    │ • 跳转到登录页          │
│           │                   │             │    │ • 弹窗提示              │
│           │                   │             │    └─────────────────────────┘
│           │                   │             │
│           │                   │             │  失败次数<100
│           │                   │             │
│           │                   │             ▼
│           │                   │    ┌─────────────────────────┐
│           │                   │    │ 顶部提示: 心跳服务不可用 │
│           │                   │    │ (等心跳恢复后自动消失)   │
│           │                   │    └─────────────────────────┘
│           │                   │
│           │                   │  心跳恢复
│           │                   │  (失败次数重置为0)
│           │                   │  │
│           │                   │  ▼
│           │                   │  ┌─────────────────────────┐
│           │                   │  │ 顶部提示消失            │
│           │                   │  │ 恢复正常状态            │
│           │                   │  └─────────────────────────┘
│           │                   │
│           └───────────────────┘
│                                                                 │
│  心跳配置来源 (system_config表):                                │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ 配置键                        │ 默认值    │ 说明        │   │
│  │------------------------------│----------│------------│   │
│  │ heartbeat_interval_seconds   │ 10       │ 心跳间隔    │   │
│  │ max_heartbeat_failures       │ 100      │ 最大失败数  │   │
│  │ timestamp_tolerance_seconds  │ 300      │ 时间戳容忍  │   │
│  │ session_renewal_threshold    │ 1800     │ 续期阈值(30分)│ │
│  │ session_extension_duration   │ 3600     │ 延长时间(1小时)│ │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  Session续期说明:                                               │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ • 每次心跳检查session剩余时间                            │   │
│  │ • 剩余时间 < 30分钟 (1800秒) 时触发续期                  │   │
│  │ • 续期后session过期时间延长1小时 (3600秒)                │   │
│  │ • 续期操作自动记录到sessions_his历史表                  │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

六、系统配置设计

6.1 核心配置项 (system_config表)

所有配置都存储在system_config表中,支持热更新:

-- 心跳相关配置 (app_name: backend-client)
INSERT INTO system_config (config_key, config_value, config_type, app_name, description) VALUES
('heartbeat_interval_seconds', '10', 'int', 'backend-client', '心跳检测间隔秒数'),
('max_heartbeat_failures', '100', 'int', 'backend-client', '心跳最大失败次数'),

-- Session自动续期配置
('session_renewal_threshold', '1800', 'int', 'backend-client', 'Session续期阈值(秒),剩余时间小于此值时触发续期'),
('session_extension_duration', '3600', 'int', 'backend-client', 'Session延长时间(秒),续期时延长的时长'),
('session_default_expiry_seconds', '86400', 'int', 'backend-client', 'Session默认有效期(秒),新登录时创建的有效期'),

-- 安全相关配置
('timestamp_tolerance_seconds', '300', 'int', 'backend-client', '时间戳容忍秒数(±5分钟)'),
('prevent_duplicate_login', 'true', 'bool', 'backend-client', '是否防止重复登录'),

-- 登录相关配置
('max_login_attempts', '5', 'int', 'backend-client', '最大登录失败次数'),
('login_lockout_minutes', '60', 'int', 'backend-client', '登录锁定分钟数'),

-- 公告相关配置
('announcement_cache_seconds', '300', 'int', 'backend-client', '公告缓存秒数'),

-- 密钥相关配置 (客户端只读)
('key_sync_interval_seconds', '60', 'int', 'backend-client', '密钥同步间隔秒数');

6.2 配置获取方式

┌─────────────────────────────────────────────────────────────────┐
│                      配置获取流程                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  配置来源:                                                       │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │ 1. 客户端启动时                                            │   │
│  │    - 调用心跳服务获取配置                                 │   │
│  │    - 首次获取完整配置                                     │   │
│  │                                                          │   │
│  │ 2. 心跳响应中返回                                         │   │
│  │    - 每次心跳都返回最新配置                               │   │
│  │    - 自动更新本地配置                                     │   │
│  │                                                          │   │
│  │ 3. 配置变更后                                              │   │
│  │    - 管理员在backend-admin中修改配置                      │   │
│  │    - 配置存储到system_config表                            │   │
│  │    - 下次心跳时客户端自动获取最新配置                     │   │
│  │    - 无需重启应用                                         │   │
│  └─────────────────────────────────────────────────────────┘   │
│                                                                 │
│  配置变更流程:                                                   │
│  ┌─────────────┐    ┌─────────────┐    ┌─────────────┐         │
│  │ 管理员修改   │ →  │ 更新数据库   │ →  │ 心跳时同步   │         │
│  │ system_config│    │              │    │ 到客户端     │         │
│  └─────────────┘    └─────────────┘    └─────────────┘         │
│                                                  ↓              │
│                                          ┌─────────────┐       │
│                                          │ 客户端生效   │       │
│                                          │ 无需重启     │       │
│                                          └─────────────┘       │
└─────────────────────────────────────────────────────────────────┘

6.3 配置验证

from typing import Any
import json

class ConfigValidator:
    """配置验证器"""

    @staticmethod
    def get_config(db, config_key: str, app_name: str) -> Any:
        """获取配置值"""
        config = db.query(SystemConfig).filter(
            SystemConfig.config_key == config_key,
            SystemConfig.app_name.contains(app_name),
            SystemConfig.is_deleted == 0
        ).first()

        if not config:
            return None

        # 类型转换
        if config.config_type == 'int':
            return int(config.config_value)
        elif config.config_type == 'bool':
            return config.config_value.lower() == 'true'
        elif config.config_type == 'json':
            return json.loads(config.config_value)
        else:
            return config.config_value

    @staticmethod
    def get_heartbeat_interval(db) -> int:
        """获取心跳间隔(秒)"""
        return ConfigValidator.get_config(db, 'heartbeat_interval_seconds', 'backend-client') or 10

    @staticmethod
    def get_max_heartbeat_failures(db) -> int:
        """获取最大心跳失败次数"""
        return ConfigValidator.get_config(db, 'max_heartbeat_failures', 'backend-client') or 100

    # ========== Session续期配置 ==========

    @staticmethod
    def get_session_renewal_threshold(db) -> int:
        """获取Session续期阈值(秒),剩余时间小于此值时触发续期"""
        return ConfigValidator.get_config(db, 'session_renewal_threshold', 'backend-client') or 1800

    @staticmethod
    def get_session_extension_duration(db) -> int:
        """获取Session延长时间(秒)"""
        return ConfigValidator.get_config(db, 'session_extension_duration', 'backend-client') or 3600

    @staticmethod
    def get_session_default_expiry(db) -> int:
        """获取Session默认有效期(秒)"""
        return ConfigValidator.get_config(db, 'session_default_expiry_seconds', 'backend-client') or 86400

    @staticmethod
    def should_renew_session(db, session_expires_at) -> tuple:
        """检查是否应该续期Session

        Args:
            session_expires_at: session过期时间 (datetime)

        Returns:
            tuple: (should_renew: bool, new_expires_at: datetime or None)
        """
        import datetime

        current_time = datetime.datetime.utcnow()
        remaining = (session_expires_at - current_time).total_seconds()
        threshold = ConfigValidator.get_session_renewal_threshold(db)

        if remaining < threshold:
            # 需要续期
            extension = ConfigValidator.get_session_extension_duration(db)
            new_expires_at = current_time + datetime.timedelta(seconds=extension)
            return True, new_expires_at

        return False, None

七、前端架构设计 (client-tauri)

7.1 技术栈

┌─────────────────────────────────────────┐
│           Vue 3 + TypeScript            │
│    (Composition API + <script setup>)   │
└──────────────────┬──────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────┐
│              Pinia (状态管理)            │
│  • authStore - 认证状态                 │
│  • configStore - 配置状态               │
│  • userStore - 用户信息                 │
│  • heartbeatStore - 心跳状态            │
└──────────────────┬──────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────┐
│            Tauri (Rust后端)             │
│  • secure_api_call - 加密API调用        │
│  • key_manager - 密钥管理               │
│  • machine_info - 机器信息获取          │
│  • heartbeat - 心跳检测                 │
└──────────────────┬──────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────┐
│          Rust原生代码 (src-tauri)        │
│  • AES加密/解密                         │
│  • RSA公钥分段混淆存储                   │
│  • 机器信息收集                          │
│  • 进程单例检查                          │
└─────────────────────────────────────────┘

7.2 核心组件结构

src/
├── components/
│   ├── common/
│   │   ├── Loading.vue           # 加载组件
│   │   ├── Message.vue           # 消息提示
│   │   └── Modal.vue             # 弹窗组件
│   ├── auth/
│   │   ├── LoginForm.vue         # 登录表单
│   │   ├── RegisterForm.vue      # 注册表单
│   │   ├── RechargeForm.vue      # 充值表单
│   │   └── UnbindDialog.vue      # 解绑弹窗
│   ├── announcement/
│   │   └── AnnouncementList.vue  # 公告列表
│   └── layout/
│       ├── Header.vue            # 顶部栏 (含心跳状态)
│       ├── Sidebar.vue           # 侧边栏
│       └── MainLayout.vue        # 主布局
├── views/
│   ├── LoginView.vue             # 登录页面
│   ├── RegisterView.vue          # 注册页面
│   ├── HomeView.vue              # 首页/仪表盘
│   ├── RechargeView.vue          # 充值页面
│   └── SettingsView.vue          # 设置页面
├── stores/
│   ├── auth.ts                   # 认证状态管理
│   ├── config.ts                 # 配置状态管理
│   ├── user.ts                   # 用户信息管理
│   └── heartbeat.ts              # 心跳状态管理
├── services/
│   ├── api.ts                    # API封装
│   ├── crypto.ts                 # 加解密服务
│   └── heartbeat.ts              # 心跳服务
├── utils/
│   ├── machine.ts                # 机器码工具
│   └── validator.ts              # 表单验证
└── App.vue

7.3 Rust后端命令 (src-tauri/)

src-tauri/src/
├── commands/
│   ├── mod.rs                        # 模块导出
│   ├── crypto/
│   │   ├── mod.rs
│   │   ├── aes.rs                    # AES加密/解密
│   │   └── rsa.rs                    # RSA加密
│   ├── key/
│   │   ├── mod.rs
│   │   ├── manager.rs                # 密钥管理
│   │   └── storage.rs                # 密钥分段存储
│   ├── auth/
│   │   ├── mod.rs
│   │   ├── login.rs                  # 安全登录
│   │   ├── register.rs               # 安全注册
│   │   └── unbind.rs                 # 安全解绑
│   ├── heartbeat.rs                  # 心跳检测
│   └── machine.rs                    # 机器信息获取
├── security.rs                       # 安全检查
├── machine.rs                        # 机器码生成
└── main.rs                           # 程序入口

7.4 密钥管理命令

// 获取RSA公钥
#[tauri::command]
pub async fn get_public_key() -> Result<PublicKeyResponse, String> {
    // 调用后端API获取公钥
    let response = api_client.get("/api/keys/public").await?;
    
    // 解析响应
    let public_key = response.get("public_key")
        .and_then(|v| v.as_str())
        .ok_or("No public key in response")?;
    
    let key_version = response.get("key_version")
        .and_then(|v| v.as_i64())
        .ok_or("No key version in response")? as i32;
    
    // 分段混淆存储
    store_rsa_key_segments(public_key.to_string());
    
    Ok(PublicKeyResponse {
        public_key: public_key.to_string(),
        key_version
    })
}

// 检查是否有缓存的公钥
#[tauri::command]
pub fn has_stored_rsa_key() -> bool {
    retrieve_rsa_key_segments().is_ok()
}

八、后端架构设计 (backend-client)

8.1 技术栈

┌─────────────────────────────────────────┐
│              FastAPI (Python)            │
│  • 自动API文档 (Swagger/OpenAPI)         │
│  • 请求验证 (Pydantic)                   │
│  • 依赖注入                              │
└──────────────────┬──────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────┐
│           SQLAlchemy (ORM)              │
│  • MySQL数据库操作                       │
│  • 自动迁移 (Alembic)                    │
└──────────────────┬──────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────┐
│           Redis (可选)                  │
│  • 会话缓存                              │
│  • 配置缓存                              │
│  • 限流计数                              │
└──────────────────┬──────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────┐
│            安全中间件                    │
│  • RSA+AES解密                           │
│  • 签名验证                              │
│  • 时间戳验证                            │
│  • Nonce防重放                          │
│  • 会话验证                              │
│  • 机器码验证                            │
│  • 卡密有效期验证                        │
└─────────────────────────────────────────┘

8.2 API路由结构

app/
├── api/
│   ├── __init__.py
│   ├── v1/
│   │   ├── __init__.py
│   │   ├── keys.py              # 密钥相关
│   │   │   └── GET /api/keys/public        (AES加密)
│   │   ├── auth.py              # 认证相关
│   │   │   ├── POST /api/auth/register      (AES+RSA)
│   │   │   ├── POST /api/auth/login         (AES+RSA)
│   │   │   ├── POST /api/auth/recharge      (AES+RSA)
│   │   │   └── POST /api/auth/unbind        (AES+RSA)
│   │   ├── announcements.py     # 公告相关
│   │   │   └── GET /api/announcements        (AES+RSA)
│   │   ├── heartbeat.py         # 心跳相关
│   │   │   └── POST /api/heartbeat           (AES+RSA, 需要登录)
│   │   ├── user.py              # 用户相关
│   │   │   └── POST /api/user/profile         (AES+RSA, 需要登录)
│   │   └── steam.py             # Steam相关
│   │       ├── POST /api/steam/bind           (AES+RSA, 需要登录)
│   │       ├── POST /api/steam/unbind         (AES+RSA, 需要登录)
│   │       └── GET /api/steam/accounts        (AES+RSA, 需要登录)
│   └── deps.py                  # 依赖注入
├── core/
│   ├── __init__.py
│   ├── config.py                # 配置管理 (从system_config读取)
│   ├── security.py              # 安全中间件
│   ├── encrypt.py               # 加解密工具
│   └── database.py              # 数据库连接
├── models/
│   ├── __init__.py
│   ├── user.py                  # 用户模型
│   ├── session.py               # 会话模型
│   ├── card.py                  # 卡密模型
│   ├── announcement.py          # 公告模型
│   ├── config.py                # 配置模型
│   └── api_key.py               # 密钥模型 (只读)
├── schemas/
│   ├── __init__.py
│   ├── auth.py                  # 认证请求/响应
│   ├── announcement.py          # 公告请求/响应
│   ├── heartbeat.py             # 心跳请求/响应
│   └── ...
├── services/
│   ├── __init__.py
│   ├── auth_service.py          # 认证服务
│   ├── heartbeat_service.py     # 心跳服务
│   ├── recharge_service.py      # 充值服务
│   └── ...
├── middleware/
│   ├── __init__.py
│   ├── audit.py                 # API日志记录
│   └── auth.py                  # 认证中间件
└── utils/
    ├── __init__.py
    ├── logger.py                # 日志工具
    └── history.py               # 历史记录工具

8.3 安全中间件

import os
import hmac
import hashlib
import base64
import time
import secrets
import datetime
from fastapi import Request, HTTPException
from functools import wraps
from .encrypt import decrypt_request, encrypt_response
from .services.session import verify_session, renew_session
from .services.machine import verify_machine_code
from .services.config import get_config
from .utils.history import create_history_record

# 服务端签名密钥 (从环境变量获取)
SERVER_SECRET = os.getenv("BUFF_API_SERVER_SECRET", "buff-server-secret-2024").encode()

def generate_response_signature(encrypted_data: str, key_version: int) -> tuple:
    """生成响应签名

    Returns:
        signature: Base64编码的HMAC-SHA256签名
        timestamp: 响应时间戳
        nonce: 响应随机字符串
    """
    timestamp = int(time.time())
    nonce = secrets.token_hex(16)

    # 构建响应签名字符串
    sign_string = f"{timestamp}:{nonce}:{encrypted_data}:{key_version}"

    # 使用服务端密钥生成签名
    signature = hmac.new(
        SERVER_SECRET,
        sign_string.encode(),
        hashlib.sha256
    ).digest()

    return base64.b64encode(signature).decode(), timestamp, nonce

def verify_client_signature(params: dict, provided_signature: str, secret_key: bytes) -> bool:
    """验证客户端签名 (使用常数时间比较防止时序攻击)"""
    try:
        timestamp = str(params.get('timestamp', ''))
        nonce = params.get('nonce', '')
        encrypted_params = params.get('encrypted_params', '')
        aes_key = params.get('aes_key', '')
        key_version = str(params.get('key_version', ''))

        # 构建签名字符串
        sign_string = f"{timestamp}:{nonce}:{encrypted_params}:{aes_key}:{key_version}"

        # 计算期望的签名
        expected_signature = hmac.new(
            secret_key,
            sign_string.encode(),
            hashlib.sha256
        ).digest()

        # 常数时间比较 (防止时序攻击)
        provided = base64.b64decode(provided_signature)
        return hmac.compare_digest(provided, expected_signature)
    except Exception:
        return False

def verify_session_and_user_status(db, session_id: str, machine_code: str) -> tuple:
    """验证session和用户状态 (通用验证逻辑)

    用于所有登录后API接口的通用验证

    Returns:
        tuple: (is_valid: bool, session: Session, error_code: int, error_msg: str)
    """
    # 1. 验证session_id有效性
    session = verify_session(session_id)
    if not session:
        return False, None, 401, "会话已过期,请重新登录"

    # 2. 验证session过期时间
    if session.expires_at < datetime.datetime.utcnow():
        return False, None, 401, "会话已过期,请重新登录"

    # 3. 验证机器码匹配
    if not verify_machine_code(session.user_id, machine_code):
        return False, None, 403, "账号已在其他设备登录,如非本人操作请联系客服"

    # 4. 验证卡密有效期
    if not check_user_expire_time(session.user_id):
        return False, None, 403, "您的卡密已到期,请前往续费或联系客户支持"

    return True, session, None, None

def renew_session_if_needed(db, session) -> dict:
    """检查并续期session

    Returns:
        dict: {
            "renewed": bool,
            "expires_at": datetime or None,
            "remaining_seconds": int
        }
    """
    current_time = datetime.datetime.utcnow()
    remaining = (session.expires_at - current_time).total_seconds()

    # 获取续期阈值配置
    renewal_threshold = get_config('session_renewal_threshold', 1800) or 1800

    result = {
        "renewed": False,
        "expires_at": session.expires_at.isoformat(),
        "remaining_seconds": int(remaining)
    }

    if remaining < renewal_threshold:
        # 需要续期
        extension_duration = get_config('session_extension_duration', 3600) or 3600
        new_expires_at = current_time + datetime.timedelta(seconds=extension_duration)

        # 更新session
        old_expires_at = session.expires_at
        session.expires_at = new_expires_at
        db.add(session)
        db.flush()

        # 创建历史记录
        create_history_record(
            db,
            "sessions",
            session,
            "RENEW",
            f"Session自动续期: {old_expires_at} -> {new_expires_at}"
        )

        db.commit()

        result["renewed"] = True
        result["expires_at"] = new_expires_at.isoformat()

    return result

def secure_endpoint(require_login: bool = True, client_secret: bytes = None):
    """安全端点装饰器

    Args:
        require_login: 是否需要登录验证 (所有登录后API都应设为True)
        client_secret: 客户端签名密钥 (用于验证客户端请求签名)
    """
    # 默认使用环境变量中的客户端密钥
    if client_secret is None:
        client_secret = os.getenv("BUFF_API_CLIENT_SECRET", "buff-client-secret-2024").encode()

    def decorator(func):
        @wraps(func)
        async def wrapper(request: Request, *args, **kwargs):
            from app.database import get_db

            # 1. 获取并解密请求参数
            encrypted_body = await request.body()
            decrypted_params = decrypt_request(encrypted_body)

            # 2. 验证时间戳 (从system_config获取容忍范围)
            timestamp = decrypted_params.get('timestamp', 0)
            tolerance = get_config('timestamp_tolerance_seconds', 300)
            current_time = int(time.time())
            if abs(current_time - timestamp) > tolerance:
                raise HTTPException(status_code=400, detail="请求已过期")

            # 3. 验证Nonce (防重放)
            nonce = decrypted_params.get('nonce')
            if not verify_nonce(nonce, request.url.path):
                raise HTTPException(status_code=400, detail="重复请求")

            # 4. 验证客户端签名
            client_signature = decrypted_params.get('signature', '')
            if not verify_client_signature(decrypted_params, client_signature, client_secret):
                raise HTTPException(status_code=400, detail="签名验证失败")

            # 5. 通用验证 (所有登录后API接口)
            session = None
            if require_login:
                session_id = decrypted_params.get('session_id')
                machine_code = decrypted_params.get('machine_code')

                # 获取数据库连接
                db = get_db()

                # 执行通用验证
                is_valid, session, error_code, error_msg = verify_session_and_user_status(
                    db, session_id, machine_code
                )

                if not is_valid:
                    raise HTTPException(status_code=error_code, detail=error_msg)

                # 将session注入到请求中
                request.state.session = session
                request.state.db = db

            # 6. 执行实际业务逻辑
            response_data = await func(decrypted_params, request.state, *args, **kwargs)

            # 7. 心跳接口特殊处理: session续期
            session_info = {}
            if require_login and session:
                session_info = renew_session_if_needed(db, session)

            # 8. 加密响应数据
            encrypted_response, aes_key = encrypt_response(response_data)

            # 9. 生成响应签名
            response_signature, response_timestamp, response_nonce = \
                generate_response_signature(encrypted_response, decrypted_params.get('key_version', 1))

            # 10. 返回加密响应和签名
            result = {
                "code": 200,
                "msg": "成功",
                "encrypted_data": encrypted_response,
                "timestamp": response_timestamp,
                "nonce": response_nonce,
                "signature": response_signature,
                "key_version": decrypted_params.get('key_version', 1)
            }

            # 心跳接口返回session续期信息
            if session_info:
                result["data"] = {
                    "config": response_data.get("config", {}),
                    "user_info": response_data.get("user_info", {}),
                    "session_info": session_info
                }

            return result

        return wrapper
    return decorator

8.4 配置管理

from app.models.system_config import SystemConfig
from sqlalchemy.orm import Session

class ConfigService:
    """配置服务"""
    
    @staticmethod
    def get_config(db: Session, config_key: str, app_name: str = 'backend-client') -> str:
        """获取配置值"""
        config = db.query(SystemConfig).filter(
            SystemConfig.config_key == config_key,
            SystemConfig.app_name.contains(app_name),
            SystemConfig.is_deleted == 0
        ).first()
        
        return config.config_value if config else None
    
    @staticmethod
    def get_heartbeat_interval(db: Session) -> int:
        """获取心跳间隔"""
        value = ConfigService.get_config(db, 'heartbeat_interval_seconds')
        return int(value) if value else 10
    
    @staticmethod
    def get_max_heartbeat_failures(db: Session) -> int:
        """获取最大心跳失败次数"""
        value = ConfigService.get_config(db, 'max_heartbeat_failures')
        return int(value) if value else 100
    
    @staticmethod
    def get_max_login_attempts(db: Session) -> int:
        """获取最大登录失败次数"""
        value = ConfigService.get_config(db, 'max_login_attempts')
        return int(value) if value else 5

九、数据流转图

9.1 完整数据流转

┌─────────────────────────────────────────────────────────────────────────────┐
│                           完整数据流转图                                     │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  前端 (Vue3+Tauri)                                                           │
│       │                                                                       │
│       │ 1. 收集机器信息                                                      │
│       │ 2. 生成MD5机器码                                                     │
│       │ 3. 调用Tauri命令                                                     │
│       ▼                                                                       │
│  ┌─────────────────────────────────────────┐                                │
│  │ Tauri Rust命令层                          │                                │
│  │ • secure_api_call (加密请求)              │                                │
│  │ • 获取RSA公钥                             │                                │
│  │ • 解密响应并返回给前端                     │                                │
│  └──────────────────┬──────────────────────┘                                │
│                     │                                                        │
│                     │ 4. 加密请求 (AES+RSA)                                   │
│                     │    - 生成随机AES密钥                                    │
│                     │    - 使用AES密钥加密请求                                │
│                     │    - 使用RSA公钥加密AES密钥                             │
│                     ▼                                                        │
│  ┌─────────────────────────────────────────┐                                │
│  │ backend-client FastAPI服务               │                                │
│  │                                          │                                │
│  │ 中间件层:                                 │                                │
│  │ • 解密请求参数                            │                                │
│  │ • 验证签名                                │                                │
│  │ • 验证时间戳                              │                                │
│  │ • 验证Nonce (防重放)                      │                                │
│  │                                          │                                │
│  │ 认证中间件:                               │                                │
│  │ • 验证session                            │                                │
│  │ • 验证机器码                              │                                │
│  │ • 验证卡密有效期                          │                                │
│  │                                          │                                │
│  │ 业务层:                                   │                                │
│  │ • 处理业务逻辑                            │                                │
│  │ • 操作数据库                              │                                │
│  │ • 从system_config获取配置                 │                                │
│  │ • 记录API日志                             │                                │
│  │ • 生成响应数据                            │                                │
│  │                                          │                                │
│  │ 响应层:                                   │                                │
│  │ • 使用请求中的AES密钥加密响应              │                                │
│  │ • 返回加密响应                            │                                │
│  └──────────────────┬──────────────────────┘                                │
│                     │                                                        │
│                     │ 5. 加密响应                                            │
│                     │    - 使用请求中的AES密钥加密                            │
│                     ▼                                                        │
│  ┌─────────────────────────────────────────┐                                │
│  │ Tauri Rust命令层                          │                                │
│  │ • 解密响应数据 (使用本地AES密钥)          │                                │
│  │ • 返回明文给前端                          │                                │
│  └──────────────────┬──────────────────────┘                                │
│                     │                                                        │
│                     ▼                                                        │
│  前端更新UI                                                                 │
│                                                                              │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │ 数据库层 (MySQL)                                                       │  │
│  │                                                                        │  │
│  │ 主表操作 → 自动记录到历史表 (_his)                                      │  │
│  │                                                                        │  │
│  │ users → users_his                                                      │  │
│  │ cards → cards_his                                                      │  │
│  │ sessions → sessions_his                                                │  │
│  │ system_config (动态配置)                                               │  │
│  │ api_keys (RSA密钥)                                                     │  │
│  │ api_nonces (防重放)                                                    │  │
│  │ api_logs (API日志)                                                     │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

9.2 心跳数据流转

┌─────────────────────────────────────────────────────────────────┐
│                      心跳检测数据流转                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                 │
│  登录成功                                                        │
│      │                                                          │
│      ▼                                                          │
│  ┌─────────────────────────────────────────┐                   │
│  │ 调用心跳服务 (AES+RSA加密)                                               │
│  │ POST /api/heartbeat          - 心跳检测 (返回系统配置)
│  │ Request: {session_id, machine_code}     │                   │
│  └──────────────────┬──────────────────────┘                   │
│                     │                                           │
│                     ▼                                           │
│  ┌─────────────────────────────────────────┐                   │
│  │ 后端处理                                 │                   │
│  │                                          │                   │
│  │ 1. 验证session有效性                     │                   │
│  │ 2. 验证机器码                            │                   │
│  │ 3. 检查用户卡密有效期                    │                   │
│  │                                          │                   │
│  │ 4. 从system_config表获取配置:            │                   │
│  │    • heartbeat_interval_seconds          │                   │
│  │    • max_heartbeat_failures              │                   │
│  │    • timestamp_tolerance_seconds         │                   │
│  │                                          │                   │
│  │ 5. 获取用户状态:                         │                   │
│  │    • expire_time                         │                   │
│  │    • days_remaining                      │                   │
│  │                                          │                   │
│  │ 6. 记录API日志                           │                   │
│  │                                          │                   │
│  │ 7. 加密响应数据 (使用请求中的AES密钥)    │                   │
│  └──────────────────┬──────────────────────┘                   │
│                     │                                           │
│                     ▼                                           │
│  ┌─────────────────────────────────────────┐                   │
│  │ 前端处理响应                             │                   │
│  │                                          │                   │
│  │ 1. 解密响应数据                          │                   │
│  │ 2. 更新配置缓存                          │                   │
│  │ 3. 更新用户状态                          │                   │
│  │ 4. 重置失败计数                          │                   │
│  │ 5. 设置定时器 (根据新间隔)               │                   │
│  │                                          │                   │
│  │ 失败处理:                                │                   │
│  │ • 失败次数+1                             │                   │
│  │ • 更新UI提示                             │                   │
│  │ • 超过阈值 → 退出登录                    │                   │
│  └─────────────────────────────────────────┘                   │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

十、开发计划

10.1 第一阶段:基础框架搭建

  • 初始化 client-tauri 项目结构
  • 初始化 backend-client 项目结构
  • 配置数据库连接
  • 实现基础AES加密工具类
  • 实现RSA公钥获取和管理
  • 实现机器码获取模块
  • 实现system_config配置读取

10.2 第二阶段:认证系统

  • 实现用户注册接口
  • 实现用户登录接口
  • 实现会话管理模块
  • 实现机器码绑定机制
  • 实现机器解绑接口
  • 实现前端登录界面

10.3 第三阶段:商业化系统

  • 实现卡密充值接口
  • 实现充值记录管理
  • 实现公告系统
  • 实现前端充值界面
  • 实现公告展示组件

10.4 第四阶段:安全加固

  • 实现请求签名验证
  • 实现时间戳验证
  • 实现Nonce防重放
  • 实现登录失败次数限制
  • 实现API日志记录
  • 实现RSA公钥分段存储

10.5 第五阶段:心跳与配置

  • 实现心跳检测接口
  • 实现系统配置管理
  • 实现配置热更新
  • 实现前端心跳状态显示
  • 实现登录状态自动恢复

10.6 第六阶段:Steam集成

  • 集成 steam-client 服务
  • 实现Steam账号绑定接口
  • 实现Steam账号管理
  • 实现Steam操作相关接口

十一、总结

11.1 核心安全机制

  1. AES+RSA混合加密 - 保障传输安全,客户端生成AES密钥
  2. 机器码绑定 - 限制账号使用范围
  3. 会话管理 - 控制登录状态
  4. 签名验证 - 防止请求伪造
  5. Nonce防重放 - 防止重放攻击
  6. 历史记录 - 完整操作审计

11.2 核心业务功能

  1. 用户认证 - 注册、登录、登出
  2. 卡密充值 - 有效期管理
  3. 公告系统 - 通知展示
  4. 机器解绑 - 设备管理
  5. 心跳检测 - 在线状态监控
  6. 配置同步 - 动态配置更新

11.3 架构设计原则

  1. 前后端分离 - Vue3 + FastAPI
  2. 安全优先 - 所有传输加密
  3. 配置数据库化 - 支持热更新
  4. 历史记录 - 完整审计追踪
  5. 模块化设计 - 便于扩展维护
  6. 密钥管理分离 - 管理端生成密钥,客户端仅使用

11.4 与管理端的职责划分

功能 backend-admin backend-client client-tauri
管理员认证 ✅ ❌ ❌
RSA密钥生成 ✅ ❌ ❌
RSA密钥轮换 ✅ ❌ ❌
RSA密钥存储 ✅ ❌ (只读) ❌ (只存公钥)
用户认证 ❌ ✅ ✅
系统配置管理 ✅ (增删改) ✅ (读取) ✅ (使用)
会话管理 ❌ ✅ ✅
API日志记录 ❌ ✅ ❌

十二、客户端exe核心规范

12.1 RSA公钥获取规范

┌─────────────────────────────────────────────────────────────────────────────┐
│                          RSA公钥获取与同步规范                               │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  【公钥获取时机】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  1. 客户端exe启动时:                                                 │   │
│  │     - 立即调用 POST /api/keys/public 获取RSA公钥                     │   │
│  │     - 使用AES加密方式 (无需RSA公钥即可调用)                          │   │
│  │     - 获取失败则阻塞后续操作,显示错误提示                           │   │
│  │                                                                        │   │
│  │  2. 登录成功后定时同步:                                               │   │
│  │     - 每60秒调用 POST /api/keys/sync                                 │   │
│  │     - 使用AES+RSA加密方式                                            │   │
│  │     - 携带session_id和machine_code进行验证                           │   │
│  │     - 获取最新的公钥并更新本地缓存                                   │   │
│  │                                                                        │   │
│  │  3. 公钥版本变更时:                                                   │   │
│  │     - 后端返回新版本公钥时立即更新                                   │   │
│  │     - 下次请求使用新公钥                                             │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【加密方式说明】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  /api/keys/public (启动时调用)                                       │   │
│  │  ├── 加密方式: AES (使用服务端内置AES密钥)                          │   │
│  │  ├── 请求体: {} (空JSON)                                            │   │
│  │  ├── 后端使用内置AES密钥解密                                        │   │
│  │  └── 返回: { public_key, key_version }                              │   │
│  │                                                                        │   │
│  │  /api/keys/sync (登录后定时调用)                                     │   │
│  │  ├── 加密方式: AES+RSA (使用已缓存的RSA公钥)                        │   │
│  │  ├── 请求体: { session_id, machine_code }                           │   │
│  │  ├── 后端验证session和machine_code                                   │   │
│  │  └── 返回: { public_key, key_version }                              │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

12.2 接口访问规则

┌─────────────────────────────────────────────────────────────────────────────┐
│                          接口访问控制规则                                    │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  【登录前接口】 (无需session和machine_code验证)                              │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  接口                    │ 加密方式   │ 需要验证项                    │   │
│  │  -----------------------│-----------│------------------------------│   │
│  │  POST /api/auth/login   │ AES+RSA   │ 签名、时间戳、Nonce          │   │
│  │  POST /api/auth/register│ AES+RSA   │ 签名、时间戳、Nonce          │   │
│  │  POST /api/auth/recharge│ AES+RSA   │ 签名、时间戳、Nonce、machine_code│
│  │  POST /api/auth/unbind  │ AES+RSA   │ 签名、时间戳、Nonce、machine_code│
│  │  GET /api/announcements │ AES+RSA   │ 签名、时间戳、Nonce          │   │
│  │  POST /api/keys/public  │ AES       │ 签名、时间戳、Nonce          │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【登录后接口】 (需要session、machine_code、卡密有效期验证)                  │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  接口                    │ 加密方式   │ 需要验证项                    │   │
│  │  -----------------------│-----------│------------------------------│   │
│  │  POST /api/heartbeat    │ AES+RSA   │ 签名、Nonce、session、        │   │
│  │                         │           │ machine_code、卡密有效期      │   │
│  │  POST /api/user/profile │ AES+RSA   │ 签名、Nonce、session、        │   │
│  │                         │           │ machine_code、卡密有效期      │   │
│  │  POST /api/steam/bind   │ AES+RSA   │ 签名、Nonce、session、        │   │
│  │                         │           │ machine_code、卡密有效期      │   │
│  │  POST /api/steam/unbind │ AES+RSA   │ 签名、Nonce、session、        │   │
│  │                         │           │ machine_code、卡密有效期      │   │
│  │  GET /api/steam/accounts│ AES+RSA   │ 签名、Nonce、session、        │   │
│  │                         │           │ machine_code、卡密有效期      │   │
│  │  POST /api/keys/sync    │ AES+RSA   │ 签名、Nonce、session、        │   │
│  │                         │           │ machine_code、卡密有效期      │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【验证失败处理规则】                                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  错误类型              │ HTTP状态码 │ 客户端处理                     │   │
│  │  ---------------------│-----------│------------------------------│   │
│  │  session无效/过期      │ 401       │ 自动退出登录                  │   │
│  │  机器码不匹配          │ 403       │ 自动退出登录                  │   │
│  │  卡密已过期            │ 403       │ 自动退出登录,提示续费        │   │
│  │  账户被禁用            │ 403       │ 自动退出登录                  │   │
│  │  心跳服务异常          │ 5xx       │ 累计失败次数,超限退出        │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

12.3 心跳服务规范

┌─────────────────────────────────────────────────────────────────────────────┐
│                            心跳服务设计规范                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  【心跳触发时机】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  1. 登录成功后立即调用                                               │   │
│  │  2. 之后按配置间隔定时调用                                           │   │
│  │  3. 间隔时间从心跳响应config中获取                                   │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【心跳请求参数】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  {                                                                   │   │
│  │    "session_id": "uuid-xxx",                                        │   │
│  │    "machine_code": "MD5哈希值",                                     │   │
│  │    "timestamp": 1704105600,                                         │   │
│  │    "nonce": "a1b2c3d4...",                                          │   │
│  │    "signature": "base64签名"                                        │   │
│  │  }                                                                   │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【心跳后端处理】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  1. 验证签名                                                        │   │
│  │  2. 验证时间戳和Nonce                                               │   │
│  │  3. 验证session有效性和过期时间                                     │   │
│  │  4. 验证machine_code匹配                                            │   │
│  │  5. 验证卡密有效期                                                  │   │
│  │  6. 检查session是否需要续期                                         │   │
│  │  7. 从system_config获取客户端配置                                   │   │
│  │  8. 获取用户状态信息                                                │   │
│  │  9. 生成响应签名并返回                                              │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【心跳响应内容】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  {                                                                   │   │
│  │    "code": 200,                                                     │   │
│  │    "msg": "成功",                                                   │   │
│  │    "encrypted_data": "base64(AES加密数据)",                         │   │
│  │    "timestamp": 1704105601,                                         │   │
│  │    "nonce": "h8g7f6...",                                            │   │
│  │    "signature": "base64(服务端签名)",                               │   │
│  │    "key_version": 1                                                 │   │
│  │  }                                                                   │   │
│  │                                                                        │   │
│  │  解密后data内容:                                                      │   │
│  │  {                                                                   │   │
│  │    "config": {                       // 从system_config获取          │   │
│  │      "heartbeat_interval_seconds": 10,                              │   │
│  │      "max_heartbeat_failures": 100,                                 │   │
│  │      "session_renewal_threshold": 1800,                             │   │
│  │      "session_extension_duration": 3600,                            │   │
│  │      "timestamp_tolerance_seconds": 300,                            │   │
│  │      ... 其他所有客户端配置                                         │   │
│  │    },                                                                │   │
│  │    "user_info": {                                                    │   │
│  │      "expire_time": "2026-03-01",                                   │   │
│  │      "days_remaining": 30                                           │   │
│  │    },                                                                │   │
│  │    "session_info": {                                                 │   │
│  │      "expires_at": "2026-02-02T10:30:00",                           │   │
│  │      "remaining_seconds": 3600,                                      │   │
│  │      "renewed": true                                                │   │
│  │    }                                                                 │   │
│  │  }                                                                   │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【配置获取规则】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  客户端所有配置必须通过心跳服务从system_config表动态获取:           │   │
│  │                                                                        │   │
│  │  - heartbeat_interval_seconds     - 心跳间隔秒数                     │   │
│  │  - max_heartbeat_failures         - 心跳最大失败次数                │   │
│  │  - session_renewal_threshold      - Session续期阈值(秒)             │   │
│  │  - session_extension_duration     - Session延长时间(秒)             │   │
│  │  - timestamp_tolerance_seconds    - 时间戳容忍秒数                   │   │
│  │  - max_login_attempts             - 最大登录失败次数                 │   │
│  │  - login_lockout_minutes          - 登录锁定分钟数                   │   │
│  │  - key_sync_interval_seconds      - 密钥同步间隔秒数                 │   │
│  │  - 其他客户端相关配置...                                             │   │
│  │                                                                        │   │
│  │  客户端本地不存储任何配置,全部通过心跳动态获取                      │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

12.4 自动退出登录机制

┌─────────────────────────────────────────────────────────────────────────────┐
│                        自动退出登录触发条件与处理                            │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  【触发条件】                                                                │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  触发场景                        │ 错误码   │ 说明                    │   │
│  │  ------------------------------│--------│------------------------│   │
│  │  1. session过期/无效            │ 401    │ 会话已过期              │   │
│  │  2. 机器码不匹配                │ 403    │ 账号在其他设备登录      │   │
│  │  3. 卡密已过期                  │ 403    │ 卡密到期                │   │
│  │  4. 账户被禁用                  │ 403    │ 账号被禁用              │   │
│  │  5. 心跳服务异常(累计超限)      │ -      │ 连续失败超过配置次数    │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【心跳失败处理流程】                                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │                                                                        │   │
│  │  客户端检测到心跳失败                                                 │   │
│  │         │                                                            │   │
│  │         ▼                                                            │   │
│  │  ┌─────────────────────────┐                                        │   │
│  │  │ 失败次数 + 1            │                                        │   │
│  │  └───────────┬─────────────┘                                        │   │
│  │              │                                                      │   │
│  │              ▼                                                      │   │
│  │  ┌─────────────────────────────────────────┐                      │   │
│  │  │ 失败次数 >= max_heartbeat_failures?     │                      │   │
│  │  └──────────────────┬──────────────────────┘                      │   │
│  │                     │                                              │   │
│  │         ┌───────────┴───────────┐                                  │   │
│  │         ▼                       ▼                                  │   │
│  │  ┌─────────────┐        ┌─────────────────────┐                  │   │
│  │  │ 是          │        │ 否                  │                  │   │
│  │  │ 触发退出    │        │ 继续执行            │                  │   │
│  │  └──────┬──────┘        └─────────────────────┘                  │   │
│  │         │                                                            │   │
│  │         ▼                                                            │   │
│  │  ┌─────────────────────────────────────────┐                      │   │
│  │  │ 执行退出登录流程                         │                      │   │
│  │  └─────────────────────────────────────────┘                      │   │
│  │                                                                        │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【退出登录执行步骤】                                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  1. 停止心跳定时器                                                   │   │
│  │  2. 停止密钥同步定时器                                               │   │
│  │  3. 关闭所有子线程                                                   │   │
│  │  4. 清除本地会话缓存 (session_id, user_id)                          │   │
│  │  5. 跳转到登录页面                                                   │   │
│  │  6. 显示友好的错误提示:                                              │   │
│  │     - session过期: "会话已过期,请重新登录"                         │   │
│  │     - 机器码不匹配: "账号已在其他设备登录"                          │   │
│  │     - 卡密过期: "卡密已到期,请前往续费"                            │   │
│  │     - 心跳异常: "服务连接失败,请检查网络后重试"                    │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【心跳失败次数配置】                                                        │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  配置项: max_heartbeat_failures                                     │   │
│  │  来源: system_config表                                              │   │
│  │  默认值: 100                                                        │   │
│  │  作用: 连续心跳失败超过此次数则自动退出登录                          │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

12.5 配置管理规范

┌─────────────────────────────────────────────────────────────────────────────┐
│                          客户端配置管理规范                                  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  【配置获取方式】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  1. 客户端启动时:                                                    │   │
│  │     - 登录成功后首次调用心跳获取完整配置                            │   │
│  │                                                                        │   │
│  │  2. 心跳响应中获取:                                                  │   │
│  │     - 每次心跳都返回最新的完整配置                                  │   │
│  │     - 客户端更新本地配置缓存                                        │   │
│  │     - 无需重启应用,配置立即生效                                    │   │
│  │                                                                        │   │
│  │  3. 配置变更后:                                                      │   │
│  │     - 管理员在backend-admin中修改system_config                     │   │
│  │     - 下次心跳时客户端自动获取最新配置                              │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【配置存储规则】                                                            │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  - 客户端本地不持久化任何配置                                        │   │
│  │  - 所有配置从心跳响应中动态获取                                      │   │
│  │  - 配置仅在内存中缓存,客户端重启后重新获取                          │   │
│  │  - 以心跳响应中的配置为准                                            │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
│                                                                              │
│  【配置优先级】                                                              │
│  ┌─────────────────────────────────────────────────────────────────────┐   │
│  │  1. system_config表中的配置 (最高优先级)                            │   │
│  │  2. 心跳响应返回的配置覆盖本地缓存                                   │   │
│  │  3. 客户端代码中无硬编码配置                                         │   │
│  └─────────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────────┘

文档结束