# CTMS 桌面端第一阶段详细方案 日期:2026-06-30 ## 目标 第一阶段只交付 macOS 在线桌面客户端。桌面端使用 Tauri 承载现有 Vue/Vite 前端,连接已有 CTMS 服务端,不内嵌后端、不内嵌数据库、不做离线能力。 本阶段结束后,应能在 macOS 上启动 CTMS 桌面 App,配置 CTMS 服务端地址,完成登录,并执行现有 Web 端的常规在线业务流程。Web 端构建和现有 Docker/nginx 部署必须继续可用。 ## 范围边界 本阶段必须做: - 在 `frontend/` 内加入 Tauri 工程结构。 - 复用现有 Vue 3、Vite、Element Plus、Pinia、Vue Router、Axios 前端代码。 - 让桌面端连接已有 CTMS 后端公开入口,推荐连接 nginx 入口而不是直接连接后端容器。 - 提供桌面端服务端地址配置能力。 - 抽出最小运行时适配层,避免业务页面直接依赖 Tauri API。 - 保持认证、权限、审计和业务数据持久化由 FastAPI 后端裁决。 - 产出 macOS 开发构建流程和后续签名、公证、DMG 发布路径。 本阶段明确不做: - 离线登录、离线浏览、离线草稿、离线队列、离线同步。 - 本地 PostgreSQL、SQLite、IndexedDB 业务数据缓存或本地 API 镜像。 - 内嵌 Python/FastAPI 后端服务。 - 重写 CTMS 业务页面。 - Windows 安装包交付。 - 自动更新实现。 - token/session 安全存储迁移;该事项进入第二阶段。 ## 当前代码影响点 现状: - `frontend/src/api/axios.ts` 和 `frontend/src/api/authClient.ts` 的 `baseURL` 都是 `/`。 - API 调用路径均为 `/api/v1/...`。 - Web 端依赖 nginx 同域反代 `/api`。 - Tauri 打包后页面运行在桌面 WebView 中,不能继续假设 `/api` 一定代表 CTMS 服务端。 - `frontend/src/utils/auth.ts` 当前使用 `localStorage` 保存 token。 - `frontend/src/session/sessionManager.ts` 依赖 `localStorage`、`sessionStorage` 和 `BroadcastChannel`。 第一阶段的核心改动是先解决“桌面端如何知道并访问服务端地址”,同时尽量不触碰业务页面。 ## 目标架构 ```mermaid flowchart LR A["Tauri macOS App"] --> B["Bundled Vue/Vite Frontend"] B --> C["Runtime Adapters"] C --> D["Configured CTMS Server URL"] D --> E["nginx / HTTPS entry"] E --> F["FastAPI backend"] F --> G["PostgreSQL"] ``` 桌面端只负责本地窗口、运行时配置和少量平台能力。业务数据仍走服务端 API,权限和审计仍由后端完成。 ## Tauri 工程方案 Tauri 目录放在现有前端工程内: ```text frontend/ src-tauri/ tauri.conf.json Cargo.toml src/ main.rs capabilities/ default.json ``` 建议新增脚本: ```json { "scripts": { "tauri": "tauri", "desktop:dev": "tauri dev", "desktop:build": "tauri build", "desktop:bundle:dmg": "tauri build -- --bundles dmg" } } ``` Tauri 配置方向: - `build.beforeDevCommand`: `npm run dev` - `build.beforeBuildCommand`: `npm run build` - `build.devUrl`: `http://localhost:5173` - `build.frontendDist`: `../dist` - 主窗口标题:`CTMS` - 主窗口初始尺寸建议:`1440x900` - 最小窗口尺寸建议:`1180x760` - bundle identifier 待最终确认,临时建议:`cn.huapont.ctms.desktop` 第一阶段不加载远程 Web UI。Tauri 只加载本地打包出的前端资源,远程访问仅限 Axios 访问配置好的 CTMS 服务端。 ## Vite 调整方案 当前 `vite.config.ts` 为 Docker 开发做了固定代理: - dev server 端口:`5173` - HMR client port:`8888` - `/api` 代理目标:`http://backend:8000` 为兼容 Web、Docker 和 Tauri,建议改成环境变量驱动: - `VITE_DEV_API_PROXY_TARGET`:默认 `http://backend:8000`。 - `VITE_HMR_CLIENT_PORT`:Docker/nginx 开发时设为 `8888`,桌面端开发不设置。 - `TAURI_DEV_HOST`:按 Tauri/Vite 推荐方式兼容 Tauri dev。 Vite 配置需要补充: - `server.strictPort: true`,避免 Tauri devUrl 与 Vite 实际端口不一致。 - `server.watch.ignored: ["**/src-tauri/**"]`,避免 Rust 目录变动触发不必要的前端监听。 - `clearScreen: false`,避免 Rust 编译错误被 Vite 清屏隐藏。 - `build.target` 按 Tauri 平台设置:Windows 使用 Chromium 目标,macOS 使用 Safari/WebKit 目标。 - `envPrefix` 保留 `VITE_`,并允许 `TAURI_ENV_*`。 ## 服务端地址配置 新增运行时适配层: ```text frontend/src/runtime/ platform.ts apiBaseUrl.ts desktopServerConfig.ts ``` 职责: - `platform.ts` - 判断当前是否运行在 Tauri。 - 判断目标平台是 Web、macOS 桌面端还是 Windows 桌面端。 - 业务页面不得直接读取 Tauri 全局对象。 - `desktopServerConfig.ts` - 保存和读取桌面端服务端地址。 - 第一阶段可继续使用 `localStorage` 保存服务器地址;这不是业务数据,也不是离线能力。 - key 建议:`ctms_desktop_server_url`。 - 仅接受 `http://localhost`、`http://127.0.0.1` 或 `https://...`。非本地生产服务不接受明文 HTTP。 - `apiBaseUrl.ts` - Web 端返回 `/`,保持现有同域 `/api` 行为。 - 桌面端返回配置的服务端 origin,例如 `https://ctms.example.com/`。 - 统一规范尾斜杠,避免拼接出错。 Axios 调整: - `frontend/src/api/axios.ts` 使用 `resolveApiBaseUrl()` 初始化 `baseURL`。 - `frontend/src/api/authClient.ts` 同步使用同一 baseURL。 - 当桌面端服务端地址变化时,需要更新两个 Axios 实例的 `defaults.baseURL`。 - 现有 API path 继续保持 `/api/v1/...`,不修改业务 API 文件。 ## 服务端配置入口 第一阶段需要一个轻量的桌面端服务端设置入口。 建议实现方式: - 桌面端启动时,如果没有服务端地址,则在登录页前展示服务端设置界面。 - 登录页提供“服务器设置”入口,允许修改当前服务端地址。 - 保存前调用 `${serverUrl}/health` 做连通性检查。 - 连通性检查失败时允许用户重新输入,不自动降级到离线模式。 - Web 端不显示桌面端服务器设置入口。 建议新增文件: ```text frontend/src/views/DesktopServerSettings.vue frontend/src/router/desktopGuard.ts frontend/src/runtime/desktopServerConfig.ts ``` 路由策略: - 保持现有 Web 路由结构。 - 桌面端未配置服务端时,将用户导向 `/desktop/server-settings`。 - `/login` 页面中允许打开服务器设置。 - Web 端访问 `/desktop/server-settings` 时重定向到 `/login` 或显示不可用状态。 ## 认证和会话策略 第一阶段保持现有认证机制: - 登录仍使用后端登录公钥和加密登录流程。 - token 仍通过 `frontend/src/utils/auth.ts` 存储在 `localStorage`。 - 会话超时、token keep-alive、401 refresh 仍沿用现有逻辑。 本阶段只允许做为 Tauri 接入所必需的最小改动: - API baseURL 可切换。 - 服务端地址变化时清理当前 token 和项目上下文,要求重新登录。 - 不引入桌面安全存储;该事项归入第二阶段。 ## Tauri 权限策略 第一阶段不需要文件系统、Shell、系统通知、自动更新或单实例插件。 权限原则: - 只启用主窗口运行所需的最小 core capability。 - 不开放宽泛文件系统权限。 - 不开放 shell 执行能力。 - 不开放远程页面访问 Tauri command 的能力。 - 如确实需要读取 App 版本或平台信息,优先通过窄适配层处理,并明确 capability。 第一阶段建议不新增自定义 Tauri command。服务端地址配置可以先由前端 `localStorage` 完成。 ## macOS 打包路径 开发构建: ```bash cd /Users/zcc/MyCTMS/ctms-dev/worktrees/ctms-desktop/frontend npm run desktop:dev ``` 生产构建: ```bash cd /Users/zcc/MyCTMS/ctms-dev/worktrees/ctms-desktop/frontend npm run desktop:build ``` DMG 构建: ```bash cd /Users/zcc/MyCTMS/ctms-dev/worktrees/ctms-desktop/frontend npm run desktop:bundle:dmg ``` 正式分发前需要: - Apple Developer 账号。 - macOS 代码签名证书。 - notarization 所需 App Store Connect API 或 Apple ID 凭据。 - 明确是否分发 DMG,第一阶段推荐 DMG。 第一阶段可以先产出未签名或 ad-hoc 签名的内部开发构建,但不能把它描述为正式可分发版本。 ## 实施步骤 1. 初始化 Tauri - 安装 `@tauri-apps/cli`。 - 在 `frontend/` 下生成 `src-tauri/`。 - 固定基础配置:app name、window title、bundle identifier、devUrl、frontendDist。 2. 调整 Vite - 引入 Tauri 兼容配置。 - 保持 Docker/nginx 开发代理不破坏。 - 使用环境变量控制代理和 HMR。 3. 新增运行时适配层 - 添加 `platform.ts`。 - 添加 `apiBaseUrl.ts`。 - 添加 `desktopServerConfig.ts`。 - 新增单元测试覆盖 URL 规范化、Web/Tauri 分支和非法 URL 拒绝。 4. 改造 API client - `axios.ts` 使用统一 baseURL。 - `authClient.ts` 使用统一 baseURL。 - 服务端地址变化后刷新 Axios baseURL。 - 保持 API path 和业务模块不变。 5. 添加桌面服务端设置界面 - 桌面端未配置服务端时拦截到设置页。 - 保存时校验 URL 和 `/health`。 - 修改服务端地址时清理当前登录态和项目上下文。 6. 最小 Tauri 权限 - 检查 `capabilities/default.json`。 - 移除第一阶段不需要的插件和权限。 - 不新增自定义 command,除非实现过程证明必要。 7. macOS 验证和文档 - 运行 Web 构建、类型检查、单元测试。 - 运行 Tauri dev。 - 验证 macOS 打包。 - 补充桌面端运行说明。 ## 验收清单 功能验收: - macOS 桌面 App 可以启动。 - 首次启动未配置服务端时进入服务器设置。 - 服务端地址保存前会校验 `/health`。 - 配置有效服务端后可以登录。 - 登录后能进入项目列表和项目工作区。 - 现有权限控制、401 refresh、会话超时行为与 Web 端一致。 - 切换服务端地址会清理当前登录态。 工程验收: - `npm run build` 通过。 - `npm run type-check` 通过。 - `npm run test:unit` 通过,或明确记录失败原因。 - `npm run desktop:dev` 可启动 macOS 窗口。 - `npm run desktop:build` 可完成构建。 - Web 端 Docker/nginx 访问不回归。 边界验收: - 没有新增本地业务数据缓存。 - 没有内嵌后端服务。 - 没有本地数据库。 - 没有离线队列或同步机制。 - Tauri API 没有散落在业务页面中。 - Tauri capability 没有开放第一阶段不需要的文件系统或 shell 权限。 ## 风险和决策点 - bundle identifier 需要正式确认,建议在第一阶段实现前由产品或组织负责人定稿。 - 桌面端连接公开 CTMS 服务时,生产环境应使用 HTTPS;本地开发可允许 localhost HTTP。 - 当前后端 CORS 是宽松配置。第一阶段可先不改后端,但正式分发前应评估是否收紧允许来源。 - token 第一阶段仍在 `localStorage`,这是有意识的阶段性选择;安全存储迁移必须排入第二阶段。 - macOS 正式分发需要 Apple Developer、签名和 notarization,不应等到最后一天处理。 ## 参考资料 - Tauri 创建项目与向现有前端加入 Tauri:https://tauri.app/start/create-project/ - Tauri + Vite 配置:https://tauri.app/start/frontend/vite/ - Tauri capabilities:https://tauri.app/security/capabilities/ - Tauri DMG 分发:https://tauri.app/distribute/dmg/ - Tauri macOS 签名与公证:https://tauri.app/distribute/sign/macos/ - Tauri updater 资料,供第二阶段使用:https://tauri.app/plugin/updater/