从零到一:值班排班管理系统的设计与实现

举报
哦啦啦啦啦 发表于 2026/08/21 14:10:24 2026/08/21
【摘要】 本文记录了一个值班排班管理系统从需求分析、算法设计、编码实现到云端发布的完整过程。 一、为什么需要这个系统团队有 6 名固定值班人员,每周 7 天都需要有人值班。手工排班面临几个痛点:轮休安排复杂:每人每月要轮休 8 天,且通常两人同时轮休,手动配对容易出错排班不均匀:有些人连续多天值班,有些人却长期空闲年度不公平:到年底一算,有人值了 60 天,有人才 50 天,引发抱怨季节性扩员:9-1...

本文记录了一个值班排班管理系统从需求分析、算法设计、编码实现到云端发布的完整过程。

一、为什么需要这个系统

团队有 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 后端(3API 接口)
├── 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 仓库默认分支是 maingit push -u origin main 报错 src refspec main does not match any。解决方案:git branch -m master main 重命名后再推送。

七、总结与展望

做对了什么

  1. 先问后做:10 个问题问完再动手,避免了多次返工
  2. 算法简单有效:贪心 + 补偿,不需要复杂数学,验证结果达标
  3. 技术选型克制:没有过度工程化,Flask + 原生 JS 足矣
  4. 自动化发布:从截图到门禁到 API 提交,全流程自动化

还能改进什么

这是 v1.0,后续计划:

  • 节假日支持:目前不考虑节假日,实际场景中节假日可能需要特殊排班
  • 手动调整:自动排班后允许手动微调某天的人员
  • 通知功能:排班生成后自动通知当周值班人员
  • 历史对比:对比不同年份的排班均衡度
  • 多团队支持:目前单团队,扩展为多团队独立排班

一点感悟

排班看似简单,实则涉及多目标优化(年度均衡、月度均衡、稀疏分布)。不需要追求最优解,贪心策略 + 补偿机制就能达到"足够好"的结果。工程实践中,"足够好"往往比"最优"更有价值——因为前者简单、可维护、可解释,后者可能需要引入复杂数学工具,维护成本陡增。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。