Files
ele-HeiXiu/CHANGELOG.md
YoungestSongMo 136a17c0bf feat: 任务列表右键菜单回调 + 主进程下载 + sharp 缩略图 + IPC 精简重构
- 任务右键菜单:实现打开输出目录(inputs/outputs/.cache 结构)、
    复制资源链接、重新编辑、再次生成、拉取结果、重新下载、收藏、删除记录
  - 主进程下载:通过 Electron net 模块绕过 CORS,支持 CDN 媒体文件下载
  - sharp 缩略图:替换 nativeImage,支持 4K/8K+ 大尺寸素材的流式处理
  - IPC 精简:40+ 冗余通道 → 15 个分组通道,移除未使用的 preload 桥接方法
  - 任务存储:新增收藏字段、schema 迁移、task-store 持久化
  - 清理废弃模块:canvas-ipc / updater / request / pricing

refactor: IPC/Preload/Services 模块化拆分 — 单一职责 + 向后兼容

  主要变更:
  - shared/constants/ipc-channels.ts → ipc/ 模块 (base/storage/canvas/updater)
  - electron/preload.ts → preload/ 模块 (base/safe-storage/file-storage/canvas)
  - electron/main/ipc/canvas-ipc.ts → canvas/ 模块 (types/walk/sidecar/mime/thumbnail)
  - electron/main/ipc/storage-ipc.ts → 提取 utils/ (path/download/thumbnail)
  - electron/main/updater.ts → updater/ 模块 (provider/platform-updaters)
  - src/services/request.ts → request/ 模块 (token/interceptors/methods)
  - src/shared/utils/pricing.ts → pricing/ 模块 (types/helpers/format/calculate/fields)
  - src/modules/home/center/views/ModelInputForm.tsx → 提取 utils + DependentFormItem

  架构优化:
  - 所有拆分均保持向后兼容(旧导入路径仍可用)
  - TypeScript 编译通过 ✓
  - 每个模块单一职责,平均 ~130 行
2026-07-03 11:13:36 +08:00

501 lines
27 KiB
Markdown
Raw Permalink 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.
# 更新日志
> 每次发版前修改此文件,`npm run build:win` 打包后会自动读取生成 update-info.json。
---
## 0.0.292026-06-29
**IPC/Preload/Services 模块化拆分 — 单一职责 + 向后兼容**
### 模块化拆分
- **shared/constants/ipc**`ipc-channels.ts` (105行) → `ipc/` 模块 (base/storage/canvas/updater)
- **electron/preload**`preload.ts` (229行) → `preload/` 模块 (base/safe-storage/file-storage/canvas/app-runtime)
- **electron/main/ipc**
- `canvas-ipc.ts` (705行) → `canvas/` 模块 (types/walk/sidecar/mime/thumbnail/scan-roots)
- `storage-ipc.ts` (357行) → 提取 `utils/` (path/download/thumbnail)
- **electron/main/updater**`updater.ts` (479行) → `updater/` 模块 (provider/platform-updaters)
- **src/services/request**`request.ts` (479行) → `request/` 模块 (token/interceptors/methods)
- **src/shared/utils/pricing**`pricing.ts` (488行) → `pricing/` 模块 (types/helpers/format/calculate/fields)
- **ModelInputForm.tsx**:提取 `utils.ts` + `DependentFormItem.tsx` 子组件
### 架构优化
- 所有拆分均保持向后兼容(旧导入路径仍可用)
- 每个模块单一职责,平均 ~130 行
- TypeScript 编译零错误
---
## 0.0.282026-06-29
**项目架构重构VCHSM 五层模块化)+ 公告 Badge 提醒**
### 架构重构
- **VCHSM 模块化**`pages/` + `services/modules/` 分散模式 → 统一 `modules/` 五层架构
- **V**iews — 页面组件(纯 JSX 结构)
- **C**ontrollers — 控制器(交互逻辑 + API 调用)
- **H**ooks — 自定义 Hook组件间复用逻辑
- **S**ettings — 配置层(常量映射、字段配置、注册表)
- **M**odel — 数据模型(纯 TS 类型/接口)
- **7 个业务模块**navbar / announcement / auth / home / account / canvas / settings
- **Home 模块三栏嵌套**left输入/ center预览/ right模型/任务),每栏独立 VCHSM
- **Account 模块子功能分目录**info / vip / recharge / billing / orders / wechat-work
- **应用壳抽离**`src/app/` 目录(入口 + Provider + 布局 + 路由分发)
- **跨模块共享**`src/shared/` 目录components / hooks / services / utils / infrastructure / theme
- **导入路径全面更新**~500+ 处引用同步TypeScript 编译零错误
### 公告
- **Badge 角标提醒**antd `Badge` 组件包裹 NavItem未读公告数量实时显示
- **缓存与轮询**`useAnnouncements` Hook — localStorage 缓存已读 ID + 每 15 分钟轮询对比
- **标记已读**:打开公告抽屉时自动将当前公告 ID 写入 localStorage角标消除
### 日志系统
- **全量审计修复**~60+ 处 try/catch 补充 `logger.warn()` 记录
- **存储路径可配置**`setLogDirResolver()` / `setDataDirResolver()` 函数注入模式
- **15 个日志分类**request / auth / updater / event / app / ui / ipc / safe-storage / storage / canvas / canvas-window / …
---
## 0.0.272026-06-24
**无限画布 UI 重构 + 预览键盘控制 + 拖拽体验优化**
### 无限画布
- **双栏布局重构**AntD LayoutSider + Content左侧导航 + 右侧画布,页面级别渲染预览组件解耦 DOM
- **CanvasSidebar**:项目/集数选择器 + 镜头搜索 + 镜头列表(滚动条 + 底部固定刷新按钮)+ 工作区切换
- **CategoryTabs 双模式**Assets 阶段 → 全部/角色/道具/场景/收藏;其他阶段 → 所有[阶段名]/收藏/待审核/通过/返修/已弃用
- **分类数量徽标**:每个标签显示数据计数,"收藏"标签额外显示媒体类型细分(视频/图片/音频)
- **Ctrl/Cmd + 拖拽平移**:替代 Space 键修饰,释放 Space 给媒体控制;`pointerEvents: none` 防止拖拽时误抓缩略图
- **画布锁**:预览打开时 `previewActiveRef` 双守卫阻止穿透缩放/拖拽
- **无限画布入口按钮**StageTabs 右侧按钮,调用 `OPEN_CANVAS_WINDOW` IPC
### 预览键盘控制
- **Space 暂停/播放**capture 阶段拦截 + `stopPropagation`,避免与原生 `<video controls>` 双重触发
- **← → 快退/快进**capture 阶段直接设置 `currentTime`,步长 ±0.5 秒
- **↑ ↓ 音量**:透传给原生 controls 处理
### 主进程
- **local-media:// Range 头转发**:协议 handler 只提取并转发 `Range` 请求头到 `net.fetch`,确保音视频 seek 返回正确的 206 Partial Content
### UI 调整
- 全局字号上调(侧栏、工具栏、标签、状态栏等)
- CanvasSidebar 滚动条定制 + 主题色适配
- ProjectSelector / EpisodeSelector 宽度自适应
---
## 0.0.262026-06-23
**无限画布滚轮缩放 + 项目管理入口 + 文件拖拽**
### 无限画布(企业版项目管理)
- **滚轮缩放视口**:纯滚轮 10%/tick 快速缩放Ctrl/Cmd+滚轮 3%/tick 精细控制,范围 2%~1600%
- **鼠标拖拽平移** + **Ctrl/Cmd+0 重置缩放** + **ZoomOverlay 百分比指示器**
- **预览事件穿透防护**:预览打开时锁定底层视口(`previewActiveRef` 双守卫)
- **预览内独立缩放**:图片灯箱支持滚轮缩放(2%~800%) + 拖拽平移 + 百分比指示器
- **原生文件拖拽**:缩略图/预览 `webContents.startDrag()` → Photoshop/资源管理器等外部应用
- **local-media:// 协议**:支持 UNC 网络路径 + 本地路径安全加载
### 主进程
- **画布窗口 `before-input-event`**:拦截 Chromium 页面级缩放,滚轮事件完整到达渲染层
- **`canvas-ipc.ts`**:文件扫描/缩略图生成/文件操作/sidecar 读写/startDrag 等 15+ IPC handler
- **数据源抽象**`ICanvasDataSource` 接口,离线 `LocalFileDataSource` + 未来在线模式平滑切换
### Bug 修复
- **passive event listener**React `onWheel` → window 级原生 `{ passive: false }` listener
- **Zustand v5 `getState()` 批处理旧值**:双 `ref``currentZoomRef` + `setZoomRef`)读写,绕过原生事件中的批处理快照
---
## 0.0.252026-06-22
**业务错误 Modal 引导 + 左侧栏 UI 增强**
### 业务错误 Modal 引导体系
- **HTTP 402 + code 1001 → 余额不足 Modal**`request.ts` 拦截器双条件精确匹配,`App.tsx` 弹 Modal 引导"去充值"
- **HTTP 403 + code 4040 → 软件到期 Modal**:拦截 403+4040弹 Modal 引导"去购买 VIP"
- **`BusinessErrorEvent` 事件总线类型**:标准化 `{ code, message }` 负载,新增 `BALANCE_INSUFFICIENT``SUBSCRIPTION_EXPIRED` 事件
- **`RequestError.displayed` 标记**Modal 已展示的错误标记 `displayed=true``ModelInputForm.doSubmit` 读取标记跳过 `message.error` 重复 toast
- **防御性兜底**HTTP 200 + code 1001 也入拦截器(兼容后端行为变体)
### 左侧栏 UI 增强
- **模型列表 emoji 渲染**:从后端 `ui_config.badge` / `meta_config.icon` 提取 emoji 显示在模型名称前
- **标题栏色条加深**:模型选择器 + 任务记录标题栏加 `borderLeft: 3px` 色条 + `colorFillSecondary` 背景 + 图标染色
- **任务列表分页器固定**`Pagination` 独立到表格外部,`pagination={false}` on Table分页器始终固定在底部
- **水平滚动条固定**:独立 `div` + 双向 `scroll` 事件同步 + MutationObserver不随垂直滚动移动
- **统一滚动条样式**`::-webkit-scrollbar` 暗色/亮色双套,纵向 6px、横向 6px风格统一
- **表头取消加粗**`.ant-table-thead > tr > th { font-weight: normal }`
- **模型列表与任务列表间距**`paddingBottom: 4` / `paddingTop: 4`
---
## 0.0.242026-06-22
**去除构建时版本变量 + 窗口标题动态版控 + 退出登录保留缓存**
### 版控体系重构
- **去除构建时 `EDITION` 环境变量**`--personal`/`--enterprise` 参数从 `build.mjs` 移除,`vite.config.ts` 不再注入 `process.env.EDITION``.env.*` 清理相应行
- **统一的安装包文件名**`船长-HeiXiu-{os}-{版本号}-Setup.{ext}`(移除 `${env.EDITION}`
- **`package.json` 简化**:移除 3 条 `:enterprise` 构建脚本
- **`scripts/*.mjs` 统一**`upload-oss`/`generate-update-info`/`publish-release` 不再接受 `edition` 参数
### 窗口标题动态版控
- **新格式**`船长-HeiXiu-{os}-{版本号}`(未登录)/ `船长-HeiXiu-{os}-{版本号}·{版控}`(登录后)
- **运行时链路**`AppProvider` 根据 `user.account_type` 派生 `edition``useEffect``window.appRuntime.setWindowEdition()` → IPC `set-window-title` → 主进程 `buildWindowTitle``mainWindow.setTitle`
- **Edtion 类型守卫**`src/vite-env.d.ts` + `electron/electron-env.d.ts` 双端类型声明
- **更新检查 API** 移除 `edition` 查询参数
### 本地存储生命周期修正
- **退出登录不再自动清缓存**`AppProvider.logout()` 移除 `clearUserStorage()` 调用。数据已按 `user_id` 隔离,同一用户重新登录缓存直接命中
- **`clearUserStorage` 竞态修复**:添加 `isOpen()` 前置守卫,防止数据库未初始化时执行 SQL移除尾部 `closeDatabase()`,确保退出→重新登录周期内数据库持续可用
- **手动清理保留**`DataManageSetting` 的「清理缓存」按钮仍可主动触发
---
## 0.0.232026-06-22
**三栏等比缩放修复 + 代码重组**
### 三栏布局修复
- **约束逻辑修正**:双遍最小值约束改为 clamp + 超限兜底,修复窗口缩小至 1036px 以下时中列被挤出视口
- **双触发器**`ResizeObserver` + `window.resize` 事件双重驱动,防止 React 批量更新吞掉重绘
- **三栏等比缩放**:旧算法只保 left:right 比例,中列被动吸收剩余;新算法通过 `prevWidthRef` 反推中列上次宽度,三栏按各自占比同步缩放——窗口从半屏拉到全屏保持 ≈1:1:1
### 代码重组(第二轮)
- **src/utils/**11 文件精简为 7 文件 —— `env.ts`(platform+device)、`bus.ts`(event-bus+ipc+logger)、`display.ts`(display+logo+type)、`media-upload.ts`
- **controls/**14 文件精简为 6 文件 —— `simple-widgets.tsx`(6合1)、`upload-widgets.tsx`(ImageInput+VideoInput 去重)、`list-upload-widgets.tsx`(ImageListInput+AudioListInput)
- **FormField 提升**:从 `auth/components/` 提升到 `src/components/ui/``FieldConfig` 类型同步移到 `ui/types.ts`
- **data-sync 归位**:从 `settings/sections/` 移到 `infrastructure/storage/`
- **electron/main/ 分组**3 个 IPC handlerlog-ipc/safe-storage-ipc/storage-ipc收拢到 `ipc/` 子目录
- **shell/ → frame/**`src/components/shell/` 重命名为 `frame/`,更直观表达"应用框架"
- **storage/ 分层**15 文件平铺 → `core/`4+ `stores/`5+ `services/`4+ 入口
### 文档
- 37 个文件夹全部生成 README.md不进 Git
- PROJECT.md 目录树同步更新
---
## 0.0.222026-06-18
**三栏布局比例缩放 + SeedanceContent 视频参考上传 + 本地存储基建**
### 三栏布局:比例自适应缩放
- ResizeObserver 改为**等比例分配**策略(替代固定默认值恢复):窗口 Snap/最大化/手动缩放时左-中-右三栏按当前比例同步缩放
- 修复拖拽期间窗口缩放竞态:`handleMouseUp` 即时清除 `draggingRef` + `needsRecalcRef` 延迟补算,解决拖拽中最大化后放开鼠标布局卡死
- 使用 `containerRef.current` 动态读取 DOM替代闭包捕获 mount 时 `el`),避免 Provider 重渲染后读到旧 DOM
### Seedance2Content 内容编排
- `SeedanceContent` 从占位文本重构为完整内容编排控件:文本描述 + 参考图片/视频/音频三条上传通道
- 根据 `param_schema[].spec.image_files / video_files / audio_files` 动态渲染对应上传区域,**不依赖 widget 名称的隐式约定**
- 内部值类型 `{ text, imageUrls[], videoUrls[], audioUrls[] }` → 表单 JSON 字符串 → 提交时序列化为 API `ContentItem[]``text` / `image_url` / `video_url` / `audio_url` 判别联合)
- `useModelUI` 新增 `widgetName` 字段 + `spec` 媒体文件计数传参(`imageFiles`/`videoFiles`/`audioFiles`/`uploadProvider`
- `ModelInputForm.doSubmit` 新增 `seedance2Content` 分支序列化逻辑
### 图片上传兼容视频文件
- `ImageInput` / `ImageListInput``buildAccept` 根据 `file_filter` 自动区分 `image/` vs `video/` 前缀(原硬编码 `image/` 导致视频文件不可选)
- `createMediaBeforeUpload` 根据 `filters` 动态放行 `video/*` MIME
- 公共模块 `media-upload-utils.ts``VIDEO_EXTENSIONS` + `buildAccept()` + `createMediaBeforeUpload()` 三组件共享
### 本地存储基建sql.js + safeStorage
- **数据库**sql.jsWASM SQLite渲染进程内存数据库DDL 7 张表task_records / order_records / param_history / media_cache / sync_cursor / app_settings / _schema_version
- **加密**safeStorageOS DPAPI/Keychain加密持久化base64 编解码管线
- **IPC 文件操作**8 个双向 IPC 通道FILE_WRITE/READ/DELETE/MAKE_DIR/SHOW_ITEM_IN_FOLDER/GET_DATA_DIR/FILE_SIZE/FILE_EXISTS`resolveSafe()` 路径穿越防护
- **数据目录**`<userData>/heixiu-data/`(沙箱内),含 `heixiu.db`(加密镜像)+ `media-cache/<userId>/<date>/`
- **用户隔离**JWT `user_id` 区分数据,`getTodayKey()` 日期分桶
- **同步策略**:本地优先 + 手动触发("加载历史数据"按钮),游标分页(`sync_cursor` 表),每批 100 条
- **设置集成**`DataManageSetting` 卡片(缓存统计 + 加载历史数据 + 保存到磁盘 + 清理缓存)
### 媒体输出预览优化
- `<img>/<video>` 直接加载远程 URL无 CORS 限制),移除阻塞式 axios 下载
- 类型检测URL 扩展名正则 + `output_type` 补充CDN 无扩展名链接)
- `<Image fallback>` 加载失败占位图
### 其他
- 修复 `errorMessageRef` 在渲染期间写入react-hooks/refs 警告)
- CSP 添加 `wasm-unsafe-eval` 支持 WebAssembly + `https:` CDN 媒体源
---
## 0.0.212026-06-15
**文件结构重组components/ pages/ 按职责分层**
### components/ → 三层拆分
- `providers/`状态管理AppProvider / ThemeProvider / SettingsProvider
- `shell/`应用外壳Layout / PageDispatcher / navbar/
- `ui/`通用原子FloatingPanel / LegalTextModal
### pages/ → 重命名消除歧义
- `headers/``panels/`NavBar 面板页,非 HTTP headers
- `login/``auth/`(含登录/注册/忘记密码/修改密码)
- `settings/blocks/``sections/`
- `home/components/useUI/``home/controls/`
### home/ → 四层子结构
- `panels/` — 布局壳LeftPanel / CenterPanel / RightPanel
- `features/` — 功能面板Banner / ModelInput / ModelSelector / Output / TaskHistory
- `controls/` — 表单控件库12 个控件 + Widget 注册表)
- `hooks/` — 纯 HookuseModelUI
### 其他
- HomeContext 从 HomeContent.tsx 拆分独立文件(解决 react-refresh ESLint 警告)
- uploadValidation 内联到 ImageInputcontrols/ 不再混工具函数)
- 删除空占位 Test3.tsx
### 支付系统 & 订单明细
- **RechargePage antd 6.x 适配**`InputNumber.addonAfter``Space.Compact``Space direction``orientation`,消除弃用警告
- **OrdersPage 订单明细页面**`/orders`,方案 B — 独立订单管理页):
- 状态筛选 Segmented全部/待支付/已支付/已过期/支付失败)+ 分页 Table
- "继续支付"操作:打开支付 URL + `startPolling` 单订单轮询
- `pollingRef: Map<orderId, timer>` 管理多订单并发轮询,幂等启动(重复点击防重)
- 局部 state 更新 + 终态全量刷新(`fetchOrders`
- `AbortController` 防竞态:切换筛选/分页时取消旧请求
- `columns` 使用 `useMemo` 稳定引用
- 错误态 Result + 重试按钮
- **PageDispatcher**:注册 `/orders` 路由width: 820, height: 640新增 `CLOSE_PAGE` / `PAGE_LOCK` 事件监听
- **payments.ts**:支付 API 模块类型体系(`CreateOrderResponseBody``OrderListResponseBody` / `OrderInfoResponseBody` 名义类型),`PaymentAPI.getOrdersListInfo` 支持 `AbortSignal`
---
## 0.0.202026-06-12
**任务状态轮询 + Token 刷新容错增强 + 提交防抖 + 文件上传 Hook**
### 任务状态轮询
- 新增 `useTaskPolling` hook批量轮询非终态任务状态递归 `setTimeout` + `next_poll_ms` 自适应间隔
- `TaskHistory` 集成批量轮询,`OutputPreview` 改为监听终态转换Context 桥接)触发 `refetch()`,消除重复请求
- `POLLING_STATUSES` 统一管理非终态状态,所有轮询逻辑引用此常量
- `batchTaskStatus` 使用 GET + 逗号拼接 `task_ids=a,b,c`,修复 axios 默认 bracket 序列化
### Token 刷新容错增强
- **主动刷新**JWT `exp` 解码驱动定时器,在 75% 过期点通过 `setTimeout` 提前续期
- **失败分级**`refreshHandler` 永久失败refresh_token 过期)→ **throw** → 清 token 弹登录;临时失败(网络超时)→ **return null** → 仅拒绝当前请求不登出
- 新增 `RefreshTokenExpiredError` 类区分失败类型
- `AppProvider` 登录/自动登录/退出 启停主动刷新定时器
### 提交按钮防抖
- 两级防护2s 节流防抖("你点击的太快了"+ 二次确认弹窗("您已提交,确认要再次提交吗?"
- `doSubmit` 抽取为 `useCallback``handleSubmit` 负责门控
- 修复 `useAsyncMutation.execute` 返回值判空(原 `try/catch` 为死代码)
### 文件上传 Hook
- 新增 `useFileUpload` hook封装 `request.upload()` 为 antd Upload 兼容 `customRequest`,复用全局拦截器
- `ImageInput` / `ImageListInput` / `AudioListInput` 集成 `customRequest`
- `uploadValidation.beforeUpload` 返回值修正:`false``true`(校验通过后放行至 `customRequest`
### 其他
- `AnnouncementDrawer` 接入公告 API + `EnumResolver`
- billing / announcement / banner 服务模块完善
- 文档ARCHITECTURE.md 新增轮询/Token刷新/上传/防抖章节
---
## 0.0.192026-06-11
**数据获取架构重构 + Mac 指纹采集 + Token 刷新容错 + 账单页面**
### 数据获取架构重构
- `useAsyncData` 集成 AbortControllercleanup 时真正中断 HTTP 请求,不再仅丢弃响应(解决 StrictMode 双重请求)
- fetcher 签名变更:`() => Promise<T>``(signal: AbortSignal) => Promise<T>`
- API 模块统一 `signal?: AbortSignal` 可选参数,透传 axios configbanner/auth/models/task/vip
- `use-api.ts` 成为数据 Hook 集中管理中心:
- 新增 `useAccountInfo``Promise.all` 并行获取用户信息 + VIP 等级
- 新增 `useBillingBalance` — 账户余额
- 新增 `useBillingLedger` — 账单明细(支持分页参数)
- `AccountInfo.tsx` 重构:~40 行手写状态管理(`useRef`+`useEffect`+`cancelled`+`retryTick`)→ 1 行 `useAccountInfo()`
- 共享工具 `src/utils/display.ts``centToYuan` / `formatDate`,消除 AccountInfo/BillingInfo 重复定义
- 移除 `modelFirstFetchDone` 模块级变量StrictMode 不安全)
### Mac 硬件指纹采集
- `electron/preload/device.ts` Mac 完整实现:
- UUID`ioreg -d2 -c IOPlatformExpertDevice``IOPlatformUUID`
- CPU`sysctl -n hw.model` → 机型标识
- HDD三级回退 `system_profiler SPNVMeDataType``SPSerialATADataType``SPHardwareDataType`
- `isMacOS()` / `runMacCommand()` / `getMacHddValue()` 辅助函数
- `getComponentId()` / `fingerprintSha256()` Mac 早退分支,与 Windows 共享 SHA256 管线
### 账单页面
- `BillingInfo.tsx` — 余额卡片BalanceCard+ 账单表格LedgerTable+ 分页
- `BillingAPI``getBalance` / `getLedger``signal` 参数透传
- `PageDispatcher` 注册 `/billing` 页面FloatingPanel 960×680
- 修复 `billing.ts` signal 参数位置 bug`get(url, {signal})``get(url, undefined, {signal})`
- BillingInfo 标题精简:移除冗余页面 Title 和 Card title仅保留 FloatingPanel 标题栏
### Bug 修复
- **Token 刷新容错**`auth-token.ts`):网络错误/超时时保留 refresh_token仅 RefreshErrortoken 过期)时清除。
解决网络短暂中断后被迫手动重新登录的问题
- **AppProvider 自动登录**`email`/`admin_permissions` 等 nullable 字段默认值修复(`?? ''` / `?? []`
- **antd Table align**`align: 'left'``align: 'left' as const`,修复 TS2322 类型错误
- **分页器位置**:显式添加 `position: ['bottomRight']`
### 文档
- CLAUDE.md 精简为 AI 行为规则,项目技术内容移入 ARCHITECTURE.md
- ARCHITECTURE.md 新增数据获取架构、页面调度详情、Token 刷新错误分类、设备指纹
---
## 0.0.182026-06-10
**前后端联调 — 任务创建 + 账户信息 + 预估花费**
### 任务创建联调
- `CreatTaskRequestBody.device_id` 改为后端必填参数API 层 `Omit` 封装,自动注入 `getDeviceId()`UUID v4 持久化,与登录/注册统一)
- `useSubmitTask` 泛型同步改为 `Omit<CreatTaskRequestBody, 'device_id'>`,调用方无感
- `submitTask` 移除 `body.device_id || getDeviceId()` 兜底逻辑,始终以本地生成的设备 ID 为准
### 预估花费计算(与 QT/Python 后端对齐)
- `src/utils/pricing.ts` — 从 Python 后端 `calculate_cost` 完整移植六种定价模式:
- 服务端:`SIMPLE` · `MULTI_DIMENSION` · `BILLING_ADAPTER`per_k_chars / rate + 条件附加费)
- 客户端:`fixed` · `per_k_chars` · `rate_matrix` · `matrix`
- `ModelInputForm.tsx` — 三层兜底预估消费展示:完整动态计价 → 配置文件兜底提取 → 模式标签
- **修复 6 个定价 Bug**
- SIMPLE 浮点冗余 `(x/100)*100` 在 IEEE 754 下不恒等 → `Math.round(分子)/100`
- `rate_matrix` / `matrix` 内联维度匹配缺少 `.trim().toLowerCase()` + null 跳过 → 统一 `matchDimensions()`
- `lookupMappingValue` / `matchDimensions` 缺 null 守卫 → `Cannot use 'in' operator` 崩溃
- `rate_matrix` 错误应用 `unit_scale`Python spec 无此行为)→ 移除乘法
- `matrix` 模式 `dimensions` 空值提前返 null`MULTI_DIMENSION` 不一致)→ 对齐处理
- 矩阵维度提取 `row.raw` — API 实际在行顶层存储维度(如 `resolution``raw` 子对象不存在 → `matrixRowData()` 兼容两种格式
- `pricing_config` 类型修正:`Record<string, unknown>``Record<string, unknown> | null`(对齐 OpenAPI `anyOf`
- 修复 React Hooks 顺序违例 — `estimatedCostDisplay` useMemo 移至提前返回之前
### 账户信息联调
- `AccountInfo.tsx` "当前套餐"从 VIP 等级列表匹配详细数据:
- `vip_level_id``VipLevelsItem.id` 映射查找
- 新增套餐说明、套餐周期字段,席位上限优先取 VIP 等级值
- 到期时间提取 `isExpired` 变量复用
- `VipAPI.getVipLevels()``AuthAPI.getUserInfo()` 并行加载
### 模型列表缓存
- 加密持久化缓存safe-storage启动时 `Promise.all` 初始化,缓存优先渲染消除 loading 闪烁
- `useModelList` Stale-while-revalidate即时展示缓存 → 后台拉新 → 覆盖缓存
### 其他
- VIP API 模块(`src/services/modules/vip-api.ts`)— 激活码/等级列表/订单/模拟支付
- ESLint/TS 类型修复(未使用符号清理、分号补充)
- CLAUDE.md 更新系统兼容性说明
---
## 0.0.172026-06-09
**修复接口重复调用 — 请求量减半**
- AppProvider 自动登录 useEffect 增加 aborted 清理标志,修复 StrictMode 双重挂载导致 refreshToken/getUserInfo 各调两次
- useModelList 提升到 LeftPanel 单例调用,修复 ModelSelector 和 TaskHistory 各自独立请求全量模型列表
- 开发模式 DevTools 中"已停止"的请求即为 StrictMode 第一轮挂载被丢弃的请求
- 模型输入栏三层架构useUI 组件体系10 种 widget → antd 映射)+ useModelUI Hookparam_schema → UIFieldConfig[]
- constraints_config.requires 动态联动enable_switch 开关控制依赖字段启用/禁用
- 修复模型默认值不加载antd 外部 form 实例忽略 initialValues+ DependentFormItem prop 透传断裂
- PageDispatcher 统一页面调度Modal/Drawer/FloatingPanel 三容器,全局互斥,动态视口尺寸
- 账户信息页AuthAPI.getUserInfo() 驱动,积分/套餐/统计三卡片布局
- 代码去重runFieldChecks 校验运行器 + createImageBeforeUpload 上传校验工厂
- 主题切换性能优化 — View Transitions API + 0.15s 统一过渡 + 设置页增强0.0.15 延续)
- 目录调整wechat-work/ → headers/
---
## 0.0.162026-06-09
**模型输入栏重构 + 统一页面调度 + 依赖联动**
- 模型输入栏三层架构useUI 组件体系10 种 widget → antd 映射)+ useModelUI Hookparam_schema → UIFieldConfig[]
- constraints_config.requires 动态联动enable_switch 开关控制依赖字段启用/禁用
- 修复模型默认值不加载antd 外部 form 实例忽略 initialValues+ DependentFormItem prop 透传断裂
- PageDispatcher 统一页面调度Modal/Drawer/FloatingPanel 三容器,全局互斥,动态视口尺寸
- 账户信息页AuthAPI.getUserInfo() 驱动,积分/套餐/统计三卡片布局
- 代码去重runFieldChecks 校验运行器 + createImageBeforeUpload 上传校验工厂
- 主题切换性能优化 — View Transitions API + 0.15s 统一过渡 + 设置页增强0.0.15 延续)
- 目录调整wechat-work/ → headers/
---
## 0.0.152026-06-09
**主题切换性能优化 — 桌面端流畅度提升**
- View Transitions API 驱动主题切换GPU 合成器单次 cross-fade 替代 80+ 个独立 CSS 过渡,消除 Electron 卡顿
- 主题更新时序统一:`applyHtmlTheme` 从 updater → `useLayoutEffect`CSS 变量与 React 提交同帧
- 过渡参数对齐:全部 `0.2s ease``0.15s linear`,桌面应用更利落
- CSS 过渡精准覆盖antd 容器组件Layout/Card/Modal/Table/Input/Menu 等 ~80 个类)替代 `html *` 全局选择器
- 设置页 Esc 键关闭 + macOS 关闭按钮适配 + 关闭后焦点防跳转
---
## 0.0.14 -- 🚀更新公告
**模型选择器焕新**
- 模型列表改为标签页分组展示新增「全部模型」Tab
- 列表项新增悬停高亮效果,维护中模型自动置灰并提示
- 修复选择模型后参数表单重复 key 报错
- 类型体系重构,支持后端分类 slug 自动映射
---
## 0.0.132026-06-07
- 修复主题切换后首页状态重置问题:设置页去路由化,改为事件总线驱动的 Modal 叠加
- 首页三栏分隔条视觉对齐:移除右侧多余 margin左右完全对称
- 轮播图新增关闭按钮session 级别隐藏,每次启动重新展示
- 确立 QT 多窗口模型 → Web 架构映射原则:叠加层走 Modal/Drawer + 事件总线,不走路由
- 修复首页三栏布局溢出:拖拽分隔条增加最大宽度约束 + 视口缩小时自动收窄面板,防止右侧面板被挤出视口
---
## 0.0.122026-06-04
- 重构更新检查流程无更新不再抛异常PreCheck → 直接 IPC 通知
- 新增用户决定下载流程:检查到新版本后展示更新内容,用户确认后再下载
- 设置页新增"查看详情"弹窗Markdown 格式化展示更新日志
- 新增"下载更新"按钮,与"安装更新"分离为两个独立操作
- 修复测试服务器版本排序(字符串序 → 语义版本序0.0.10 正确排在 0.0.9 之后)
- electron-updater Provider 增加 TTL 缓存,避免重复 API 请求
- 修复 Windows cmd `set` 命令空格导致的 Invalid URL 问题
- 修复 electron-updater 'error' 事件拦截,无更新不再爆红
---
## 格式说明
每个版本以 `## x.y.z日期` 开头,内容为无序列表。打包脚本会提取最新版本的日志填入 update-info.json。