# desktop-plugin(饭卡打印站客户端 · Flutter 版) 用 Flutter 重写打印站客户端,替代 C# 版 card-printer-agent。HTTP 协议**完全不变**,前端(`biz-pc-cloud-zax/src/api/plugin/cardPrinter.ts`)零改动,两版助手可并存灰度替换。 - 目录名 `desktop-plugin`,Dart 包名 `desktop_plugin`(Dart 标识符不允许连字符) - macOS bundle id:`com.zax.desktop-plugin`(`macos/Runner/Configs/AppInfo.xcconfig`) - 总体计划见 `../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 依赖) ├── 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 提交(参数列表传参防注入) ├── 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 + 任务队列 + 端口顺延(单测 5 个全过,接口自测全过) - [x] Phase 2:macOS CUPS 后端(真机 status `HiTi CS-200e, 2.4.2 就绪`;真实出卡一张,printer-state 4→3 约 24s) - [ ] Phase 3:Windows PavoApi ffi 后端(需 Windows 实机 + windows/ 目录) - [ ] Phase 4:托盘/首次引导/URL scheme 唤起(zaxprint scheme 已写入 Info.plist)/开机自启 - [ ] Phase 5:打包签名分发(mac ad-hoc 就地签名;参考 C# 版签名铁律)