
简介这份资源是面向Java全栈学习者与课程设计/毕业设计人群的智能停车场管理系统完整源码包采用Springboot后端、Vue前端与关系型数据库构建覆盖车辆出入、车位状态、用户权限等核心业务适合作为信息管理系统分析与设计的实战参考。压缩包共113个文件约6.22MB以62个Java源文件为主体配合9个CSS、9个JS、5个HTML构成前后端页面与交互逻辑另有properties、yml、xml等配置文件和xls数据表结构清晰便于按模块阅读。目前已有958人学习下载。资源包含可运行的源代码与数据库脚本读者可据此理解Spring依赖注入、RESTful接口设计、Vue组件化与状态管理等技术要点并参考车牌识别、车位预测等人工智能扩展思路快速搭建自己的停车管理项目或完成课程设计。1. 智能停车场管理系统从车牌识别到计费出场的全链路拆解小区门口那套道闸早高峰时一辆车卡在识别区后面排起长队保安拿着对讲机喊——这种场景做物业系统的人都不陌生。智能停车场管理系统要解决的核心问题就三件事车进来时怎么认、停着的时候怎么算钱、出去的时候怎么放行。基于 Java Spring Boot Vue 的这套方案是目前中小型停车场项目里落地成本最低、招人最容易、二次开发最顺手的技术组合。Spring Boot 负责后端业务与硬件对接Vue 负责管理后台和岗亭端界面数据库存车辆档案、进出记录、计费规则和订单流水。这篇文章面向的是手里有真实停车场项目、或者要交课程设计/接私活的开发者我会把表结构、车牌识别对接、计费引擎、前后端联调这几块拆开讲源代码和数据库脚本按这个思路组织就能跑通。适合谁会 Java 基础、能看懂 Spring Boot 注解、Vue 能改页面的人跟着做能出一套可演示、可交付的系统。2. 数据库表结构与核心实体设计别等写到计费才发现字段不够停车场系统的复杂度不在增删改查在于「一辆车从进场到出场中间跨了多少张表、状态怎么流转」。我见过太多人上来就建一张car_record表字段写个in_time、out_time、fee就开干结果做到月卡续费、免费时段、跨天计费的时候全部推倒重来。这一章先把表结构定死后面所有业务都从这里长出来。2.1 六张核心表与字段设计一套能跑通的停车场系统最少需要这六张表车辆表、车位表、进出记录表、计费规则表、订单表、用户/角色表。下面给出建表 SQL字段注释写清楚用途直接拿去执行。-- 车辆表登记固定车辆和月卡车辆 CREATE TABLE t_vehicle ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 主键, plate_no VARCHAR(16) NOT NULL COMMENT 车牌号唯一, owner_name VARCHAR(32) DEFAULT NULL COMMENT 车主姓名, phone VARCHAR(20) DEFAULT NULL COMMENT 联系电话, car_type TINYINT DEFAULT 1 COMMENT 车辆类型 1临时车 2月卡 3免费车, card_expire DATETIME DEFAULT NULL COMMENT 月卡到期时间, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_plate (plate_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT车辆信息表; -- 进出记录表每次抬杆生成一条出场时回填 CREATE TABLE t_pass_record ( id BIGINT NOT NULL AUTO_INCREMENT, plate_no VARCHAR(16) NOT NULL COMMENT 车牌号, park_id BIGINT NOT NULL COMMENT 停车场/岗亭ID, in_time DATETIME NOT NULL COMMENT 入场时间, out_time DATETIME DEFAULT NULL COMMENT 出场时间未出场为NULL, in_gate VARCHAR(32) DEFAULT NULL COMMENT 入口编号, out_gate VARCHAR(32) DEFAULT NULL COMMENT 出口编号, status TINYINT DEFAULT 0 COMMENT 0在场 1已出场, fee DECIMAL(10,2) DEFAULT 0.00 COMMENT 实收金额, PRIMARY KEY (id), KEY idx_plate_status (plate_no,status), KEY idx_in_time (in_time) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT车辆进出记录表; -- 计费规则表支持分时段、免费时长、封顶 CREATE TABLE t_fee_rule ( id BIGINT NOT NULL AUTO_INCREMENT, rule_name VARCHAR(64) NOT NULL, free_minutes INT DEFAULT 15 COMMENT 免费时长(分钟), first_hour_fee DECIMAL(10,2) DEFAULT 5.00 COMMENT 首小时费用, per_hour_fee DECIMAL(10,2) DEFAULT 3.00 COMMENT 超出每小时费用, day_cap DECIMAL(10,2) DEFAULT 30.00 COMMENT 单日封顶, effective_start TIME DEFAULT 00:00:00 COMMENT 规则生效开始, effective_end TIME DEFAULT 23:59:59 COMMENT 规则生效结束, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT计费规则表;t_pass_record里status字段是关键0 表示车还在场内1 表示已出场。查「当前在场车辆」就是WHERE status 0查历史流水就是status 1。idx_plate_status这个联合索引必须建出场时按车牌找未出场记录是最高频的查询没索引在数据量上万后直接拖垮接口。2.2 实体类与 MyBatis-Plus 映射Spring Boot 侧用 MyBatis-Plus 能省掉大量 XML。实体类字段和表字段用驼峰映射TableName指定表名。Data TableName(t_pass_record) public class PassRecord { TableId(type IdType.AUTO) private Long id; private String plateNo; private Long parkId; private LocalDateTime inTime; private LocalDateTime outTime; private String inGate; private String outGate; private Integer status; // 0在场 1已出场 private BigDecimal fee; }LocalDateTime对应 MySQL 的DATETIME别用java.util.Date序列化到前端时格式会乱。配置文件里加一行spring.jackson.date-format和时区否则 Vue 拿到的出场时间会差 8 小时这个坑后面避坑章节还会提。提示plate_no建唯一索引前先确认业务允不允许同一车牌多条记录。如果允许多停车场唯一键要改成(plate_no, park_id)联合唯一。3. 车牌识别对接与进出场业务从硬件回调到抬杆指令车牌识别是停车场系统的入口也是翻车最多的地方。市面上常见的识别一体机如臻识、华夏智信等品牌基本都提供 HTTP 或 SDK 回调识别到车牌后主动 POST 一个 JSON 到你的服务端。这一章讲怎么接这个回调、怎么判断该不该抬杆、怎么把记录写进库。3.1 识别回调接口的实现硬件回调的典型报文包含车牌号、识别时间、出入口编号、置信度。服务端要做的判断链是车牌是否合法 → 是不是固定车/月卡 → 月卡有没有过期 → 临时车是否放行。下面是对接接口的核心代码。RestController RequestMapping(/api/gate) public class GateController { Autowired private PassRecordService passRecordService; /** * 硬件识别回调返回 1 表示抬杆0 表示不抬 */ PostMapping(/notify) public MapString, Object notify(RequestBody GateNotifyDTO dto) { // 1. 基础校验车牌为空或置信度过低直接拒绝 if (StringUtils.isBlank(dto.getPlateNo()) || dto.getConfidence() 0.8) { return Result.fail(识别置信度不足); } // 2. 入场逻辑 if (in.equals(dto.getDirection())) { boolean allow passRecordService.handleEnter(dto); return allow ? Result.ok(抬杆) : Result.fail(禁止入场); } // 3. 出场逻辑先算费再决定是否抬杆 return passRecordService.handleExit(dto); } }handleEnter里要做的事查t_vehicle判断车辆类型月卡车检查card_expire是否过期临时车直接放行并插入一条status0的进出记录。handleExit则要先找到该车牌status0的记录算出费用写回out_time、fee、status1再返回抬杆结果。3.2 计费引擎的三种规则与实现计费是停车场系统的「黑匣子」规则没写清楚业主和车主都能吵起来。常见规则有三种按小时计费、分时段计费、包月不限次。核心算法是算停车时长再套规则。public BigDecimal calcFee(PassRecord record, FeeRule rule) { LocalDateTime in record.getInTime(); LocalDateTime out LocalDateTime.now(); long minutes Duration.between(in, out).toMinutes(); // 1. 免费时长内不收费 if (minutes rule.getFreeMinutes()) { return BigDecimal.ZERO; } // 2. 计费时长 总时长 - 免费时长向上取整到小时 long chargeMinutes minutes - rule.getFreeMinutes(); long hours (long) Math.ceil(chargeMinutes / 60.0); // 3. 首小时 超出部分 BigDecimal fee rule.getFirstHourFee(); if (hours 1) { fee fee.add(rule.getPerHourFee() .multiply(BigDecimal.valueOf(hours - 1))); } // 4. 单日封顶 if (fee.compareTo(rule.getDayCap()) 0) { fee rule.getDayCap(); } return fee; }参数说明freeMinutes是免费时长一般设 15 或 30 分钟firstHourFee首小时费用perHourFee超出后每小时单价dayCap单日封顶价防止停一天收出天价。Math.ceil向上取整是行业惯例停 61 分钟按 2 小时算这个要在规则说明里写清楚否则投诉不断。注意跨天停车要按自然日拆分计算上面这段只处理了单日。跨天场景需要循环每一天分别套用封顶否则停 3 天只收一天封顶价直接亏钱。3.3 抬杆指令的下发与重试服务端算出结果后要通知硬件抬杆。常见做法是回调接口直接返回抬杆标志硬件收到后自己抬杆。但如果是服务端主动控制道闸就需要下发指令这里必须做重试和超时。Retryable(value Exception.class, maxAttempts 3, backoff Backoff(delay 500)) public boolean sendOpenCommand(String gateNo) { // 调用道闸控制 SDK 或 HTTP 接口 String url gateConfig.getBaseUrl() /open?gate gateNo; ResponseEntityString resp restTemplate.getForEntity(url, String.class); return resp.getStatusCode().is2xxSuccessful(); }Retryable需要引入spring-retry依赖并在启动类加EnableRetry。maxAttempts3表示最多试 3 次delay500是每次间隔 500 毫秒。道闸这种硬件网络抖动导致第一次失败很常见重试能救回大部分情况。但要注意幂等——如果第一次其实成功了只是响应超时重试会重复抬杆所以指令里最好带唯一流水号硬件侧去重。4. Vue 管理后台与前后端联调岗亭端要能实时看到在场车辆后端接口写完了前端要让人能用。停车场系统的前端分两块管理后台配置规则、查流水、管车辆和岗亭端实时在场车辆、手动开闸、收费放行。Vue 用 Element Plus 搭界面最快这一章讲接口对接、跨域、实时刷新这几个实操点。4.1 接口封装与跨域配置Vue 项目里统一封装 axios把 baseURL 指向 Spring Boot 服务。开发阶段前后端分离必然遇到跨域两种解法后端加 CORS 配置或者前端配代理。推荐后端配一劳永逸。Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/api/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE) .allowCredentials(true) .maxAge(3600); } }allowedOriginPatterns(*)配合allowCredentials(true)是 Spring Boot 2.4 之后的写法老版本用allowedOrigins(*)会报错。maxAge(3600)表示预检请求缓存一小时减少 OPTIONS 请求。前端 axios 封装import axios from axios const request axios.create({ baseURL: /api, timeout: 10000 }) // 响应拦截统一处理错误码 request.interceptors.response.use( res res.data, err { console.error(请求失败, err) return Promise.reject(err) } ) export default request4.2 在场车辆列表的实时刷新岗亭端最核心的界面是「当前在场车辆」要求几秒刷新一次。用setInterval轮询最简单但要注意组件销毁时清掉定时器否则内存泄漏。script setup import { ref, onMounted, onUnmounted } from vue import request from /utils/request const list ref([]) let timer null const loadData async () { list.value await request.get(/pass/onSite) } onMounted(() { loadData() timer setInterval(loadData, 5000) // 5秒刷新 }) onUnmounted(() { clearInterval(timer) // 必须清理否则切页面后还在跑 }) /scriptonUnmounted里clearInterval是血泪经验不写的话用户切到别的菜单定时器还在后台请求开十个页面就是十个定时器接口直接被压垮。如果项目要求更高可以上 WebSocket 推送但中小停车场 5 秒轮询完全够用。4.3 出场收费弹窗与金额确认出场时岗亭端要弹出收费窗口显示车牌、入场时间、停车时长、应收金额收费员确认后点「放行」。这个交互的关键是金额必须由后端算前端只展示防止篡改。const handleExit async (plateNo) { // 先请求后端算费拿到金额再弹窗 const res await request.post(/gate/exit/preview, { plateNo }) if (res.code 200) { ElMessageBox.confirm( 车牌${res.data.plateNo}\n停车时长${res.data.duration}分钟\n应收${res.data.fee}元, 出场收费确认, { confirmButtonText: 确认放行, cancelButtonText: 取消 } ).then(async () { await request.post(/gate/exit/confirm, { plateNo }) ElMessage.success(已放行) }) } }/gate/exit/preview只算费不落库/gate/exit/confirm才真正写out_time和fee。这样设计的好处是收费员可以反复预览不会因为误点就产生一条出场记录。5. 避坑与排查那些让系统上线三天就崩的细节这一章是我踩过的坑每条都按「现象 → 原因 → 解决」写照着排查能省大量时间。现象一出场时找不到入场记录报空指针。原因车牌识别有误差入场识别成「京A12345」出场识别成「京A1234S」两个车牌对不上。解决车牌匹配要做模糊容错去掉易混淆字符如 0 和 O、1 和 I或者用「最近一条未出场记录 车牌相似度」匹配。更稳妥的是入场时就记录识别图出场人工核对。现象二月卡车过期了还能进。原因card_expire判断用了而不是或者时区不对导致比较结果偏差。解决统一用LocalDateTime.now()和数据库时间比较判断逻辑写成cardExpire.isAfter(now)并且加单元测试覆盖边界时间。现象三并发出场同一辆车生成两条出场记录。原因两个岗亭同时操作或者收费员连点两次确认。解决出场确认接口加分布式锁或数据库乐观锁UPDATE t_pass_record SET status1 WHERE id? AND status0根据影响行数判断是否成功返回 0 行说明已被处理。现象四Vue 打包后放进 Spring Boot刷新页面 404。原因前端路由是 history 模式刷新时请求打到后端后端没有对应接口。解决Spring Boot 里加一个转发配置把非/api开头的请求转发到index.html。Controller public class IndexController { RequestMapping(value /{path:[^\\.]*}) public String forward() { return forward:/index.html; } }现象五计费金额出现 0.01 元误差。原因用了double做金额计算浮点精度丢失。解决所有金额字段用BigDecimal数据库用DECIMAL(10,2)Java 里new BigDecimal(5.00)而不是new BigDecimal(5.00)字符串构造才精确。提示这五条里车牌匹配和并发出场是最容易在上线首周暴露的建议开发阶段就用 Postman 模拟重复请求压一遍。6. 进阶把计费规则做成可配置以及一套自测清单做到这里系统能跑了但真实项目里计费规则经常变——今天搞活动免费两小时明天节假日翻倍。硬编码在代码里改一次发一次版运维能骂人。进阶做法是把规则抽成配置用数据库驱动。6.1 规则配置化与优先级在t_fee_rule基础上加两个字段priority优先级和rule_type规则类型普通/节假日/活动。计费时按优先级从高到低匹配命中就用没命中用默认规则。字段类型说明rule_typeTINYINT1普通 2节假日 3活动priorityINT数字越大优先级越高effective_dateVARCHAR(64)生效日期如2025-01-01,2025-01-03计费入口改成先查规则列表按priority降序逐条判断当前时间是否在生效范围命中即返回。这样运营改规则只动数据库不用重新部署。6.2 上线前的自测清单交付前我会跑一遍这个清单每条都对应一个真实故障临时车入场 10 分钟出场费用应为 0免费时长内。临时车入场 61 分钟出场费用应按 2 小时算。月卡车过期当天入场应被拒绝或转临时车计费。同一车牌连续两次入场请求第二次应提示「车辆已在场」。出场确认接口连点两次只应产生一条出场记录。跨天停车 25 小时费用应按两天封顶计算。前端刷新任意路由页面不应 404。数据库断开后重启连接池应能自动恢复。这套清单我一般写成 JUnit 测试 一个 Postman 集合每次改计费逻辑先跑一遍。停车场系统不怕功能少怕的是算错钱和放错车这两件事出一次业主就不信任你了。希望帮到你。本文还有配套的精品资源点击获取