本地优先排班应用怎么做:循环排班、事务与版本迁移实践

利益相关:我是《班妥了》的开发者。本文主要复盘产品实现中遇到的数据建模问题,涉及产品时会明确说明。

排班应用看起来像是“日期对应一个班次”,真正做起来却很容易在换班、跨月、夜班和规则调整时出错。尤其是本地优先的应用,既要保证离线可用,又要让旧数据在版本升级后继续可靠地工作。

这篇文章整理几个我在开发鸿蒙排班应用时反复推敲的问题。

1. 不要把每天的结果都当成独立记录

如果用户采用“白班、白班、夜班、夜班、休、休”这样的循环,最直接的做法是提前生成几个月的逐日记录。但这样会带来两个问题:

  • 修改循环后,需要重算大量未来数据;
  • 很难区分“循环计算出的班次”和“用户临时换班”。

更合适的模型是把数据拆成三层:

  1. 班次模板:名称、颜色、开始时间、结束时间;
  2. 循环规则:锚点日期和班次序列;
  3. 单日覆盖:只保存请假、换班、临时加班等例外。

查询某一天时,先检查单日覆盖;没有覆盖,再按循环规则计算。这样数据量小,也能清楚表达“规则”和“例外”。

循环下标需要特别注意负数日期。某些语言里负数取模仍然是负数,可以统一写成:

index = ((相差天数 % 循环长度) + 循环长度) % 循环长度

这样查询锚点之前的日期也不会越界。

2. 排班规则变更,要保留历史分段

现实中很少有人永远使用同一套轮班规则。某个月可能是四班三倒,下个月改成两班倒。如果直接覆盖原规则,过去的日历也会跟着变化,统计结果就失真了。

我的处理方式是把循环规则做成“生效区间”:

  • 旧规则保留原起始日期和结束日期;
  • 新规则从指定日期开始生效;
  • 查询日期时,先找到覆盖该日期的规则分段,再计算循环位置。

这和配置版本化很像:新配置只影响未来,不篡改历史。实现时还需要检查区间是否重叠,以及新分段是否留下无法解释的空档。

3. 跨午夜班次不能只比较时分

夜班常见的时间是 20:00 到次日 08:00。若只存“开始分钟”和“结束分钟”,结束值会小于开始值。

一种简单可靠的表达方式是增加“跨日”标记:

duration = endMinute - startMinute;若 crossDay 为真,再加 24 × 60。

统计工时时按持续时长计算;日历归属则明确采用“班次开始日”或产品约定的规则。两者不要混用,否则月末跨夜班很容易被算到错误月份。

4. 本地优先不等于只调用几次数据库 API

《班妥了》的鸿蒙版采用本地存储,不要求登录。这个体验很轻,但数据层反而要更谨慎:

  • 创建排班规则、班次和关联记录时使用事务;
  • 删除模板前检查是否仍被循环或覆盖记录引用;
  • 数据库升级使用明确的 schema version;
  • 每次迁移都要支持旧版本数据逐级升级;
  • 迁移失败时回滚,避免出现“界面能打开、数据已半迁移”的状态。

开发阶段我会用至少三类样本回归:全新安装、上一个正式版本升级、包含跨月与临时换班的真实规模数据。

5. 桌面卡片也是数据一致性的一部分

鸿蒙版提供 2×2 和 2×4 桌面卡片。卡片展示今天或近期班次,看似只是另一套 UI,实际上是排班结果的另一个消费者。

当用户修改循环、单日覆盖或班次模板后,除了刷新应用页面,还要主动更新卡片;跨过午夜后也需要重新计算“今天”。如果卡片和应用使用两套计算逻辑,就很容易出现一个显示白班、另一个显示休息的情况,因此最好共用同一套查询服务。

从工程结构回到产品边界

目前《班妥了》有鸿蒙版本和微信小程序版本。

  • 个人版侧重循环排班、单日覆盖、提醒,以及月度/年度统计;
  • 团队版侧重成员、岗位、逐日班次和月历中的在岗情况;
  • 鸿蒙版免费、无广告、无需登录,数据保存在本地。

它定位为轻量排班工具,不是把考勤、薪资和劳动合规全部包进去的 HR 系统。这个边界也直接影响数据模型:先把“哪天上什么班、临时如何调整”做稳,再考虑更重的组织流程。

最后放一张当前鸿蒙版的个人排班界面,便于理解上述模型最终落到什么样的交互上:

在这里插入图片描述

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐