跳转至

把酒店 PMS 做成单二进制:Go + SQLite + go:embed 的离线优先架构

酒店场景对软件的要求和互联网产品很不一样:客人结账时不能"服务暂时不可用",前台电脑可能根本没有稳定外网,老板不愿意按月付 SaaS 订阅费。这些约束把技术方案推向了一个少见形态——单二进制、离线优先、买断部署。

为什么不用微服务 + 云数据库

中小酒店常见 10~60 间房,一台收银电脑或一台小主机就是全部算力。微服务拆分带来的运维成本(服务发现、链路追踪、灰度发布)在这个规模下是纯负担;云数据库则把系统可用性绑死在宽带上——而酒店的宽带在旅游旺季恰恰是最不可靠的资源。

所以我们把约束反过来定:

  1. 断网必须可用:入住、收银、退房、房卡发放全程不依赖外网;
  2. 一个文件交付:安装 = 拷贝一个 exe + 一个配置文件;
  3. 数据在本机:账务数据不出店,老板拥有完整数据文件。

技术形态

最终形态是单进程 Go 程序:

  • 存储用 SQLite(WAL 模式):零运维、单文件备份(拷走即迁移)、事务语义足够覆盖酒店业务(日租业务的并发写压力极低);
  • 前端用 go:embed 打进二进制:SPA 静态资源由应用进程自己托管,升级 = 替换一个文件;
  • HTTP 单端口:应用直接监听 8443,TLS 可由前置 nginx 终结,也可由应用直挂;
  • 后台任务进程内起:自动夜审、UPS 低电关机、数据自检,全部是进程内 goroutine,不需要 crontab。
hotel-server(单二进制)
├── 静态前端(go:embed)
├── HTTP API(多租户路由 + JWT + 权限)
├── 业务模块(预订/接待/账务/夜审/会员/库存/报表...)
├── 设备驱动(发卡器/身份证读卡器/门锁/打印机,含模拟器)
└── SQLite(WAL;业务数据单文件)

踩过的坑

SQLite 的写锁不是问题,事务边界才是。 夜审批量落账、团队入住批量建房这类多写操作,务必显式开事务;否则 WAL 模式下读写不互斥的便利会让你忽略"半批成功"的一致性风险。

go:embed 的代价是前端构建进 CI。 前端产物没有构建就编译,嵌入的是旧资源——我们让 make run 依赖一个显式的 make web 步骤,并在 CI 里校验产物哈希,避免"改了前端没生效"这类事故。

Windows 服务注册交给 sc.exe 而不是自己写守护。 跨平台守护进程是坑坑洼洼的领域,用操作系统原生机制(Windows 服务 / Linux systemd)加一个 systemd unit 文件,比在应用里做双进程守护可靠得多。

收益

最终交付物:一个 exe、一个 YAML、一个数据文件。现场安装十分钟内完成;断网演练全程可用;老板看得懂"数据就在这台机器上"。

离线优先不是技术怀旧,是对这个场景可用性约束的正面回答。