No description
  • Go 53.6%
  • TypeScript 40.9%
  • Shell 4.2%
  • CSS 0.7%
  • Makefile 0.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
xiaoman1221 66d66b8560 新增支付系统、按页计费与 VIP 套餐购买
- 支付:接入 ePayGo/彩虹 v1(MD5)、v2(RSA)与 Mock 沙箱,
  支持异步通知验签、金额校验、幂等激活与主动查单
- 计费:打印任务按页数 × 份数计费,黑白/彩色独立单价;
  PDF 解析真实页数,图片与文本使用估算规则
- 订单:待支付任务不进入设备队列,支付成功后自动入队;
  支持取消、过期重付与未支付任务重试保护
- 套餐:管理员配置价格/时长/每日配额/优先级,购买后自动开通或续期,
  到期惰性降级
- 前端:会员套餐、支付页、二维码、套餐管理、支付订单页面;
  创建任务展示价格并跳转支付
- 文档:支付配置、商户接入、Mock 测试、计费规则与 API 说明
- 测试:v1/v2 签名验签、页数计价、订单幂等、套餐续期、取消与 Mock 端到端
2026-10-17 21:58:28 +08:00
cloud 新增支付系统、按页计费与 VIP 套餐购买 2026-10-17 21:58:28 +08:00
deploy 新增多用户认证、彩色打印、打印预览、多平台代理与 OTA 更新 2026-10-17 19:10:51 +08:00
docs 新增支付系统、按页计费与 VIP 套餐购买 2026-10-17 21:58:28 +08:00
local 新增多用户认证、彩色打印、打印预览、多平台代理与 OTA 更新 2026-10-17 19:10:51 +08:00
scripts 新增多用户认证、彩色打印、打印预览、多平台代理与 OTA 更新 2026-10-17 19:10:51 +08:00
.gitattributes 新增支付系统、按页计费与 VIP 套餐购买 2026-10-17 21:58:28 +08:00
.gitignore 新增多用户认证、彩色打印、打印预览、多平台代理与 OTA 更新 2026-10-17 19:10:51 +08:00
go.work 初始化云端打印控制台与树莓派打印代理 2026-10-17 17:42:44 +08:00
go.work.sum 新增支付系统、按页计费与 VIP 套餐购买 2026-10-17 21:58:28 +08:00
Makefile 新增多用户认证、彩色打印、打印预览、多平台代理与 OTA 更新 2026-10-17 19:10:51 +08:00
README.md 新增支付系统、按页计费与 VIP 套餐购买 2026-10-17 21:58:28 +08:00

RpiPrinter

面向树莓派与多平台的云端打印解决方案,由两个独立模块组成:

  • 云端模块(cloud/):Go + Gin + GORM + SQLite 后端,负责账号与角色、设备管理、按页计费、VIP 套餐购买、支付回调、任务下发、彩色能力协商、打印预览、代理 OTA 发布,并内嵌 React 管理控制台。
  • 本地控制模块(local/):Go 代理,可在树莓派/Linux、macOS(CUPS)与 Windows(系统打印队列)上运行;主动长轮询领取任务、调用打印驱动、上报打印机能力与任务进度,并支持崩溃自动重启与 OTA 自更新。
┌──────────────────────────────┐        HTTP/JSON 长轮询        ┌──────────────────────────┐
│          云端模块 cloud/      │  <──────────────────────────  │   本地控制模块 local/     │
│                              │                               │                          │
│  React 控制台 (Zustand/Router)│  ──────────────────────────>  │  注册 / 心跳 / 领取任务    │
│  Gin API ─ GORM ─ SQLite     │       任务文件 + 状态回报       │  CUPS / ESC-POS / RAW    │
└──────────────────────────────┘                               └────────────┬─────────────┘
                                                                             │ lp / 设备写入
                                                                        ┌────▼─────┐
                                                                        │  打印机   │
                                                                        └──────────┘

目录结构

RpiPrinter/
├── cloud/                        云端模块(独立 Go module)
│   ├── cmd/server/               服务入口
│   ├── internal/
│   │   ├── api/                  用户/管理/设备/支付/OTA API
│   │   ├── auth/                 JWT、bcrypt、密钥管理
│   │   ├── billing/              页数统计、计价、订单激活与套餐权益
│   │   ├── payment/              彩虹 v1/v2、RSA/MD5 验签、Mock 支付
│   │   ├── config/               环境变量配置
│   │   ├── database/             SQLite 初始化与 AutoMigrate
│   │   ├── jobs/                 任务状态机、优先级领取
│   │   ├── model/                User / Plan / PaymentOrder / Device / Printer / PrintJob
│   │   └── storage/              上传文件与代理发布包存储
│   └── web/                      React 控制台
│       ├── src/components/       布局、预览、任务对话框、shadcn 风格组件
│       ├── src/pages/            登录注册 / 仪表盘 / 打印机 / 任务 / 会员 / 支付 / 用户 / 套餐 / 订单 / 发布 / 设置
│       ├── src/stores/           Zustand(主题、认证)
│       └── src/lib/              API 客户端、格式化、状态映射
├── local/                        本地代理(独立 Go module)
│   ├── cmd/agent/                代理入口(跨平台信号处理)
│   ├── internal/
│   │   ├── agent/                会话、心跳、任务处理、OTA 调度
│   │   ├── cloud/                云端 HTTP 客户端
│   │   ├── config/               JSON 配置加载
│   │   ├── printer/              Driver 抽象 + CUPS / Windows / ESC/POS / RAW
│   │   └── update/               校验、替换可执行文件、更新标记
│   └── config.example.json
├── scripts/
│   ├── deploy-local.sh           构建 + SSH 上传代理
│   ├── remote-install.sh         树莓派端安装/supervisor 启动
│   ├── run-agent.sh              轻量守护循环(OTA/崩溃重启)
│   ├── publish-agent.sh          多平台构建并发布 OTA
│   └── setup-mi-printer.sh       小米喷墨打印机 CUPS 配置
├── docs/api.md                   设备端、用户端与管理端 API 文档
├── docs/payment.md               ePayGo 支付、按页计费与 VIP 套餐说明
├── docs/printer-mi-inkjet.md     小米米家喷墨一体机兼容说明
├── deploy/systemd/               树莓派 systemd 服务
├── deploy/windows/               WinSW 服务配置
├── deploy/macos/                 launchd 服务配置
├── go.work                       两个 Go module 的 workspace
└── Makefile

技术栈

模块 技术
云端后端 Go 1.26、Gin、GORM、SQLite(glebarez/sqlite 纯 Go 驱动,无需 CGO)、JWT、bcrypt、log/slog
云端前端 React 18、TypeScript、Vite 7、Tailwind CSS v4、shadcn 风格组件、Zustand、React Router 7、qrcode.react
支付 ePayGo / 彩虹易支付 v1(MD5)、v2(RSA/SHA256)、Mock 沙箱;PDF 页数解析(dslipak/pdf)
本地代理 Go 1.26、HTTP 长轮询、CUPS(Linux/macOS)、Windows Spooler(PowerShell)、ESC/POS(GBK)、RAW
OTA 云端版本清单 + SHA-256 校验 + 可执行文件替换,由 systemd/launchd/WinSW/supervisor 重启

快速开始

前置:Go 1.26+、Node.js 20.19+。Windows / macOS / Linux 均可开发和运行代理。

首次打开控制台需要注册账号:第一个注册用户会自动成为管理员。请先完成管理员注册,再对外开放服务。

支付功能默认关闭,打印任务免费且直接进入队列。需要收费时设置 APP_PAYMENT_ENABLED=true 并配置 ePayGo/彩虹商户信息,详见 docs/payment.md。开发环境可以使用 Mock 支付。

1. 启动云端服务

cd cloud
export APP_ENROLLMENT_KEY=dev-secret        # PowerShell: $env:APP_ENROLLMENT_KEY="dev-secret"
go run ./cmd/server

服务默认监听 :8080,数据写入 cloud/data/(SQLite + 上传文件)。首次启动会自动建表。

2. 启动管理控制台

cd cloud/web
npm install
npm run dev

打开 http://localhost:5173。开发模式下 Vite 会把 /api 代理到 http://localhost:8080。

生产构建后由云端直接托管:

cd cloud/web && npm run build
cd .. && go run ./cmd/server   # 存在 web/dist 时自动托管 SPA

3. 启动树莓派本地代理

cd local
cp config.example.json config.json
# 编辑 config.json:server_url、enrollment_key、device_name、printers
go run ./cmd/agent -config config.json

代理会以本地生成的 device_uid 注册设备,保存云端下发的 Token,然后开始心跳与长轮询。CUPS 打印机自动发现;ESC/POS 与 RAW 打印机在配置文件中声明。

在控制台“打印任务”页面选择打印机、上传文档即可下发。任务流转:

pending → claimed → printing → completed / failed
pending / claimed → canceled
failed / canceled → pending(控制台重试)

4. 一键构建并部署到树莓派

在 Git Bash / Linux / macOS 中执行:

bash scripts/deploy-local.sh

默认目标为 xiaoman1221@192.168.10.124,部署到远端 ~/rpiprinter/。脚本会:

  1. 交叉编译 linux/arm64 与 linux/arm(兼容 32/64 位 Raspberry Pi OS);
  2. 打包二进制、示例配置和打印机配置脚本;
  3. 通过一条 SSH 连接上传并安装,只需在终端输入一次 SSH 密码;
  4. 远端已有 config.json 时保留配置并重启代理;首次部署会生成配置但不启动,等待你填写 server_url 与 enrollment_key。

常用选项:

bash scripts/deploy-local.sh --host xiaoman1221@192.168.10.124
bash scripts/deploy-local.sh --no-start        # 只上传
bash scripts/deploy-local.sh --build-only      # 只构建,产物在 .deploy-build/
RPI_PORT=2222 bash scripts/deploy-local.sh     # 非标准 SSH 端口
make deploy-pi                                 # 等价于执行部署脚本

部署完成后查看日志:

ssh xiaoman1221@192.168.10.124
tail -f ~/rpiprinter/agent.log

该脚本使用 nohup 在用户目录运行代理,不依赖 sudo。若希望注册为系统服务,请参考下方 systemd 部署方式。

小米米家喷墨一体机兼容

目标设备 USB ID:302c:1511 Mi All-in-One Inkjet Printer。

该机型无官方 Linux 驱动,但支持 AirPrint,Linux 下首选 CUPS + ipp-usb + IPP Everywhere 无驱动方案。部署代理后,在树莓派执行:

sudo bash ~/rpiprinter/setup-mi-printer.sh
# 可选:提交测试页
sudo TEST_PAGE=1 bash ~/rpiprinter/setup-mi-printer.sh

脚本会安装 CUPS/ipp-usb、校验 USB 设备、自动创建名为 Mi_Inkjet 的驱动无关队列。若固件未暴露 IPP-over-USB,脚本会给出 Wi-Fi IPP 与兼容 PPD 的回退方案。

详细说明、验证命令与故障排查见 docs/printer-mi-inkjet.md。

配置

云端环境变量

变量 默认值 说明
APP_ADDR :8080 HTTP 监听地址
APP_DATA_DIR ./data 数据目录
APP_DB_PATH $APP_DATA_DIR/printer.db SQLite 文件路径
APP_UPLOAD_DIR $APP_DATA_DIR/uploads 打印文档目录
APP_WEB_DIR ./web/dist 前端构建产物目录,存在时托管 SPA
APP_CORS_ORIGINS http://localhost:5173,http://127.0.0.1:5173 允许的前端来源,逗号分隔
APP_ENROLLMENT_KEY 空 设备注册密钥;为空时允许任意设备注册,仅限开发
APP_MAX_UPLOAD_MB 50 单个上传文件大小上限
APP_MAX_POLL_WAIT_SEC 30 设备长轮询最长挂起时间
APP_DEVICE_OFFLINE_SEC 45 超过该时长无心跳视为离线
APP_HEARTBEAT_SEC 15 下发给设备的心跳间隔建议值
APP_JWT_SECRET 自动生成 JWT 签名密钥(至少 32 字符);未设置时保存到 $APP_DATA_DIR/jwt.secret
APP_TOKEN_TTL_HOURS 24 登录令牌有效期
APP_REGISTRATION_ENABLED true 是否开放注册;首个注册用户自动成为管理员
APP_USER_DAILY_QUOTA 20 普通用户每日打印任务配额,0 表示不限
APP_VIP_DAILY_QUOTA 100 VIP 每日打印任务配额,0 表示不限
APP_RELEASE_DIR $APP_DATA_DIR/releases 代理 OTA 发布包目录
APP_MAX_AGENT_UPLOAD_MB 100 单个代理发布包大小上限
APP_PAYMENT_ENABLED false 支付总开关;关闭时打印任务免费
APP_PAYMENT_PROVIDER mock mock / rainbow_v1 / rainbow_v2
APP_PAYMENT_BASE_URL 空 ePayGo / 彩虹网关地址
APP_PAYMENT_PID / APP_PAYMENT_KEY 空 彩虹商户 PID 与 v1 MD5 密钥
APP_PAYMENT_PRIVATE_KEY / _FILE 空 v2 商户 RSA 私钥 PEM(内容或文件)
APP_PAYMENT_PLATFORM_PUBLIC_KEY / _FILE 空 v2 平台公钥 PEM
APP_PUBLIC_URL 空 公网 HTTPS 地址,用于回调与支付返回;启用支付时必填
APP_PAYMENT_DEFAULT_CHANNEL alipay 默认支付渠道
APP_PAYMENT_CHANNELS alipay,wxpay 前端可选渠道,逗号分隔
APP_PAYMENT_MOCK_ENABLED false Mock 模式下是否允许模拟支付
APP_PRICE_BW_FEN 30 黑白每页单价(分)
APP_PRICE_COLOR_FEN 100 彩色每页单价(分)
APP_PAYMENT_MAX_PAGES 200 单任务最大计费页数
APP_PAYMENT_ORDER_TTL_MIN 120 订单展示有效期
APP_PAYMENT_RECONCILE_SEC 60 主动查单/对账间隔
APP_PAYMENT_NOTIFY_SKEW_SEC 3600 v2 通知时间戳容忍范围

支付配置的完整说明与商户配置步骤见 docs/payment.md。

本地代理 config.json

字段 默认值 说明
server_url http://127.0.0.1:8080 云端地址
enrollment_key 空 与云端 APP_ENROLLMENT_KEY 一致
device_name 主机名 控制台显示名称
state_dir ./data 设备 UID / Token 持久化目录
spool_dir $state_dir/spool 下载任务文件的临时目录
poll_wait_seconds 25 长轮询等待秒数(云端最大 30)
heartbeat_seconds 15 心跳间隔,最小 5
worker_count 1 并发打印 worker 数,最大 8
keep_spool false 是否保留打印后的 spool 文件
auto_update true 是否启用云端 OTA 自动更新
update_check_seconds 21600 更新检查间隔,最小 30 秒
printers[] — 非系统自动发现的打印机声明,见下

printers[] 字段:

字段 说明
name 全局唯一打印机名,任务按此路由
display_name 控制台显示名称
driver escpos 或 raw(Linux/macOS 的 CUPS 队列无需配置)
port ESC/POS 或 RAW 直写设备,如 /dev/usb/lp1、COM3:
queue RAW 模式下的 CUPS 队列名(以 -o raw 提交;Windows 不支持)
is_default 是否标记为默认打印机

常用环境变量可覆盖配置:RPI_SERVER_URL、RPI_ENROLLMENT_KEY、RPI_DEVICE_NAME、RPI_STATE_DIR、RPI_SPOOL_DIR。

账号、角色与配额

  • 首个注册用户自动成为 管理员;后续注册用户默认 user。
  • 角色:
    • user:创建/查看自己的任务,按每日配额限制。
    • vip:更高每日配额;任务优先级为 100,会被设备优先领取。
    • admin:不受配额限制,可管理用户、查看全部任务、发布代理版本。
  • 管理员可在“用户管理”页面调整角色、启用/禁用账号、重置密码;必须保留至少一个启用管理员。
  • 用户密码使用 bcrypt 存储;JWT 默认 24 小时有效,改密/重置密码/禁用会使旧令牌失效。
  • 配额按“当日创建的任务数”计算,超限返回 429;控制台仪表盘会显示已用/剩余配额。

彩色打印与预览

  • 代理在上报打印机时附带 supports_color 与 capabilities:
    • CUPS 通过 lpoptions -p <queue> -l 探测 print-color-mode / ColorModel / ColorMode;
    • Windows 通过 Get-PrintConfiguration 获取 Color;
    • ESC/POS 与 RAW 直写固定为仅黑白。
  • 创建任务时用户可选择“打印机默认 / 黑白 / 彩色”;后端会拒绝在不支持彩色的打印机上选择彩色。
  • CUPS 驱动会把颜色模式转换为对应的 -o 参数。
  • 创建任务时可预览本地 PDF/图片/文本;历史任务可点击“预览”通过鉴权接口拉取文件后预览。上传目录不会静态暴露。

支付与 VIP 套餐

  • 支付网关:ePayGo,支持彩虹 v1(MD5)、v2(RSA/SHA256)与本地 Mock 沙箱;默认关闭。
  • 打印任务按 页数 × 份数 收费,黑白/彩色分别定价。PDF 使用真实页数,图片按 1 页,文本按每 50 行一页估算。
  • 付费任务创建后状态为 pending_payment,支付成功后才进入 pending,设备在此之前无法领取。
  • 用户可在“会员套餐”页面购买后台配置的套餐;支付成功后自动开通/续期 VIP,到期后自动恢复普通用户。
  • VIP 享受更高每日配额与任务优先级,但打印仍需付费;管理员免支付、免配额。
  • 管理员可在“套餐管理”配置价格、时长、配额与优先级,在“支付订单”查看订单并主动查单。
  • 未支付任务占用配额,取消后释放;同一用户最多保留 5 个待支付任务。

完整流程、环境变量、商户配置与 Mock 测试见 docs/payment.md。

打印驱动

  • CUPS(Linux/macOS):通过 lpstat -p / lpstat -o 自动发现队列并查询状态,通过 lp 提交文件。支持任务 options 中的 CUPS 选项(如 media、sides)。
  • Windows Spooler:通过 PowerShell Get-Printer / Get-PrintConfiguration 枚举打印机与彩色能力;文本使用 Out-Printer,PDF/图片使用 Start-Process -Verb PrintTo。成功仅代表已提交到 Windows 打印队列,彩色/份数控制为尽力而为。
  • ESC/POS:向配置的 port 写入指令流。文本文件转为 GBK 并发送初始化、中文模式、走纸与切纸指令;.prn / .bin 原样发送。可用任务选项 cut(默认 true)和 feed(默认 3)控制。
  • RAW:通过 CUPS 队列 -o raw 或直接写设备文件,原样发送字节,适合标签机等自带指令格式的设备。

CUPS 的 lp 只保证“已进入队列”,代理会在提交成功后立即回报 completed。如需精确的物理打印完成状态,后续可增加 lpstat -W completed 跟踪或 IPP 事件监听。

树莓派部署

交叉编译 64 位 ARM 二进制:

make agent-arm64        # 输出 bin/rpi-printer-agent

安装为 systemd 服务(按实际用户名修改 unit 中的 User):

sudo install -d /opt/rpiprinter /etc/rpiprinter /var/lib/rpiprinter
sudo install -m 0755 bin/rpi-printer-agent /opt/rpiprinter/
sudo install -m 0644 local/config.example.json /etc/rpiprinter/config.json
# 编辑 /etc/rpiprinter/config.json:
#   "state_dir": "/var/lib/rpiprinter",
#   "spool_dir": "/var/lib/rpiprinter/spool"
sudo install -m 0644 deploy/systemd/rpi-printer-agent.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now rpi-printer-agent
journalctl -u rpi-printer-agent -f

OTA 自更新要求 /opt/rpiprinter 对运行用户可写。若 service 以非 root 用户运行,请先 sudo chown -R <user>:<group> /opt/rpiprinter。systemd unit 已设置 Restart=always,更新退出后会自动拉起新版本。

树莓派需先安装并配置 CUPS:

sudo apt install cups cups-client

使用 make deploy-pi 或 bash scripts/deploy-local.sh 部署时,会使用 run-agent.sh 作为轻量 supervisor,同样支持崩溃重启和 OTA。

多平台运行

一次性构建所有目标:

make agent-all

产物位于 bin/:

rpi-printer-agent-linux-arm64
rpi-printer-agent-linux-arm
rpi-printer-agent-linux-amd64
rpi-printer-agent-darwin-arm64
rpi-printer-agent-darwin-amd64
rpi-printer-agent-windows-amd64.exe
rpi-printer-agent-windows-arm64.exe

Windows

  1. 将对应 .exe、config.json 放到同一目录(不要放在 Program Files,自更新需要写权限)。配置模板见 deploy/windows/config.example.json。
  2. 前台测试:
    .\rpi-printer-agent-windows-amd64.exe -config .\config.json
    
  3. 安装为服务:参考 deploy/windows/rpi-printer-agent.xml,使用 WinSW 注册服务。WinSW 的 onfailure 会在 OTA 退出后自动拉起新版本。
  4. 打印机通过 Windows Spooler 自动发现;文本、PDF/图片的打印能力依赖本机已安装的打印处理器与文件关联。

macOS

  1. 将 rpi-printer-agent-darwin-arm64(或 amd64)安装到 /usr/local/rpiprinter/,放入 config.json。
  2. 前台测试:
    ./rpi-printer-agent -config ./config.json
    
  3. 使用 deploy/macos/com.rpiprinter.agent.plist 注册 launchd 服务:
    launchctl load -w ~/Library/LaunchAgents/com.rpiprinter.agent.plist
    
  4. macOS 使用内置 CUPS,打印机可通过 lpstat -p 自动发现。

macOS 未签名/未公证的 OTA 二进制可能被 Gatekeeper 拦截;生产发布建议进行签名与公证。

代理 OTA 自动更新

管理端“代理发布”页面可以按平台/架构上传代理二进制并发布;也可以使用脚本一次性构建并发布:

VERSION=v0.3.0 NOTES="支持彩色打印" bash scripts/publish-agent.sh

代理行为:

  1. 每 update_check_seconds(默认 6 小时)向云端查询当前 OS/Arch 的最新已发布版本;
  2. 下载到可执行文件所在目录,校验大小与 SHA-256;
  3. 等待当前任务和长轮询结束,阻止新任务领取;
  4. 备份当前二进制为 .old,原子替换,然后以专用退出码 75 退出;
  5. supervisor / systemd / launchd / WinSW 拉起新版本;新版本首次启动会清理更新标记与 .old。

要求:可执行文件目录对运行用户可写,并且有服务管理器负责重启。回滚时可手动将 rpi-printer-agent.old 改回 rpi-printer-agent。

API 文档

设备端与管理端接口、请求/响应示例见 docs/api.md。

常用命令

make tidy          # 整理两个 Go module 依赖
make run-cloud     # 启动云端
make web-dev       # 启动前端开发服务器
make web-build     # 构建前端
make agent         # 本机运行代理(读取 local/config.json)
make agent-arm64   # 交叉编译树莓派 64 位代理
make agent-all     # 构建 Linux/macOS/Windows 全部代理
make deploy-pi     # 构建并上传到树莓派(交互输入 SSH 密码)
make publish-agent # 构建并发布 OTA 版本(需管理员密码)
make test          # 运行 Go 测试

当前边界 / 后续工作

  • Windows 打印依赖本机文件关联与打印处理器,PrintTo 只能保证“已提交到 Spooler”,份数与彩色控制无法完全保证。
  • macOS 未签名/未公证的 OTA 包可能被 Gatekeeper 拦截。
  • ESC/POS 状态目前只能判断设备文件是否存在,未实现双向状态查询(如 GS r 状态回读)。
  • 设备离线期间的任务不会重新调度;已领取但未完成的任务需要人工在控制台重试。
  • 上传文件在任务完结后不会自动清理,需要后续增加保留策略。
  • 设备心跳/任务上报尚无本地 outbox,云端不可达时最终状态可能丢失(会重试 3 次)。
  • 首个注册用户成为管理员适合单实例部署;公网开放前请先完成管理员注册。
  • JWT 存储在浏览器 localStorage,需通过 HTTPS、CSP 与输入转义降低 XSS 风险。
  • 支付不提供自动退款;支付成功但打印失败时需管理员人工处理。
  • 迟到支付不会复活已取消的打印任务,订单会标记为需要人工复核。
  • 文本打印页数为估算值,实际打印机分页可能不同。
  • 真实支付需要 ePayGo/彩虹商户凭据、公网 HTTPS 回调地址;本地开发使用 Mock 支付。
  • 后续可加入 WebSocket 推送、多租户、审计日志与更细粒度的权限组。