从零到一:值班排班管理系统的设计与实现
本文记录了一个值班排班管理系统从需求分析、算法设计、编码实现到云端发布的完整过程。
一、为什么需要这个系统
团队有 6 名固定值班人员,每周 7 天都需要有人值班。手工排班面临几个痛点:
- 轮休安排复杂:每人每月要轮休 8 天,且通常两人同时轮休,手动配对容易出错
- 排班不均匀:有些人连续多天值班,有些人却长期空闲
- 年度不公平:到年底一算,有人值了 60 天,有人才 50 天,引发抱怨
- 季节性扩员:9-12 月会临时增加 2 人,排班逻辑需要动态调整
于是决定开发一个 Web 应用,用算法自动排班,保证公平。
二、需求确认:10 个关键问题
在动手之前,我逐一确认了 10 个关键问题,避免返工:
| # | 问题 | 答案 |
|---|---|---|
| 1 | 每天几个人值班? | 1 人 |
| 2 | 每人每天几班? | 1 班 |
| 3 | 固定人员数量? | 6 人 |
| 4 | 额外人员何时加入? | 9-12 月,增至 8 人 |
| 5 | 额外人员是否轮休? | 不轮休 |
| 6 | 额外人员是否计入年度均衡? | 不计入 |
| 7 | 轮休天数? | 每人每月 8 天,连续休 |
| 8 | 轮休方式? | 2 人一组同时休 |
| 9 | "稀疏分布"的含义? | 同一人值班不宜太密集 |
| 10 | 均衡优先级? | 年度均衡 > 月度均衡 > 稀疏分布 |
这 10 个问题的答案直接决定了算法的设计方向。特别是第 10 个问题——年度均衡是最高优先级,只针对固定 6 人;月度均衡针对所有参与排班的人(含额外 2 人)。
三、算法设计
3.1 整体思路
排班本质上是一个约束优化问题,但规模不大(一年 365 天,6-8 人),不需要整数规划这种重武器。我采用了贪心 + 多级权重评分的策略,简单有效。
算法分三个阶段:
轮休配对 → 逐日贪心排班 → 年度均衡调整
3.2 轮休配对
每月将 6 人分成 3 个 pair,每个 pair 连续休 8 天。
怎么分? 如果每月都用同样的配对,那某些人永远一起休假,社交面太窄。所以每月轮换 pair 组合:
月份 Pair组合
1月 (张三,李四) (王五,赵六) (钱七,孙八)
2月 (张三,王五) (李四,钱七) (赵六,孙八)
3月 (张三,赵六) (李四,王五) (钱七,孙八)
...
怎么排? 3 个 pair 的轮休期分布在月内不同时段,避免某段时间全员轮休导致无人值班:
30天的月份:
Pair 1 休第 1-8 天
Pair 2 休第 9-16 天
Pair 3 休第 17-24 天
第 25-30 天全员可用
轮休顺序也每月轮换,保证公平。
3.3 贪心评分排班
对每一天,从可用人员(未在轮休期)中选 1 人值班。选择标准是一个加权评分:
总分 = 年度均衡分 × 100 + 月度均衡分 × 30 + 稀疏分布分 × 10
三个维度分别是什么意思?
- 年度均衡(权重 100):当前全年累计值班次数越少,分越高。这是最高权重,直接驱动"落后的人优先排"。
- 月度均衡(权重 30):当前月内值班次数越少,分越高。保证月内不会出现某人值了 10 天另一个人只值了 2 天的情况。
- 稀疏分布(权重 10):距离上次值班的天数越多,分越高。避免某人连续 3 天值班。
为什么年度权重是月度的 3 倍多? 因为年度均衡是用户明确要求的最高优先级。月度均衡是"锦上添花"——在年度差距不大的前提下,让月内也尽量均匀。稀疏分布权重最低,因为它更多是"体验优化"而非"公平性要求"。
3.4 年度均衡调整
光靠贪心评分还不够。假设张三因为某些原因(比如某月轮休期恰好撞上很多节假日),前几个月落后了,单靠评分权重可能追不上。所以需要一个补偿机制:
每月结束后,检查 6 人的累计值班次数差距。如果某人明显落后,在下月的评分中给他额外加分。这就像跑步比赛中的"让步"——落后的人起点往前挪一点。
# 每月结束后的补偿逻辑
avg = sum(annual_counts[p] for p in fixed_six) / 6
for p in fixed_six:
deficit = avg - annual_counts[p]
if deficit > 0:
compensation[p] = deficit * 50 # 下月评分时加分
目标是年底 6 人总次数差距不超过 2。
3.5 验证结果
2025 年全年验证结果:
| 人员 | 全年值班天数 |
|---|---|
| 张三 | 55 |
| 李四 | 55 |
| 王五 | 56 |
| 赵六 | 56 |
| 钱七 | 55 |
| 孙八 | 56 |
6 人全年均衡差距仅 1(目标 ≤ 2),达标。额外 2 人(周九、吴十)各 16 天,完全均衡。
这个结果说明贪心 + 补偿的策略是有效的。不需要复杂的整数规划,简单的启发式算法就能达到很好的效果。
四、技术实现
4.1 技术选型
| 层 | 技术 | 理由 |
|---|---|---|
| 后端 | Python Flask | 轻量级,适合小型应用,3 个 API 足矣 |
| 前端 | 原生 HTML/CSS/JS | 无需框架,减少依赖,加载快 |
| 算法 | 纯 Python | 标准库足矣,无需 numpy/scipy |
没有用 React/Vue 这些前端框架,因为页面就一个,交互不复杂,原生 JS 完全够用。引入框架反而增加构建复杂度。
4.2 项目结构
scheduling-app/
├── app.py # Flask 后端(3 个 API 接口)
├── scheduler.py # 排班算法核心
├── templates/
│ └── index.html # 主页面
├── static/
│ ├── css/style.css # 响应式样式
│ └── js/app.js # 前端交互逻辑
└── README.md
4.3 后端 API 设计
只有 3 个接口,简洁明了:
GET /api/schedule?year=2025&month=8 # 获取某月排班
GET /api/stats?year=2025 # 获取全年统计
POST /api/regenerate?year=2025 # 重新生成某年排班
4.4 前端交互
前端用原生 JS 实现,主要功能:
- 年份选择器:支持 2024-2027
- 月份切换标签:12 个月一键切换
- 排班表格:日期、星期、值班人、轮休人一目了然
- 统计面板:当月统计 + 全年统计并排展示
- 颜色标记:9-12 月额外人员用不同颜色,视觉上区分固定班底和临时增员
五、部署与发布
5.1 运行环境
应用在华为云 CodeArts(AI DevSpace)沙箱中开发,Flask 监听 8080 端口。沙箱提供了完整的 Python 3.12 环境和预装的 Flask。
5.2 DevBridge 隧道
为了让外部用户能访问到沙箱内运行的应用,使用了华为云 DevBridge 开发隧道:
devbridge host -p 8080 -e 5
-e 5 表示隧道有效期 5 小时。创建后获得一个公网可访问的 URL:
https://gasmaumf-8080.cn-north-4-bridge.myhuaweicloud.com
5.3 代码托管到 GitCode
代码推送到了 GitCode 仓库:
https://gitcode.com/lightyearssssr/scheduling-app.git
六、开发过程中的踩坑记录
6.1 沙箱文件不可见于宿主机
CodeArts 沙箱使用 bwrap(bubblewrap)隔离,文件写在 overlay 文件系统的 upper 层,宿主机上 ls 看到的是空目录。解决方案:通过 acpx 会话在沙箱内启动 HTTP 服务器,宿主机通过共享网络命名空间 curl 下载 tar 包。
6.2 Playwright 模块解析路径
Playwright 安装在 skill 的 scripts/node_modules/ 下,自写脚本如果放在 /tmp/ 会报 Cannot find module 'playwright'。解决方案:脚本放在 skill 的 scripts/ 目录下,用 createRequire(import.meta.url) 解析模块。
6.3 base64 嵌入图片过大
将截图 base64 编码后用 sed 替换 HTML 中的占位符,因 base64 字符串过长导致 Argument list too long。解决方案:改用 file:// 协议引用本地图片路径,避免 base64 内联。
6.4 GitCode 仓库已存在
创建仓库时返回 422 “Project with same name already exists”。这是因为之前会话已经创建过同名仓库。直接使用已有仓库推送即可。
6.5 git 默认分支名
git init 默认创建 master 分支,但 GitCode 仓库默认分支是 main。git push -u origin main 报错 src refspec main does not match any。解决方案:git branch -m master main 重命名后再推送。
七、总结与展望
做对了什么
- 先问后做:10 个问题问完再动手,避免了多次返工
- 算法简单有效:贪心 + 补偿,不需要复杂数学,验证结果达标
- 技术选型克制:没有过度工程化,Flask + 原生 JS 足矣
- 自动化发布:从截图到门禁到 API 提交,全流程自动化
还能改进什么
这是 v1.0,后续计划:
- 节假日支持:目前不考虑节假日,实际场景中节假日可能需要特殊排班
- 手动调整:自动排班后允许手动微调某天的人员
- 通知功能:排班生成后自动通知当周值班人员
- 历史对比:对比不同年份的排班均衡度
- 多团队支持:目前单团队,扩展为多团队独立排班
一点感悟
排班看似简单,实则涉及多目标优化(年度均衡、月度均衡、稀疏分布)。不需要追求最优解,贪心策略 + 补偿机制就能达到"足够好"的结果。工程实践中,"足够好"往往比"最优"更有价值——因为前者简单、可维护、可解释,后者可能需要引入复杂数学工具,维护成本陡增。
- 点赞
- 收藏
- 关注作者
评论(0)