地铁安防管理系统这类题目,几乎是每年计算机毕业设计里的常青树。但同样一个题目,有人做出来像课堂作业,有人做出来像能直接交付的工程,差别主要在技术选型和建模思路上。我当年选这个题,前后对比过SSM、SSH和SpringBoot,最后选了SpringBoot。后来参与实际的城市轨道交通安全防控项目时发现,当年在毕设里搭出来的模块边界、缓存策略和应急链路,居然能直接沿用。这篇文章把我踩过的坑、验证过的方案都整理出来,给正在选这个题或者准备拓展成求职项目的同学一个完整的参考。
1. 选题时的真实考量:为什么地铁安防必须用SpringBoot而不是SSM
1.1 地铁安防管理平台要解决的核心问题
先别急着写代码,想清楚一个关键问题:地铁安防和普通的后台管理系统到底差在哪?我自己的理解是三多一快——多线路、多站点、多设备,事件响应要快。
- 多线路:每条线路有起始站、终点站、开通时间,后续还要扩展新线路,线路和站点之间是天然的一对多关系。
- 多站点:每个站点有安检信息、摄像头、巡更点、设备台账,数据天然按站点隔离,所以后端的权限设计必须支持“数据级隔离”,不是光有按钮权限就够。
- 多设备:安检门、X光机、闸机、摄像头、巡更点,每类设备的字段差异很大,设备状态还关系到巡检提醒。
- 响应快:从告警上报到启动应急预案,最好能分钟级甚至秒级联动,这就不是单纯增删改查的问题,而是要有清晰的事件状态机和异步通知机制。
很多同学把这个题当成“车站信息管理”来做,只做线路站点的CRUD,那就废了。安防系统的核心是“事件链路”,也就是从安检异常、巡更超时、设备故障这些源头事件,一路流转到告警、预案、处置、回访的完整闭环。SpringBoot恰恰适合搭这种链路清晰、模块边界分明的系统。
1.2 SpringBoot相对SSM在毕设中的优势
如果你是2025年还在纠结要不要用SSM做毕设,我的建议是别纠结了。SSM不是不能做,而是做这个题目时,你要把大量精力浪费在配置上:Spring核心配置、SpringMVC配置、MyBatis配置、各种xml扫描,还没开始写业务逻辑,光环境就折腾两三天。
SpringBoot把“约定优于配置”做到了极致,主要有几个点对毕设特别友好:
- 起步依赖(Starter)机制:想用Redis就引spring-boot-starter-data-redis,想用消息队列就引starter-activemq,依赖是一颗一颗往pom里加糖,不需要手动拼版本号。
- 内置Tomcat:不用单独配置服务器,直接一个java -jar就能跑起来,部署到答辩现场的Windows电脑也没问题。
- 自动装配:框架自动读取配置并创建Bean,开发时只需要关注自定义业务Bean。
- 生态兼容:SpringSecurity、MyBatis-Plus、Redis、ES这些地铁安防系统要用的组件,都有对应的starter,集成成本极低。
1.3 自动装配原理:毕设答辩时的加分点
SpringBoot的自动装配不要求背源码,但至少要能说清楚它的机制,因为答辩老师大概率会问。核心就三句话:
- @SpringBootApplication由@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan组合而成。
- @EnableAutoConfiguration会通过SpringFactoriesLoader读取META-INF/spring.factories文件,把所有自动配置类先加载出来。
- 每个自动配置类用@ConditionalOnClass、@ConditionalOnProperty等条件注解做过滤,只有classpath里存在对应的类、配置也匹配时才真正生效。
举个例子:你pom里引入spring-boot-starter-data-redis后,classpath会出现RedisTemplate等类,RedisAutoConfiguration配置类满足条件,于是自动创建RedisTemplate的Bean。如果你没引入依赖,这个配置类直接不生效,不会报错。
这个机制解释了为什么我们这些开发者可以“零配置”地集成中间件——不是框架替我们做了,而是它把选择权交给了条件判断。这句话写进论文摘要里,整个设计说明的档次都会不一样。
2. 系统模块划分与数据表设计:先理业务再动手敲代码
2.1 六大业务模块的边界划分
我见过一堆人一上来就建表,建到一半发现数据对不上,又回头改表,这就是没做模块边界。地铁安防系统建议分成六大模块,模块之间通过接口调用而不是直接穿透表:
| 模块 | 核心职责 | 代表性的实体 |
|---|---|---|
| 基础信息管理 | 线路、站点的增删改查及上下线 | 线路、站点 |
| 用户与权限 | 用户登录、角色菜单、站点数据范围 | 用户、角色、菜单 |
| 安检与设备 | 安检信息登记、危险品管理、设备台账 | 安检记录、危险品、设备 |
| 视频与巡更 | 摄像头管理、巡更点规划、打卡记录 | 摄像头、巡更点、巡更记录 |
| 告警与应急 | 告警生成分级、预案匹配、事件处置 | 告警、预案、应急事件 |
| 统计与报表 | 客流趋势、告警排名、安检量统计 | 统计视图 |
模块之间怎么联动?我的做法是:基础信息模块被其他模块查询引用,安检模块发现异常往告警模块扔数据,巡更模块超时也往告警模块扔,告警模块根据类型匹配应急预案,最后统一进入事件闭环。数据流是单向的,这样后续加功能不容易乱。
2.2 核心数据表与字段设计
表结构不追求多,但要贴业务。下面是梳理出的核心表,字段我按能落地的程度做了精简:
| 表名 | 用途 | 核心字段 |
|---|---|---|
| line_info | 线路信息 | id, line_code, line_name, start_station, end_station, open_date |
| station_info | 站点信息 | id, station_code, station_name, line_id, address, manager, phone |
| sys_user | 用户账号 | id, username, password, real_name, station_id, role_id, phone, status |
| sys_role | 角色 | id, role_code, role_name, data_scope |
| security_check_info | 安检记录 | id, check_no, station_id, check_date, operator_id, item_name, item_type, danger_level, handling_way |
| equipment_info | 设备台账 | id, eq_code, eq_name, eq_type, station_id, install_date, last_maintain_date, maint_cycle |
| patrol_point | 巡更点 | id, point_code, point_name, station_id, location_desc, qr_code_url |
| patrol_record | 巡更打卡记录 | id, record_no, point_id, user_id, patrol_time, result, remark, photo_url |
| alarm_info | 告警信息 | id, alarm_no, alarm_type, station_id, source, level, content, status, create_time, deal_time, deal_result |
| emergency_plan | 应急预案 | id, plan_code, plan_name, plan_type, level, content, task_template |
| emergency_event | 应急事件 | id, event_no, plan_id, station_id, event_type, level, description, status, start_time, end_time |
| camera_info | 摄像头 | id, cam_code, cam_name, station_id, rtmp_url, status, area_type |
这里要注意一个很多人容易忽略的点:所有业务表建议统一加create_time、update_time、deleted(逻辑删除)三个审计字段。毕设阶段不要用物理删除,因为演示的时候你想恢复某条数据,物理删除后悔都来不及。
2.3 表关系与外键策略:性能与规范怎么平衡
表之间的关系很清晰:线路一对多站点,站点一对多设备/摄像头/巡更点,用户关联站点,告警关联站点。但在物理表上,我的建议是不建外键约束,只保留逻辑外键(即普通索引字段)。原因有两点:
- 性能:地铁安防系统在演示时数据量不大,但答辩问答环节如果聊到扩展,外键约束会影响大批量导入和删除数据的效率。
- 灵活性:逻辑外键可以控制在service层做关联校验,代码可读性和维护性都更好。
但记住一个底线:不建外键不代表不校验。比如删除一个站点时,必须先检查该站点下有没有设备、摄像头、巡更点,有的话就给出提示,不然会出现孤儿数据。这个检查逻辑写在service层,写完这部分可以当作答辩的一个业务亮点。
3. 核心功能实现:安防领域最容易卡壳的五个环节
3.1 基于Spring Security + JWT的权限控制
地铁安防系统涉及的角色很典型:超级管理员、站点管理员、安检员、巡更员、指挥中心值班员。不同角色能看的站点范围不同,能点的菜单也不同,所以我直接放弃了传统拦截器方案,改用Spring Security + JWT。
为什么要用JWT而不是Session?因为前后端分离已经成为标配,前端Vue和后端SpringBoot分开部署,PDA扫码端、巡检App也要调用接口。如果Session存储,要么引入Redis做Session共享,要么处理跨域Cookie的复杂问题。JWT的Token机制天然适合这种多端场景。
核心逻辑有三层:
- 登录接口校验用户名密码,通过后用
io.jsonwebtoken.JJWT生成Token,把用户ID、角色、站点数据权限编码进去。 - 自定义
OncePerRequestFilter过滤器,解析请求头中的Token,把用户信息放入SpringSecurity的SecurityContextHolder。 SecurityConfig里配置接口权限规则,/api/auth/login放行,/api/alarm/**需要对应角色,其余默认需要认证。
@Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeHttpRequests() .antMatchers("/api/auth/login").permitAll() .antMatchers("/api/alarm/**").hasAnyRole("ADMIN", "DISPATCHER") .anyRequest().authenticated(); http.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); } }这里有个容易踩的坑:SpringSecurity 5.x的antMatchers在2.7.x还能用,但到SpringBoot 3.x对应的是SpringSecurity 6.x,全改成requestMatchers了。后面第5章会细讲版本选择,先记住这个区别。
3.2 危险品登记与安检台账:不只是表单提交
安检记录听起来像一张普通表单,但地铁安防场景里有个特殊要求:当安检员登记管制刀具等危险品时,系统要自动产生告警并通知值班员。也就是说,安检模块和告警模块之间存在业务联动。
我的实现方式是:在SecurityCheckService.add()方法里提交记录后,判断dangerLevel,如果级别为HIGH,就调用AlarmService.generateAlarm()创建一条告警,同时向ActiveMQ发送一条异步通知消息。这样用一张表记录业务,另一张表产生事件,后面统计报表也有的放矢。
public void addCheck(SecurityCheckInfo info) { checkMapper.insert(info); if (DangerLevel.HIGH.equals(info.getDangerLevel())) { AlarmInfo alarm = new AlarmInfo(); alarm.setAlarmType("SECURITY_CHECK"); alarm.setStationId(info.getStationId()); alarm.setLevel("HIGH"); alarm.setContent("安检发现高危物品:" + info.getItemName()); alarmService.generateAlarm(alarm); } }高危险品处置方式建议做成枚举:没收、移交公安、登记放行等。这比简单的文本框更专业,答辩时也能强调这是业务规则的落地。
3.3 视频监控管理中的数据流设计
视频监控模块要避免一个误区:不要试图用Java后端做视频解码。真实项目里摄像头输出RTSP流,前端Vue播放器并不直接支持RTSP协议,所以主流做法是接入流媒体服务转换协议,比如用ZLMediaKit把RTSP转成HTTP-FLV或HLS,然后前端用flv.js或video.js播放。
毕设阶段怎么体现这个模块?我的做法是:
- 摄像头表存设备编码、安装站点、所属区域,以及
rtmp_url字段。 - 做一个视频播放页面,选择站点后展示该站点的摄像头列表,点击摄像头播放对应视频流。
- 如果演示现场不方便接真实摄像头,准备几个公开的RTSP测试流地址,或者用本地视频文件循环播放充当视频源。
实训录像是加分项:给camera_info表加一个record_file字段,存录像文件路径,前端按时间轴选择录像。这个模块看起来技术含量高,但真实实现并不复杂,关键是把数据表和播放地址的关系设计清楚。
3.4 应急预案执行与事件上报链路
应急指挥是标题里的核心词,也是拉开档次的功能。我把应急事件设计成一个状态机:待处理 → 执行中 → 已结束 → 已复盘。每个事件都来自告警,在执行过程中可以分配处置人、上传处置说明。
public void startEvent(EmergencyEvent event) { // 1. 根据告警类型和级别匹配预案 EmergencyPlan plan = planService.matchPlan(event.getEventType(), event.getLevel()); // 2. 从预案的task_template解析出任务清单 List<String> tasks = planService.parseTasks(plan.getTaskTemplate()); // 3. 生成事件并进入执行状态 event.setPlanId(plan.getId()); event.setStatus("EXECUTING"); eventMapper.insert(event); }预案里最重要的字段是task_template,我用JSON数组存任务模板,比如[{"task":"站内广播通知","owner":"值班站长"},{"task":"启动安检升级","owner":"安检组长"}]。事件启动后按任务清单逐项推进,每完成一项打卡记录。这个设计比单纯传一段预案文字要更“系统化”,答辩时可以说“把预案拆成了可执行的任务单元”。
3.5 巡更和设备点的二维码打卡逻辑
巡更管理的核心功能是“扫码打卡”。实现方式:巡更点表里存一个二维码内容(可以是随机生成的授权码),巡检员用PDA或手机扫二维码,请求后端接口记录打卡。
需要考虑三个实际场景:
- 防重复打卡:同一个人同一个巡更点在5分钟内不能重复打卡,用
patrol_record表上的唯一索引配合时间判断。 - 离线兜底:地铁站台信号不一定好,所以打卡接口支持先记录本地时间,上传时以服务器时间为准。
- 超时未巡更自动告警:用
@Scheduled定时任务,每10分钟扫描一次巡更点,如果当前时间超过计划巡更时间30分钟还没有记录,自动生成一条巡更异常告警。
@Scheduled(cron = "0 */10 * * * ?") public void checkOvertimePatrol() { List<PatrolPoint> points = patrolPointMapper.selectAll(); for (PatrolPoint point : points) { PatrolRecord last = patrolRecordMapper.selectLatestByPoint(point.getId()); if (last == null || Duration.between(last.getPatrolTime(), LocalDateTime.now()).toMinutes() > 30) { alarmService.generateAlarm(PatrolOvertimeAlarmBuilder.build(point)); } } }定时任务这部分内容放进论文里几乎是送分题,因为很多同学的毕设根本没有“主动触发”的功能,全是靠人点击按钮。你有了定时扫描,系统就活起来了。
4. 中间件组合:Redis、ActiveMQ、Elasticsearch、MinIO在地铁场景的正确用法
4.1 Redis:会话共享和热点客流的缓存
Redis在地铁安防系统里主要有三件事:验证码缓存、Token黑名单、热点客流统计。
- 验证码:登录页的图形验证码存到Redis,设置
expire时间为120秒,防止暴力尝试。 - Token黑名单:用户修改密码或退出登录后,把Token加入Redis黑名单,过期时间跟Token一致。虽然JWT本身是无状态的,但配合黑名单可以解决“被踢用户还能访问系统”的问题。
- 客流统计:每个站点的进站客流用Redis的
INCR命令计数,定期同步到MySQL。用Hash结构存当天各小时段的客流数字,前端展示曲线图非常流畅。
spring: redis: host: localhost port: 6379 database: 0 lettuce: pool: max-active: 8 max-idle: 8一个容易忽略的小技巧:Redis的Key一定要设计前缀,比如metro:captcha:{uuid}、metro:token:blacklist:{token}、metro:stationflow:{stationId}:{yyyyMMdd}。前缀相当于命名空间,后面加新业务不会乱,也方便用SCAN命令批量清理。
4.2 ActiveMQ:告警通知的异步解耦
当系统产生海量告警时,如果每条告警都同步发短信、推消息,接口响应会被拖跨。ActiveMQ在这里做的是削峰填谷:告警产生后先入队,再让消息消费者慢慢处理通知动作。
我集成ActiveMQ的步骤:
- 引入
spring-boot-starter-activemq依赖。 - 在yml里配置broker地址,生产环境用failover协议做高可用。
- 定义队列名称常量,比如
queue.alarm.notify。 - 生产者发送消息,消费者用
@JmsListener监听并处理。
@Component public class AlarmNotifier { @JmsListener(destination = "queue.alarm.notify") public void onAlarm(String alarmNo) { // 调用短信网关、生成站内通知、推送大屏 } }用MQ的另一个好处是方便扩展。比如后续要接入微信告警通知,只需要新增一个消费者监听同一个队列,不用改生产端代码。我在论文里把这段话写上,老师基本不会追问细节。
4.3 Elasticsearch + HanLP:告警日志的快速检索与中文分词
为什么告警查询要用ES?因为alarm_info表数据增长快,而且需要按“报警内容”“物品名称”做模糊搜索。MySQL的LIKE '%管%刀%'只能用左模糊匹配,性能差。ES的倒排索引则能秒级返回结果。
HanLP在这里解决的是中文分词问题。比如用户搜索“管制刀具”,ES分词器默认对中文是按单字拆的,导致“管制”和“刀具”被拆散。引入HanLP分词器,可以把“管制刀具”识别为一个完整词条,搜索结果更准确。
<dependency> <groupId>com.hankcs</groupId> <artifactId>hanlp</artifactId> <version>portable-1.8.4</version> </dependency>List<String> words = HanLP.segment("地铁站查获管制刀具"); // 输出:地铁/站/查获/管制刀具如果觉得集成ES太复杂,毕设阶段可以用一个折中方案:用ES索引告警数据,但查询只做简单的match查询,不强行优化分词器。毕竟数据量就那么点,ES已经比MySQL快一个维度了。关键是证明你理解为什么需要ES,而不是真的做TB级压测。
4.4 MinIO:摄像头抓拍图片与视频片段存储
图片和视频不能直接存数据库,更不能堆在本地磁盘。我选择MinIO作为对象存储,理由很简单:它兼容S3协议,部署轻量,而且有漂亮的Web控制台,演示时很加分。
@Autowired private MinioClient minioClient; public void uploadFile(String bucketName, InputStream in, String objectName) { boolean exists = minioClient.bucketExists(BucketExistsArgs.builder() .bucket(bucketName).build()); if (!exists) { minioClient.makeBucket(MakeBucketArgs.builder() .bucket(bucketName).build()); } minioClient.putObject(PutObjectArgs.builder() .bucket(bucketName) .object(objectName) .stream(in, in.available(), -1) .build()); }实践建议开两个Bucket:一个是security-images存安检抓拍图片,一个是video-clips存摄像头录制的报警片段。对象名用日期和摄像头编码做前缀,比如2025/06/12/cam004_143015.mp4,检索和清理都方便。这个环节放到答辩演示里,比单纯展示数据表有说服力得多。
5. 部署前的那些坑:版本选择、配置细节和没有源码时的排查办法
5.1 SpringBoot版本选择:为什么闭眼用3.x会翻车
先说结论:如果是普通毕设,我强烈建议用SpringBoot 2.7.x,不要一上来就选最新的3.x。原因很现实——SpringBoot 3.x要求JDK17,很多学校的毕设机器还停留在JDK8,涉及环境重装不说,MyBatis-Plus、Springdoc等一堆依赖都要换成适配版本。
我实际遇到过的问题:
- 用SpringBoot 3.2.5 + MyBatis-Plus 3.5.x,启动报
ClassNotFoundException: javax.annotation.PostConstruct。原因是JDK17里javax.annotation被移除了,得引入jakarta.annotation-api依赖。 - SpringSecurity 6.x把
WebSecurityConfigurerAdapter彻底废弃,原来的antMatchers配置全改成requestMatchers,旧教程里的写法直接编译不过。 - 高版本SpringBoot对
spring.factories自动装配的兼容性不同,部分第三方starter没升级的话,条件注解不生效。
所以选版本的正确姿势是:先去Maven仓库看依赖的兼容性矩阵,确定2.7.x能搭配的组件版本,再决定用哪个JDK。我最终用的是SpringBoot 2.7.18 + JDK8 + MySQL8,整套组合非常稳。
5.2 多环境配置结构和随机端口的调试技巧
很多同学的毕设只有一个application.yml,开发、测试、演示全用它,这是隐患。推荐多环境配置:
# application.yml spring: profiles: active: dev # application-dev.yml server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/metro_security?useUnicode=true&characterEncoding=utf8 username: root password: root # application-prod.yml server: port: 8080 spring: datasource: url: jdbc:mysql://10.0.0.5:3306/metro_security username: metro password: ${MYSQL_PASSWORD}启动时用--spring.profiles.active=prod区分环境。答辩现场最怕两种问题:一是数据库地址不对连不上,二是端口被占用。随机端口能缓解后者:
server: port: ${PORT:0}这样每次启动会分配一个随机端口,适合本地同时启多个实例做调试。但演示时别用随机端口,不然前端配置的系统地址对不上。我一般演示固定8080,本地多开用随机端口。
还有个小知识点:如果引入了Springdoc,不想让接口文档在演示时暴露,可以显式关闭:
springdoc: api-docs: enabled: false5.3 只有一个jar包时怎么找回业务逻辑
这个坑我是在帮一个学弟调试时遇到的:他的毕业论文是某培训机构给的,里面只有打包好的demo.jar,源码丢了。想改需求却发现无从下手。这种场景下,反编译就成了排查问题的合理手段。
注意,我这里说的是排查自己合法持有的项目,不是让你去破解别人的商业系统。在毕设交接、代码丢失这类场景里,反编译用于找回自己的代码逻辑是有实际价值的。
常用工具是JD-GUI和IDEA自带的Java Decompiler插件。步骤是:
- 用解压软件打开
jar包,把BOOT-INF/classes下的class文件解压出来。 - 打开JD-GUI,把class文件拖进去,会还原出大致可读的Java源码。
- 配置文件在
BOOT-INF/classes/application.yml里,可以直接看到数据库地址和中间件配置。 - SpringBoot打包的jar里依赖都在
BOOT-INF/lib下,想确认框架版本,看lib目录下的jar文件名就行。
反编译出来的代码一般缺少注释、变量名混乱,但它能帮你快速搞清楚“哪个接口调用了哪些Service”。在毕业设计这种以自己学习为主的场景里,这个方法比瞎猜高效得多。把这段经历写进博客,也算是对“代码丢失”这种突发情况的一种应急预案。
5.4 前后端联调的那些奇怪问题
SpringBoot + Vue前后端分离的项目,联调阶段的坑比后端开发阶段还多,我整理三个高频的:
- 跨域问题:前端请求被浏览器拦截。解决方式是写一个CorsConfig,允许指定的前端地址访问,不要用
*允许所有来源。安全起见,allowedOriginPatterns配置成具体的前端域名。 - 时间格式错乱:后端返回
LocalDateTime默认是一串数组,前端显示成“[2025,6,12,10,30,0]”。解决方式是加Jackson配置,统一格式为yyyy-MM-dd HH:mm:ss。 - 文件上传大小限制:SpringBoot默认允许上传文件大小为1MB,安检图片经常超限。需要在yml里配置:
spring: servlet: multipart: max-file-size: 20MB max-request-size: 50MB这些坑不踩一遍,答辩演示当天绝对手忙脚乱。我把联调的检查点做成清单,每次启动前后端都要过一遍。
6. 上线与答辩:从本地到Docker的平滑迁移
6.1 Dockerfile和docker-compose编排
毕设能跑起来只是及格,能用Docker部署是加分。特别是最后答辩如果有现场演示环节,Docker可以保证环境一致性——你上午在实验室调好的样子,下午在答辩机器上一点不差。
我用多阶段构建写Dockerfile:
FROM maven:3.8-openjdk-8 AS build COPY . /app WORKDIR /app RUN mvn clean package -DskipTests FROM openjdk:8-jre COPY --from=build /app/target/metro-security.jar /app/metro-security.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "/app/metro-security.jar", "--spring.profiles.active=prod"]配套的docker-compose.yml可以一次性把MySQL、Redis、MinIO和后端服务编排起来:
version: '3.8' services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: metro_security ports: - "3306:3306" volumes: - mysql-data:/var/lib/mysql redis: image: redis:7 ports: - "6379:6379" backend: build: . ports: - "8080:8080" depends_on: - mysql - redis volumes: mysql-data:注意depends_on不代表等待MySQL完全启动。稳妥的做法是在后端启动命令里加个等待脚本,或者用healthcheck。毕设演示时可以先手动docker compose up后等十几秒再访问,问题不大。
6.2 答辩演示前必须自测的流程
答辩现场讲的不是所有功能,而是“系统能解决一个完整问题”。我建议把演示过程设计成一条主链路,大约5分钟能讲完:
- 登录:展示图形验证码和JWT认证过程,说明用户角色的权限差异。
- 浏览客流统计:打开一个站点的客流曲线图,指出数据来自Redis缓存。
- 新增一条危险品安检记录,界面弹出告警提示,此时后台自动生成了告警。
- 进入告警中心,找到刚才生成的告警,点击“启动应急预案”,系统解析出任务清单。
- 模拟巡更超时,等待定时任务扫描生成告警,展示主动告警能力。
- 打开统计报表,展示一条趋势曲线。
演示前重点检查三件事:一是演示用的数据库里有没有足够的、能证明趋势的数据;二是定时任务的触发时间要提前算好,不要演示现场等5分钟才出告警;三是前端页面不要出现接口报错弹窗,提前把后端日志级别调成info,不要打印一堆乱码。
6.3 演示数据怎么准备才显得真实
准备演示数据是这个项目最耗时也最容易被低估的一步。我的做法是写一个DataGenerator类,在系统启动时检测数据库的告警表记录数,如果少于阈值就自动插入过去30天的模拟数据:
- 每天每个站点生成几十条安检记录,物品名称从“管制刀具”“易燃易爆品”“普通可疑物”里按比例随机。
- 每天生成几条告警,时间分布在客流高峰时段。
- 客流数据按小时生成:早高峰7-9点和晚高峰17-19点数值高,其他时段低。
- 巡更记录保证每个巡更点每天至少有两三次打卡,但故意留一两个站点在最近两天有超时未巡更的情况,方便演示定时告警。
这30天模拟数据一进去,报表页面的曲线立刻真实了。很多同学演示时数据库里只有演示当场建出来的三五条记录,曲线图一眼就能看出是造假的。
最后再分享一个小技巧:演示时把数据库备份文件放在U盘里,万一答辩机器上的MySQL被搞得乱七八糟,直接恢复备份。我当年答辩前靠这一手避免了一次翻车——现场机器上的MySQL字符集不对,中文全变问号,我恢复了自己的备份后一切正常。这个系统做完之后,也可以往两个方向扩展:前端加一个大屏驾驶舱展示客流热力图和告警分布,后端接入更完整的视频分析模型做异常行为识别。地铁安防这个题目的上限,基本取决于你愿意在业务深度上走多远。