docs: Windows 迁移计划修订——SwiftUI 留作 Mac 分支,Windows 侧用 Tauri 2 做 GUI

- 结论先行与依赖清单:GUI 行改为「SwiftUI(App/)=Mac 分支保留;新建 App.Win/(Tauri 2 + 内嵌 Vue3)」
- 阶段 4 改为「Windows GUI 分支」:三方案选型(推荐 Tauri 2)、Mac/Win 代码组织、
  Windows 特性与 macOS(SwiftUI) 的等价物对照表
- 速查表与落地形态同步更新
This commit is contained in:
FinDataHub committed 2026-10-07 00:49:14 +08:00
1 parent 73875acc99
commit 9138904cad
4 files changed
+263 -16

No files matched your search

+18
View File
@@ -6,3 +6,21 @@
- 盘点:20 个 .sh、31 个 launchd plist、258 py、1804 前端文件;
macOS 专属 Python 点集中在约 8 文件(/tmp/、Library/、signal、start_new_session、libproc)。
- 唯一硬阻塞候选:akshare 依赖 py_mini_racer(V8 预编译),须阶段 0 在 Windows 验证 3.12 wheel。
## 基础依赖安装脚本(阶段 0 前置,皇上要求先写、待 Windows 端做好再装)
- 产出 `Scripts/windows/install-deps.ps1` + `install-deps.cmd`(双击入口,fltmc 自提权)。
- 覆盖依赖:VC++ Redist 2015-2022 x64(py_mini_racer/numpy 等原生扩展运行时,注册表键 `HKLM:\Software\Microsoft\VisualStudio\14.0\VC\Runtimes\x64` 检测)→ Git(Git.Git,含 Git Bash) → Python3.12(Python.Python.3.12,InstallAllUsers=1 PrependPath=1) → Node LTS(OpenJS.NodeJS.LTS) → uv(pip 装,清华镜像) → pnpm(npm -g,npmmirror) → MySQL(默认仅检测引导,`-IncludeMySQL` 才 winget 静默装,因 MSI 静默无法可靠设 root 密码)。
- 编码纪律(沿用 nodeagent 沉淀):ps1 带 BOM+CRLF;cmd 纯 ASCII+CRLF;已用 python 脚本处理并校验(BOM=True/裸LF=0/cmd 无非ASCII)。
- 决策:全部系统级安装 + cmd 自提权(winget 装 Node/Python 系统级需管理员;用户级装会落到管理员账户 LOCALAPPDATA,踩过坑)。uv 不用官方 install.ps1(自提权后 %USERPROFILE% 错位),改用 `python -m pip install uv` 确定性最高。
## 待办:远程在 Windows 真机跑安装脚本(皇上已拍板「等回同一局域网再远程跑」)
- 皇上要求用 nodeagent 远程执行 install-deps 脚本;2026-10-07 00:47 实测连不上:`E_TIMEOUT wss://192.168.0.21:8765` + ping 100% 丢包。
- 根因:Mac 当前网段 `10.70.144.99`(不在家 `192.168.0.x`),无 Tailscale/组网隧道,nodeagent 点对点直连不可达。
- 约定:皇上回到 192.168.0.x 局域网(连家里 WiFi)后告知臣,臣再远程执行。
- 执行链路(已备好):`nodeagent push` 把 `Scripts/windows/install-deps.ps1`+`.cmd` 推到 Windows → `nodeagent exec`/`bg` 运行;先 `nodeagent exec "fltmc"` 探测 agent 是否管理员权限(决定能否静默装系统级软件、是否需 UAC)。
## Windows 迁移计划修订(皇上决策)
- SwiftUI App 不迁移、不删除,保留 `App/` 作 FinDataHub 的 **Mac 应用分支**。
- Windows 侧因地制宜新做 GUI:选 **Tauri 2(Rust 壳 + 系统 WebView2)内嵌已有 Vue3 前端**,
新建 `App.Win/` 目录,与 `App/`(Mac 分支)并存;理由:复用 1804 前端、Rust 契合技术栈、
体积小、Windows 特性(托盘/自启/通知/单实例/DPAPI)有现成插件。
+39 -16
View File
@@ -5,16 +5,16 @@
## 结论先行
**最大的事实:SwiftUI App 无法迁移到 Windows。** 本仓库的 `App/`(77 个 Swift 文件,`Package.swift` 声明 `platforms: [.macOS(.v13)]`)是 macOS 原生应用,Windows 没有 SwiftUI 运行时。所以"Windows 为主平台"的正确策略不是"把 App 搬过去",而是**角色重划**:
**最大的事实:SwiftUI App 无法迁移到 Windows。** 本仓库的 `App/`(77 个 Swift 文件,`Package.swift` 声明 `platforms: [.macOS(.v13)]`)是 macOS 原生应用,Windows 没有 SwiftUI 运行时。**处置:`App/` 保留为 FinDataHub 的 Mac 应用分支**,不迁移、不删除;Windows 侧因地制宜、按 Windows 特性新做一套原生 GUI 应用。
| 职责 | 现状(macOS) | Windows 目标 |
|---|---|---|
| 数据采集管线 | tushare/akshare fetcher(Python) | ✅ 直接迁移(已高度可移植) |
| 后端服务 | kuaixun-service(FastAPI,14 端点) | ✅ 直接迁移 |
| 用户界面 | **SwiftUI App(macOS 专属)** | **改用已有的 Vue3 前端(kuaixun-frontend,1804 文件)** |
| **GUI 应用** | **SwiftUI App(`App/`,Mac 分支,保留)** | **新建 Windows 分支:Tauri 2 原生壳 + 内嵌已有 Vue3 前端** |
| 调度 | launchd(31 个 plist) | Windows 任务计划程序 / NSSM |
| 数据库 | Homebrew MySQL | MySQL Installer / Docker |
| 桌面客户端(可选) | SwiftUI | 冻结或降级为 macOS-only 客户端,连同一后端 |
| 凭据 | macOS Keychain | Windows DPAPI / 凭据管理器 |
**好消息**:数据层已高度可移植——258 个 Python 文件里,macOS 专属点集中在 **约 8 个文件**(硬编码 `Library/*`、`/tmp/` 路径、信号处理、`start_new_session`、libproc 性能工具)。核心逻辑(`findatahub_core`、两个 fetcher、`kuaixun-service` 的 `app/`)用的是 `pathlib` + `os.environ` 配置 + `uv`,跨平台没问题。
@@ -24,7 +24,7 @@
| 类别 | 具体内容 | 数量/位置 | macOS 专属性 | Windows 替代 |
|---|---|---|---|---|
| **GUI 应用** | SwiftUI App(`App/`) | 77 Swift 文件 | 完全专属 | Vue3 前端(已存在) |
| **GUI 应用** | SwiftUI App(`App/`) | 77 Swift 文件 | 完全专属(Mac 分支,保留) | **Tauri 2 + 内嵌 Vue3**(新建 Windows 分支 `App.Win/`) |
| **Shell 脚本** | `build-app.sh` `bundle-python.sh` `mysql_toggle.sh` `*.sh` 同步脚本 | 20 个 | `#!/bin/bash`、`set -eo pipefail`、`rsync` | PowerShell / WSL bash / 重写 |
| **调度** | `com.{tushare,akshare,ths}.{daily,weekly,monthly}` plist | 31 个 plist | 完全专属(launchd) | 任务计划程序 / NSSM |
| **Python 路径** | `Path.home()/"Library/Logs/FinDataHub"`、`Library/Preferences/...plist`、`/tmp/findatahub-*.pid/heartbeat` | 8 文件 | 专属 | 统一 `platformdirs` / `%LOCALAPPDATA%` / `tempfile` |
@@ -126,16 +126,39 @@ Windows 无 SwiftUI。**必须改用 Vue 前端**,否则"主要工作平台"
**风险**:任务计划程序的权限/网络上下文(需登录还是无需登录);代理(v2rayN 需常驻)。
**验证**:手动触发一次定时任务,日志/报告与 macOS 一致;跨一次真实交易日无人工介入。
### 阶段 4:SwiftUI App 处置(决策 + 1–2 天)
**目标**:明确 macOS App 的去留,把"工作平台"收敛到 Windows。
### 阶段 4:Windows GUI 分支(技术选型 + 搭建,3–7 天)
**目标**:在 Windows 上落一个原生 GUI 应用,复用已有 Vue3 前端;SwiftUI App 保留为 Mac 分支。
三个选项(给框架,皇上定):
- **A(推荐)冻结**:SwiftUI App 停止新功能,仅留作 macOS 上的可选客户端,连同一后端;主开发全切 Windows + Web 前端。
- **B 降级**:SwiftUI 只保留启动/看板壳,其余全由 Web 前端承担。
- **C 移除**:删 `App/`,彻底以 Web 为唯一界面(最彻底,但损失 SwiftUI 已有的看板/能耗优化成果)。
**技术选型(因地制宜,按 Windows 特性)**:
**风险**:A/B 需维护双客户端的一致性;C 不可逆。
**验证**:Windows 前端功能 ≥ 原 SwiftUI 关键功能(看板、搜索、收藏、快讯推送)。
| 方案 | 壳技术 | 界面 | 评价 |
|---|---|---|---|
| **A(推荐)Tauri 2** | Rust + 系统 WebView2 | 内嵌 kuaixun-frontend(1804 文件零改写) | 体积 5–15MB(对比 Electron 100MB+);Rust 契合皇上技术栈;WebView2 是 Windows 自带 |
| B WPF + WebView2 | C#/.NET 8 | 同上内嵌 Vue | 成熟稳定,但用 C# 而非 Rust,与现有 Rust 加速路线不一致 |
| C 纯原生 WinUI 3 | C# | 重写界面 | 最"原生",但放弃已有 Vue 成果、工作量大 |
选 A 的理由:① 复用 1804 文件的 Vue 前端,界面零重写;② Rust 与项目"Python 主力 + Rust 加速"路线一致;③ 用 Windows 特性(托盘、开机自启、通知、单实例、DPAPI)都有现成 Tauri 插件,正好对应原 SwiftUI 用的 Keychain/launchd/托盘/通知。
**代码组织(Mac 分支 / Win 分支并存)**:
```
App/ # Mac 分支:SwiftUI(保留,独立演进,连同一后端)
App.Win/ # Win 分支:Tauri 2(src-tauri/ Rust 壳 + frontendDist → kuaixun-frontend/dist)
Services/… # 公共:数据管线 + FastAPI 后端 + Vue 前端(两分支共享)
```
**Windows 特性落地对照**(原 SwiftUI 用 macOS 特性做的事 → Windows 等价物):
| macOS(SwiftUI) | Windows(Tauri) |
|---|---|
| Keychain 存 MySQL 口令 | DPAPI(keyring crate / windows-rs) |
| launchd 定时 | 任务计划程序(Rust 调注册表/COM)或 NSSM |
| 菜单栏托盘 | Tauri tray-icon |
| 本地通知 | tauri-plugin-notification |
| 单实例 | tauri-plugin-single-instance |
| 开机自启 | tauri-plugin-autostart |
**风险**:WebView2 运行时依赖(Win10/11 自带,Win7/8 需装);Tauri 需 Rust 工具链;两分支界面一致性。
**验证**:Windows 分支能启动、托盘可常驻、开机自启生效、通知可达;界面功能 ≥ 原 SwiftUI 关键功能(看板、搜索、收藏、快讯推送)。
### 阶段 5:平台专属代码收敛(持续,1–2 周)
**目标**:消除残余 macOS 假设,让 `sys.platform == "win32"` 也能全绿。
@@ -157,7 +180,7 @@ Windows 无 SwiftUI。**必须改用 Vue 前端**,否则"主要工作平台"
| 1 | 路径/编码/DB 连接 | 日更报告与 macOS 行数一致 |
| 2 | 信号/进程封装、端口 | 浏览器全功能可用 |
| 3 | 调度权限/代理常驻 | 定时任务无人介入跑通 |
| 4 | 双客户端一致性 | 前端覆盖 SwiftUI 关键功能 |
| 4 | WebView2 运行时、Rust 工具链、双分支一致性 | Windows 分支托盘/自启/通知可用,界面 ≥ SwiftUI 关键功能 |
| 5 | 改动面广回归 | CI windows-latest 全绿 |
---
@@ -165,10 +188,10 @@ Windows 无 SwiftUI。**必须改用 Vue 前端**,否则"主要工作平台"
## 五、落地形态(Windows 为主平台后的最终态)
- **开发机**:Windows,`uv` + `Git Bash` + VS Code,全栈开发(Python 管线 / FastAPI / Vue)。
- **运行态**:MySQL(服务)+ 后端(NSSM 服务)+ 采集器(NSSM 服务)+ 前端(静态 + 反向代理),全部开机自启。
- **运行态**:MySQL(服务)+ 后端(NSSM 服务)+ 采集器(NSSM 服务)+ Windows GUI(Tauri,内嵌前端),全部开机自启。
- **调度**:任务计划程序三个定时任务。
- **界面**:浏览器 `localhost:8100`(或局域网访问)。
- **macOS App**:按阶段 4 决策冻结/降级/移除,不再作为主开发目标。
- **界面**:Windows GUI 应用(Tauri,托盘常驻);浏览器 `localhost:8100` 作降级访问。
- **macOS App**:`App/` 作为 Mac 分支保留,独立演进,连同一后端,不再作为主开发目标。
---
+17
View File
@@ -0,0 +1,17 @@
@echo off
setlocal
rem FinDataHub Windows deps installer - double-click entry point
rem Pure ASCII only (do not put non-ASCII text in this file)
fltmc >nul 2>&1
if errorlevel 1 (
echo Requesting administrator privileges...
powershell -NoProfile -Command "Start-Process -FilePath '%~f0' -Verb RunAs"
exit /b
)
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0install-deps.ps1"
echo.
echo Install finished. If a program shows as [--], close this window and reopen.
pause
+189
View File
@@ -0,0 +1,189 @@
# FinDataHub Windows 端 · 基础依赖安装脚本
# 仅安装运行 FinDataHub 所需的「基础依赖软件」,不安装 FinDataHub 本体。
# 依赖清单:Git / Python 3.12 / Node.js LTS / uv / pnpm / MySQL(默认仅检测引导)
#
# 用法:
# 双击 install-deps.cmd(自动请求管理员权限)
# 或 PowerShell(管理员):powershell -ExecutionPolicy Bypass -File install-deps.ps1
# 若要静默安装 MySQL: powershell -ExecutionPolicy Bypass -File install-deps.ps1 -IncludeMySQL
[CmdletBinding()]
param(
[switch]$IncludeMySQL # 显式传入才尝试静默安装 MySQL(默认仅检测 + 引导)
)
$ErrorActionPreference = 'Continue'
# ---------- 日志辅助 ----------
function Write-Step { param([string]$m) Write-Host "`n==> $m" -ForegroundColor Cyan }
function Write-Ok { param([string]$m) Write-Host " [OK] $m" -ForegroundColor Green }
function Write-Warn { param([string]$m) Write-Host " [!] $m" -ForegroundColor Yellow }
function Write-Err { param([string]$m) Write-Host " [X] $m" -ForegroundColor Red }
function Test-Cmd([string]$name) {
return [bool](Get-Command $name -ErrorAction SilentlyContinue)
}
# ---------- winget ----------
function Get-Winget {
if (Test-Cmd 'winget') { return $true }
Write-Warn '未检测到 winget(Windows 10 需先安装「应用安装程序 / App Installer」)。'
Write-Warn '请打开 Microsoft Store 搜索安装「应用安装程序」,或参考 https://aka.ms/getwinget'
return $false
}
function Install-WingetPkg([string]$id, [string]$override) {
Write-Step "安装 $id"
$pkgArgs = @('install', '--id', $id, '-e', '--source', 'winget',
'--silent', '--accept-package-agreements', '--accept-source-agreements')
if ($override) { $pkgArgs += '--override'; $pkgArgs += $override }
& winget @pkgArgs
if ($LASTEXITCODE -eq 0) {
Write-Ok "$id 安装完成"
} else {
Write-Err "$id 安装失败(退出码 $LASTEXITCODE),可手动运行 winget search $id 确认包名后重试"
}
}
# ---------- 刷新当前会话 PATH(安装后本会话内验证用) ----------
function Refresh-Path {
$add = @(
'C:\Program Files\Git\cmd',
'C:\Program Files\Python312',
'C:\Program Files\Python312\Scripts',
'C:\Program Files\nodejs'
)
foreach ($p in $add) {
if ((Test-Path $p) -and ($env:Path -notlike "*$p*")) {
$env:Path = "$p;$env:Path"
}
}
}
# ---------- 各依赖软件 ----------
function Ensure-Git {
if (Test-Cmd 'git') { Write-Ok "git 已存在: $(git --version)"; return }
Install-WingetPkg 'Git.Git'
}
function Ensure-Python312 {
if (Test-Cmd 'python') {
$v = python --version 2>&1
if ($v -match '3\.12') { Write-Ok "Python 已存在: $v"; return }
Write-Warn "已装 Python 但非 3.12(当前: $v),FinDataHub 要求 3.12,继续安装 3.12"
}
# InstallAllUsers=1 装到 C:\Program Files\Python312;PrependPath=1 写入系统 PATH
Install-WingetPkg 'Python.Python.3.12' '/quiet InstallAllUsers=1 PrependPath=1 Include_test=0 Include_launcher=0'
Refresh-Path
if (Test-Cmd 'python') { Write-Ok "Python: $(python --version)" }
else { Write-Err 'Python 3.12 安装后未能识别,请关闭本窗口重开后再验证' }
}
function Ensure-NodeLTS {
if (Test-Cmd 'node') { Write-Ok "Node 已存在: $(node --version)"; return }
Install-WingetPkg 'OpenJS.NodeJS.LTS'
Refresh-Path
if (Test-Cmd 'node') { Write-Ok "Node: $(node --version)" }
else { Write-Err 'Node 安装后未能识别,请关闭本窗口重开后再验证' }
}
function Ensure-Uv {
if (Test-Cmd 'uv') { Write-Ok "uv 已存在: $(uv --version)"; return }
Write-Step '安装 uv(pip 安装,走清华镜像)'
python -m pip install -i https://pypi.tuna.tsinghua.edu.cn/simple uv
if ($LASTEXITCODE -eq 0) { Write-Ok "uv: $(uv --version)" }
else { Write-Err 'uv 安装失败' }
}
function Ensure-Pnpm {
if (Test-Cmd 'pnpm') { Write-Ok "pnpm 已存在: $(pnpm --version)"; return }
Write-Step '安装 pnpm(npm 全局安装,走 npmmirror 镜像)'
npm install -g pnpm --registry=https://registry.npmmirror.com
if ($LASTEXITCODE -eq 0) { Write-Ok "pnpm: $(pnpm --version)" }
else { Write-Err 'pnpm 安装失败' }
}
function Ensure-MySQL {
$hasCmd = Test-Cmd 'mysql'
$svc = Get-Service -Name '*mysql*' -ErrorAction SilentlyContinue
if ($hasCmd -or $svc) {
Write-Ok "MySQL 已检测到(服务: $($svc.Name -join ', '))"
return
}
Write-Warn '未检测到 MySQL(FinDataHub 数据落地依赖)'
if ($IncludeMySQL) {
Install-WingetPkg 'Oracle.MySQL'
} else {
Write-Warn 'MySQL 静默安装无法可靠设置 root 密码,为避免留下无密码 root,默认不自动装。'
Write-Warn '请手动安装 MySQL 8.x 并设置 root 密码;或带 -IncludeMySQL 重试:'
Write-Warn ' powershell -ExecutionPolicy Bypass -File install-deps.ps1 -IncludeMySQL'
}
}
# ---------- VC++ 运行库(py_mini_racer 等原生扩展必需) ----------
function Ensure-VCRedist {
# 检测 VC++ 2015-2022 x64 运行时注册表键
$key = 'HKLM:\SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64'
$installed = $false
if (Test-Path $key) {
$v = (Get-ItemProperty -Path $key -Name 'Installed' -ErrorAction SilentlyContinue).Installed
if ($v -eq 1) { $installed = $true }
}
if ($installed) { Write-Ok 'VC++ Redistributable (2015-2022 x64) 已存在'; return }
Install-WingetPkg 'Microsoft.VCRedist.2015+.x64'
}
# ---------- 汇总 ----------
function Show-Summary {
Write-Host "`n" ('=' * 60) -ForegroundColor Cyan
Write-Host ' FinDataHub 基础依赖检测结果' -ForegroundColor Cyan
Write-Host ('=' * 60) -ForegroundColor Cyan
$items = @(
@{ n = 'git' ; ok = (Test-Cmd 'git') },
@{ n = 'python' ; ok = (Test-Cmd 'python') },
@{ n = 'node' ; ok = (Test-Cmd 'node') },
@{ n = 'npm' ; ok = (Test-Cmd 'npm') },
@{ n = 'pnpm' ; ok = (Test-Cmd 'pnpm') },
@{ n = 'uv' ; ok = (Test-Cmd 'uv') },
@{ n = 'mysql' ; ok = (Test-Cmd 'mysql') }
)
foreach ($it in $items) {
$ver = ''
if ($it.ok) { $ver = (& $it.n --version 2>$null | Out-String).Trim() }
$mark = if ($it.ok) { '[OK]' } else { '[--]' }
$color = if ($it.ok) { 'Green' } else { 'Yellow' }
Write-Host (" {0} {1,-8} {2}" -f $mark, $it.n, $ver) -ForegroundColor $color
}
Write-Host ('=' * 60) -ForegroundColor Cyan
Write-Host ' 提示:若某软件 [--],请关闭本窗口重开后再单独确认。' -ForegroundColor DarkGray
Write-Host ' MySQL 若 [--],请按上文引导手动安装并设置 root 密码。' -ForegroundColor DarkGray
Write-Host ('=' * 60) -ForegroundColor Cyan
}
# ---------- 主流程 ----------
Write-Host '============================================================' -ForegroundColor Cyan
Write-Host ' FinDataHub Windows 端 · 基础依赖安装' -ForegroundColor Cyan
Write-Host ' 仅安装基础依赖,不安装 FinDataHub 本体' -ForegroundColor DarkGray
Write-Host '============================================================' -ForegroundColor Cyan
$isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
if (-not $isAdmin) {
Write-Warn '当前非管理员权限,部分系统级软件(Node/Git/Python)可能安装失败。'
Write-Warn '建议关闭本窗口,改双击 install-deps.cmd 以自动提权。'
}
if (-not (Get-Winget)) {
Write-Err 'winget 不可用,请先安装 App Installer 后重试。'
return
}
Ensure-VCRedist
Ensure-Git
Ensure-Python312
Ensure-NodeLTS
Ensure-Uv
Ensure-Pnpm
Ensure-MySQL
Show-Summary
Write-Host "`n安装流程结束。" -ForegroundColor DarkGray