Skip to content

feat(windows): 提供托盘客户端与完整认证运行时 - #56

Merged
mel0nyrame merged 27 commits into
masterfrom
feat/windows-tray-client
Sep 28, 2026
Merged

mel0nyrame merged 27 commits into
masterfrom
feat/windows-tray-client

Conversation

@mel0nyrame

@mel0nyrame mel0nyrame commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

背景

Refs #37。该规格要求 Windows 10/11 x64 托盘客户端支持校园网账号配置、自动认证、状态查看、设备管理和应用更新。

变更轮廓

启动、owner 与命令流。

flowchart LR
  subgraph Native["Windows 原生宿主"]
    Runner["wWinMain / FlutterWindow<br/>单实例、后台参数、窗口与托盘"]
    Tunnel["Wi-Fi TCP 隧道<br/>接口 DNS、源地址绑定、IP_UNICAST_IF"]
  end
  subgraph Dart["Flutter / Dart"]
    Entry["main.dart"]
    TrayClient["WindowsTrayClient"]
    TrayApp["WindowsTrayApp → WindowsHome"]
    Runtime["WindowsAuthRuntime<br/>Windows 进程唯一运行时"]
    Controller["AuthRuntimeController"]
    Coordinator["AuthenticationCoordinator"]
    Protocol["SrunAuthenticationProtocol"]
    Http["注入的 http.Client"]
    WifiChannel["WindowsWifiChannel"]
    WifiState["WindowsWifiNetworkState<br/>runtime 与 coordinator 共用"]
    Settings["WindowsSettingsPage"]
    ConfigManager["ConfigManager"]
    ConfigRepository["ConfigRepository + SecretStore"]
    AutoStart["WindowsAutoStartClient"]
    Update["UpdateDialog / UpdateUtil"]
  end
  subgraph Release["Release 资产"]
    Installer["Windows 安装器"]
  end
  Entry -->|Windows 启动时先 await initialize| Runtime -->|初始化完成后 runApp| TrayApp
  Runner -->|托盘动作| TrayClient --> TrayApp
  TrayApp -->|检查、注销、踢设备等命令| Runtime
  Coordinator -->|认证状态| Runtime -->|状态流| TrayApp
  Runner -->|getSnapshot / WLAN / IPv4 事件| WifiChannel -->|初始与变化事件| Runtime
  Runtime -->|apply revision| WifiState
  Coordinator -->|读取同一网络状态| WifiState
  Runtime --> Controller --> Coordinator --> Protocol --> Http
  Runtime -->|networkChanged| Controller --> Coordinator
  Http -->|connectionFactory| WifiChannel
  WifiChannel -->|openTunnel + 一次性令牌| Runner --> Tunnel
  TrayApp --> Settings --> ConfigManager --> ConfigRepository
  ConfigManager -->|配置标记 / runtimeNotifier| Runtime
  Settings --> AutoStart --> Runner
  TrayApp --> Update --> Installer
Loading

wWinMain 以系统互斥量限制单实例并解析 --background;main.dart 在 runApp 前初始化日志、把配置通知绑定到同进程运行时,再等待 WindowsAuthRuntime.initialize()。初始化时 WindowsWifiChannel.start 先注册原生变化回调,再请求 getSnapshot;运行时应用初始 revision 并发布 Wi-Fi 状态,然后读取配置。有配置时启动协调器并等待首次检查返回(是否发出认证请求仍由当前 Wi-Fi 状态决定),无配置时发布配置提示而不发认证请求。后续事件由运行时按 revision 去重,应用新快照后经 controller 发出 networkChanged 命令;协调器使旧网络结果失效,并在恢复或切换时通过单飞入口立即检查。初始化完成后才创建托盘客户端并显示 Flutter 界面。后台启动隐藏窗口并跳过启动时更新检查。auth_runtime_test.dart 用 fake 配置和 Wi-Fi 事件覆盖无配置不请求、有配置时等待 Wi-Fi 以及网络变化后的检查;windows_tray_contract_test.dart 和 windows_installer_contract_test.dart 以源码契约检查覆盖单实例、托盘动作、后台启动与自启接线,不模拟 Windows 托盘的实机点击。

配置、认证与状态语义。 Windows 设置支持账号、密码、用户类型、自动/手动 ACID 与认证服务器的新增、修改和删除;密码继续保存在平台安全存储,普通配置和日志不记录密码或完整认证参数。成功保存或用户修改先写 repository,随后同步“已配置”标记并通知运行时;删除后也同步标记并通知。main.dart 将 runtimeNotifier 绑定到 Windows 进程内的 WindowsAuthRuntime.configurationChanged,运行时再经 AuthRuntimeController 让同一个 AuthenticationCoordinator 停止旧周期并重读配置;配置仍存在时恢复监控,删除后发布缺配置状态。config_repository_test.dart 覆盖安全存储与配置写入选择运行时通知回调;它没有断言持久化与通知的先后顺序。手动检查进入协调器的单飞流程;注销先停调度、等待在途认证,再提交协议请求,UI 只显示“已要求注销”,这不是服务端已断开确认。踢设备等待当前认证轮次,只有 kicked 才手动刷新设备状态;accepted 显示“已要求下线,但未能确认已断开”,该文案也不作为服务端受理证明。运行时遇到无法识别的踢设备结果会回退到同一提示;测试覆盖通用运行时客户端对未知状态的处理,没有直接覆盖此 Windows 映射。auth_runtime_test.dart 覆盖命令路由、网络变化和设备结果;windows_home_test.dart 覆盖用户确认、结果文案及踢成功后的刷新。

Wi-Fi 事件与认证出口。 FlutterWindow 监听 WLAN 关联和 IPv4 地址变化,按 WLAN 接口 GUID 匹配有 IPv4 的网卡。关联事件参与原生快照相等性判断并递增 revision;MethodChannel payload 发送 connected/address/adapter/name/revision,其中 name 是严格解码后的 SSID,解码失败或为空时使用 WLAN profile 名称。Dart WindowsWifiNetworkState 丢弃旧 revision,再将有效连接变化送到协调器;没有名称时界面显示通用提示。协调器在断开时停止无效调度并使旧结果/连接池失效,恢复或切换时通过同一单飞入口立即重查。

认证协议仍由 SrunAuthenticationProtocol 按当前轮次执行 Reality 探测、ACID、Portal 和在线确认请求。它收到 Windows 注入的 http.Client;新建 TCP 连接时,connectionFactory 经 WindowsWifiChannel.openTunnel 请求原生隧道。原生为单次回环连接生成随机令牌并返回本机端口和令牌;Dart 连接后先提交令牌,原生校验通过才连接远端并转发 HTTP socket 数据。远端 DNS 通过指定 Wi-Fi 接口解析,socket 绑定 Wi-Fi IPv4 并设置 IP_UNICAST_IF。该设计只定向认证流量,不改系统默认路由。windows_wifi_contract_test.dart 检查原生源码契约、通道字段和 Dart 接线,并用 mock MethodChannel/本机回环连接验证连接工厂;它不执行真实 Windows socket,也不证明物理出口。ADR-0011 记录了路由选择依据和仍需实机验证的限制。

界面与通知呈现。 WindowsTrayApp 订阅认证状态与 Wi-Fi 连接变化,并从共享快照读取当前名称;左键打开 compact 浮层,完整模式在同一 Flutter 窗口提供概况、设置和日志页面。概况呈现 IP、流量、在线时长和设备信息;设备在线信息仅在认证状态与 rad_user_info 都确认在线时显示。设置提供认证参数和登录自启操作;日志页支持查看、复制、清空和失败反馈。关闭主窗口会提示 LinkUp 仍在托盘运行并隐藏窗口,托盘菜单“退出”才释放运行时;右键菜单提供打开窗口、立即检查、设置、日志和退出。Windows 当前没有系统 toast/balloon 通知通道:状态变化只更新托盘 tooltip 的简短状态,浮层展示失败原因和下一步,前台操作结果使用窗口内反馈;普通重试不会弹系统通知,持续失败详情需打开浮层查看,悬停 tooltip 不显示详情。Wi-Fi 名称沿上述快照显示;无名称时使用通用提示(名称展示缺口由合并的 PR #55 修复)。windows_home_test.dart 覆盖浮层状态、Wi-Fi 文案、设备和配置操作;windows_logs_page_test.dart 覆盖日志状态和操作反馈;windows_tray_contract_test.dart 对首次关闭提示和右键命令做源码契约检查。

自启、更新与安装。 自启由设置页经 WindowsAutoStartClient 和原生启动通道读写当前用户 Run 项;Inno Setup 卸载时清理该启动项。UpdateUtil 在应用内按平台筛选资产:Windows 必须匹配 LinkUp-Setup-<version>.exe,Android APK 优先选 linkup.apk 并保留 .apk 回退;Windows 将安装器下载到临时目录,校验 HTTP 状态和内容长度后 detached 启动交互安装器,不等待安装完成。共享 UpdateDialog 的失败路径改为内联错误、重试和浏览器下载按钮,这项 UI 变化也影响 Android。Release workflow 的 build-windows job 构建并上传安装器 artifact;build-android job 依赖它,下载该 artifact 后与 APK 一起创建 Release。因此 Windows 构建失败会阻止当次 APK/Release 发布。update_util_test.dart、update_dialog_test.dart 和 windows_update_test.dart 覆盖资产选择及更新对话框;windows_installer_contract_test.dart 检查自启、安装配置接线,Windows CI 执行 release 构建并做安装、重装、卸载 smoke。

验证汇总

  • tool/flutter_sdk.sh flutter pub get:成功。
  • tool/flutter_sdk.sh flutter analyze --no-fatal-infos:通过;94 条现有 info,无 error 或 warning。
  • tool/flutter_sdk.sh flutter test --reporter=compact:210 项全部通过。
  • tool/flutter_sdk.sh dart format --output=none --set-exit-if-changed .:通过,无格式漂移。
  • git diff --check origin/master...origin/feat/windows-tray-client:通过。
  • 合并前 PR fix(windows-tray): 显示当前 Wi-Fi 名称 #55 的 CI run 中 CI checks 与 Build Windows app 均通过;PR 正文和安全检查也通过。合并提交与受检提交的源码树相同。
  • PR feat(windows): 提供托盘客户端与完整认证运行时 #56 本次正文更新触发的检查未等待完成。

评审注意事项

Windows 10/11 x64 真机、真实校园 Wi-Fi 的认证出口,以及托盘左右键、首次关闭提示、登录自启和后台行为的实机验收尚未完成;CI 与源码契约测试不能替代。第 24 条的 Windows 通知当前由 tooltip 简短状态和浮层详情承载,没有系统 toast;需要在实机上确认持续失败的详情可被清楚发现。另有一项规格差异待评审:#37 第 13 条描述“详细信息”进入独立窗口,当前实现是在同一原生 Flutter 窗口切换浮层和完整模式,没有创建第二个窗口。交互与视觉依据见 托盘交互研究 和 Windows 托盘视觉稿。#37 按其实机验收约定保持打开。首版不支持有线认证,也不按 SSID 建白名单。Android 应用内资产选择仍使用 APK,但 Release 发布 job 现在依赖 Windows 安装器 job。

风险与回滚

没有服务端数据迁移;revert 本 PR 不会主动删除已保存的本地配置或平台安全存储数据。Standards 轴提出两项判断意见并接受现状:windows/runner/flutter_window.cpp 的 OnCreate、托盘消息处理、WLAN 快照和注册表自启共用 FlutterWindow,暂不拆分以保留现有宿主边界;WindowsHome._buildStatusCard 与 _buildPopup 分别投影状态文案,若未来状态文案持续需要两处同步再抽共享组件。

feat(windows): 建立唯一认证运行时与手动检查入口
端口已在 OpenWifiTunnel 限定为 1–65535;传入 htons 前显式转换为 u_short,消除 MSVC C4244 窄化警告。
feat(windows): 响应 Wi-Fi 变化并定向认证请求
Windows 托盘状态浮层与单实例窗口生命周期
feat(windows): 提供桌面配置与网络详情窗口
feat(windows): 管理在线设备与账号会话
feat(windows): 查看与管理诊断日志
失败日志显示首次静默安装检查拿到空的 LASTEXITCODE。改用 Start-Process -Wait -PassThru 读取进程退出码,并保留安装文件与卸载断言。同步更新 Windows 安装器契约测试中的 ISCC 参数。
feat(windows): 交付安装包并支持登录自启
chore(windows): 明确安装器检查范围并补充自启导航
feat(windows-update): 支持 Windows 应用内更新并发布安装包
@mel0nyrame
mel0nyrame merged commit f35f1c3 into master Sep 28, 2026
7 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.

1 participant