神慧
Thu Jul 02 2026 08:00:00 GMT+0800 (China Standard Time)·6 min readBackend

酒店自助入驻:办理状态机与外设异常回退

证件、选房、制卡、支付任何一步失败,都要让旅客知道下一步去哪。

#酒店#状态机#.NET#外设

酒店自主入驻把前台流程搬到终端:核验证件 → 匹配订单 → 选房 → 制卡/发码 → 完成。流程长,外设多,最怕停在「灰色中间态」——订单已锁房但卡没发出去,旅客站在机器前不知道是该重试还是找人工。后端用显式状态机建模整条办理链,比 scattered if-else 更容易对接 PMS、支付与运维台。

状态机要覆盖异常

每个关键态在代码里显式定义四件事:成功迁移到哪、失败回退到哪、是否允许重试、是否需要人工接管。状态变更写流水表,记录 fromState / toState / reason / traceId,方便和 PMS 日志、支付回调对齐。

典型态设计

  • 待验证 → 证件读取成功进 待选房,失败留在本态并计数重试
  • 待制卡 → 发卡机成功进 已完成,失败回 待制卡 而非直接 已完成
  • 待人工 → 超过重试上限或外设报硬故障时进入,前台 App 可接管

例如制卡失败:订单与房态不要先锁死;应回到「可重试制卡」,屏幕提示「请联系前台补卡」,同时把 PMS 侧房态标记为「待确认入住」,避免下一旅客被分配到同一间。

外设对接

证件阅读器、发卡机、打印机、支付模块都可能超时或返回厂商私有错误码。驱动层统一封装:固定超时、重试次数、错误码映射表;业务层只处理语义化结果(Success / Retryable / Fatal),不解析底层十六进制码。

超时与重试策略

可重试错误(纸卡暂缺、读卡干扰)应指数退避,上限后转 待人工。不可重试错误(设备离线、硬件自检失败)直接转人工,避免旅客无意义循环。所有外设调用带同一 traceId,排障时可还原完整时间线。

常见坑

  • 制卡成功但网络断在回写 PMS 前:本地流水先记「制卡成功、同步待确认」,定时任务补偿,而不是让旅客再扫一次证件
  • 同一订单并发在两台终端办理:用订单号做分布式锁,第二台提示「已在另一终端办理中」
  • 超时后直接标失败并释放房态:卡其实已吐出,造成重复发卡或空房纠纷

与 PMS 的边界

房态、订单金额以 PMS 为准,终端是执行器。状态机迁移成功后再调 PMS 接口;PMS 拒绝时要把终端态回滚到可重试点,并在 UI 展示 PMS 返回的可读原因,而不是笼统「系统错误」。

前台监控

大堂经理需要实时看到:哪台终端卡在哪一步、停留多久、最近一次外设错误是什么。值班台提供「远程重置到安全态」——不是删数据,而是把终端迁回 待验证 并解锁关联预占,避免运维只能现场拔电源。

支付与退款衔接

若终端集成预授权或押金扣款,支付状态应作为状态机独立分支,与制卡结果解耦。支付成功、制卡失败时,不能默认「钱已收流程结束」,而要进入「待退款或转人工结算」态,并在 UI 给出明确指引。退款接口同样要幂等,避免重复退。

上线前检查清单

  • [ ] 任意办理记录可按 traceId 查全链路
  • [ ] 每种外设故障有明确 UI 文案与前台接管入口
  • [ ] 幂等:同一订单重复提交不会重复锁房或重复扣款
  • [ ] 终端断网可完成本地可恢复步骤,恢复后自动补偿同步
  • [ ] 支付与制卡失败组合场景有定义态与 Runbook

自助不是无人,而是「异常时有人可接手」。状态机写清楚了,接手的人才知道该补哪一步;旅客体验上的「下一步去哪」,本质上就是状态机是否把异常路径设计完整。

小结

自助入驻的体验分打在异常回退是否清楚。支付、制卡、PMS 更新必须允许分步失败并重试,前台监控按 traceId 串联。上线前用高峰演练验证:终端卡住时,人工接管路径是否真的可执行。