Files
ele-HeiXiu/PROJECT.md
YoungestSongMo 7032d1c3f2 feat: 忘记密码/修改密码 + 设置页结构重组 + barrel cleanup + LoginPage 版本统一
新增:
  - ForgotPasswordModal — 手机号+短信验证码重置密码 (AuthAPI.resetPassword)
  - ChangePasswordModal — 当前密码验证后修改密码 (AuthAPI.changePassword)
  - useSmsCooldown Hook — 通用短信发送冷却逻辑,供注册/忘记密码复用
  - PasswordSetting — 设置页账号安全区块,双选项:修改密码 / 短信验证码重置

  修改:
  - LoginForm \"忘记密码?\" 链接接入 ForgotPasswordModal(替换 TODO)
  - LoginPage 移除 edition 业务版本 UI 区分,登录/注册 Tab 全局统一
  - 设置页 Section 组件移入 blocks/ 子目录(git mv 保留历史)
  - auth.ts sendSms URL 修正: /sms/send → /send-sms

  清理:
  - settings/index.ts 移除 4 个未使用 re-export,精简为仅 SettingsPage
  - navbar/index.ts 移除未使用 re-export,精简为仅 NavBar

  文档:
  - WORKLOG.md 新增 2026-06-08 工作日志
  - ARCHITECTURE.md / PROJECT.md 更新设置页目录结构与新组件
  - TODO.md 忘记密码流程标记已完成 + 短信人机验证列入后期优化
2026-06-08 11:57:35 +08:00

333 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 项目概览
- 本项目使用 Vite + React 19 + TypeScript + Electron 重构现有 Python Qt
桌面应用打造跨平台Windows/macOS的多窗口桌面程序。
- 核心界面为单页应用SPA同时支持通过 Electron 打开独立窗口以承载子功能。
# 核心需求
- 完整还原原有 Qt 应用的业务逻辑与视觉体验,并对交互进行现代化改造。
- 必须支持 多窗口(主窗口 + 可动态创建的辅助窗口),窗口间需通过事件/消息进行低耦合通信。
- 适配 Windows 与 macOS处理好快捷键、托盘、菜单、窗口边框等平台差异。
- 采用 离线优先 架构,保障本地数据处理性能,合理使用本地存储(如 electron-store 或 SQLite
- 所有对系统底层能力的调用必须通过 Electron 主进程,严禁在渲染进程直接使用 Node.js API除非通过 preload 安全暴露)。
# 技术架构
## 整体分层
- 主进程Main ProcessElectron 主进程管理窗口生命周期、系统菜单、托盘、IPC 事件调度、本地文件/数据库操作。
- 预加载脚本Preload桥接渲染进程与主进程使用 contextBridge 安全暴露有限 API。
- 渲染进程Renderer基于 React 19 的 SPA主窗口与独立窗口的 React 入口,负责 UI 展示与用户交互。
- 共享层Shared进程间共享的 TypeScript 类型、常量、工具函数、事件名称枚举。
## 前端架构原则
- 组件化:以函数组件 + Hooks 为唯一组件形式UI 拆分为展示组件与容器组件。
- 单向数据流:全局状态使用 Context + Provider 模式AppContext / ThemeContext / SettingsContext
遵循 Provider → Context → Comsumer 单向流动。组件内部状态使用 useState/useReducer不污染全局 Context。
- 事件驱动EDA
1. 主进程与渲染进程通信基于 Electron IPCipcMain/ipcRenderer定义明确的事件通道与载荷类型。
2. 渲染进程内跨组件通信优先使用 Context需要解耦的场景使用自定义事件总线src/utils/event-bus.ts
所有 emit 调用自动记录到日志文件用于审计追踪。
3. 自定义错误体系RequestError / RefreshError通过事件总线解耦请求拦截器抛出自定义错误 →
全局捕获 → emit 事件 → UI 层响应。
4. 窗口间通信统一通过主进程转发(或使用 MessagePort 等 Electron 支持的机制),保持窗口独立性。
- 路由与导航:主窗口为单页应用,使用 React Router v7+ 管理页面切换。**遵循 QT 多窗口模型映射原则**
- 叠加层(设置、公告、素材库等)→ Modal/Drawer + 事件总线驱动,主页面不卸载
- 页面跳转(真正的视图切换)→ React Router需跨页保持的状态放 Provider 或 localStorage
- 辅助窗口(设置、预览、导入导出等)以 Modal/Drawer 叠加在主窗口之上,不与主窗口共享 DOM但保持在同一个 React 组件树中以确保状态一致性。
# 目录结构
```md
├── electron/ # Electron 主进程
│ ├── main.ts # 入口:窗口创建 / IPC 注册 / 生命周期
│ ├── preload.ts # 预加载脚本contextBridge
│ └── main/
│ ├── updater.ts # 自定义更新API 版本检查 + OSS 下载 + NSIS 安装)
│ ├── logger.ts # 主进程日志核心JSON Lines 写入 / 轮转 / 清理)
│ ├── log-ipc.ts # 渲染进程日志 IPC 通道接收
│ ├── net-request.ts # Electron net.request 的 axios 风格封装
│ ├── menu/index.ts # 系统菜单
│ └── utils/ # 平台判断 / 图标路径
├── src/ # 渲染进程React 19 SPA
│ ├── main.tsx # React 挂载点 + 全局错误捕获
│ ├── App.tsx # 根组件(路由 + 登录弹窗)
│ ├── components/ # 通用 UI 组件 + Provider
│ │ ├── AppProvider.tsx # 全局状态platform / edition / login
│ │ ├── ThemeProvider.tsx # 主题状态light / dark
│ │ ├── SettingsProvider.tsx # 设置状态(存储路径等)
│ │ ├── Layout.tsx # 页面壳NavBar + Outlet
│ │ └── navbar/ # 导航栏 + 公告抽屉
│ ├── contexts/ # Context 定义app / settings / theme
│ ├── hooks/ # 自定义 HooksuseUpdater / useTheme
│ ├── pages/ # 页面组件
│ │ ├── home/ # 首页仪表盘
│ │ ├── login/ # 登录 / 注册页
│ │ └── settings/ # 设置面板(主题 / 存储 / 账号安全 / 系统信息 / 更新)
│ ├── router/index.tsx # HashRouter 路由配置
│ ├── services/ # HTTP 请求封装
│ │ ├── request.ts # Axios 封装 + 拦截器 + Token 刷新
│ │ ├── types.ts # 自定义错误类型RequestError / RefreshError
│ │ ├── auth-token.ts # refresh_token 管理
│ │ └── modules/ # API 模块auth / announcement
│ ├── theme/ # 主题系统
│ │ ├── tokens.ts # 设计令牌
│ │ └── globals.css # Tailwind + 全局样式
│ └── utils/ # 纯函数工具
│ ├── event-bus.ts # 事件总线on / off / emit + 日志钩子)
│ ├── logger.ts # 渲染进程日志器IPC 转发 / DEV 控制台)
│ ├── ipc.ts # 安全 IPC 守卫safeIpcOn / send / invoke
│ ├── platform.ts # 平台判定
│ └── device.ts # 设备 ID 生成
├── shared/ # 主进程 & 渲染进程共享
│ ├── types/ # 类型定义API / IPC / update / logging
│ ├── constants/ # 常量IPC 通道 / version / app
│ └── utils/ # 共享工具
│ ├── index.ts # debounce / throttle / compareVersions
│ └── sanitize.ts # 日志脱敏工具ID / 邮箱 / 手机 / 名称)
├── scripts/ # 构建脚本
│ ├── build.mjs # 构建总管
│ ├── clean.mjs # 清理产物
│ ├── upload-oss.mjs # OSS 上传
│ ├── publish-release.mjs # 发布到 API
│ └── generate-update-info.mjs # 生成更新元信息
├── package.json # 含 electron-builder 打包配置
├── vite.config.ts
├── tsconfig.json
└── tailwind.config.ts
```
# 多窗口与平台判定
- windowManager.ts 负责统一创建 BrowserWindow根据环境变量/参数加载不同入口(如 src/main/index.html 或
src/sub/settings/index.html
- src/utils/platform.ts 提供 isMacOS、isWindows 等函数(基于 navigator.userAgent 或 preload 暴露的 process.platform。在主进程的
electron/main/utils/platform.ts 中直接使用 Node.js API。
- 打包配置 (package.json build 字段) 须针对平台设置不同的图标、签名策略、安装包格式。
# 代码风格
## TypeScript
- 所有语句必须以分号 ; 结尾,缺失分号将被视为错误。
- 启用 strict 模式,禁止隐式 any优先使用 interface 定义对象类型,复杂类型可适度使用 type。
- 文件名使用小写短横线连接kebab-case如 use-window-state.ts组件文件名与组件名保持一致PascalCase
ThemeAdapter.tsx。
- 导入顺序:第三方库 → 项目内共享模块 → 相对路径模块,中间用空行分隔。
## React
- 只允许函数组件与 Hooks禁止使用类组件。
- 每个组件必须有明确的 Props 类型,使用 React.FC<Props> 或直接标注函数参数类型(推荐直接标注)。
- 事件处理函数命名以 handle 开头(如 handleClick自定义 Hook 以 use 开头。
- 避免直接操作 DOM除非封装在自定义 Hook 中(如焦点管理)。
- 使用 React.memo、useMemo、useCallback 优化性能,但不要过早优化。
## 状态管理
- 使用 React Context + Provider 模式管理全局状态按领域拆分AppContext平台/登录、ThemeContext主题
SettingsContext设置项。避免引入 Redux 等重量级方案。
- Provider 组件在 main.tsx 中按层级嵌套挂载:
AppProvider → ThemeProvider → SettingsProvider → AntdApp → App
- 各模块通过自定义 Hook 消费useAppContext / useTheme / useSettingsHook 内部做 null 检查。
## 日志系统
- 双端日志主进程直接写文件electron/main/logger.ts渲染进程通过 IPC send 上报src/utils/logger.ts
两端暴露一致的 API`logger.debug / info / warn / error(category, message, error?, context?)`
- 文件存储JSON Lines 格式(每行一个 JSON路径 `{userData}/logs/app-YYYY-MM-DD.log`
每日轮转 + 7 天自动清理。
- 事件总线联动event-bus.ts 暴露 `setEventLogListener` 钩子logger 注册后所有 emit 自动记录 DEBUG 日志。
- 错误日志request.ts 拦截器根据错误类型自动分级记录AUTH_EXPIRED → debug5xx → error其余 → warn
- 401 判断策略HTTP 401 拦截处双重校验——同时检查 HTTP 状态码和响应体中的业务码 `data.code`
`status === 401 && data?.code === 401` 才走 token 刷新流程;其他业务码(如密码错误)穿透到正常错误处理,
透传后端的 `data.message`。这确保登录接口返回的"账号或密码错误"不被 RefreshError("登录已过期") 覆盖。
- 脱敏shared/utils/sanitize.ts 在日志写入前自动处理 ID哈希、邮箱/手机/名称/Token 等字段。
# 安全与认证
## Token 加密存储
- `access_token``refresh_token` 通过 Electron `safeStorage` API 加密存储,不落明文:
- macOS → Keychain
- Windows → DPAPI
- Linux → libsecret
- 实现文件:
- `electron/main/safe-storage-ipc.ts` — 主进程加密/解密 IPC 处理(`BIDIRECTIONAL.SAFESTORAGE_ENCRYPT / DECRYPT`
- `src/utils/safe-storage.ts` — 渲染进程桥接层(`initSafeStore / getSafeStore / setSafeStore / clearSafeStore`
- `src/services/request.ts` — token 存取通过 `getSafeStore / setSafeStore`,无需感知加密细节
- `src/services/auth-token.ts` — refresh_token 同理通过 safeStorage 存取
## 双 Token 机制
- `access_token`(短效)+ `refresh_token`(长效),登录成功同时下发
- 请求拦截器自动附加 `Authorization: Bearer <access_token>`
- 401 响应 → 双重校验HTTP `status` + 业务 `data.code`)→ 仅 `code === 401` 时触发刷新
- 刷新期间并发请求排队(`failedQueue`),刷新完成后批量重试
- refresh 接口自身 401 直接拒绝防死循环login 接口 401 穿透透传后端 `message`
- 启动时 `refresh_token` 静默续期(自动登录),失败仅清除标记不影响用户名回填
- 登录/退出通过事件总线解耦(`LOGIN_SUCCESS` / `LOGOUT` / `AUTH_REQUIRED`
## 日志脱敏
- `shared/utils/sanitize.ts` 在日志写入前自动脱敏处理
- 覆盖字段:用户 IDSHA256 哈希)、邮箱(`***@domain`、手机号掩码、用户名称、Token截断
- 主进程 `electron/main/logger.ts` 和渲染进程 `src/utils/logger.ts` 均写入前调用 `sanitizeLogEntry()`
# 样式与主题
- 优先使用 Tailwind 工具类 完成布局与常规样式,自定义设计令牌应映射为 Tailwind 扩展(在 tailwind.config.ts 中配置
theme.extend
- Ant Design 组件样式通过 ConfigProvider 的 theme 属性覆盖,确保与 Tailwind 主题令牌同步。
`ConfigProvider` 同时配置 `locale={zhCN}` 实现全局中文(分页器"条/页"等无需逐个组件设置)。
在 src/theme/ 中创建 antd-theme.ts导出主题配置对象引用 tokens.ts 中的设计变量。
组件级 token如 Pagination.itemActiveColor / itemActiveBg在 sharedComponents 中统一管理,
亮色/暗色主题共享,避免 CSS !important hack。
- 禁止在组件中硬编码颜色、字号等设计令牌,一律使用主题变量或 Tailwind 类名。
- 全局样式文件 globals.css 只引入 Tailwind 指令和必要的重置样式,不堆积组件样式。
# 事件与 IPC
- 所有 IPC 通道名称定义在 shared/constants/ipc-channels.ts 中,分为三类:
MAIN_TO_RENDERER / RENDERER_TO_MAIN / BIDIRECTIONAL。
- 渲染进程通过 src/utils/ipc.ts 中的安全守卫访问 IPChasIpc 检查 → safeIpcOn / send / invoke
非 Electron 环境下静默跳过,保证浏览器预览不崩溃。
- 主进程事件处理函数需做好错误捕获与日志记录,渲染进程调用 IPC 时使用 try-catch 并处理超时。
- 窗口间通信走主进程转发:发送窗口 safeIpcSend主进程接收后定位目标窗口并 webContents.send。
# 开发与构建命令
- 开发模式npm run dev 或 yarn dev同时启动 Vite 与 Electron支持热重载
- 构建npm run build 编译渲染进程 → 打包 Electron 应用。
- 代码检查npm run lintESLint + Prettier规则已配置为强制分号、单引号等
- 类型检查npm run typechecktsc --noEmit
# 自动更新系统
## 架构
```
客户端 (Electron 主进程)
checkForUpdates()
├─ ① HeiXiuProvider.getLatestVersion() → 调用自有版本检查 API
│ └─ hasUpdate=false → 直接 IPC 通知 UI不经过 electron-updater
└─ ② hasUpdate=true → electron-updater.checkForUpdates()
├─ HeiXiuProvider.getLatestVersion() → TTL 缓存命中,不重复请求
├─ emit 'update-available'(含 releaseNotes→ UI 展示版本信息
└─ 用户点击"下载更新" → downloadUpdate()
├─ 差分下载(对比 .blockmap仅下载变更区块
├─ SHA512 校验
└─ 下载完成 → 用户点击"安装更新" → NSIS/DMG/AppImage 执行
```
| 模块 | 位置 | 职责 |
|-------------------|-----------------------------------------------|------------------------------------------|
| `HeiXiuProvider` | `electron/main/updater.ts` | 自定义 Provider调用自有 API无更新返回 APP_VERSION |
| `checkForUpdates` | `electron/main/updater.ts` | 预检版本 → 无更新直发 IPC / 有更新交 electron-updater |
| `downloadUpdate` | `electron/main/updater.ts` | 用户确认后触发下载(从 checkForUpdates 分离) |
| `netRequest` | `electron/main/net-request.ts` | Electron net.request 的 axios 风格封装 |
| `useUpdater` | `src/hooks/use-updater.ts` | 渲染进程更新状态 Hook单例 IPC 监听) |
| `UpdateSetting` | `src/pages/settings/blocks/UpdateSetting.tsx` | 设置页更新区块(含"查看详情"弹窗) |
| `server.py` | `test-server/server.py` | 本地测试服务器,扫描 release/ 目录,语义版本排序 |
## 增量更新(差分下载)
- `package.json` 配置 `"differentialPackage": true`electron-builder 打包时自动生成 `.exe.blockmap` 文件
- 自定义 Provider 模式:`HeiXiuNsisUpdater extends NsisUpdater` / `HeiXiuMacUpdater extends MacUpdater` /
`HeiXiuAppImageUpdater extends AppImageUpdater`
- 三平台 Updater 均覆盖 `getUpdateInfoAndProvider()` 注入 `HeiXiuProvider`(自定义 API 版本检查),差分下载由
electron-updater 父类原生处理
- blockmap 文件流程:
- `scripts/generate-update-info.mjs` — 扫描 blockmap 文件,写 `blockmap.url` + `blockmap.size``update-info.json`
- `scripts/upload-oss.mjs` — 安装包 + blockmap 文件一并上传 OSS支持 Node.js 直传和 ossutil 两种模式)
- `scripts/publish-release.mjs` — 通过 `--blockmap-url` 参数将 blockmap 信息发布到后端 API
- 效果:日常更新仅下载变更区块,预计节省 60%~90% 流量
## 环境变量加载机制(主进程)
加载顺序后者覆盖前者OS 环境变量始终优先):
```
① OS 环境变量set KEY=VALUE && .\app.exe ← 最高优先级
② .env.local不进 git个人覆盖用
③ .env.{mode}dev→.env.developmentbuild→.env.production
④ updater.ts 硬编码默认值https://www.heixiu.com
```
| 文件 | 何时生效 | 进 git | 用途 |
|--------------------|---------------------|:-----:|--------------------|
| `.env.development` | `npm run dev` | ✅ | 本地开发API → 测试服务器) |
| `.env.production` | `npm run build:xxx` | ❌ | 构建时参考API → 生产服务器) |
| `.env.local` | 始终(后加载) | ❌ | 个人特殊配置(打包后连本地测试) |
> `.env.production` 和 `.env.local` 不打包进 ASAR。打包后无这些文件时自动使用代码默认值。
## 测试工作流
```powershell
# 1. 构建两个版本
npm run build:win # → release/0.0.3/
# 改 package.json version = "0.0.4"
npm run build:win # → release/0.0.4/
# 2. 启动测试服务器
cd test-server && python server.py --release-dir ../release --port 8000
# 3. 启动旧版本客户端
set UPDATE_API_URL=http://127.0.0.1:8000 && .\release\0.0.3\win-unpacked\船长·HeiXiu.exe
# → 5 秒后自动检测到 0.0.4 → 差分下载 → 提示安装
```
相关文档:[UpdateA.md](UpdateA.md)、[test-server/README.md](test-server/README.md)
# 与其他 AI 协作的补充说明
- 所有生成的代码片段必须放在完整的文件上下文中,并注明所属文件路径。
- 若涉及跨平台逻辑,必须同时给出 Windows 与 macOS 的兼容实现或判断分支。
- 新增窗口时,需同步说明窗口入口、预加载脚本、主进程创建逻辑及 IPC 通道注册。
- 修改主题令牌时,需同步更新 tokens.ts、tailwind.config.ts 及 antd-theme.ts确保三处一致。