Phase 4 剩余三项: - 首次引导向导(setup/first_run + ui/setup_wizard):服务自检(含 C# 助手共存 提示)→ 打印机检测与 HiTi 驱动安装指引(官网链接/重新检测,mac 与 win 文案 分支,允许无打印机完成)→ 开机自启开关 + scheme 注册;侧栏底部常驻设置区 (自启开关 + 重新运行引导) - 开机自启(setup/autostart):mac 写 LaunchAgent(RunAtLoad 一次性,不用 KeepAlive——托盘退出不能被拉回);Windows 走 HKCU Run(reg 命令免 win32 依赖) - URL scheme 闭环:单实例唤醒端口 38210(setup/single_instance)——zaxprint:// 唤起或重复双击启动第二进程时通知常驻实例弹窗后自退,跨平台一致(弃用 app_links 的 mac 事件转发不确定性);Windows 启动时幂等注册 HKCU scheme; 前端 cardPrinter.ts 加 launchAgent()(zaxprint://open),开卡抽屉离线时显示 "启动打印助手"按钮,点击唤起后 1s×6 轮询自动上线(biz 前端改动待用户提交) Phase 5: - tool/package_macos.sh:构建 → ad-hoc 就地签名(--deep,勿加 --options=runtime: hardened runtime 库校验会因 ad-hoc 无 Team ID 拒载 framework,实测踩坑)→ hdiutil dmg;实测产出 22MB dmg + 签名后冒烟通过 - windows/installer.iss:Inno Setup 安装包脚本(快捷方式/桌面图标/开机自启 Tasks/zaxprint scheme 注册与卸载清理/HiTi SDK DLL 随 Release 目录打包) 依赖 +url_launcher +shared_preferences;13 测试全过、analyze 零问题。
103 lines
7.7 KiB
Markdown
103 lines
7.7 KiB
Markdown
# desktop-plugin(资安信插件助手)
|
||
|
||
**资安信插件助手**:桌面插件集成软件(目录名 desktop-plugin,Dart 包名 desktop_plugin,bundle id com.zax.desktop-plugin,App 显示名"资安信插件助手")。当前内置第一个插件"饭卡打印机",后续插件在 `lib/ui/routes.dart` 注册即自动出现在左侧菜单。关闭窗口 = 最小化到后台托盘,服务持续运行;退出入口在托盘菜单。
|
||
|
||
用 Flutter 重写打印站客户端,替代 C# 版 card-printer-agent。HTTP 协议**完全不变**,前端(`biz-pc-cloud-zax/src/api/plugin/cardPrinter.ts`)零改动,两版助手可并存灰度替换。
|
||
|
||
- 图标源:资安信 logo(`biz_zax_app_cloud/unpackage/res/icons/1024x1024.png` 深蓝盾牌+白色人形);`dart run tool/gen_app_icons.dart <源图>` 重新生成 mac AppIcon/Windows ICO/托盘图标
|
||
- 总体计划见 `../card-printer-agent/flutter-client-plan.md`
|
||
|
||
## 协议(与 C# 版逐字段对齐)
|
||
|
||
监听 `127.0.0.1:38200`,被占用顺延 38201/38202;CORS `*`(含 OPTIONS 预检);响应统一 `{code, msg, data}`,code=0 成功;JSON 字段一律驼峰。
|
||
|
||
| 端点 | 说明 |
|
||
|---|---|
|
||
| `GET /manifest` | `{plugins:[…], agent:"desktop-plugin", backend:"pavo\|cups", mock}` |
|
||
| `GET /api/card-printer/status` | PrinterStatus(online/model/firmware/connection/ribbon/cardLow/cardLeft/status/statusText) |
|
||
| `POST /api/card-printer/print` | `{copies, front:{color,k?}, back:{color,k?}}` → `{taskId}` |
|
||
| `GET /api/card-printer/task/{taskId}` | `{taskId, state:printing\|done\|error, status, statusText}` |
|
||
|
||
任务串行执行(打印机物理一次一张),完成后保留 30 分钟。
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
lib/
|
||
├── main.dart # Flutter 窗口入口(服务状态 + 打印机状态 + 运行日志)
|
||
├── agent.dart # 核心组合:参数解析 + 日志缓冲 + 后端/队列/服务装配(纯 Dart,无 Flutter 依赖;stop 幂等)
|
||
├── ui/
|
||
│ ├── routes.dart # 插件路由注册表(左侧菜单与内容页共用,加插件=加一项)
|
||
│ ├── app_shell.dart # 应用外壳:左侧插件菜单 + IndexedStack 内容区
|
||
│ ├── card_printer_page.dart # 饭卡打印机插件页:主状态一眼可懂 + 指标卡 + 技术详情折叠
|
||
│ └── app_tray.dart # 后台托盘:关窗隐藏(window_manager)/托盘菜单(tray_manager)/恢复窗口/退出
|
||
├── server/
|
||
│ ├── protocol.dart # 协议 DTO(toJson 手写驼峰,对齐 C# ApiModels.cs)
|
||
│ ├── http_server.dart # shelf 服务:端口顺延、CORS、四端点
|
||
│ └── print_queue.dart # 串行任务队列 + 30 分钟清理
|
||
├── print/
|
||
│ ├── backend.dart # PrintBackend 抽象 + OS 工厂
|
||
│ ├── mock_backend.dart # --mock 模拟(打印 2.5s 完成)
|
||
│ ├── cups_backend.dart # macOS:lpoptions 发现队列 + lp 提交(参数列表传参防注入)
|
||
│ └── pavo/ # Windows:HiTi PavoApi ffi 直打
|
||
│ ├── pavo_ffi.dart # 结构体/导出绑定(PavoApiW_x64.dll,W 后缀回退兼容)
|
||
│ ├── pavo_printer.dart # 枚举/状态/色带/单卡直打/轮询/内存管理(对齐 C# PavoPrinter.cs)
|
||
│ └── pavo_backend.dart # 后端实现(全部跑在 Isolate.run,主 isolate 不阻塞)
|
||
├── image/
|
||
│ └── card_image.dart # PNG data URL → 缩放铺白底 → BGR24/Gray8(对齐 C# CardImage.cs)
|
||
├── utils/
|
||
│ └── command_runner.dart # 外部命令执行(LC_ALL=C、超时杀进程、参数列表)
|
||
bin/agent.dart # headless 入口(开发联调,行为对齐 C# 控制台助手)
|
||
tool/make_test_card.dart # 生成 1014×642 测试卡(真实打印验收用)
|
||
```
|
||
|
||
## 常用命令
|
||
|
||
```bash
|
||
flutter pub get
|
||
flutter analyze
|
||
flutter test
|
||
|
||
# headless 联调(不需要窗口/真机)
|
||
dart run bin/agent.dart --mock # mock 模式,端口 38200-38202 顺延
|
||
dart run bin/agent.dart --port 38203 # 指定端口(本机 C# 助手占用时的开发姿势)
|
||
dart run bin/agent.dart # 真实模式(macOS 走 CUPS 队列)
|
||
|
||
# 测试卡 + 真实打印
|
||
dart run tool/make_test_card.dart
|
||
# 生成 data URL 后 POST /api/card-printer/print,观察 lpoptions 的 printer-state 4→3
|
||
|
||
# 构建
|
||
flutter build macos --release
|
||
```
|
||
|
||
## 本机开发注意(血泪坑,勿再踩)
|
||
|
||
- **同机 C# 助手占端口**:测试前 `launchctl unload ~/Library/LaunchAgents/com.zax.cardprinteragent.plist`,测完 `launchctl load` 恢复;两个版本的 manifest.agent 字段可区分(card-printer-agent / desktop-plugin)
|
||
- **AMFI 卡死进程**:早前测试遗留的 CardPrinterAgent 进程 STAT=UE/UNE,kill -9 无效,占用 38201 直到重启;Flutter 版会自动落到其他候选端口,前端自适应
|
||
- **macOS 必须关 App Sandbox**(`macos/Runner/*.entitlements` 已置空):沙箱拦截 127.0.0.1 端口绑定与 lp/lpoptions 子进程访问 CUPS
|
||
- **macos 平台目录生成前提**:本机 flutter config 曾禁用 `enable-macos-desktop` 且缺 CocoaPods,均已修复(brew install cocoapods;注意 brew 装 ca-certificates 链接冲突时先 `brew unlink` 再重装)
|
||
- **windows/ 目录无法在 Mac 生成**:需在 Windows 实机上 `flutter create --platforms windows .` 补齐(Phase 3)
|
||
|
||
## 进度(2026-09-21)
|
||
|
||
- [x] Phase 1:协议服务器 + mock + 任务队列 + 端口顺延(单测全过,接口自测全过)
|
||
- [x] Phase 2:macOS CUPS 后端(真机 status `HiTi CS-200e, 2.4.2 就绪`;真实出卡一张)
|
||
- [x] Phase 3 代码:Windows PavoApi ffi 后端(待 Windows 实机验收)
|
||
- [x] Phase 4 全部:插件化 UI + 后台托盘(红点/`_close:` 直退已修,根因=模板 AppDelegate 的 `applicationShouldTerminateAfterLastWindowClosed=true`)+ 资安信品牌 + **首次引导向导**(服务自检/打印机检测与驱动指引/自启配置)+ **开机自启**(mac LaunchAgent RunAtLoad / Win HKCU Run,侧栏开关)+ **URL scheme 闭环**(单实例唤醒端口 38210:`zaxprint://` 或重复双击 → 唤醒常驻实例弹窗;Win 启动时幂等注册 HKCU scheme;前端抽屉离线时"启动打印助手"按钮)
|
||
- [x] Phase 5 mac:`tool/package_macos.sh`(构建 → ad-hoc 就地签名 → dmg,实测 22MB 产物 + 签名后冒烟通过);Windows:`windows/installer.iss`(Inno Setup,含 scheme/自启注册与卸载清理,待 Windows 机上 iscc 编译)
|
||
- [ ] Windows 侧打包与实机验收(Inno 编译 + Pavo 直打/色带/免驱动实测)
|
||
|
||
### 已知坑(本项目实测)
|
||
- SliverList 里 `Row(crossAxisAlignment: stretch)` 必须包 `IntrinsicHeight`,否则子项收到 h=Infinity 直接布局断言(test/ui_smoke_test.dart 守护)
|
||
- testWidgets 里 `agent.start()/stop()` 是真实 socket I/O,必须 `tester.runAsync` 包裹,否则 fake async zone 永久挂起;且勿用 pumpAndSettle(页面含动画组件永不 settle)
|
||
- image 包 `Image()` 默认 3 通道不透明黑(透明图须 numChannels:4);`compositeImage` 对全透明像素非 src-over 语义
|
||
- tray_manager 0.5.3:无双击事件(单击即 MouseDown);菜单须先 setContextMenu 再无参 popUpContextMenu
|
||
|
||
### Windows 实机验收清单(Phase 3 收尾)
|
||
1. `flutter create --platforms windows .` 对照生成的目录结构核对手动实例化的差异(或直接构建)
|
||
2. `flutter build windows --release` 编译过;确认 vendor 四文件拷到 exe 旁
|
||
3. `dart run bin/agent.dart`(或直接跑 App)→ status 返回真机型号/固件/色带余量
|
||
4. 提交测试卡真实出卡(tool/make_test_card.dart 生成后走 print 接口)
|
||
5. 顺带实测:不装 HiTi 官方驱动能否直打(PavoApiW_x64 疑似用户态直连 USB)
|