ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

执勤综合管理系统开发实践:Spring Boot + Vue 排班签到与巡更模块全解析

执勤综合管理系统开发实践:Spring Boot + Vue 排班签到与巡更模块全解析 简介《基于Web的执勤综合管理系统的设计与实现》是一份完整的毕业设计论文文档面向需要参考ASP.Net技术及B/S模式开发管理信息系统的计算机专业学生和开发者。论文以执勤管理为业务背景完整覆盖需求分析、系统设计、系统实现与系统测试四个阶段功能涵盖用户登录、排班管理、执勤人员管理、网络查勤员管理、要事记录、公告信息管理、个人信息管理等模块并详细给出了数据库访问实现、主要功能模块的关键代码以及测试结果分析。资源包内共1个doc文件整体约1.5MB内容结构完整便于直接阅读与参考。目前已有112人学习。借助这份论文读者可以系统掌握基于ASP.Net的Web管理系统从需求建模、数据结构设计到安全机制实现的全过程同时可作为撰写同类毕业设计或课程论文的框架与范例。1. 执勤综合管理系统在解决什么问题从纸质台账到实时协同的跨越一个没有执勤管理系统的单位排班靠 Excel 和微信群来回对签到靠纸质本子巡更记录是巡查完回去补填的月底统计要把台账翻一遍再手动汇总。这个标题里的执勤综合管理系统就是把排班、签到签退、巡更打卡、事件上报、统计报表这五件事从纸面搬到 Web 端让执勤人员、班组长和管理层在同一个系统里完成闭环。这类系统在高校毕业设计和企业信息化项目里出现频率都极高技术上完全有成熟套路Spring Boot 做后端、Vue 做前端、MySQL 存数据核心难点不在某个高科技算法而在排班规则落地、GPS 校验、并发防重这些业务细节。下面按架构、数据库、代码实现、踩坑、验证这条线讲完照着做能省至少两周的返工时间。2. 系统架构与数据库设计B/S 三层结构和 5 张核心表2.1 技术选型为什么 Spring Boot Vue 是这类系统的最稳组合执勤综合管理系统这类企业内部系统最常见、最可靠的组合就是 Spring Boot 做后端服务、Vue Element UI 做前端页面、MySQL 做数据存储、MyBatis Plus 做 ORM。这个组合的好处是生态成熟遇到问题搜一下基本都有答案部署简单一个 jar 包加一个前端静态目录就能跑起来而且对于做毕业设计的场景论文里的架构图画出来就是标准的 B/S 三层结构表现层Vue 页面、业务逻辑层Spring Boot 控制器和服务层、数据层MySQL。做过 web 前端开发的都清楚后台管理系统七八成页面是表格加表单Element UI 的表格组件、表单校验、弹窗确认开箱即用开发效率比手写组件高一大截。有些同学会纠结要不要上微服务把 Nacos、Gateway 都堆上去。我的观点很直接执勤管理系统顶多几十个并发部署环境往往还是单位的一台老服务器微服务除了让论文显得高级一点带来的全是运维负担。真正让论文有亮点的地方是把排班算法、GPS 校验、报表统计这些业务细节做扎实。做 java web 项目选型最忌讳技术栈堆砌企业级 web 开发的评价标准是稳定可用不是框架新潮。前端选 Vue 2 Element UI 还是 Vue 3 Element Plus我的建议是看团队熟悉度。Vue 2 的现成模板和教程多毕业设计和中小型项目图稳可以选它团队本来就用 Vue 3 的话直接上 Element Plus接口设计完全一样所有数据交互走 RESTful API和 Vue.js web API 的标准写法一致后续要换前端框架都不用动后端。这里唯一要注意的是如果是部署在内网环境的系统前端构建产物必须是纯静态文件不能依赖外网 CDN否则内网打开页面会白屏。这个问题后面避坑章节里细说。2.2 核心数据库设计排班表、签到表、巡更点表的字段规划数据库设计是这类系统里最容易返工的部分。见过太多人上来就建二三十张表结果一半的表根本用不上。执勤综合管理系统核心就五类数据用户、排班、签到、巡更、事件。先看排班表CREATE TABLE duty_shift ( id BIGINT PRIMARY KEY AUTO_INCREMENT, shift_date DATE NOT NULL COMMENT 执勤日期, shift_type TINYINT NOT NULL COMMENT 1-早班 2-中班 3-晚班, user_id BIGINT NOT NULL COMMENT 执勤人员ID, location_id BIGINT NOT NULL COMMENT 执勤点位ID, start_time TIME NOT NULL COMMENT 班次开始时间, end_time TIME NOT NULL COMMENT 班次结束时间, status TINYINT NOT NULL DEFAULT 0 COMMENT 0-待执行 1-进行中 2-已完成 3-已调班, remark VARCHAR(255) DEFAULT NULL COMMENT 备注, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_date_user (shift_date, user_id, shift_type) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4 COMMENT 执勤排班表;这个设计的几个关键点第一shift_date 是业务日期和 start_time、end_time 拆开存后面跨天夜班判断会用到这是很多人踩坑的地方第二唯一索引 uk_date_user 保证同一个人同一天同一个班次只能有一条记录这是排班冲突的第一道防线第三status 字段用整型枚举而不是字符串避免脏数据代码里用常量类统一管理。第四update_time 用 ON UPDATE CURRENT_TIMESTAMP 自动维护调班、改状态时不用手动赋值。签到表要记录每次签到的完整事实包括时间、定位、照片、设备信息CREATE TABLE duty_attendance ( id BIGINT PRIMARY KEY AUTO_INCREMENT, shift_id BIGINT NOT NULL COMMENT 关联排班ID, user_id BIGINT NOT NULL COMMENT 执勤人员ID, check_type TINYINT NOT NULL COMMENT 1-签到 2-签退, check_time DATETIME NOT NULL COMMENT 打卡时间, longitude DECIMAL(10, 6) DEFAULT NULL COMMENT 经度, latitude DECIMAL(10, 6) DEFAULT NULL COMMENT 纬度, address VARCHAR(255) DEFAULT NULL COMMENT 定位地址描述, photo_url VARCHAR(255) DEFAULT NULL COMMENT 现场照片, device_info VARCHAR(255) DEFAULT NULL COMMENT 设备标识, UNIQUE KEY uk_shift_type (shift_id, check_type) ) ENGINE InnoDB DEFAULT CHARSET utf8mb4 COMMENT 执勤签到表;这里有个重要决策签到记录必须关联 shift_id 而不是只记 user_id 和时间因为后面统计某个班次有没有人迟到、有没有按时签到全靠这个外键。唯一索引 uk_shift_type 从数据库层面挡住重复签到——同一次执勤同一个签到类型只能有一条记录这是并发防重的最底层兜底。经纬度用 DECIMAL(10,6) 是为了支持到 6 位小数对应大约 0.1 米的精度足够 GPS 校验用。巡更表记录每次巡更打卡字段和签到类似但多了巡更点编号和路线 ID事件表记录执勤过程中上报的异常情况比如安全隐患、设备故障、突发纠纷。加上用户表五张核心表就够了。不要一开始就建考勤统计表月报汇总表——这些应该用 SQL 实时算出来而不是物理存储否则每次排班调整都要同步改汇总数据迟早翻车。2.3 权限模型与接口规范从登录到角色权限的一整套约定执勤系统的用户角色一般分四级超级管理员管系统配置、单位管理员管排班和报表、执勤组长看本组数据和审批调班、执勤人员签到签退和上报。权限模型不需要引入复杂的 RBAC 框架Spring Security 或者 Sa-Token 的注解鉴权足够。我用 Sa-Token 多一些上手成本比 Spring Security 低而且内置了踢人下线、账号封禁这些对执勤场景很实用的功能。权限校验做在拦截器层面每个接口按角色标注访问要求这就把 web 安全的基础打住了——至少不会出现普通执勤人员直接调管理接口的情况。接口规范上前后端分离的典型约定是统一返回结构、统一异常处理、Token 鉴权。返回结构固定为 code、message、data 三件套{ code: 200, message: success, data: {} }所有业务异常在全局处理器里拦截业务失败返回 code 4000 系列登录失效返回 401前端 axios 响应拦截器统一判断并跳转登录页。这套约定看似基础但它是后面接企业微信通知、做移动端 H5、甚至接第三方打卡设备时不用改接口的前提。做 web 工程的人应该都体会过接口格式不统一的痛苦所以在项目第一天就把规范定下来后面省心很多。3. 核心功能实现排班、签到、巡更三条主线的代码落地3.1 排班模块轮值算法与冲突检测的 Java 实现排班是执勤系统的核心业务。常见需求是指定若干个点位每个点位每天需要早中晚三个班次执勤人员轮转。最朴素的实现是管理员手动拖拽排班但作为系统设计至少要能按规则自动生成整月排班然后允许手动微调。轮值算法的实现思路是把人员列表按序号排好按日期 班次递增的规律分配人员每人每天最多一个班次。核心代码/** * 生成整月轮值排班 * param locationId 执勤点位ID * param month 月份格式 2025-06 * param userIds 参与轮值的人员ID列表 * param shiftsPerDay 每天班次数 */ public ListDutyShift generateMonthlySchedule(Long locationId, String month, ListLong userIds, int shiftsPerDay) { ListDutyShift result new ArrayList(); YearMonth ym YearMonth.parse(month); int days ym.lengthOfMonth(); int userCount userIds.size(); int cursor 0; // 轮值指针记录当前排到第几个人 for (int day 1; day days; day) { LocalDate date ym.atDay(day); for (int shiftType 1; shiftType shiftsPerDay; shiftType) { DutyShift shift new DutyShift(); shift.setShiftDate(date); shift.setShiftType(shiftType); // 取模循环分配排到队尾后回到队头 shift.setUserId(userIds.get(cursor % userCount)); // 从班次配置表读取该班次的时间范围 setShiftTimeRange(shift, shiftType); result.add(shift); cursor; } } return result; }这段代码逻辑不复杂核心就两件事第一cursor 指针按顺序遍历人员列表取模实现循环轮转第二双层循环按天和班次生成所有排班记录保证每天每个班次都有且仅有一条记录。setShiftTimeRange 从配置表读取班次时间把 start_time 和 end_time 填进去。参数 shiftsPerDay 由点位配置决定有的点位一天三班有的点位只需要早晚两班算法不用改传参就行。实际落地时每人每天最多一个班次这个约束不能只靠算法逻辑保证因为手动调整排班时可能违反。我一般在保存排班的服务层再做一次校验查同一个人同一天是否已有记录有就拒绝。这就是算法逻辑 数据库唯一索引 服务层二次校验三层防线排班数据基本不会出脏数据。排班模块还有两个容易忽略的需求。一个是调班审批执勤人员甲和乙要互换班次需要组长审批后同时更新两个班次的 user_id注意这里要用事务包裹否则只改了甲没改乙排班就乱了。另一个是节假日规则不少单位要求节假日和周末的班次密度与工作日不同。自动生成逻辑里可以预置工作日模板和节假日模板管理员在生成前选择用哪套每个模板定义班次数量和起止时间系统按日期匹配模板生成。3.2 签到签退接口GPS 校验与防重复提交的 Spring Boot 实现签到签退是调用频率最高的接口也是并发问题最集中的地方。先看控制器层代码PostMapping(/api/attendance/checkin) public ResultAttendanceVO checkIn(RequestBody CheckInRequest request) { // 1. Token 鉴权由拦截器完成这里直接拿当前登录用户 Long userId StpUtil.getLoginIdAsLong(); // 2. GPS 范围校验执勤点位半径内才算有效打卡 DutyLocation location locationService.getById(request.getLocationId()); double distance GeoUtil.distance( request.getLatitude(), request.getLongitude(), location.getLatitude(), location.getLongitude()); if (distance location.getAllowedRadius()) { return Result.error(4001, 不在执勤点位范围内无法签到); } // 3. 查当前用户待执行的排班判断签到时间是否在允许窗口内 DutyShift shift dutyShiftService.getCurrentShift(userId, LocalDateTime.now()); if (shift null) { return Result.error(4002, 当前时间无执勤排班); } LocalTime now LocalTime.now(); if (now.isBefore(shift.getStartTime().minusMinutes(30))) { return Result.error(4003, 未到签到时间); } // 4. 防重复提交先查再插数据库唯一索引兜底 Attendance exists attendanceService.getByShiftAndType(shift.getId(), CheckType.CHECK_IN); if (exists ! null) { return Result.error(4004, 今日已签到请勿重复操作); } Attendance attendance new Attendance(); attendance.setShiftId(shift.getId()); attendance.setUserId(userId); attendance.setCheckType(CheckType.CHECK_IN); attendance.setCheckTime(LocalDateTime.now()); attendance.setLongitude(request.getLongitude()); attendance.setLatitude(request.getLatitude()); attendance.setAddress(GeoUtil.reverseGeocode(request.getLatitude(), request.getLongitude())); attendanceService.save(attendance); return Result.success(attendance); }四个步骤拆开说。第一步拿当前登录用户 ID不需要前端传用户标识避免越权。第二步 GPS 距离用的是球面距离公式allowedRadius 存在点位配置表里不同点位可以设不同半径——室外开阔点位 150 到 300 米室内点位信号漂移严重可能要 300 到 500 米这个参数直接影响签到成功率调太小执勤人员站在点位旁边因为 GPS 漂移签不上调太大人在隔壁小区也能打卡这个度要靠点位实测数据来标定。第三步查排班时调用了 getCurrentShift 方法这个方法内部会处理跨天班次而不是简单地按当天日期查原因在避坑章节第 4.1 条详述。第四步先查再插配合唯一索引是签到接口唯一可靠的防重方案——只靠前端按钮置灰一定能被绕过因为客户端完全可以直接发 HTTP 请求。签退接口逻辑和签到几乎一样只是 check_type 变成 CHECK_OUT窗口期变为班次开始后到班次结束后 30 分钟。注意签退时同样要做 GPS 校验不要默认能签退的人必然在点位。3.3 巡更路线与异常上报Vue.js web API 交互与后端校验巡更模块的典型需求是管理员预先配置巡更点和路线执勤人员按路线逐点打卡系统记录每个点的到达时间。前端我用 Vue 2 高德地图 JS API在地图上标点、连线、显示当前位置// 前端巡更页面核心逻辑模板部分省略 export default { data() { return { routePoints: [], // 当前路线的巡更点列表 checkedPointIds: [], // 已打卡的点位ID map: null, }; }, methods: { // 巡更打卡到达点位后点击按钮上报 async checkPoint(pointId) { const res await this.$http.post(/api/patrol/check, { pointId, longitude: this.currentLng, latitude: this.currentLat, }); if (res.data.code 200) { this.checkedPointIds.push(pointId); this.updateMarkerStyle(pointId); // 已打卡的点位在地图上变色标绿 } else { this.$message.error(res.data.message); } }, }, };前端逻辑本身简单真正要注意的是后端校验。每个巡更点是否按路线顺序打卡、相邻两个打卡点的时间间隔是否合理比如两个点距离 500 米间隔不可能只有 1 秒必须在后端校验前端只是展示。防止人没到现场、找人代打、回去补打是巡更模块的核心价值PatrolRecord last patrolService.getLastRecordByUser(userId); if (last ! null) { long intervalSeconds ChronoUnit.SECONDS.between( last.getCheckTime(), LocalDateTime.now()); // 按点位距离与步行速度计算理论最短间隔这里用固定值 30 秒做兜底 if (intervalSeconds 30) { return Result.error(4101, 打卡间隔过短请按路线顺序巡更); } }30 秒只是一个粗糙防线更精细的做法是按两个点位的实际距离除以平均步行速度一般取值 1.2 米/秒算出理论最短时间再乘一个 0.8 的安全系数。阈值写死会误判——点位之间距离差异过大时远的点位要求走很久近的点位几秒就能到。所以这个值应该按路线单独配置存到 route 表的 min_interval_seconds 字段里。事件上报模块就是一个表单加一个图片上传接口字段包括事件类型、等级、描述、现场照片、位置。上报后按等级触发通知一般事件记录即可重大事件要推送所有管理员。通知渠道在 Web 系统里最靠谱的是站内信加邮件有企业微信环境可以加群机器人推送但内网环境就别指望了。4. 执勤系统落地避坑时间边界、并发防重和定位校验的 5 个坑4.1 跨天夜班的日期归属凌晨的打卡算哪一天现象晚班是 16:00 到次日 00:30执勤人员在 00:20 签退系统提示当前时间无执勤排班打卡失败。原因排班表的 shift_date 存的是班次开始日期签退时间已经到了第二天。开发时按 LocalDate.now() 去匹配排班记录第二天当然查不到前一天晚上的班次。时间判断逻辑写得太简单是这类系统最常见的坑。解决写一个专用的 getCurrentShift 方法逻辑是先查当天的排班再查前一天的所有排班遍历判断当前时刻是否落在 [start_time, end_time] 区间内end_time 小于 start_time 说明是跨天班次比较时给 end_time 加 24 小时。这个坑的本质是业务日期和自然日期不是一回事整条时间链路都要围绕业务日期设计签到、签退、补卡、统计四个地方复用同一套方法保证规则一致。4.2 并发重复签到双击提交产生两条记录现象执勤人员在 4G 信号差的地方点签到页面卡住又点了一次后台出现两条签到记录月底统计执勤次数直接翻倍。原因前端按钮没有 loading 状态后端先查询再插入的逻辑在高并发下存在竞态条件——两个请求同时查到无记录然后都执行了插入。这种问题在低并发系统里特别容易忽视但签到场景恰好是所有人集中在同一时间段操作。解决三层防护。第一层前端按钮提交后立即 disabled请求结束再恢复防误触第二层后端在 service 层加锁最轻量的是 JVM 内按用户 ID 班次 ID维度做 synchronized 或者用 Redis 分布式锁挡住并发请求第三层数据库唯一索引 uk_shift_type 兜底插入时捕获 DuplicateKeyException 转成友好文案今日已签到请勿重复操作。很多人只做第一层就以为结束了实际上客户端完全可以绕过按钮限制直接发 HTTP 请求第二层第三层才是真正防住的。4.3 浏览器兼容与内网部署web 页面 PDF 打印在部分浏览器翻车现象管理员在单位老浏览器上打开统计报表页面ECharts 图表空白用浏览器自带的打印功能打印执勤台账打印预览里只有标题没有表格内容。原因图表库 ECharts 5 要求浏览器支持较新的 Canvas 特性老内核浏览器直接渲染失败打印样式没单独适配flex 等现代布局在部分打印引擎下解析不一致内容被裁掉。解决开发期就定目标环境内网系统不要追求所有浏览器都能用明确支持 Chrome 系即可页面检测到非目标浏览器时给出提示而不是白屏。web 页面 PDF 打印要单独写一套 media print 样式表格用 table 布局不用 flex字号用 pt 不用 px页眉输出当前登录用户和打印时间。整个打印模块的原则是能出表格就行地图图表全部排除。如果想要 PDF 文件而不是打印纸别用浏览器打印为 PDF凑合Chrome 的打印预览和实际 PDF 输出经常有差异直接用 jsPDF 或服务端模板生成 PDF 更可控代价是额外开发量。4.4 GPS 模拟定位与点位半径测试机假坐标和参数玄学现象测试人员用手机模拟定位软件随便选了一个坐标系统提示签到成功模拟定位没有起到拦截作用。原因浏览器 H5 页面拿到的经纬度来自设备定位接口系统无法区分是真实 GPS 还是模拟器注入的假坐标。后端只能校验坐标与点位的距离校验不了坐标的来源。解决这是有限防御要认清边界。交互上做限制——签到必须通过手机端 H5 页面进行后端继续校验距离但增加两个辅助信号一是检查相邻两次签到的坐标变化是否物理合理二是记录设备标识同一设备短时间内覆盖多个不同点位坐标的可疑行为标记出来供管理员复核。点位半径参数按场景配置点位场景推荐半径米说明室外开阔点位150-300GPS 信号好漂移小室内大堂300-500建筑遮挡导致漂移大地下空间不适用 GPS信号不可用改用扫码或蓝牙信标完全杜绝模拟定位不现实论文里如实写清限制比夸大效果更能体现工程严谨性也更能过答辩。4.5 时区与数据库时间CST 引发的 8 小时差现象服务器部署在 CentOS 上数据库连接串没加 serverTimezone 参数签到时间比实际时间晚了整整 8 小时。原因MySQL JDBC 驱动 8.x 默认要求显式指定时区不指定时按驱动探测的服务器时区做转换。应用容器时区和数据库服务器时区不一致时DATETIME 字段的写入和读取就会错位表现就是所有时间差 8 小时。解决统一时区配置。MySQL 连接串固定加 serverTimezoneAsia/Shanghai 和 useLegacyDatetimeCodefalseJVM 启动参数加 -Duser.timezoneAsia/ShanghaiMySQL 服务端 time_zone 显式设为 08:00。三处统一之后代码里禁止出现任何 DateUtil.addHours(now, 8) 之类的补丁代码——这种补丁能掩盖问题但会在跨年、跨月统计时给出魔幻数字到时候排查成本是当初省下的几十倍。5. 从系统到论文验证方法与三个进阶方向5.1 功能验证清单论文测试章节怎么写才不会空论文里系统测试一章最容易写成流水账。与其列几十条点击按钮、观察结果不如按功能模块整理可验收的用例每类写清测试项、操作步骤、预期结果、实际结果。我一般建议覆盖四类测试功能测试覆盖排班生成、调班审批、签到签退、巡更打卡、事件上报、报表导出接口测试用 Postman 验证越权访问、重复签到、非法参数性能测试用 JMeter 模拟 50 个并发用户同时签到观察接口响应时间是否在 2 秒内兼容性测试覆盖 Chrome、Edge、主流手机浏览器。这套用例表放到论文里比空谈系统性能良好有说服力得多。5.2 三个值得做的进阶方向通知推送、数据大屏和移动端系统跑通之后想在论文里加差异化亮点可以从三个方向选一个做深。一是消息通知排班生成后自动推送提醒、签到时通知组长、事件上报按等级通知管理员技术实现不复杂但业务价值很明显。二是数据大屏把点位实时状态、今日签到率、事件处理进度投影到管理大屏技术栈 Vue ECharts工作量不大展示效果好如果执勤点位装了摄像头web 端实时视频接入是大屏方向的高阶扩展。三是移动端适配执勤人员大多数时间不在电脑前H5 移动端是刚需同一套 Vue 代码做响应式适配签到和巡更页面优先按移动端交互设计。以我做这类项目的经验最值得先做的是第三项。执勤系统的用户场景决定了移动端优先级高于 PC 端但大多数团队都是先把 PC 管理端做得很重最后发现实际操作场地上没人开电脑再回头补移动端这就是典型的需求倒置。反过来一开始就以移动端签到、巡更为主链路PC 端只做排班和报表系统落地效果好得多。我吃过这个亏后来再做类似项目都先把使用场景想清楚再动手。希望这个顺序能帮到你。本文还有配套的精品资源点击获取
返回列表