SSM车牌识别停车收费系统:开发细节与毕设部署全解析

📅 发布时间:2026/10/21 2:40:01
SSM车牌识别停车收费系统:开发细节与毕设部署全解析
1. 从选题到方案定型这个毕设到底在做什么如果你最近正在刷毕业设计题目大概率看到过这样一条SSM图像识别的停车收费系统-计算机毕业设计源码22336。我最初拿到这个题的时候第一反应也是这不就是很普通的SSM管理系统套个车牌识别吗。后来真正动手拆需求、跑通源码才发现里面值得展开讲的东西比想象中多得多。这套系统本质上解决的是停车场“无人化收费”的问题车辆进场时摄像头抓拍车牌系统自动识别并记录入场时间车辆离场时再次识别车牌根据停车时长计算费用完成支付后放行。放到毕业设计的语境里它同时踩中了三个加分点SSM后台框架能体现JavaWeb基本功图像识别能展示算法落地能力停车收费业务又有完整的表结构、流程和状态变化答辩时从头到尾都能讲出东西来。适合看这篇文章的人有两类。一类是正打算用SSM做毕业设计、但又不想做普通增删改查管理系统的同学另一类是已经拿到这套源码、想快速理解它并顺利复现演示的同学。接下来我会从技术选型、架构设计、识别链路、计费逻辑、避坑点和演示话术几个角度把整个项目拆开讲清楚。1.1 SSM在毕业设计里的真实定位先说一个经常被问的问题现在新项目都用Spring Boot为什么还要选SSM答案是毕业设计考察的重点不是技术新旧而是你对框架原理的理解。Spring Boot自动配置很好用但很多东西被藏起来了SSM里的Spring管理Bean、SpringMVC处理请求、MyBatis写SQL每一样都是可以当面讲清楚的东西。面试或答辩时老师问“你项目里控制反转体现在哪里”如果你用的是SSM可以顺着配置文件和源码一路讲下去。所以这套题目把SSM放在前面并不是因为它过时而是因为它是教学和考核场景里“刚刚好”的复杂度。Service层负责业务规则Controller层负责接收参数和返回结果Mapper层负责数据库交互三层边界清楚再加上拦截器做登录校验、AOP做事务控制这些都是毕业设计里非常稳定的得分点。1.2 图像识别方案的选型千万别一上来就自己训练模型题目里“图像识别”这四个字最容易把人带偏。我见过不少同学一拿到这个题就去研究车牌检测网络、准备几千张标注图片、训练深度学习模型最后项目没做完光是环境就装了一个月。这是典型的“题目还没看清楚先给自己加难度”。实际上作为毕业设计图像识别部分的目标只有一个输入一张包含车牌的图片输出一个车牌字符串。围绕这个目标可行方案有三类方案优点缺点适合情况OpenCV做定位OCR完全本地化无网络依赖识别准确率依赖图像质量中文车牌需要额外处理想展示图像处理全流程Python车牌识别库准确率较高代码量少需要跨语言调用或单独起服务想保住功能完整度云平台OCR接口接入简单准确率最高需要联网部分服务收费只追求演示效果我最终采用的是第二类思路把车牌识别单独做成一个本地HTTP服务SSM主系统通过接口调用。这样既不用自己训练模型又能把“图像识别”作为独立模块在论文里写清楚后期如果换识别引擎主系统代码完全不用动。这个方案的核心价值在于职责分离识别难做就让它孤岛化不要和业务代码纠缠在一起。1.3 我最终确定的系统边界整个系统的边界我是这么划定的SSM端负责登录、用户管理、车位管理、车辆管理、停车记录、费率配置、订单支付、统计报表。识别服务端负责接收图片返回车牌文本。两者之间只通过一个HTTP接口通信不共享数据库不共享Session。这个边界的好处是哪怕识别服务中途挂了SSM端还能靠人工补录车牌完成业务流程整套系统不至于一崩到底。边界想清楚之后后面的代码写起来就会非常顺。2. 系统分层与功能模块先画出可以答辩的架构图拿到源码之后不要急着跑先把项目结构看清楚。这套SSM项目的标准目录包含controller、service、mapper、entity、config、interceptor等包前端页面放在WebRoot或webapp下面。整体调用关系可以用一句话概括浏览器请求先到ControllerController调用ServiceService调用MapperMapper操作MySQL数据库。浏览器不直接碰数据库也不直接碰识别服务。2.1 三个层次加一个独立识别模块Controller层负责接收前端传来的参数比如用户登录的账号密码、上传的车牌图片、查询订单的条件。它不处理业务规则只负责“接参”和“返回结果”。Service层是本系统的核心。停车时长怎么算、费用怎么算、支付后状态怎么改、同一个车牌能不能重复入场这些规则全部写在Service里。MyBatis的事务控制也在这层做比如一次出场操作需要同时更新停车记录、生成订单、扣减余额如果中间某一步失败了整个操作要回滚不能出现“订单生成了但停车记录还是停车中”的情况。Mapper层就是数据访问层只做SQL操作。MyBatis里面比较重要的几个点结果集映射、动态SQL、以及下划线和驼峰命名转换。比如数据库字段叫plate_number实体类属性叫plateNumber你就需要在MyBatis配置里打开map-underscore-to-camel-case否则查出来全是null这种问题排查起来很隐蔽。2.2 功能模块怎么划分后台管理端和用户端是分开的。管理员登录后台之后能看到完整的功能菜单车位管理维护停车场内的车位编码、区域和状态。车辆管理登记固定车辆信息绑定用户。停车记录查看所有车辆的入场、出场历史支持按车牌和时间模糊搜索。费率管理设置免费时长、单价、每日封顶金额。订单管理查看每笔停车订单的金额、支付状态。统计报表按日或按月统计收入、停车次数。用户端的功能相对简单注册登录后可以绑定自己的车牌查询自己的停车记录查看待支付订单使用账户余额完成支付。为了让系统显得完整还会加上账户充值、修改密码这些常规功能。2.3 数据库表设计不要把所有东西塞进一张表停车记录和订单是两个不同的概念千万别混在一张表里。停车记录反映的是“这辆车什么时候进、什么时候出”订单反映的是“这次停车要收多少钱、有没有付”。我把它们分开建表也是为了让状态更清楚。核心表至少有这么几张用户表、车辆表、车位表、停车记录表、费用订单表、费率规则表。停车记录表的字段设计大概长这样CREATE TABLE t_parking_record ( id INT NOT NULL AUTO_INCREMENT, plate_number VARCHAR(20) NOT NULL COMMENT 车牌号, car_image_url VARCHAR(255) DEFAULT NULL COMMENT 入场抓拍图片, entry_time DATETIME NOT NULL COMMENT 入场时间, exit_time DATETIME DEFAULT NULL COMMENT 出场时间, space_id INT DEFAULT NULL COMMENT 绑定车位, status TINYINT NOT NULL DEFAULT 1 COMMENT 1停车中 2已离场, PRIMARY KEY (id), KEY idx_plate (plate_number), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT停车记录表;这里有两个细节很多人会忽略。第一plate_number一定要建索引因为每次查询停车记录都是按车牌查的没有索引数据量稍微大一点就会变慢。第二entry_time建议用DATETIME而不是VARCHAR因为后面所有计费计算都依赖时间类型存字符串只会给自己找麻烦。费用订单表我会把冗余字段也放进去。比如entry_time、exit_time、duration_minutes、amount这些字段虽然从停车记录表也能关联查到但订单一旦生成这些信息不应该再随原记录变化。订单是账单账单要具有不可变性这一点在表设计时就该想清楚。3. 图像识别这条链路从摄像头抓拍到车牌字符串入库停车系统里图像识别并不是独立的功能它必须和业务流程串起来。串起来之后的完整流程是车辆到达入口摄像头抓拍图片图片上传到SSM后端后端调用识别服务识别服务返回车牌文本后端校验车牌格式写入停车记录整个入场流程才算完成。3.1 图片上传这一步就有讲究很多初次接触这个项目的同学把图片存到了Tomcat的webapps目录下的某个临时文件夹里结果项目一重启历史图片全丢了。正确做法是把图片存到一个跟Tomcat部署目录无关的本地目录比如D:/upload/parking/然后在SpringMVC里配置静态资源映射mvc:resources mapping/upload/** locationfile:D:/upload/parking/ /这样页面上就能通过/upload/xxx.jpg访问到图片。上传的时候要注意MultipartFile获取的文件名不要直接用最好用当前时间戳加随机数重新生成不然不同车辆抓拍图重名后上传的会把之前的覆盖掉。还有一点是上传大小限制。车牌识别图片一般不需要太大但摄像头原图可能有好几兆我建议在SpringMVC配置里设置max-file-size5MB同时代码里把图片等比压缩到宽度不超过1024像素再发给识别服务识别速度会明显提升。3.2 车牌识别的内部处理流程识别服务内部大概是这样的流程先把图片转成灰度图再做去噪、边缘检测然后根据车牌的颜色特征或几何特征定位车牌区域接着对车牌区域做字符分割最后把每个字符送入OCR模型识别。毕业设计不需要自己实现这些算法直接用现成库就行。灰度化去掉颜色干扰减少计算量。高斯模糊消除图像噪点让轮廓更连续。边缘检测找出图像中轮廓明显变化的区域车牌区域通常有矩形边缘特征。颜色识别蓝底白字、绿底黑字、黄底黑字通过特定色域过滤可以锁定车牌范围。字符分割是整个识别链路里最容易失败的环节。图片亮度不均、车牌倾斜、有遮挡都会导致字符被切断或者粘连。所以处理之前要先做倾斜校正如果图片里的车牌歪了先通过透视变换把它拉正再做分割。识别之后还要做一步后处理过滤掉置信度特别低的字符再用车牌正则校验一下格式。前端把图片提交给SSM后端之后Controller只需要做很薄的一层转发PostMapping(/recognize) ResponseBody public Result recognize(RequestParam(file) MultipartFile file) { if (file null || file.isEmpty()) { return Result.error(图片不能为空); } String imageUrl fileStorage.save(file); String plate plateRecognizer.recognize(imageUrl); if (!PlateValidator.isValid(plate)) { return Result.error(未识别到有效车牌请手动补录); } return Result.success(plate); }识别服务返回plate字段之后后端不要直接信任最好再用正则做一次格式校验。车牌有一定的组成规律普通车牌通常是省份简称、字母、数字的组合新能源车牌会多一位。正则校验能挡掉相当一部分识别错误把脏数据挡在入库之前。哪怕正则没过也不要直接报错而是退回人工补录页面让管理员手动填写车牌这样系统在真实场景里才能用得住。4. 停车计费与订单流程不是简单算一下时间差很多人觉得计费就是出场时间减去入场时间再乘以单价。表面看起来没问题但真正写代码的时候会遇到一堆边界情况不足一小时怎么算、跨天了怎么算、免费时长怎么算、封顶金额怎么算、同一辆车重复入场怎么处理。这些才是体现业务功底的细节。4.1 先定好一套可解释的计费规则费率不能写死在代码里要建一张费率规则表。我常用的规则是这样的入场后前30分钟免费超过免费时长后按1小时计费不足1小时按1小时向上取整每小时5元单日封顶50元跨天停车不重复收取当日封顶而是按自然日分别封顶总费用不能超过每日封顶加上后续时长费用。这套规则解释起来很简单答辩时也容易讲。计算时长的时候我强烈建议用LocalDateTime而不是Date加SimpleDateFormat。后者是线程不安全的而且处理时间加减特别别扭。用Java 8的时间API写计费逻辑会清爽很多public BigDecimal calcFee(LocalDateTime entryTime, LocalDateTime exitTime, FeeRule rule) { long minutes ChronoUnit.MINUTES.between(entryTime, exitTime); if (minutes rule.getFreeMinutes()) { return BigDecimal.ZERO; } BigDecimal hours BigDecimal.valueOf(minutes) .divide(BigDecimal.valueOf(60), 0, RoundingMode.UP); BigDecimal fee hours.multiply(rule.getUnitPrice()); BigDecimal dailyCap rule.getDailyCap(); if (dailyCap ! null fee.compareTo(dailyCap) 0) { fee dailyCap; } return fee.setScale(2, RoundingMode.HALF_UP); }4.2 入场、出场、支付的状态机停车记录有一个状态字段订单也有一个状态字段两者不要混用。车辆入场之后停车记录的状态是“停车中”此时还没有订单。车辆出场时先生成订单订单状态是“待支付”然后用户支付订单状态变成“已支付”同时停车记录更新为“已离场”。这里我特意把“生成订单”和“支付完成”拆成两步。因为在真实停车场里先出场后付费是很常见的情况如果一步就把订单状态变成已支付那“支付”这个动作就没有边界了。毕业设计也是如此把状态拆细一点页面上的按钮和提示才会自然。支付我建议做成模拟支付而不是真的接入第三方支付渠道。用户账户里会有余额先给演示账号充值之后出场时从余额里扣款。这样做的好处是不需要申请商户号不需要接口回调演示也不依赖外网环境。4.3 同一个车牌重复入场的并发问题这是我在第一版代码里踩过的坑。入场接口如果没做防重两个请求同时到达会查出两条“停车中”的记录。解决思路是在出场之前增加一步“抢占”操作用一个事务把当前车牌、状态为停车中的记录更新为已处理更新影响行数为0说明没有可退场的车。比如用一条带条件的UPDATE语句UPDATE t_parking_record SET status 0 WHERE plate_number #{plate} AND status 1 AND exit_time IS NULL再用影响行数去判断是否成功而不是先SELECT再UPDATE。先查后改在高并发下会出问题直接UPDATE保证只有一次能成功。这个细节在答辩时可以主动讲出来老师会觉得你确实考虑了真实场景。5. 开发过程中最容易踩的坑这个项目从零开始跑通我遇到最多的不是识别算法问题而是环境配置和细节实现问题。下面这几个坑如果你也要复现这套源码可以提前绕开。5.1 图片上传后刷新就丢失如前所述图片如果存到了Tomcat临时目录重启就没了。我把图片路径独立出来用一个配置文件专门存放。同时保存路径字段不要写死做成全局配置项。另外数据库里保存的是相对URL路径比如/upload/20250401/xxx.jpg不要保存完整物理路径否则以后换服务器历史数据就全乱了。5.2 识别服务用HTTP调用而不是前端直连识别服务如果独立部署在另一个端口前端不要直接用AJAX去请求那个端口。一是会有跨域问题二是识别服务和主系统的鉴权逻辑分离会很麻烦。正确做法是前端只请求SSM后端接口由后端用RestTemplate或HttpURLConnection转发到识别服务。这样前端永远只面对一个域名、一个端口识别服务挂在哪个地址由后端配置决定。5.3 金额计算千万别用float和double停车费用涉及钱用浮点类型做乘法、除法、四舍五入很容易出现0.10.2不等于0.3这样的精度问题。我在数据库里用DECIMAL(10,2)Java里用BigDecimal所有金额运算都在BigDecimal上完成最后再setScale(2)。如果不想写那么多BigDecimal代码直接在数据库字段存“分”单位的长整数也可以页面展示的时候再除以100。5.4 MyBatis动态SQL的坑列表查询需要支持多条件组合车牌照、时间范围、状态这几个条件经常只传一部分。用MyBatis的where标签加if判断最合适。但要注意if testplateNumber ! null and plateNumber ! 这行代码里不能直接写实体类没有的属性。如果你传的是plateNumber但实体类里叫plateNum运行时会直接报错这个错误还只能在查询时暴露启动阶段发现不了。所以所有查询条件尽量不要用Map而是单独建一个Query对象里面字段名和Mapper里写的一致。6. 从源码到可演示的毕设部署与演示要点源码拿到手最后一步是把它跑起来并且跑得漂亮。这里给出我实际操作过的环境清单和演示建议。6.1 环境清单组件版本建议说明JDK1.8兼容性最好不建议用太高版本Maven3.6以上管理依赖包Tomcat8.5与JDK8搭配稳定MySQL5.7建表脚本和中文支持都友好识别服务随源码提供如果使用Python库需要匹配对应版本项目如果用的是Maven导入依赖时第一件事就是检查本地仓库是否已经下载了缺失的Jar包。我的习惯是先在源码目录下执行mvn clean package让Maven把依赖和编译错误一次性暴露出来。能成功打包说明项目代码环境没问题再部署到Tomcat问题就只会在运行时出现排查范围会小很多。6.2 跑通项目的基本步骤第一步在MySQL中创建数据库并执行源码里带的SQL脚本建好表并写入初始管理员账号和费率数据。第二步修改数据库连接配置把密码换成你自己的。第三步配置图片上传物理路径和识别服务地址。第四步启动识别服务确认接口能返回车牌文本。第五步启动Tomcat访问项目首页先用管理员账号登录后台把停车记录、费率、订单各页面点一遍。这个时候不要急着测试识别功能先把增删改查和登录权限确认清楚。基于SSM的项目最可能先出问题的是登录拦截器误拦截了静态资源和登录接口。如果登录后一直跳回登录页先检查拦截器的放行路径配置。6.3 演示时怎么讲图像识别演示顺序很重要。我的建议是先展示车牌识别再展示计费订单最后展示统计报表。入口处上传一张车牌清晰的图片识别成功后停到“停车记录”页面把这条记录展示出来强调入场时间被自动记录。然后再出场上传同一辆车的图片系统会自动匹配停车中的记录并计算出费用。支付完成后再回到订单列表展示状态变化。整个过程就是一个完整闭环比零散地演示各个CRUD页面有说服力得多。这里还有一个小技巧演示前准备一张光照不太好、但车牌肉眼还能看到的图片故意让识别服务第一次识别失败然后页面弹出人工补录框。这个“异常处理”的演示很容易被忽略但往往能给评委留下好印象因为你证明了系统不只是理想情况下能用。补录之后后续的计费流程照常走异常数据也不影响主流程。最后再分享一点我个人的体会。做这种带业务闭环的毕设题目最容易犯的错误是只看局部功能不看全局流程。图像识别做得再花哨计费规则算得再复杂如果数据库表之间关联混乱订单状态前后对不上答辩时一深聊就会露馅。建议你拿到这套源码之后先在纸上把“入场-出场-支付”完整走一遍把每一步涉及的表、字段、状态变化列出来再去看代码很多疑惑会瞬间解开。后面如果想扩展还可以把固定车辆包月计费、车位预约、微信小程序端加进去但核心的这套SSM识别停车链路不要动它才是整个项目的骨架。