尧图精选

教学工作量统计系统开发实战:Python+Vue3+FastAPI

🕒 发布时间:2026/10/2 3:19:59 📁 来源:尧图网络
教学工作量的统计这也能算个项目说实话我接到这个需求时第一反应也是这句话。当时学院的教务处秘书从文件柜里搬出厚厚一摞Excel每学期结束都要靠人工核对课程表、教师任课记录、课时津贴表来回折腾两周以上还经常被老师质疑“课时算少了”。学院领导拍板后端用Python前端用Vue3做一套学院教学工作量统计系统目标是能填、能算、能审、能看。我原本以为就是给Excel表加个网页壳子真正把需求吃透之后才发现这套系统的核心难点根本不在写界面而在“工作量”三个字背后的业务规则——课时怎么折算、不同课程怎么加权、平行班重复班怎么处理、跨学期调课怎么追溯。这篇文章把我从需求调研到上线运行全过程的思路、踩坑和优化经验完整记录下来给准备接高校内部管理系统尤其是信息填报、审批流转、统计报表类项目的朋友做个参考。1. 一个教学秘书的Excel噩梦和系统最初的需求边界1.1 旧流程的数据到底乱在哪先把旧流程拆开看。学院每个学期的数据主要来自三块教务系统导出的课程安排表老师手工提交的调课记录以及实验、实习、毕业设计这类实践环节的统计表。这三块数据格式完全不一致课程安排表里一个老师可能对应好几门课调课记录是临时产生的实践环节又没有统一模板全靠秘书在Excel里手工拼。我调研时印象最深的一幕是秘书姐姐给我演示她怎么用VLOOKUP跨表匹配教师工号结果因为一个单元格里有空格匹配出来全是错误值她再逐行手工改。这种场景在高校里太常见了数据本身不大但散落在各类表格里又没有统一的编码规范一学期的数据清理就能耗掉大半精力。最要命的是“折算规则”藏在人脑里。同一门课理论课一个学时算1个工作量实验课可能只算0.8但如果实验课分了组、每组人数超过阈值又要乘一个班容量系数。这些规则从来没有一份文档写清楚全靠历任秘书口口相传。所以系统开发的第一件事不是建表写代码而是把所有折算规则通过和教学秘书、系主任的多次访谈逐条确认落到纸面上给学院签字确认。这一步我强烈建议不要省业务规则不锁定后面所有代码都是空中楼阁。1.2 技术选型的两个“不折腾”理由为什么选Python Vue3而不是其他组合复盘下来有两个很现实的原因。第一学院里能长期维护这类系统的通常是信息中心的老师或研究生Python的学习曲线平缓、生态齐全处理Excel有 pandas 和 openpyxl做报表有现成方案后端写起来比Java那一整套工程化链路轻快得多。第二前端用Vue3是因为组件化思路清晰配合Element Plus能快速搭建表单、表格、弹窗这些管理系统的高频组件而且Vue3的Composition API在处理筛选条件、分页、弹窗状态这类逻辑时比Vue2的Options API顺手很多代码不容易越写越乱。这类内部系统不追求极端性能追求的是“开发快速、长期可维护、换人也能接手”。Python和Vue3恰好都占住了这三点。另外我后端没有选Django而是用了FastAPI原因是这个项目的接口几乎都是JSON数据交换FastAPI的异步支持和Pydantic参数校验让代码更简洁自动生成的OpenAPI文档也方便前端同学对接。如果你更熟悉Django用Django实现同样的功能也没问题只是要在模型层多一些配置。1.3 整体架构与三类角色系统架构上我采用前后端分离后端Python FastAPI前端Vue3 Vite Element Plus ECharts数据库MySQL。整个系统用户分为三类普通教师——登录后只能查看和填报自己的工作量教学秘书——负责审核、导入基础课程数据、维护折算系数学院领导——只看汇总统计和可视化看板不参与流程。这个三角色模型是所有功能设计的地基后面每个模块都在围绕它展开。数据流向也很简单秘书每学期初导入开课数据教师在期末核对确认工作量秘书审核汇总领导看板完成最终决策支持。整个链路闭合没有多余的环节。2. 工作量不是课时的简单加总业务模型与折算逻辑2.1 工作量课时×系数的基本换算工作量统计最容易踩的坑就是把它当成“课时求和”。实际上学院的工作量体系是一套乘性模型核心公式是课程工作量 计划学时 × 课程类型系数 × 班容量系数 × 平行班系数 × 新开课系数每一层系数都对应一条业务规则。比如理论课的基础系数是1.0上机课是0.8实验课是0.9班容量超过60人后系数上浮到1.1超过100人上浮到1.2一个老师带两个平行班第二个班就要打五折新开课程因为备课成本高首学期给1.2的加成但只加成一次。这些规则看起来不复杂但它们必须做成可配置的而不是硬编码在if语句里。因为学院可能每年调整政策比如某年规定“班容量超过120人才上浮”如果写在代码里改一次规则就要发一次版完全不现实所以我把系数表单独建了一张配置表界面上做成系统参数维护页面秘书自己能改。2.2 课程类型、班容量与折算系数的数据建模数据建模上我用五张核心表承载这套逻辑teacher表存教师基本信息course表存课程基本信息teaching_task表存某个学期某位老师承担的某门课是工作量计算的最小单位workload_record表存每个条目的工作量明细和计算过程中的中间值coefficient_config表存所有类型、容量区间的系数按学期版本生效。teaching_task表设计上要特别留意的字段包括teacher_id、course_id、semester、class_type理论/实验/上机、plan_hours计划学时、class_count选课人数、group_count实验分组数、is_repeated是否平行班、is_new_course是否新开课。这些字段看似冗余但它们是计算的全部输入。最开始的版本我只存了一个class_type和plan_hours结果发现实验课是按组算的没有组数字段根本算不对后来才补上的。工作量明细的计算流程我做成一个独立服务不在接口里直接写公式。因为同一批数据可能因为规则调整需要重算独立成服务后只需要重跑一次计算任务所有记录就会按最新规则刷新层级清晰排查问题时也方便哪条记录算错了直接把输入参数和每一步中间值拉出来对。2.3 跨学期与调课场景的边界处理边界情况才是最考验系统设计的地方。调课记录怎么算是我和秘书反复确认最多的问题。比如一位老师原本这学期上A课程32学时因为院里临时安排期中调走了4个学时给同事代课那么原老师的32学时就要拆成284代课老师的4学时单独生成一条记录。拆单时还要处理“新开课系数是否保留”的问题因为代课老师接手时这门课已经开过了不应该享受新课加成。这类边界如果不提前想清楚系统上线后就会被业务人员用真实案例问倒。跨学期延期的课程也要单独处理。有些毕业设计指导是一个学年的事分两个学期计算每学期按一半工作量计。我当时加了parent_id字段做关联把两个学期的指导记录绑在一起方便做全学年汇总。总结一句话业务建模阶段多花时间听秘书讲“特殊情况”写代码阶段就能少改一半需求。3. Python后端数据模型、计算引擎与报表实现3.1 数据模型与关系设计FastAPI加SQLAlchemy的组合在这个项目里很顺手。教师和课程之间是多对多关系通过teaching_task中间表关联workload_record又和teaching_task一对一存储计算出来的最终工作量。设计时有个细节workload_record里除了最终值我额外存了base_hours、final_coef、total_workload三个字段分别对应原始学时、综合系数、折算结果。这个设计的价值在于最终汇总表的每一行都能解释清楚“这个数字是怎么来的”老师有疑问时可以直接看到明细不用层层解谜。字段类型上也有教训。教师工号这种字段一开始建成了整型后来发现有些老师工号是0开头的字符串整型直接截掉了前缀导致导入匹配失败。凡是编号类字段一律用字符串存这个习惯从那以后我再没改过。学期字段我也用统一的格式比如2023-2024-1而不是随便填“2023秋”否则排序、筛选、按学期汇总都会出问题。3.2 计算引擎的实现思路计算引擎我单独抽成了一个函数核心逻辑大致是def calc_task_workload(task: TeachingTask, cfg: CoeffConfig) - WorkloadRecord: base_coef cfg.get_class_type_coef(task.class_type) size_coef cfg.get_size_coef(task.class_count) repeat_coef 0.5 if task.is_repeated else 1.0 new_course_coef cfg.new_course_coef if task.is_new_course else 1.0 final_coef base_coef * size_coef * repeat_coef * new_course_coef total round(task.plan_hours * final_coef, 2) return WorkloadRecord(...)coefficient_config表的数据结构用“生效学期类型编码条件区间下限条件区间上限系数值”来表达比如class_type_coef, theory, 0, 9999, 1.0就是理论课基础系数1.0。这样新增一种课程类型或调整一个区间只改配置不改代码。计算引擎还需要有幂等性同一批任务重算多次结果必须一致这样秘书可以放心在学期末随时点“重算全部”。批量计算性能上最初版本是逐条查配置表数据量到几百条时没什么感觉但学期末一次性重算几千条记录时明显变慢。后来我把配置表一次性加载成内存字典计算循环内线程安全地读取速度瞬间从十几秒降到一秒以内。这个优化很简单但收益非常直观。3.3 Excel导入导出看起来简单实则最磨人系统的数据入口是Excel导入因为教务系统导出的开课清单就是Excel格式。导入这块用了pandas openpyxl但真正磨人的不是读写本身而是脏数据清洗。最典型的问题有单元格里有不可见空格导致工号匹配不上课程名称中英文括号混用人数列是文本格式且带“人”字后缀调课记录表头在不同Excel文件里偶尔不在同一列。我最终写了一个清洗管线读入数据后先做列名校准、字段类型强制转换、工号/课程号格式化再逐行校验必填字段把校验失败的记录输出到一个错误报告Excel标记清楚原因方便秘书下载修改后重新导入。第一次上线时秘书反馈“导入报错都不知道哪一行错了”加了错误定位报告后基本没有再来找我抱怨过。导出端则要生成符合学院汇总要求的工作量报表表格的合并单元格、页脚合计这些细节都要处理生成后用自动化脚本抽查几组数据对账确保导出值和一个手工核算结果一致。4. Vue3前端填报页、审核流和可视化看板4.1 为什么用Composition API Element Plus前端用Vue3 Vite Element Plus是综合考虑后的选择。Vite的冷启动和热更新速度比Webpack时代快了一个量级开发体验完全不同。Element Plus直接提供了 Table、Form、Dialog、Tabs 等管理系统的标准组件我几乎没怎么写UI底层全部精力都放在业务交互上。Vue3的Composition API在这个项目里优势很明显最直接的表现是三个功能页面各自独立维护状态。以工作量填报页为例我用一个useWorkloadStore的组合式函数统一管理筛选条件、分页、选中行、弹窗开关页面组件代码量少、逻辑集中后续加需求时不用在data/methods/computed三个区域来回跳。如果换成Vue2的Options API几十个data字段混在一起维护起来很容易晕。4.2 填报与审核的交互细节填报页面是整个系统使用频率最高的地方交互设计直接决定系统会不会被老师们嫌弃。每个老师登录后只看到自己的教学任务列表每条任务显示课程名称、学时、班级人数、折算系数系统自动算好工作量。老师要做的只是核对如果信息有误可以发起“修改申请”填写说明后推给秘书审核。这样设计有一个好处大部分情况下老师只是查看确认真正需要手动填的内容不多界面看起来清爽使用门槛也低。审核页面针对秘书的角色做了批量操作设计。秘书打开待审核列表后可以勾选多条记录进行批量通过或驳回驳回时必须填写原因因为后台要记录完整审核日志否则老师不知道被驳回的理由沟通成本会变高。审核列表我用Element Plus的Table组件加多选列配合分页和筛选器一学期几千条记录的审核可以分课程类型、分教师快速过滤。前端状态管理我用了Pinia但只在全局登录状态和用户角色上使用页面级状态都用组合式函数自己维护。小系统没必要把所有状态都塞进Store塞多了反而让不同页面互相耦合。数据请求统一封装了一个axios实例带JWT拦截器请求失败时根据状态码做统一提示和401跳转这部分是每个Vue3项目都该有的基础设施。4.3 可视化看板让数据会说话领导不看明细只看汇总。看板页面我用了ECharts做了四个核心视图学院整体工作量按系部分布柱状图、各课程类型的工作量占比饼图、教师个人工作量排名Top10横向条形图、近三个学期工作量趋势折线图。这些图表全部从后端汇总接口一次取数前端不做二次计算因为汇总口径必须后端统一否则前端算出来的数可能和后端对不上。图表的数据接口设计上我把时间范围、系部筛选条件做成可选参数领导可以在看板页筛选“某一个系部的工作量分布”也可以查看全院数据。ECharts在Vue3里用起来很简单关键是setOption的数据结构要提前设计好避免图表组件里做大量字段转换容易出错也不好维护。另外图表容器高度需要显式设置否则初始渲染时默认高度0图表出不来这个细节当时也坑了我十分钟排查后才发现是容器高度问题。5. 权限体系设计与审批链路上线前的最后一道坎5.1 三类角色、两种数据范围的权限矩阵权限设计我花了不少心思因为之前见过太多管理系统“看起来有角色实际上所有人都能看所有数据”。这套系统最终遵循一个简单的矩阵教师只能看本人数据和自己的任务秘书可以看全院数据但只有审核和导入权限不能修改老师填写的原始申请领导只读汇总看板和数据导出不能改动任何业务数据。具体落地时后端每个接口都根据当前用户角色做了数据范围控制而不是只靠前端隐藏菜单。比如查询工作量列表的接口教师角色会自动追加teacher_id 当前用户的过滤条件秘书和领导则可以带系部或全院条件。这个逻辑必须写在后端因为前端路由和按钮说到底只是用户体验层面的限制真正的数据保护在后端的查询边界。5.2 审批流的状态机设计工作量填报审批状态我设计了四个草稿、待审核、已通过、已驳回。每个状态下能执行的操作完全确定不能乱跳。比如已通过的任务不能再次提交修改必须先由老师发起“更正申请”并经过秘书审核后才能走更新流程。状态机我用一张状态流转表驱动前端显示按钮、后端校验合法性都基于这张表。这个设计的价值在系统运行后体现得很明显。凡是状态机定义清晰的功能几乎不会有“这个操作不该出现但出现”的问题。反而是那些图省事直接写if判断状态的功能后期需求一变就容易漏掉某条路径产生脏数据。如果项目要做得更完整还可以加“已生效”和“已归档”两个状态做学期封存我当时因为学院要求简单就没有做但留给后续扩展的字段已经预留好了。5.3 JWT接口鉴权与前后端联动登录认证用的JWT方案配置了短时效的access_token两小时和长时效的refresh_token七天前端在请求拦截器里遇到401时自动刷新。FastAPI侧采用OAuth2PasswordBearer做依赖注入自定义了角色校验依赖接口上直接声明需要的角色权限即可。router.get(/api/workloads, dependencies[Depends(require_role(secretary))])前端路由用了一个简单的动态路由方案根据登录用户的角色渲染不同的菜单。这项配置在build阶段就确定运行时不做模糊判断。部署时要注意的安全点是前端所有涉及上传的接口要限制文件类型和大小Excel导入文件大小我限制为5MB防止异常文件把服务拖垮上传后服务器做病毒扫描不是内部小系统的必须项但至少要做文件类型白名单校验这是成本最低的防护。6. 实测里最值得记录的坑与性能优化6.1 多学期数据累积后的查询性能回退系统上线时数据量不大查询基本秒开。运行到第二学期末累积了几万条工作量明细后汇总接口开始出现明显卡顿耗时最多到了三秒以上。排查后发现是典型的N1问题查工作量记录列表时每条记录都额外查一次教师和课程信息几百条记录就是几百次数据库查询速度自然上不去。解决方案是改用SQLAlchemy的joinedload一次性预加载关联对象同时给semester、teacher_id、course_id这些高频筛选字段建联合索引。优化后列表接口响应时间稳定在500毫秒以内。另外汇总统计SQL里我尽量用一次group by完成所有维度的聚合而不是在Python里循环计算尤其在看板接口上这个改动把数据加载时间从四秒降到了不到一秒响应速度提升非常明显。6.2 并发提交导致的数据覆盖问题还有一次线上问题很值得记录两个老师同时提交修改申请时偶尔会出现其中一个提交被静默覆盖最终表现在数据库里的内容既不是A的也不是B的而是两人表单字段的混合体。定位后发现是前端在编辑弹窗里直接拿了列表行的对象引用做双向绑定两个弹窗同时打开时修改同一个响应式对象导致相互污染。修复方案有两个层面前端方面打开编辑弹窗时用JSON.parse(JSON.stringify(row))做深拷贝确保弹窗里的数据是独立副本后端方面给workload_record表加version字段做乐观锁提交时校验版本号不一致就返回冲突提示让用户刷新后再操作。两个措施配合后这类问题再没出现过。6.3 部署、浏览器兼容与运维细节部署上我用的是常规的Nginx Gunicorn MySQL组合前端构建产物直接放到Nginx静态目录接口走反向代理到后端。这里要提醒一个细节如果系统要部署在子路径下Vue3的静态资源路径必须配置base为对应子路径否则CSS和JS全部404。我第一版部署就是因为忘了改这个配置排查了好久。浏览器兼容方面系统本身只要求现代浏览器但学院里确实有老师还在用IE内核的旧Edge内核模式。我在入口页写了一个简单的环境检测检测到非现代浏览器就提示升级同时确保主要页面在Chrome和Edge新内核下表现一致。表单校验、日期选择这些交互在浏览器间的差异主要集中在颜值层面不影响业务做到主流程可用即可。运维上我加了每天凌晨的数据库全量备份脚本保留最近两周备份虽然学院数据量不算大但这个习惯让我睡得安稳很多。最后再分享一个做这类系统的心得也是我这次项目里最深的体会内部管理系统的技术占比其实不到一半更关键的是把业务规则吃透、把数据口径统一、把状态流转闭环。如果你正在准备做类似的工作量统计、课时填报、绩效管理系统多花时间在需求访谈和规则梳理上远比纠结用哪个框架更值得。Python加Vue3这套组合干这类活足够高效剩下的就是上线后根据老师的真实反馈一点一点把细节打磨顺滑。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →