# 船长 · HeiXiu — 前端架构文档 > 最后更新:2026-06-03 ## 目录结构 ``` src/ ├── components/ # 全局组件(Provider、导航栏、通用组件) │ ├── AppProvider.tsx # 应用根 Provider:登录态 + 平台/版本 + 自动登录 │ ├── ThemeProvider.tsx # 主题 Provider:亮/暗切换 + Ant Design ConfigProvider │ ├── navbar/ # 自定义导航栏(替代系统菜单) │ └── common/ # 通用组件(LegalTextModal 等) ├── contexts/ # React Context 定义 + Consumer Hook │ └── app-context.ts # AppContextValue 类型 + useAppContext() ├── hooks/ # 通用 Hook │ ├── use-theme.ts # 主题切换 │ └── use-updater.ts # 版本更新 ├── pages/ # 页面 │ └── login/ # 登录/注册 │ ├── index.tsx # LoginPage — Modal 弹层 │ ├── components/ # LoginForm / RegisterForm / FormField │ ├── config/ # 字段配置 / 校验规则 │ ├── hooks/ # use-auth-state(记住账号偏好) │ └── types.ts # LoginFormValues / RegisterFormValues ├── services/ # HTTP 层 + API 模块 │ ├── request.ts # Axios 封装(通用 HTTP 层,不知晓 auth) │ ├── auth-token.ts # Token 生命周期(refresh_token 存取 + 401 刷新回调) │ ├── types.ts # ApiResponse / RequestError / 分页类型 │ └── modules/ # API 模块(一个模块一个文件) │ ├── index.ts # barrel 统一导出 │ ├── auth.ts # AuthAPI class + DTO 类型 + AuthUrlObj │ └── announcement.ts ├── theme/ # Tailwind + Ant Design 主题 ├── utils/ # 工具函数 │ ├── event-bus.ts # 发布-订阅事件总线 │ ├── device.ts # getDeviceId() — 设备 UUID │ ├── platform.ts # 平台检测 │ └── ipc.ts # Electron IPC 安全封装 └── assets/ # 静态资源 ``` --- ## 分层架构 ``` ┌─────────────────────────────────────────────────┐ │ UI 层(React 组件) │ │ AppProvider / LoginPage / NavBar / ... │ │ 职责:渲染 + 用户交互 + 消费 Context │ ├─────────────────────────────────────────────────┤ │ 状态层(Context + Hooks) │ │ AppContext / useAuthState / event-bus │ │ 职责:跨组件共享状态 + 跨模块事件通信 │ ├─────────────────────────────────────────────────┤ │ 服务层(API + Token) │ │ AuthAPI / auth-token / request.ts │ │ 职责:HTTP 通信 + Token 生命周期 + 401 重试 │ ├─────────────────────────────────────────────────┤ │ 存储层 │ │ localStorage(access_token / refresh_token / │ │ device-id / auth 偏好) │ └─────────────────────────────────────────────────┘ ``` ### 层间依赖规则 - **UI 层** → 可以 import 状态层 + 服务层 - **状态层** → 可以 import 服务层 - **服务层** → 可以 import 工具层(event-bus),不 import UI 或状态层 - **request.ts** → 通用 HTTP 层,**不 import 任何 auth 模块**(通过 `setTokenRefreshHandler` 解耦) --- ## 核心流程 ### 1. 登录 ``` LoginForm.onSubmit(values) → LoginPage.handleLogin → AuthAPI.login({ username, password, device_id, user_ip }) → AppProvider.login(auth) ├─ setToken(access_token) → localStorage ├─ setStoredRefreshToken(...) → localStorage ├─ setUser(auth) → state ├─ setIsLoggedIn(true) → state └─ emit(LOGIN_SUCCESS, auth) → 事件总线 → onClose() → 关闭登录弹窗 ``` ### 2. 自动登录(静默 refresh) ``` App 启动 → AppProvider useEffect → getAutoLoginFlag() === true ? → 是 → getStoredRefreshToken() → AuthAPI.refreshToken({ refresh_token }) ✅ 成功 → setToken + setUser + emit(LOGIN_SUCCESS) (无弹窗) ❌ 失败 → clearStoredRefreshToken + clearSavedAuth (下次需手动登录) → 否 → 跳过 ``` ### 3. 401 → 刷新 Token → 自动重试 ``` 任意请求发起 → 服务端返回 401(业务码 401 或 HTTP 401) → request.ts handle401(config) → refreshHandler 已注册? → 是 → 调 refreshHandler() → auth-token.ts 回调 → AuthAPI.refreshToken({ refresh_token }) ✅ 成功 → 用新 token 重试原请求(用户完全无感) ❌ 失败 → clearToken + emit(AUTH_REQUIRED) → 弹出登录框 → 否 → clearToken + emit(AUTH_REQUIRED) → 弹出登录框 并发 401 处理: 请求 A 收到 401 → 开始 refresh 请求 B 同时收到 401 → 发现 isRefreshing=true → 加入队列等待 refresh 完成 → 新 token 广播给所有排队请求 → 全部重试 ``` ### 4. 退出登录 ``` NavBar / 设置页 → AppProvider.logout() ├─ clearToken() ├─ clearStoredRefreshToken() ├─ setUser(null) ├─ setIsLoggedIn(false) └─ emit(LOGOUT) ``` --- ## 关键模块说明 ### request.ts(通用 HTTP 层) - 职责:Axios 实例 + 拦截器 + `get/post/put/del` + 错误分类 - **不依赖**任何 auth 模块 - 暴露 `setTokenRefreshHandler(fn)` 供 auth-token.ts 注册 401 回调 - 暴露 `getToken()/setToken()/clearToken()` 供外部存取 access_token - 401 拦截逻辑内置重试队列,支持并发请求去重 ### auth-token.ts(Token 生命周期) - 职责:refresh_token 的存取 + 注册 `handle401` 回调 - `initAuthRefresh()` 在 AppProvider 挂载时调用一次 - 回调内部调用 `AuthAPI.refreshToken()`,成则更新双 token,败则清除 ### event-bus.ts(事件总线) - 纯发布-订阅,不依赖任何框架 - 用于跨模块解耦通信(如 axios 拦截器无法直接用 React Context) - 预定义事件:`AUTH_REQUIRED` / `SHOW_LOGIN` / `LOGIN_SUCCESS` / `LOGOUT` ### API 模块规范 每个 `services/modules/*.ts` 遵循统一范式: 1. `XxxUrlObj` — 模块路由常量(不 export,模块私有) 2. DTO 类型 — 请求体/响应体 interface 3. `XxxAPI` class — 静态方法,调用方式 `XxxAPI.method()` 示例: ```ts // auth.ts const AuthUrlObj = { login: '/api/v1/auth/login', ... } as const; export interface LoginRequestBody { ... } export interface LoginResponseBody { ... } export class AuthAPI { static login(data: LoginRequestBody): Promise { return post(AuthUrlObj.login, data); } } ``` --- ## 数据存储(localStorage) | Key | 类型 | 说明 | |-----|------|------| | `ele-heixiu-token` | `string` | JWT access_token | | `ele-heixiu-refresh-token` | `string` | JWT refresh_token | | `ele-heixiu-device-id` | `string`(UUID v4) | 设备唯一标识,首次自动生成 | | `ele-heixiu-auth` | `{ username, remember, autoLogin }` | 记住账号偏好(**不存密码**) | --- ## 环境变量 | 变量 | 开发值 | 生产值 | |------|--------|--------| | `VITE_API_BASE_URL` | `http://8.160.179.64:8000` | `https://www.heixiu.net` | | `VITE_EDITION` | `personal` | `personal` / `enterprise` | | `UPDATE_API_URL` | — | `https://www.heixiu.com` |