Skip to content

Conversation

@jsrcode
Copy link
Collaborator

@jsrcode jsrcode commented Jan 3, 2026

📋 变更概述

本 PR 引入完整的供应商管理系统,重构 Dashboard 和设置页面,提升多账户管理能力和用户体验。

🎯 主要功能

1. 供应商管理系统(Provider Management)

  • 核心架构:独立的供应商数据管理系统(~/.duckcoding/providers.json
  • 数据迁移:自动从全局配置迁移到供应商系统(global_to_providers migration)
  • CRUD 操作:创建/编辑/删除/激活供应商
  • 字段管理:name/api_key/base_url/notes/color/tags/quota/expiry
  • 前端页面:ProviderManagementPage(表格视图 + 表单对话框 + 删除确认)
  • 类型安全:完整的 TypeScript 类型定义(types/provider.ts

2. Dashboard 重构

  • 状态分离:Dashboard 状态从 providers.json 迁移到独立的 dashboard.json
  • 功能增强
    • 供应商选择器(Tabs 切换)
    • 工具实例选择器(Local/WSL/SSH 下拉菜单)
    • 工具卡片显示实例版本和状态
  • Bug 修复:修复实例选择器切换不生效问题(类型从对象改为字符串)
  • 数据管理:DashboardManager 管理工具实例选择和供应商激活状态

3. 设置页面优化

  • 移除冗余配置:删除全局 DuckCoding 账户设置(user_id/system_token)
  • 层级优化
    • 「基本设置」→「系统设置」
    • 移除「应用设置」Tab,整合到「系统设置」
    • 内部分两个 section:「基本设置」(开机自启动)、「开发者设置」(单实例模式)
  • 组件重构:BasicSettingsTab 改为自包含组件,useSettingsForm 职责缩减

4. 开机自启动功能

  • 跨平台支持:Windows(注册表)、macOS(LaunchAgents)、Linux(systemd/autostart)
  • 配置持久化:存储在 ~/.duckcoding/config.json
  • 前端集成:系统设置 Tab 中的开关控件

5. 统计功能增强

  • 供应商级统计:支持按供应商查询统计数据
  • API 优化:适配新的供应商系统架构
  • 向后兼容:保留全局统计功能

📊 代码变更统计

50 files changed, 3361 insertions(+), 549 deletions(-)

新增模块

  • commands/provider_commands.rs (192行)
  • commands/dashboard_commands.rs (86行)
  • commands/startup_commands.rs (99行)
  • models/provider.rs (97行)
  • models/dashboard.rs (74行)
  • services/provider_manager.rs (166行)
  • services/dashboard_manager.rs (181行)
  • utils/auto_startup.rs (345行)
  • ProviderManagementPage/ (前端,650+行)
  • DashboardPage/hooks/useDashboardProviders.ts (138行)

重构模块

  • DashboardPage/index.tsx(+323行,重构为 Provider Tabs 架构)
  • BasicSettingsTab.tsx(重写,-129行 +241行)
  • stats_commands.rs(适配供应商系统)

删除模块

  • ApplicationSettingsTab.tsx(冗余配置页面)

🧪 测试

自动化测试

  • npm run check 全部通过
  • ✅ ESLint/Clippy/Prettier/fmt 零警告
  • ✅ 所有 Rust 单元测试通过(199 tests)

手动测试

  • ✅ 供应商 CRUD 操作
  • ✅ Dashboard 供应商切换
  • ✅ 工具实例选择器切换
  • ✅ 全局配置迁移到供应商系统
  • ✅ Dashboard 状态迁移到独立文件
  • ✅ 设置页面层级调整
  • ✅ 开机自启动功能(Windows/macOS/Linux)

🔧 架构改进

SOLID 原则

  • 单一职责:ProviderManager、DashboardManager 各司其职
  • 开闭原则:供应商系统易扩展,不影响现有代码
  • 接口隔离:Provider、Dashboard 数据模型独立

DRY 原则

  • 消除双数据流:全局配置 vs 供应商配置
  • 统一凭证管理:所有 API Key 统一到供应商系统

YAGNI 原则

  • 移除不再使用的全局账户配置
  • 删除冗余的 ApplicationSettingsTab

🐛 Bug 修复

  1. 实例选择器不生效 (DashboardToolCard.tsx)

    • 问题:类型不匹配(期望对象,实际是字符串)
    • 修复:更新 props 类型为 instanceSelection?: string
  2. 自启动模块类型错误 (auto_startup.rs)

    • 问题:错误的 AppError 类型导入
    • 修复:使用 anyhow::Result 替代

⚠️ 风险评估

低风险

  • 数据迁移:自动迁移,带回滚机制
  • 向后兼容:保留全局统计 API
  • 测试覆盖:充分的单元测试和手动验证

中等风险

  • 配置文件变更:新增 providers.jsondashboard.json
    • 缓解措施:自动迁移 + 错误处理
  • 前端架构调整:Dashboard 和设置页面重构
    • 缓解措施:充分测试,保持 API 兼容

📝 后续计划

  • 补充前端 E2E 测试
  • 完善供应商配额管理功能
  • 优化 Dashboard 性能(大量供应商场景)
  • 添加供应商导入/导出功能

📸 截图

(建议补充以下截图)

  • 供应商管理页面
  • Dashboard 供应商切换
  • 系统设置页面

测试清单

  • 本地 npm run check 通过
  • 所有 Rust 测试通过
  • 手动验证核心功能
  • 代码符合 SOLID/DRY/YAGNI 原则
  • Commit message 遵循 Conventional Commits 规范

## 功能概述

实现完整的供应商(Provider)配置管理系统,支持多供应商账号管理、工具实例选择、用户配额与用量统计查询。

## 后端实现

### 数据模型(models/provider.rs)
- Provider:供应商实体(id、name、website_url、user_id、access_token 等)
- ToolInstanceSelection:工具实例选择记录(tool_id、instance_type)
- ProviderStore:JSON 存储结构(version、providers、tool_instances)

### 核心服务(services/provider_manager.rs)
- ProviderManager:供应商 CRUD 管理器,使用 DataManager 持久化
- 存储路径:~/.duckcoding/providers.json
- 支持 LRU 缓存,读写自动刷新缓存

### Tauri 命令(commands/provider_commands.rs)
- list_providers:列出所有供应商
- create_provider/update_provider/delete_provider:增删改操作
- get_tool_instance_selection/set_tool_instance_selection:实例选择管理
- validate_provider_config:配置验证

### 数据迁移(migrations/global_to_providers.rs)
- 从 GlobalConfig.user_id/access_token 迁移到 providers.json
- 自动创建名为 "DuckCoding" 的默认供应商
- 向后兼容,迁移后保留 GlobalConfig 旧字段(标记废弃)

### 统计查询增强(commands/stats_commands.rs)
- get_user_quota:查询指定供应商的用户配额
- get_usage_stats:查询指定供应商的用量统计
- 新增 provider_id 参数支持多账号查询

## 前端实现

### 供应商管理页面(ProviderManagementPage/)
- index.tsx:页面主体,供应商列表 + CRUD 操作
- ProviderFormDialog:创建/编辑表单(name、website、user_id、token)
- DeleteConfirmDialog:删除确认对话框
- useProviderManagement:Hook 封装列表加载、增删改逻辑

### Dashboard 改造(DashboardPage/)
- ProviderTabs:供应商标签页切换组件
- useDashboardProviders:Hook 管理供应商状态和实例选择
- 集成配额卡片(QuotaCard)和统计卡片(TodayStatsCard)
- 支持按供应商切换查看配额和用量数据

### 组件增强
- DashboardToolCard:新增实例类型选择器(Local/WSL/SSH)
- TodayStatsCard:支持传入 provider_id 查询指定账号统计
- TrendChartDialog:趋势图对话框组件(占位,待实现)
- AppSidebar:新增"供应商管理"导航入口

### 类型定义(types/provider.ts)
- Provider、ProviderFormData、ToolInstanceSelection 完整类型
- Tauri 命令响应类型(CreateProviderResponse、ValidationResult 等)

### API 层(lib/tauri-commands/)
- provider.ts:封装所有供应商管理 Tauri 命令
- api.ts:getUserQuota/getUsageStats 新增 providerId 参数
- types.ts:UserQuotaResult、UsageStatsResult 类型定义

## 技术特性

- **数据隔离**:独立的 providers.json 存储,与全局配置解耦
- **默认迁移**:首次启动自动从 GlobalConfig 迁移数据
- **类型安全**:完整的 Rust + TypeScript 类型定义
- **缓存优化**:ProviderManager 内置 LRU 缓存,减少文件 I/O
- **实例管理**:支持工具级别的环境选择(Local/WSL/SSH)

## 影响范围

- 后端:11 个文件(+792 行)
- 前端:17 个文件(+1421 行)
- 迁移逻辑:自动执行,向后兼容
- 破坏性变更:无(旧 API 保留)

## 测试建议

- 迁移测试:确认 GlobalConfig → providers.json 数据完整性
- CRUD 测试:创建/更新/删除供应商,验证 JSON 持久化
- 实例选择:切换工具实例类型,确认状态同步
- 多账号测试:创建多个供应商,验证配额查询隔离性
问题修复:
- 实例选择器选择后立即回退的问题
- 切换实例后版本信息未更新的问题

核心改动:
1. 数据结构重构(instance_type → instance_id)
   - 后端:ToolInstanceSelection 改为存储唯一 instance_id
   - 前端:直接使用 instance_id 匹配,消除类型模糊查找
   - 支持同类型多实例的精确识别

2. 实例版本动态显示
   - DashboardToolCard 接收 toolInstances 参数
   - 根据 instanceSelection.instance_id 查找当前实例
   - 优先显示实例真实版本而非工具统一版本

3. Dashboard UI 重构
   - 固定显示 3 个工具(claude-code/codex/gemini-cli)
   - 未安装工具显示占位卡片 + "前往安装"按钮
   - 实例选择器仅已安装工具显示
   - 添加"工具状态"和"供应商与用量统计"分段标题

4. 导航增强
   - 新增 navigate-to-config 事件支持
   - 工具配置按钮跳转到工具管理页

5. 供应商管理优化
   - 移除 ProviderTabs 的"默认"标签(避免混淆)
   - ProviderFormDialog 添加获取凭证的帮助提示

技术细节:
- 遵循 DRY 原则:统一 instance_id 查找逻辑
- 类型安全:补充占位对象 mirrorIsStale/mirrorVersion 字段
- 架构清晰:保持三层架构(Commands → Services → Utils)

测试:所有代码检查通过(ESLint + Clippy + Prettier + fmt)
## 架构变更

### 后端(Rust)
- 新增 `models/dashboard.rs`:DashboardStore 数据模型
  - `tool_instance_selections: HashMap<String, String>` - 工具实例选择
  - `selected_provider_id: Option<String>` - 选中的供应商 ID
- 新增 `services/dashboard_manager.rs`:DashboardManager 服务
  - 提供实例选择和供应商状态的 CRUD 接口
  - 实现缓存机制,持久化到 `~/.duckcoding/dashboard.json`
- 新增 `commands/dashboard_commands.rs`:4 个 Tauri 命令
  - get/set_tool_instance_selection
  - get/set_selected_provider_id
- 更新 `main.rs`:注册 DashboardManagerState 和命令
- 清理 `ProviderStore` 和 `ProviderManager` 中的实例选择逻辑

### 前端(TypeScript/React)
- 新增 `lib/tauri-commands/dashboard.ts`:Dashboard API 包装器
- 重构 `useDashboardProviders` Hook
  - 数据简化:从 `Record<string, ToolInstanceSelection>` 改为 `Record<string, string>`
  - 函数签名简化:`setInstanceSelection(toolId, instanceId)` 替代传递对象
- 修复 `DashboardToolCard.tsx` 类型不匹配 bug
  - 删除已废弃的 `ToolInstanceSelection` 类型引用
  - 修改 prop 和读取逻辑以使用字符串类型
- 新增 `DashboardPage/index.tsx` 供应商 Tab 持久化
  - 初始化时从后端加载 `selectedProviderId`
  - 切换时自动保存到后端
- 删除 `types/provider.ts` 中的 `ToolInstanceSelection` 接口

## 设计原则
- 职责分离:Dashboard 状态独立于 Provider 数据
- 数据简化:直接存储 instance_id 字符串,无需对象包装
- 架构清晰:每个模块职责明确,修改影响范围可控

## 质量保证
- ✅ 所有 ESLint/Clippy/Prettier/fmt 检查通过
- ✅ 遵循 SOLID、KISS、DRY、YAGNI 原则
## 变更内容

### 1. 移除冗余的 DuckCoding 账户配置
- 删除旧的 BasicSettingsTab(包含 user_id/system_token 字段)
- 删除 ApplicationSettingsTab.tsx
- 从 useSettingsForm hook 中移除所有账户相关状态和逻辑
- 从 SettingsPage 移除账户字段的解构和传递

**原因**: 引入供应商系统后,每个 Provider 已独立管理凭证,全局账户配置不再需要

### 2. 优化设置页面层级结构
- 将 ApplicationSettingsTab 内容重构为新的 BasicSettingsTab
- 修改 Tab 标签:「基本设置」→「系统设置」
- 移除「应用设置」Tab,整合到「系统设置」
- 内部分两个 section:
  - 「基本设置」section:开机自启动
  - 「开发者设置」section:单实例模式

### 3. 优化组件设计
- BasicSettingsTab 改为自包含组件(无 props,内部管理状态)
- 保存按钮仅在「代理设置」Tab 显示(系统设置内部自管理)
- useSettingsForm hook 职责缩减为仅管理代理设置

## 架构改进

- **SOLID - SRP**: BasicSettingsTab 单一职责,useSettingsForm 职责更清晰
- **DRY**: 消除双数据流(全局配置 vs 供应商配置)
- **YAGNI**: 移除不再使用的账户配置代码

## 测试

✅ npm run check 全部通过
✅ ESLint/Clippy/Prettier/fmt 零警告
@github-actions
Copy link

github-actions bot commented Jan 3, 2026

本评论会随各平台任务完成自动更新:
如首轮 npm run check 失败,请在本地执行 npm run check:fixnpm run check 并提交修复 commit。
如 fix 仍失败,请在本地排查并确保 npm run check 通过后再提交。
跨平台差异若无法复现,请复制日志交给 AI 获取排查建议。

平台 结果 明细 日志包 运行链接
ubuntu-22.04 ✅ 直接通过 check=success / fix=skipped / recheck=skipped pr-check-ubuntu-22.04 日志
windows-latest ✅ 直接通过 check=success / fix=skipped / recheck=skipped pr-check-windows-latest 日志
macos-14 (arm64) ✅ 直接通过 check=success / fix=skipped / recheck=skipped pr-check-macos-arm64 日志
macos-15 (x64) ✅ 直接通过 check=success / fix=skipped / recheck=skipped pr-check-macos-x64 日志

This comment auto-updates as each platform finishes:
If the first npm run check fails, run locally: npm run check:fixnpm run check and commit the fix.
If fix still fails, investigate locally and ensure npm run check passes before committing.
If cross-platform issues can't be reproduced, copy logs to an AI for hints.

Platform Status Detail Artifact Run
ubuntu-22.04 ✅ Passed check=success / fix=skipped / recheck=skipped pr-check-ubuntu-22.04 日志
windows-latest ✅ Passed check=success / fix=skipped / recheck=skipped pr-check-windows-latest 日志
macos-14 (arm64) ✅ Passed check=success / fix=skipped / recheck=skipped pr-check-macos-arm64 日志
macos-15 (x64) ✅ Passed check=success / fix=skipped / recheck=skipped pr-check-macos-x64 日志

## 修改内容

### 后端类型定义修复
1. **models/config.rs** - 将 user_id 和 system_token 改为 Option<String> 类型
   - 添加 #[serde(default)] 属性确保反序列化兼容性
   - 添加废弃注释说明由供应商系统管理

2. **commands/config_commands.rs** - 更新 generate_api_key_for_tool 命令
   - 使用 .as_ref().ok_or() 处理 Option 类型
   - 提供清晰的错误提示建议使用供应商管理功能

3. **migrations/global_to_providers.rs** - 优化迁移脚本
   - 使用 .is_some_and() 替代 .map_or() (Clippy 建议)
   - 使用 .unwrap_or_default() 安全提取值

### 测试代码修复
修复所有测试中的 GlobalConfig 实例化:
- **core/http.rs** - 2 个测试函数
- **services/proxy/proxy_service.rs** - 3 个测试函数
- **services/migration_manager/manager.rs** - update_config_version 函数
- **commands/onboarding.rs** - create_minimal_config 函数

所有 String::new() 改为 Some(String::new()),所有字符串字面量改为 Some("...".to_string())

### 前端类型同步
- **lib/tauri-commands/types.ts** - 将 user_id 和 system_token 标记为可选字段

## 技术细节

### Rust Option 类型处理
- 字段定义:`pub user_id: Option<String>`
- 判空检查:`.is_some_and(|s| !s.is_empty())`
- 值提取:`.as_ref().ok_or("错误提示")?`
- 默认值:`.unwrap_or_default()`

### 向后兼容性
- #[serde(default)] 确保旧配置文件可以正常加载(缺失字段自动填充 None)
- 迁移脚本自动处理 Option 类型,无需手动干预

## 测试结果

✅ 所有 npm run check 检查通过:
- ✅ AI Agent 配置检查
- ✅ 前端 ESLint 检查
- ✅ 后端 Rust Clippy 检查
- ✅ 前端 Prettier 检查
- ✅ 后端 Rust fmt 检查

## 关联 PR

修复 PR DuckCoding-dev#75 中的 CI 编译错误
@DuckCoding-dev DuckCoding-dev merged commit 7391462 into DuckCoding-dev:main Jan 3, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants