保安公司智慧管理系统:排班考勤与巡更防作弊的实战设计
1. 项目概述与核心需求拆解1.1 保安公司的业务痛点到底在哪很多人第一次听到“保安公司智慧管理系统”这个题目时第一反应都是这不就是个简单的人员管理系统吗和普通的企业OA有什么区别实际接了这个项目之后我才发现这里面的门道比想象中多得多。保安公司和普通企业的管理逻辑完全不同它们的痛点非常具体也相当传统。首先排班就是头号难题。一家中型的保安服务公司通常有几百甚至上千名保安员分布在十几个甚至几十个驻点单位。每个驻点班次不统一有三班倒的、两班倒的还有白夜休休的。传统做法是用Excel表格排班排班员每个月要花好几天手排最头疼的是临时换班——有人家里有事要换班得打电话找主管、找队长人找不齐就乱套。其次是巡更管理的信任问题。保安这个岗位的特殊性在于工作质量没法量化。你怎么知道他夜里两点到底有没有去巡逻传统的巡更棒虽然能记录时间点但数据要专人回收滞后不说还有代刷的现象跑一趟刷一排点位管理上根本防不住。再有就是甲方监督的压力。保安公司的客户是各个企事业单位甲方对保安服务是有考核的。比如疫情期间要求测量体温、查健康码平时要求登记访客、拍照留痕。出了纠纷甲方要调取上岗记录保安公司拿不出来的话轻则扣钱重则丢合同。加上保安行业的人员流动性极大入职离职频繁纸质台账经常错漏。证件管理更是一笔糊涂账——保安员必须有保安员证才能上岗证件过期了还在执勤出了事就是重大安全事故。所以这套系统的价值不在“智慧”两个字有多炫而在于把过去靠电话、靠Excel、靠自觉的管理动作全部换成线上留痕、自动校验、实时同步。简单来说就是把人管住、把班排清、把岗查实、把账留全。1.2 系统功能边界的确定与优先级规划接这类项目最忌讳的是甲方说啥就做啥。保安公司的人对系统没概念只会说“我需要一个能看所有东西的大屏”但你要真按大屏去设计项目必死。我的做法是先做业务闭环梳理。保安公司的日常管理动作可以抽象成一条链路人员从招聘入职到培训考证再到分配驻点排班上岗然后通过打卡和巡更确认履职最后形成考核数据和台账报表。围绕这条链路功能模块就自然浮出来了。系统核心模块我按优先级P0/P1/P2规划如下优先级模块功能描述P0人员档案管理花名册、证件台账、合同签订、黑名单管理P0排班管理班次定义、排版生成、临时换班审批P0考勤打卡驻点GPS打卡、拍照防作弊、考勤汇总P0巡更管理巡更点位维护、线路规划、扫码/打卡记录P1访客登记入场登记、访客留痕、黑名单拦截P1培训考试线上题库、考试记录、成绩归档P2物资管理制服、装备领用记录P2报表中心多维度统计与导出P0模块是第一版必须交付的少了任何一个业务闭环就断掉了。P1模块是验收加分项P2模块可以放到二期。这里有个经验想特别说一下千万不要一上来就做数据大屏。大屏是面子工程应该放在系统稳定运行、数据沉淀三个月之后再上。否则系统里没几笔真实数据大屏上全是测试数据甲方演示给领导看的时候反而减分。2. 系统总体架构与方案选型2.1 为什么选了“Java Spring Boot Vue”这套组合技术选型是这类项目里被问得最多的。说句实在话这个体量的系统用很多技术都能做出来但选什么组合背后是成本、人才、稳定性和答辩加分项的平衡。我最终选了Spring Boot Vue前后端分离的方案原因有三层。第一层是生态成熟度。Spring Boot在中小企业级信息系统里是绝对的主流遇到问题网上随便一搜就有解决方案。保安公司系统将来要对接甲方系统、对接考勤机硬件Java这边的SDK和中间件支持也最全。这套系统的核心是业务逻辑和权限管理Spring Security MyBatis-Plus这套组合拳能让我在最短时间内把合规的代码写出来。第二层是人才可替代性。说难听点毕业设计做完是要给别人维护的可能是下一届学弟也可能是公司里的技术外包。Java程序员好找Vue前端也遍地都是。如果选了个冷门框架比如Ruby on Rails或小众Go框架以后找人接手都是问题。第三层才是技术先进性。Spring Boot 2.7版本 Vue 3 Element Plus既不会太旧显得技术落后也不会因为追新版本导致教程难找、踩坑没人解。对一个偏管理类的系统来说稳定压倒一切。对比一下其他方案的成本差异用PHP写确实快但后期做接口权限、做WebSocket推送比较别扭用Python Django写管理后台很爽但对部署环境和并发支持一般而且简历上写出去给人的感觉偏脚本开发用Spring Boot写虽然初始配置多一点但架构清晰、模块化程度高后期维护和扩展就没有包袱。2.2 部署架构设计——单机部署其实就够了很多学生在设计部署架构的时候容易犯一个毛病上来就画好几个服务器节点搞Nginx负载均衡、Redis集群、消息队列看起来高大上实际上用不上。我之前接一个真实客户项目时他们的系统并发量就是同时几十个人在用撑死一百人。保安员上下班打卡的时间比较集中早上七点到八点可能有两三百个请求同时进来单机完全扛得住。系统里又没有什么视频流、大文件处理的场景何必杀鸡用牛刀。我的部署方案非常简单一台云服务器4核8G起步带宽按3M算够了Nginx做静态资源服务和反向代理Spring Boot打jar包直接跑MySQL 8.0存业务数据Redis存验证码、Token和热点数据没有引入消息队列实时通知用WebSocket解决这套方案的好处是成本可控一年服务器费用可能就两千块出头对毕设项目来说完全能负担。而且排错的时候少了很多中间环节SSH上去一条命令就能查日志。2.3 移动端采用H5而非原生App的原因保安亭里用的手机五花八门大多数是中低端安卓机。如果做原生App首先绕不开的是应用市场审核尤其是苹果的审核一个企业账号一年几百块其次版本更新极其痛苦几百个保安员的手机不可能统一升级到时候后端接口改了老版本App没覆盖运维就崩了。所以移动端我选了H5方案直接集成在微信公众号或企业微信里。保安员打开微信就能打卡、巡更不需要额外装App也不占手机内存。技术上H5用Vue 3 Vant组件库Vant是移动端H5很好的选择组件风格对中老年用户来说也算友好。打卡页面就是地图加按钮按钮做得大一点字大一点交互链路短一点。3. 数据库表结构的设计思路与权限规划3.1 核心表的设计逻辑这类系统的表结构其实说不上多难但有几张表的设计是整套系统的命脉值得单独拿出来讲。驻点work_site与组织架构。保安公司总部下有多个分公司或大队每个大队管几个驻点。所以组织表要用树形结构父节点是公司子节点是驻点。每个驻点要有明确的经纬度坐标这个坐标就是考勤打卡的基准点。还要有驻点联系人、联系电话方便甲方随时调度。人员表和用户表分开设计。人员表guard_info存的是员工的基本信息、身份证号、银行账号、紧急联系人等属于静态档案用户表sys_user存的是账号、密码、角色属于动态登录凭证。两张表通过guard_id关联。为什么分开因为保安入职的时候可能还没开通系统账号或者账号被停用了档案还要保留。混在一张表里数据会非常脏。班次表shift与排班表schedule分离。班次表存的是固定模板比如早班07:00-15:00、中班15:00-23:00、夜班23:00-07:00只定义了时间的起止和名称。排班表才是具体某个人在某天执行某个班次。这样设计的好处是排班生成是一次性写入具体的员工和日期后续改班不影响模板。打卡记录表attendance_record字段要留冗余。打卡表除了基本的时间、人员、驻点ID之外我当时还设计了几个字段打卡时定位的经纬度、打卡照片URL、打卡类型上班/下班、是否异常。算距离成绩是在后端算好之后把距离值也存进去。这样在报表查询的时候就省了每次调用地图API计算的消耗。3.2 数据权限设计——驻点隔离这是一类系统最容易被忽略却最容易出大问题的地方。保安公司的管理架构是总经理可以看到所有驻点的数据大队长只能看本大队的数据驻点队长只能看本驻点的数据保安员只能看自己的打卡记录。如果业务代码里到处写死ID过滤条件后期改动就是灾难。我用的是通用数据权限方案用户表添加一个data_scope字段有ALL、DEPT、CUSTOM三个取值。查询数据时先判断当前用户的data_scope如果是CUSTOM就再关联用户绑定的驻点关系表动态拼接过滤条件。用MyBatis-Plus的租户插件或者自定义拦截器可以把这个逻辑统一封装实现不同角色登录后看到不同的数据范围。这个设计在答辩的时候是个很好的技术亮点因为不是所有学生都能想到数据权限层面的问题大部分只做了菜单权限和按钮权限。4. 核心功能模块的实操设计与实现4.1 人员档案与证件台账管理人员档案是一个人事系统的基础但在保安这种人员流动率高的行业档案管理的核心诉求是“录入要快”和“到期要提醒”。录入要快我做了批量导入功能。格式固定为Excel模板表头包括姓名、身份证号、手机号、驻点、岗位、学历、紧急联系人。用EasyExcel解析校验身份证号格式和手机号格式不符合规则的生成错误日志文件供下载用户可以根据提示修改后重新导入。几百人的花名册导入一次大概几秒钟。证件台账这块需要维护保安员证、健康证等证件的编号、发证日期、到期日期。系统每天跑一个定时任务扫描未来30天内到期的证件把预警信息推到对应驻点队长的首页。这个功能刚上线的时候甲方觉得没什么直到两个月后查出了一个十几人的证件过期名单其中两个还就职在银行网点他们才意识到这个功能多值钱。4.2 排班管理与换班审批排班是业务逻辑里最复杂的模块。固定班型的排班生成逻辑是这样的每个驻点先配置好岗位数量和班次规则比如某岗位需要每天2人值班一个白班一个夜班。排班的时候选择起始日期和结束日期系统按轮转顺序自动生成。轮转算法本身不复杂关键是支持节假日特殊班次。我处理的办法是增加一个节假日班次表优先级高于常规班次法定节假日来临时系统自动替换。换班审批功能虽然小但涉及状态流转。A和B互相换班A发起申请选择日期和班次选择对班的B提交后给驻点队长审批。审批通过后A和B当天的排班互换。这里有个细节换班申请提交后如果有一方当天已经打过卡了要拒绝这个请求否则会出现换完班但考勤已经落库数据对不上。我踩过的坑是消息通知的时序问题。当时用WebSocket推送待办事件但用户经常收不到。排查半天发现是推送事件在执行审批逻辑之前就发送了前端拿到的是旧的审批状态。后续把推送放到了事务提交之后问题解决。这个细节在答辩的时候讲出来会显得非常有实战经验。4.3 GPS考勤打卡与防作弊考勤打卡是整个系统里最有技术含量的部分也是甲方最关注的。技术方案是前端H5页面通过浏览器Geolocation API获取经纬度然后传给后端。后端把坐标和驻点的基准坐标做距离计算小于设定阈值通常是300米就允许打卡否则提示不在范围内。有人会问为什么不直接用前端判断是否在范围内。原因很简单前端判断结果可以被伪造。用Charles抓包改参数或者直接在控制台改JS变量就能绕过限制。所以距离计算必须放在后端做前端传坐标后端起算盘。防作弊还有两招一是打卡时必须拍照后端保存现场照片管理员可以随时抽查二是打上班卡和下班卡的时间间隔必须在排班班次时长的一定范围内比如早班最短打卡时间不能少于6小时不然系统会标记异常。这里用到了高德地图的Web服务API做坐标转换。浏览器拿到的是WGS-84坐标系坐标而高德地图用的是GCJ-02如果直接用原始坐标和高德POI对比会有几十到几百米的偏移经常出现没出勤但显示请离驻点的乌龙事件。必须调用高德API做坐标转换后再计算直线距离。4.4 巡更管理巡更管理看似简单实质是在做任务状态机。每个驻点先维护巡更点位点位名称、安装的二维码编号、经纬度。巡更路线则是把点位串起来设定巡更班次和间隔时间。保安员巡逻时扫描点位上的二维码系统记录当前时间如果和计划时间偏差太大判定为早巡或晚巡。核心逻辑在现场秩序异常上报。我加了一个异常标注功能巡检人扫描点位后可以选择“正常”或“异常”异常时强制拍照并填写情况说明。这个设计来源于真实需求有些驻点夜间会有漏水、门窗未锁的情况过去是保安手写交接本第二天主管翻本子才知道。现在通过系统上报主管手机实时收到消息责任人清晰、处理及时。巡更数据要和排班表做关联校验。如果某人当天没有排班却产生了巡更记录视为异常数据。系统每天定时任务跑日志自动标记冲突记录。5. 关键技术实现与代码示例5.1 统一返回结果与异常处理这类管理系统的后端代码大同小异但代码整洁程度是答辩评审关注的点。我习惯定义一个统一的Result类所有接口都返回同样的结构code、message、data三个字段。Data public class ResultT { private Integer code; private String message; private T data; public static T ResultT success(T data) { ResultT result new Result(); result.setCode(200); result.setMessage(success); result.setData(data); return result; } public static T ResultT error(String message) { ResultT result new Result(); result.setCode(500); result.setMessage(message); return result; } }配套的还有全局异常处理。业务异常统一抛出BizException由全局处理器拦截转换成Result结构返回。千万别把异常堆栈直接返回给前端既不安全也不美观。5.2 打卡接口的核心逻辑打卡接口是整个系统最核心的接口这里展示了体积计算的完整过程我直接把关键代码放出来。PostMapping(/attendance/checkIn) public ResultAttendanceRecord checkIn(RequestBody CheckInDTO dto, RequestAttribute(userId) Long userId) { // 1. 获取当前用户的排班信息 ScheduleDetail detail scheduleService.getTodaySchedule(userId); if (detail null) { throw new BizException(今日无排班无法打卡); } // 2. 判断打卡时间是否在允许范围内 LocalTime now LocalTime.now(); boolean isStart now.isAfter(detail.getStartTime().minusMinutes(30)) now.isBefore(detail.getStartTime().plusMinutes(30)); boolean isEnd now.isAfter(detail.getEndTime().minusMinutes(30)) now.isBefore(detail.getEndTime().plusMinutes(30)); if (!isStart !isEnd) { throw new BizException(当前时间不在打卡范围内); } // 3. 计算定位距离核心防作弊 WorkSite site workSiteService.getById(detail.getSiteId()); double distance DistanceUtil.calculate( site.getLatitude(), site.getLongitude(), dto.getLatitude(), dto.getLongitude()); // 4. 距离阈值判断300米范围 if (distance 300) { throw new BizException(当前定位距离岗位过远无法打卡); } // 5. 判断是否已打卡防止重复提交 boolean hasRecord attendanceService.existRecord( userId, detail.getScheduleId(), isStart ? CheckType.START : CheckType.END); if (hasRecord) { throw new BizException(请勿重复打卡); } // 6. 保存记录 AttendanceRecord record new AttendanceRecord(); record.setUserId(userId); record.setScheduleDetailId(detail.getId()); record.setCheckTime(new Date()); record.setType(isStart ? CheckType.START : CheckType.END); record.setLatitude(dto.getLatitude()); record.setLongitude(dto.getLongitude()); record.setDistance(distance); record.setPhotoUrl(dto.getPhotoUrl()); attendanceService.save(record); return Result.success(record); }这段代码的核心是第3步的距离计算我建议直接把Haversine公式手写不要依赖外部依赖库方便答辩时讲解。5.3 排班生成的定时任务排班生成通过Spring的Scheduled注解定时执行每个月初自动生成下个月的排班。Component public class ScheduleGenerateTask { Autowired private ScheduleService scheduleService; Scheduled(cron 0 0 2 25 * ?) // 每月25号凌晨2点执行 public void generateMonthlySchedule() { // 生成下个月的班表 LocalDate nextMonth LocalDate.now().plusMonths(1); YearMonth ym YearMonth.from(nextMonth); LocalDate startDate ym.atDay(1); LocalDate endDate ym.atEndOfMonth(); scheduleService.generateSchedule(startDate, endDate); } }有人会问定时任务在集群环境下会不会重复执行。这里因为部署方式是单机不存在这个问题。但如果将来要扩展成多节点部署建议引入分布式锁或者用xxl-job否则每个月会生成两遍排班数据。这个点答辩时老师大概率会问到提前准备好回答思路。6. 常见问题与排查技巧实录6.1 定位偏移导致打卡失败最常见的问题是保安员在驻点内打卡系统却提示距离过远。原因通常是坐标坐标系不一致。解决办法很简单前端定位拿到的是WGS-84坐标后端统一转成GCJ-02坐标再计算。同时还要注意浏览器定位精度差异很大室内或者靠近窗户的地方定位漂移能到一两百米。保险起见打卡接口里加一个原始精度值判断定位精度超过100米就直接提示用户移动到开阔地点重试。6.2 多人同时打卡导致数据重复打卡请求集中在7点半到8点两个用户同时提交数据库检查是否已打卡的SQL还没执行完另外一个请求就插入了。虽然后端做了“请勿重复打卡”的判断但多线程时还是可能出现并发问题。解决方式是使用数据库唯一索引兜底。在打卡记录表里建联合唯一索引schedule_detail_id, type数据库层面保证同一班次同一类型只能插一条记录前端和后端的判断只是提升用户体验真正兜底靠数据库约束。这个是项目上线后第一个生产事故同时几十个人打卡系统里同一时间出现了三四条重复记录。加上唯一索引后问题再没出现过。6.3 手机端定位权限不弹窗H5页面在微信里调用定位的时候经常会遇到定位权限不弹窗的问题。原因是微信内置浏览器的定位权限受微信本身的控制用户需要在微信设置里开启“位置信息”权限而不是浏览器权限。建议在打卡页面顶部做一个权限自检引导加载时调用一次定位接口如果失败就弹窗提示用户去设置里开启位置信息。很多用户是中老年保安员遇到权限问题不会排查引导文案要写得特别直白比如“请点击右上角...选择设置...打开位置信息选项”。6.4 报表导出卡死问题系统用EasyExcel导出Excel报表本来几千条记录很快但保安公司一个月的考勤数据导出来有上万行用户点击导出后浏览器一直转圈。排查后发现导出操作是同步的服务端生成文件耗时HTTP请求超时了。改进方案是异步导出点击导出后先把导出任务落库返回任务ID前端轮询任务状态。任务完成后生成下载链接。后来发现这样还能顺便做历史导出记录留痕甲方反而更喜欢了因为他们领导想知道谁导出了薪酬数据便于监督。6.5 数据权限遗漏这个不是技术问题是业务漏洞。开发初期没做好数据权限隔离某个驻点队长登录后查考勤理论上只能看自己驻点的记录但调试接口时发现通过修改URL中的siteId参数他就能看到其他驻点的数据。整改方案是加了一个MyBatis拦截器自动给所有SQL拼接上数据权限条件。核心思想是统一处理避免每个Service方法里都手动传siteId漏一个就出数据安全事故。在答辩时说这个方案老师会认可你的安全意识。7. 毕设答辩要点与系统扩展方向7.1 答辩包装——从业务闭环到技术亮点的对应做完系统只是第一步答辩才是临门一脚。我的建议是准备两条逻辑链一是业务链二是技术链两条链要能对起来。业务链讲的是保安公司有排班、考勤、巡更、访客四大场景痛点系统如何逐个击破。技术链讲的是后端Spring Boot MyBatis-Plus如何支撑业务前端Vue3如何交互数据权限如何设计防作弊如何落地。好话要从业务痛点讲起落到技术方案时把亮点打出来。比如防作弊打卡既有GPS定位校验又有后端距离计算还有照片留痕和数据库唯一索引兜底一套组合拳下来既展示了你对业务理解又展示了你的技术深度。7.2 项目演示的节奏感演示的时候千万不要把自己当导游一个页面接一个页面地过评审老师记不住也抓不住重点。正确节奏是先展示一个业务闭环的完整操作给一个保安员排个班用手机或模拟器打卡一次生成一条考勤记录再到报表中心导出Excel把这个过程从头到尾演一遍。演示完之后切入两三个功能性地说明数据权限控制、证件到期提醒这些和传统管理系统差异点重点讲一下设计思路。演示的过程中可以埋一个伏笔比如现场抓包请求展示前端传的坐标被修改后后端如何拒绝。这一手几乎每个答辩老师都会觉得惊艳。7.3 系统的扩展方向这套系统后续的扩展空间相当大。第一个方向是硬件接入。目前打卡靠手机GPS但不少驻点要求更严谨的打卡方式可以考虑接入人脸识别闸机或指纹考勤机通过硬件SDK对接。第二个方向是智能排班。现在的排班算法还是固定轮转没有考虑人员技能匹配度、历史考勤习惯和劳动法工时规定。可以引入简单的运筹学模型或者基于规则的引擎来做排班建议。第三个方向是物联网联动。保安巡更可以接入烟雾报警器、门窗磁、水浸传感器异常事件自动推送。现在智能养老、智能办公场景都在做类似的事这也和当下健康检测硬件方案的热度有关——像血氧仪方案开发、体温监测终端这类硬件本质上做得都是“边缘感知平台记录异常推送”这套逻辑如果能和后勤管理系统打通把保安随身携带的终端变成移动物联网节点系统的想象空间就完全不同了。最后说点实在的这类管理系统做下来最大的收获不是Spring Boot写得多熟练而是学会了怎么把一个模糊的业务描述翻译成具体的功能模块再把功能模块落地成可运行的代码和可靠的数据库结构。如果你正在做类似的毕设或外包项目我的建议是不要一上来就写代码先花两周时间梳理业务流程画出业务闭环图和数据流图。业务想清楚了代码只是执行的事。另外一个建议是写代码时多用事务凡是涉及钱的、涉及状态的变更一律加上Transactional多花五秒钟少出大问题。所有关键操作留痕记录时间、操作人、内容这是甲方最看重的也是答辩时最有底气的。保安公司智慧管理系统是个典型的“小系统大业务”项目做好了它不只是一份毕设而是能真正接得住管理压力的业务工具。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →