diff --git a/.workbuddy/memory/2026-10-07.md b/.workbuddy/memory/2026-10-07.md index 56a4999..5a11934 100644 --- a/.workbuddy/memory/2026-10-07.md +++ b/.workbuddy/memory/2026-10-07.md @@ -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)有现成插件。 diff --git a/Docs/FinDataHub-Windows迁移计划.md b/Docs/FinDataHub-Windows迁移计划.md index 79a7be5..4a98d33 100644 --- a/Docs/FinDataHub-Windows迁移计划.md +++ b/Docs/FinDataHub-Windows迁移计划.md @@ -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 分支保留,独立演进,连同一后端,不再作为主开发目标。 --- diff --git a/Scripts/windows/install-deps.cmd b/Scripts/windows/install-deps.cmd new file mode 100644 index 0000000..cfbb9b7 --- /dev/null +++ b/Scripts/windows/install-deps.cmd @@ -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 diff --git a/Scripts/windows/install-deps.ps1 b/Scripts/windows/install-deps.ps1 new file mode 100644 index 0000000..99c1d56 --- /dev/null +++ b/Scripts/windows/install-deps.ps1 @@ -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