Files
ele-HeiXiu/ARCHITECTURE.md
YoungestSongMo 3becdb85d4 feat: 实现设置页面 + 路由系统 + JWT认证体系 + 公告优化 + 错误类型系统
## 新增功能

### 设置页面(Router + Modal 叠加)
- HashRouter 路由系统,Electron file:// 协议兼容
- 设置页以居中 Modal 叠加首页,仅 X 按钮关闭
- 主题切换 Segmented 控件(与首页 Switch 共享 Context)
- 大模型产物存储仓库路径配置(原生文件夹选择对话框)
- 企业版额外:团队创作存储仓库路径
- 系统信息展示(平台/版本/版本类型/环境)
- 版本更新检查(状态机:idle→checking→no-update/available→downloading→downloaded→error)

### JWT 认证体系
- 双 Token 机制:access_token + refresh_token
- Axios 拦截器 401 自动刷新 + 并发请求去重队列
- 启动时 refresh_token 静默续期(自动登录)
- 记住密码:Base64 编码存储,表单自动回填
- setTokenRefreshHandler 解耦模式:request.ts 不知晓 auth

### 自定义错误类型系统
- RefreshError 继承 RequestError,type = AUTH_EXPIRED
- 全局拦截器捕获 RefreshError → 自动 clearToken + AUTH_REQUIRED
- 任何位置 throw new RefreshError() 即可触发登录 Modal

### 公告卡片优化
- 长文本 Tooltip 悬浮预览
- 点击卡片弹出详情 Modal(全文保留换行)

### SettingsContext(类似 Vue Pinia)
- 集中式设置状态管理
- localStorage 持久化:ele-heixiu-output-path / ele-heixiu-team-repo-path
- useSettings() 全局消费

## 架构变更
- App.tsx → HashRouter + AppRoutes + LoginPage 弹层
- Layout.tsx = NavBar + <Outlet /> 页面壳
- HomePage.tsx 从 App.tsx 提取
- NavBar 使用 useNavigate() 真实路由导航
- useUpdater 改为模块级单例 IPC 监听器(修复 MaxListenersExceededWarning)
- electron/main.ts 新增 FILE_DIALOG IPC handler

## 依赖
- 新增 react-router-dom
2026-06-03 19:25:15 +08:00

8.1 KiB
Raw Blame History

船长 · 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 重试       │
├─────────────────────────────────────────────────┤
│  存储层                                          │
│  localStorageaccess_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.tsToken 生命周期)

  • 职责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()

示例:

// 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<LoginResponseBody> {
        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 stringUUID 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