ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于SpringBoot与Vue的无人值守停车系统实践

基于SpringBoot与Vue的无人值守停车系统实践

1. 从人工岗亭到无人车场:这个系统到底改了哪些环节

过去半年我一直在迭代一套基于SpringBoot + Vue的智能停车系统,最初的想法很朴素:让停车场不再依赖收费员在岗亭里跑进跑出,把入场、计费、抬杆、出场、支付这一整条链路交给程序自动完成。做出来的效果也确实验证了这条路走得通——车辆进场时相机识别车牌,后端生成计费订单,出场时根据停车时长计算费用,用户扫码支付后道闸自动抬起,全程不需要人工按一次按钮。这套方案就是把原来收费员做的判断,拆成"设备采集信息、后端做决策、前端做监控兜底"三个环节。

很多人一听"无人值守"就觉得门槛很高,其实拆开看,核心技术点就那么几块:车牌识别怎么接、道闸怎么控制、费用怎么算、支付怎么对账、异常怎么兜底。这篇文章我会围绕这五个点,把完整方案从架构到实现再到部署后的坑都讲一遍,适合正在做停车管理、门禁系统、园区一体化项目的开发者,也适合想评估无人值守改造的停车场运营方参考。

传统车场的痛点其实不用我多解释:上下班高峰期,入口排长队,岗亭里的收费员同时要按键开闸、登记临时车辆、接电话,动作稍慢后面就喇叭响成一片。到了夜间,收费员困得不行,遇到熟车或月卡车经常直接放行,该收的钱漏掉。更麻烦的是对账,现金、扫码、月卡混在一起,每天交接班要数半天零钱,后台账目怎么都对不上。这些问题不是换个新道闸就能解决的,核心在于人车信息没有数字化,所有决策都靠人现场判断,自然就有疏漏。

无人值守方案要做的,就是把入场、在场、出场、支付这四个环节的所有数据沉淀下来。入场时,识别相机把车牌、照片、时间推给后端,后端判断是月卡车、白名单车还是临时车,该抬杆就抬杆;车辆在场期间,订单模块记录状态;出场时再次识别车牌,后端把订单金额抛给用户,用户支付完系统确认到账才放行。整个过程里,后台管理员只需要盯着一块监控屏,处理那些系统拿不准的异常事件就行。这套逻辑看着不复杂,但真正落地时,设备协议、计费规则、支付回调、断网恢复这些细节,任何一个考虑不周都会让"无人值守"变成"无人敢值守"。

这套方案也不是所有场景都适用。如果你管的是一个没有道闸、免费开放的园区,那就没必要做收费系统;如果车位严重不足、到处乱停,那需要先解决车位供给和引导问题。它最适合的是有明确出入口、需要控制车辆进出的中小型停车场,比如商业广场、写字楼、公寓小区、产业园区,以及拥有多个车场的连锁运营方。在这些场景里,无人值守降的不只是人力成本,更重要的是把计费透明化、让管理者远程就能掌握每个车场的实时状态。

2. SpringBoot后端:订单状态机、计费规则与设备回调的建模思路

整套系统的核心发动机在SpringBoot后端。Vue只是脸面,道闸相机只是手脚,所有规则判断、金额计算、状态流转都在后端完成。所以我一直跟人强调,做这类系统先别急着写前端页面,先把后端的领域模型设计清楚,尤其是订单状态机和计费规则,否则后面改一行代码可能牵动整条链路。

2.1 系统模块怎么划分

我用的是比较直观的模块划分方式,按业务边界拆成六个部分:设备接入模块负责接收相机识别结果、向道闸发送开关指令,是整个系统的数据入口和指令出口;订单模块负责管理一次停车从入场到离场的完整生命周期;计费引擎负责根据停车时长、车型、优惠策略计算费用,单独拆出来是因为计费规则变化频繁,不能和业务逻辑揉在一起;支付模块负责对接第三方支付通道,包含预下单、回调验签、查单补单、退款;用户与权限模块管理管理员、财务、值班员等角色;管理后台模块提供流水查询、报表统计、黑名单配置等能力。

这六个模块放在一个SpringBoot工程里完全足够支撑单停车场到几十个车场的规模。没必要一上来就上微服务,停车系统的核心是实时性和数据一致性,单体应用加好索引、做好异步任务,性能完全够用。设备回调流量虽然集中,但一台4核8G的服务器扛几千个车场同时上报都没有压力,前提是接口设计要幂等,这个后面细说。

2.2 订单状态机:用状态代替一大串if-else

停车订单最容易写乱的地方就是状态判断。比如用户已经支付了,但车辆还没出场,这个订单算什么状态?车辆已经离场了但没支付成功,又算什么状态?如果不设计状态机,直接在逻辑里判断"支付成功且未离场",时间长了就分不清边界。我定义的状态有这么几个:CREATED表示车辆入场成功、计费已经开始;PAYING表示车辆到了出口、系统已经发起支付但用户还没完成支付,此时订单挂起等待支付结果;PAID表示用户支付成功、等待抬杆放行;CLOSED表示车辆已经离场、订单彻底结束。另外还有两个特殊状态:ABNORMAL用于识别失败、找不到入场记录等异常场景;FREE用于月卡和白名单车辆的免费放行。

状态机的价值在于,所有历史操作都变成状态间的一次迁移。比如支付回调到达时,我只允许PAYING状态往PAID迁移,如果当前状态已经是PAID,说明是重复回调,直接忽略。车辆离场也只允许PAID状态往CLOSED迁移。这样就不需要到处判断"是否已经付过款"这种模棱两可的条件。到了数据库层面,订单表加一个state字段,配合update_time做乐观锁,能有效防止并发情况下同一订单被重复处理。

2.3 计费规则:从"首小时5元"到"单日封顶"的配置化

停车计费看起来简单,实际做起来全是细节。首小时免费、免费15分钟、首小时5元后续每小时2元、不足1小时按1小时算、夜间收费、单日封顶30元、24小时最高收费,这些规则不是写死在代码里的,而是要走配置化。我的做法是设计一张计费规则表,把规则拆成基础参数:free_minutes免费时长、first_period分钟数、first_fee首段费用、unit_minutes后续计费单位、unit_fee单位费用、daily_cap单日封顶、night_start和night_end夜间时段,再用一个fixed_fee表示按时段一口价。后端根据这些参数组合出一个ChargeRule对象。

计算时,系统把入场时间和出场时间换算成分钟差,先扣掉免费时长,再按首段和后续分别计费,最后套用封顶逻辑。这里有一个很关键的业务取整规则:不足一个计费单位,是向上取整还是向下取整。绝大多数停车场会选择向上取整,因为超出的每一分钟都占用了车位资源,但从用户体验角度,59分钟的临停用户被按2小时收费就会投诉。我的建议是做阶梯化取整,比如首小时后不足30分钟按30分钟、超过30分钟按1小时,虽然多花了一点计算功夫,但争议少很多。计费规则准备好之后,还要预留优惠参数位,例如新能源车减免、会员折扣、节假日活动价,这些都可以在Order上叠加DiscountStrategy接口实现。

2.4 关键数据库表的设计要点

数据库设计直接决定系统能不能跑得顺。我核心用了三张表:设备表device记录每个相机和道闸所属车场、出入口方向、在线状态;订单表parking_order记录车牌、入场时间、入场设备、出场时间、出场设备、费用、支付单号、状态等字段;流水表operation_log记录每一次开闸、识别、支付回调、人工干预的痕迹。订单表里车牌字段一定要单独建索引,因为出场时主要靠车牌查入场记录;如果车场规模大、流水量上来了,还要按入场时间做分区。

这里我想强调一个经验:入场记录和订单尽量合并成一张表,但出场记录不要另开表。很多人会把入场和出场分开存,结果对账时要来回join,状态还容易对不上。正确的做法是订单表里同时有snap_in_time和snap_out_time字段,车辆识别入场时写一条订单,状态置为CREATED;识别出场时更新这条订单而非新增记录。这样一张表就能完整描述一次停车行为,报表统计和异常排查都方便很多。

3. 道闸与相机的接入细节:识别回调、抬杆指令和异常兜底

后端模型设计好了,接下来是大家最头疼的硬件对接。无人值守系统不像纯软件项目,所有逻辑都建立在真实设备的事件触发上。如果对设备协议不熟悉,很容易把时间耗在"为什么相机什么都没推过来"或者"为什么道闸有时候不抬杆"这类问题上。所以这一节我重点讲设备接入的实际经验。

3.1 硬件组成:识别相机、道闸控制器、车辆检测器

一套标准出入口设备其实就四样东西:识别相机、道闸控制器、道闸杆、车辆检测器。识别相机负责抓拍和车牌识别,现在的主流相机已经内置了识别算法,可以直接输出车牌号码、车牌颜色、置信度、抓拍图片,不需要额外接一台识别主机。道闸控制器负责电机升降,它对外一般提供开关量信号接口和网络接口两种控制方式。车辆检测器负责感知车辆是否压到线圈或雷达区域,防止道闸误落杆砸到车,这个在车辆还没有完全通过出口时尤其重要。

选设备时不用纠结是不是大品牌,关键在于接口开放程度。一定要选提供Http API或TCP协议文档的厂商,不要买那种只能配合自家私有软件使用的设备。我吃过这个亏:某次选设备只看了识别效果,结果发现它的接口只能通过自带客户端控制,后端想直接调道闸开闸必须绕一圈去模拟界面操作,非常痛苦。后面统一选支持HTTP接口的型号,后端直接发JSON请求就能开闸,省了很多事。

3.2 相机识别回调:异步推送还是主动拉取

相机识别到车牌之后,要把结果回传给后端。主流厂家一般支持两种方式:一是相机把识别结果通过HTTP POST推送到指定URL,二是后端定期从相机拉取识别记录。无人值守场景我强烈建议用推送方式,延迟低、实时性好。相机推送给后端的报文大概长这样:

{ "deviceId": "gate-entrance-01", "eventType": "ENTRY", "plateNo": "测A88888", "plateColor": "BLUE", "confidence": 96, "captureTime": "2025-06-01 08:00:00", "imageUrl": "http://camera/gate-entrance-01/capture/202506010800.jpg" }

后端收到这个报文后,先做幂等处理,因为相机的推送可能不止一次。比如同一辆车压过地感线圈时触发多次抓拍,相机可能连推三条请求,如果不做去重,系统会生成多个入场订单。我的去重逻辑很简单:以deviceId加车牌加captureTime生成一个唯一键,存入Redis,两分钟内重复的推送直接返回成功;同时用同一唯一键查数据库是否存在对应订单,存在就不重复创建。只有先保证了这一步,后面计费和开闸才不会乱。

3.3 抬杆控制:不是发个指令就完事了

收到入场请求后,后端判断该车辆有入场权限,就需要向道闸控制器发送开闸指令。这一环节很多新手会漏掉一个关键点——只发指令、不查结果。道闸控制器执行开闸有一个过程,电机转起来、杆抬到位可能需要一秒钟左右。如果程序发出指令后马上认为开闸成功,那万一设备离线了、继电器没吸合、杆被异物卡住,车辆就会撞杆或者堵在入口。我的做法是把开闸动作设计成一个异步任务:先调用道闸HTTP接口发起开闸,同时起一个定时任务轮询道闸状态,如果5秒内没有检测到抬杆到位信号,就触发告警推送给远程值守人员。

指令层的设计要区分"下发电平信号"和"查询状态反馈"两个动作。完整的道闸控制器接口通常包含开闸、关闸、查询当前状态三个命令。后端会在调用开闸后定期查询状态字段,status为UP表示到位,为DOWN表示杆已落下。这里还涉及到落杆时机的设计,车辆没有完全通过闸机区前不能落杆。如果有车辆检测器,后端的关闸逻辑可以直接依赖检测器的"车辆离场"事件;如果不依赖检测器,就得靠程序设定延时,比如开闸后固定维持15秒再落下,这种做法在极端情况下容易砸到慢速行人或二轮车,所以我建议有条件还是接上检测器。

3.4 设备离线与断网兜底

设备离线是无人值守系统最怕的事。相机断电、网络抖动、交换机故障,任何一个环节断掉,出入口就可能瘫痪。我的方案是给每个出入口部署一个边缘盒子,本质上是一台小主机,提前接下所有闸机和相机的控制权限,本地内置一份车牌白名单和入场订单缓存。断网期间,边缘盒子继续执行识别、开闸、计时这些基础操作,等网络恢复后再把缓存的进出场记录批量补传给中心服务器。中心服务器接收到补传数据时,要注意报文里的captureTime是设备本地时间,不能用服务器接收时间代替,否则出场时长会算错。

如果不想上边缘盒子,还有一个轻量方案:设备端断网后持续本地存储,网络恢复后后端主动补偿拉取。但这种方式的实时性差很多,而且很多廉价相机自带存储容量极小,最多存个几百条记录,不满足高车流场景。所以,做无人值守停车,预算再省也不要省边缘盒子这一环,它是断网情况下唯一能让车场继续运转的保障。

4. Vue监控端:实时流水、远程开闸和人工复核的交互设计

后端做得再强,管理员也不可能整天盯着日志。无人值守能不能真正替代岗亭,还取决于前端监控端好不好用。我选择Vue来写这套前端,看中的是SPA模式下页面切换无刷新,再加上组件复用能力,同一个流水组件可以同时用在PC大屏、平板值守端和H5手机上。

4.1 页面结构:一块屏看清全场动态

监控端的主页面我做成一个数据看板,核心信息一目了然。顶部是统计卡片:今日进场车辆数、出场车辆数、当前在场车辆数、今日应收金额、已支付金额、待支付金额。中部是实时进出流水列表,每一条记录显示车牌、方向、识别置信度、事件时间和当前状态,如果是待支付订单还会高亮显示。右侧是设备状态面板,列出每个入口和出口的相机、道闸、边缘盒子在线情况,有一点异常马上变色。

这个看板不是简单的统计数据展示,它有一个很重要的作用是暴露问题。比如某条流水显示置信度只有72%,说明相机识别可能不准,系统已经自动进入待复核队列,管理员点进去看抓拍图片,人工确认车牌后补录到订单里。没有这个复核机制,无人值守就是纸老虎,识别率再高也总有特殊车牌、污损车牌、夜间反光车牌会难倒算法。

4.2 数据实时性:WebSocket推送比轮询靠谱

看板上的流水要实时刷新,我用的是WebSocket,后端在设备回调、支付回调、道闸状态变化时,主动向前端推送一条事件消息。比如车辆入场成功,前端流水列表自动滚动出一条新记录,不需要管理员手动刷新页面。这里我踩过一个坑:最初用前端定时器每3秒轮询一次最新流水,高峰期几十个页面同时轮询,服务器压力陡增,而且流水刷新有3秒延迟,管理员在岗亭场景下体感不够灵敏。换成WebSocket之后,事件到达就是即时推送,服务器只在连接建立时查一次历史数据,后续全是增量消息,压力小很多。

WebSocket的接入逻辑不复杂,前端用原生WebSocket或者封装库都行,关键是后端要处理好断线重连。我的做法是后端维护一个userId与连接的映射,管理员登录后建立通道,断线后前端自动重连,重连成功后再拉一次最近五分钟的流水来补齐断开期间漏掉的消息。不要小看这个补拉动作,否则页面会一直显示停留在断线前的那一刻。

4.3 远程值守与远程开闸的权限控制

监控端最敏感的操作就是远程开闸。值班员在地铁站都还没到现场,但系统识别到了一辆月卡车,需要放行;或者出口有一个车主支付失败堵在闸机前,管理员要先远程开闸让车辆离开,再后续处理费用。远程开闸一旦放开,安全风险就来了,任何有系统权限的人都能开门放车,内部车辆免费随意进出。所以我的权限设计里,远程开闸是高危操作,只有指定角色才能执行,而且每次远程开闸都要填写原因备注,后台强制记录操作人、操作时间、目标设备并保存开闸前后的抓拍图片。

前端操作按钮要有二次确认弹窗,弹窗里展示目标设备实时状态和当前画面缩略图,避免管理员在多个车场切换时误操作了别的闸机。我还在操作日志里加入了防赖账设计:如果开闸后这台设备在一分钟内记录到车辆出场事件,会把这次出场关联到这条开闸日志上,后期对账时能追溯每一笔免费放行的原因。

4.4 H5移动端:值班员不在电脑前也能处理异常

除了PC大屏,我额外做了一套H5页面,主要给流动值班员使用。使用场景是这样的:值班员可能在另一个车场巡逻,突然收到手机推送的异常事件,他只需要扫码打开H5页面,就能看到待复核列表,确认车牌、选择放行或拦截,也可以查看车场余位和实时流水。H5页面我没有重写一套复杂逻辑,而是复用PC端的API和大部分组件,只是把布局改成移动端纵向排列,重点突出待办提醒。这套H5并不追求完美视觉,但它在关键时刻能救命,很多时候手机比电脑更容易让人快速响应。

5. 支付流程与对账:预下单、回调验签和延迟补单的完整链路

无人值守停车系统里,支付是最不能出错的一环。前面识别错了可以人工复核,道闸卡了可以手动抬杆,但钱如果算错或者对不上账,轻则影响运营方与车主信任,重则导致财务纠纷。所以支付这一块我花了很多精力做严谨的流程设计。

5.1 临时车出场:系统主动发起支付

临时车的出场流程是这样的:出口相机识别到车牌后,后端先检查该车牌是否存在待支付的出场订单,如果找不到入场记录,就进入异常复核队列;如果找到了,系统自动创建一个支付单,支付单会带上应付金额和订单号。此时前端出口显示屏会展示二维码,车主扫码后在手机上完成支付。这里有主扫和被扫两种方式,无人值守场景我用的是主扫,也就是屏幕出码、用户用手机扫,系统简单,屏幕不需要改造成扫码枪。部分车场想做无感支付,就提前让用户绑定车牌和免密支付,出场时系统直接发起扣款,扣款成功后抬杆,不需要用户掏手机。

创建支付单时,后端会把订单号和金额传给第三方支付服务商,服务商返回一个支付链接或二维码内容;用户支付成功后,服务商把支付结果以异步回调方式通知后端。这条链路里最核心的设计是回调验签,后端收到回调后不能直接改订单状态,必须先按协商好的密钥对回调参数做签名校验,确认数据确实来自支付服务商,再对比回调里的订单号、金额与本地支付单是否完全一致。只要有一个字段对不上,就不能确认收款成功。校验全部通过后,先把订单状态从PAYING改为PAID,再触发出口道闸开闸,同时向前端推送一条放行通知。

5.2 回调丢失和延迟:主动查单补单

支付回调不是100%可靠的,网络抖动可能导致回调没有送达。如果用户已经付款,但后端没收到回调,道闸不抬杆,车主被堵在出口,体验会非常差。规避办法是增加一个主动查单任务:车辆停在出口正在支付时,后端在创建支付单后启动一个定时任务,每隔10秒调用一次支付服务商的查单接口,获取当前支付单状态。如果查单结果显示已成功支付但本地订单还停留在PAYING,就执行与回调处理相同的状态更新逻辑。

这里要注意防止重复入账。查单任务和回调可能同时触发支付成功处理,所以更新订单状态的代码必须加幂等控制。我在订单上设置一个pay_success标记,状态更新时使用updatestate='PAYING'whereorder_id=?条件更新,影响行数为0就说明已被其他线程处理过,直接返回。别小看这个细节,没有幂等控制,线上很容易出现同一笔订单被标记成两次成功,后续对账时怎么都说不清。

5.3 兜底支付:现金和转人工

无人值守不意味着完全不接受现金。偶尔会有车主手机没电、不会扫码、手机没有绑定支付方式。我设计了两种兜底方案:一种是在出口对讲呼叫中心,值班员远程创建一个纸票订单,车主把现金投到岗亭或者通过其他方式缴纳,值班员在后台手工标记订单已支付再开闸;另一种是针对固定群体,比如公司月卡客户,可以先把车放行,挂账到月底一次性结算,这个相当于信用模式,只对白名单企业开放。

兜底流程千万不要做成开放入口,否则会变成逃费后门。我要求手工标记支付必须关联一个管理员账号,并且填写收款方式、备注,后台每日会生成"手工放行报表",运营方一眼就能看到当天有多少笔没有经过系统自动支付就放行的车次。

5.4 每日对账任务:凌晨自动核对

财务最关心的对账,我做成每天凌晨的定时任务。系统会拉取支付服务商前一天的交易账单,把每一笔账单流水与本地支付单按支付单号关联,然后分三个维度核对:总额是否一致、流水明细是否能一一对应、本地已标记支付成功但账单里没有的差异项。核对结果生成一张对账报表,有差异的条目单独标红,并推送提醒给财务人员。

对账任务刚上线的那几天,几乎每天都会给我发差异提醒。排查后发现原因千奇百怪:有测试环境的数据混进来、有退款单没处理、有支付成功但本地订单被人工异常关闭。这些差异本身不会导致资金损失,但不排查清楚就会在月底结算时爆炸。所以对账任务一定要尽早接入,从上线第一天就开始积累数据,后期才能越跑越干净。

6. 上线后容易被忽视的坑:误识别、断网、二次进场和道闸状态

这套系统在开发阶段跑得很顺,但真正上线第一个月,各种问题接踵而来。我不会跟你说"做完系统就万事大吉"这种话,相反,无人值守系统的复杂度恰恰是在运营阶段才逐步暴露的。这一节我挑几个最典型、也最容易被忽视的问题详细讲。

6.1 车牌误识别:0/O、8/B的相似度问题

车牌识别算法再好,也会在特殊光线或污损情况下产生误识别。最常见的是数字0和大写字母O混淆,8和B混淆,5和S混淆。如果系统完全相信识别结果,就会出现这种情况:一辆真实车牌为"测A8888X"的车入场时被识别成"测A8888B",出场时又被识别成另一个错码,导致找不到入场订单,被卡在出口。更麻烦的是,如果入场记录里的车牌是错的,出场识别出正确车牌,系统会认为这是一辆无入场记录的车。

我的处理方案是在后端加一个车牌标准化和相似度匹配层。车牌标准化负责把容易混淆的字符做归一化处理,比如在特定位置规则下将O当作0、I当作1。相似度匹配负责在出口找不到准确入场上记录时,扫描最近若干小时内与当前车牌字符差距不超过一个字符的入场订单,按编辑距离排序,让管理员快速确认是否匹配。这个功能上线后,人工复核量下降了很多。

6.2 同一辆车在同一闸机反复触发识别

高峰期会遇到一个很尴尬的情况:一辆车在入口缓慢排队,车身刚压过触发线,识别成功后正在抬杆,但车挪动了一下,相机又触发了一次识别。后端如果不做冷却限制,短短几秒内会连续创建多条入场订单,每一条都往道闸发开闸指令,闸机杆子就会像抽风一样反复抬起落下。更严重的是,如果这些重复订单都进入计费,车辆离场时会查到三条入场记录,费用直接翻三倍。

解决办法是给每个闸机设置防重入冷却期。具体来说,同一台设备连续推送的识别事件,如果车牌相同且时间间隔小于设定值,直接忽略;同一台设备在开闸动作完成后,收到下一次入场事件之前,处于"放行中"状态,不响应重复触发。这个状态在车辆检测器报告车辆已通过后自动清除,或者设定一个最长保护时间。我实际设置的冷却时间是5秒,足够覆盖车辆缓慢移动的情况。

6.3 道闸没抬起来,车已经压到杆前

道闸控制和状态反馈之间的时间差也是我踩到的坑。有一次出口车辆已经完成支付,后端向道闸发送了开闸指令,但道闸电机因为过热保护没有执行,车辆等了半天没见杆抬起来,后面车开始排队按喇叭。排查日志发现,开闸指令确实发出去了,系统也认为"开闸成功",问题就出在把指令发送成功当成了执行成功。

从那以后,我把所有开闸指令都改为异步确认模型:先发指令,随后查询道闸状态,只要杆没有抬到顶,就一直处于"开闸中"状态,超过8秒未到位就自动告警。告警消息推送到管理员手机,值班员可以远程一键重试开闸,或者直接转人工到现场处理。同时,前端大屏也会把这个闸机的状态标记为故障,运营人员一眼就能看到。

6.4 出场记录晚到导致的计费异常

边缘盒子在断网期间会把数据缓存在本地,网络恢复后再补传,这本来是合理的兜底方案,但也带来一个新问题:车辆早上8点入场、当天18点出场,离场记录由于断网直到晚上20点才补传到中心服务器,订单计算出来停车时长是12个小时而不是10个小时。车主看到账单自然不认,因为他的车18点就已经离开车场了。

这个问题要从两方面解决。一是边缘盒子补传时,中心服务器严格以设备端采集时间作为入场和出场时间,不能以收到补传的时间来算;二是补传接口做好数据校验,如果发现补传的出场时间早于当前时间超过一定阈值,就在订单上打一个"补传"标记。运营方看到这个标记后,可以要求现场留存离场照片作为凭证,处理车主投诉时也有据可查。说到底,时间戳的信任边界必须定在设备端,而不是服务端。

6.5 僵尸车和超时补费

有的车一停就是好几天,甚至几周。按计费规则,单日封顶之后,每天费用是恒定的,这没问题。但部分车场对长期占位车辆有额外处理策略,比如超过72小时之后不再享受当日封顶优惠,按正常小时费率累计计算,目的就是防止有人把停车场当车库长期占用。我实现了这个逻辑之后,又遇到一个新问题:车辆实际离场时识别出错,导致该订单一直处于在场状态,费用每天都在增长,等一个多月后用户回来处理时,费用已经高得离谱。

所以除了设定超时计费规则,还要有超时订单自动提醒机制。系统每天扫描在场超过24小时的订单,推送消息给管理员;超过72小时的订单,进入高优先级告警队列,管理员可以查看车辆抓拍照片,确认车辆是否还在场内。每个月我还会跑一次"幽灵订单"清理任务,把最后活动时间在一个月前、且没有后续流水的在场订单单独列出来,交给运营方逐条核实。这套机制能有效防止数据越积越脏。

6.6 服务器时区和时间同步

最后提一个很多人会忽略的坑:服务器时区。SpringBoot默认使用的时区可能跟本地不一致,具体表现在订单时间比实际时间差8个小时,或者定时任务运行时间不对。我早期就在这个问题上翻过车,对账任务明明设置凌晨1点跑,结果因为时区配置不当,每天白天才执行,导致报表数据总是慢半拍。统一解决办法是在应用配置里显式设置timezone为Asia/Shanghai,同时数据库连接串也指定serverTimezone参数,不能用系统默认值;边缘盒子和相机的时钟也要做NTP同步,否则设备上报时间和服务器时间互相偏差,计费就是一笔糊涂账。

7. 部署成本与落地清单:从单进单出到多车场的扩展建议

最后聊聊预算和落地。这套方案不是一套纯软件,硬件和施工成本占据大头,而且不同地域、不同车场动线差别很大。我给一个相对通用的参考,实际项目请以本地报价为准。

7.1 单进单出车场的成本参考

一个标准的单入口单出口小型车场,设备配置可以这样估算:

项目数量参考区间
车牌识别一体相机2台4000-9000元/台
道闸杆与控制器2套2500-5000元/套
地感线圈或雷达检测器2套800-2000元/套
边缘盒子1台1500-3000元/台
交换机、网线、布线施工按点位2000-5000元
出口显示屏及语音对讲1套2000-4000元

合计大约在1.5万到3.5万之间。如果车场本来就有旧的立柱、闸机基础,施工费可以降低不少。服务器方面,初期单车场用一台4核8G的云服务器就够,价格不高,但带宽要按车场并发使用量评估,出口扫码页面的请求量不算大,5M到10M带宽通常够用。不过我建议把所有图片和视频文件放到对象存储,不要直接传服务器,否则用不了多久磁盘就会被打满。

7.2 多车场架构:从一开始就预留扩展位

虽然很多项目一开始只做一两个车场,但代码层面我建议从第一天就预留多车场字段。订单表、设备表、流水表都带上park_id字段,配置表按车场维度隔离,前端登录后默认只能看到自己有权限的车场。这样后面接入新场地时,不需要改动任何表结构,只需新增一条park配置和设备记录,系统自动完成隔离。

我见过不少项目一开始只写死单停车场,后面扩张时发现所有代码都假设只有一个车场,动一处就要改几十个地方。多车场字段不是过度设计,它只是把一个未来大概率会发生的行为提前用便宜的方式放进数据模型里。停车运营方一旦验证单点模式跑通,扩张速度会非常快,技术必须提前接得住。

7.3 上线前的自查清单

根据这些踩坑经验,我整理了一份上线前必须完成的验收清单,供你直接对照执行:

  • 出入口设备联调:相机识别到车牌后,后端能在2秒内完成订单创建并触发开闸,开闸状态能实时反馈。
  • 幂等验证:同一车辆识别事件连续推两次,后端不会创建重复订单。
  • 断网演练:断开中心服务器网络,边缘盒子能否本地正常开闸并记录进场,恢复后能否自动补传。
  • 支付全链路测试:模拟支付成功回调、模拟回调丢失后查单补单、模拟支付金额与订单不一致。
  • 权限测试:普通员工不能远程开闸、不能手工标记已支付订单。
  • 对账跑批:确认每日对账任务能拉取账单并生成差异报表。
  • 车牌人工复核流程:模拟低置信度事件进入待复核队列,管理员可完成确认或修改车牌。

每一项都建议用真实设备完成,不要只在Postman里测接口。停车系统最终对接的是物理世界,任何没有经过真机环境验证的逻辑都可能在实际运行中出问题。

7.4 几个值得做的扩展方向

这套系统跑稳之后,可以很自然地做一堆扩展。比如车位引导,在车场内部署地磁或摄像头识别车位状态,前端看板显示实时余位,月卡用户可以绑定App端查看哪里有空位。比如预约车位,针对写字楼访客预约场景,用户提前录入车牌和预约时间,入场时系统自动比对预约单放行。再比如充电桩联动,对新能源车停车时给充电减免时段,前提是订单里要记录车辆入场时识别到的车辆类型。这些都是基于同一套订单和设备体系扩展的,不需要重写核心逻辑。

如果让我重新做一遍这个项目,我最先优化的还是订单状态机和幂等控制,这两块是整个系统的地基。地基稳了,设备怎么换、支付通道怎么换、前端页面怎么改都不怕。停车系统不像互联网产品可以快速试错,它每天都在处理真实车辆和真实资金,稳定性永远比花哨功能重要。先在一个出入口试点跑起来,把异常处理流程磨合顺,再逐步复制到整个车场和更多场地,这是我认为最稳妥也最节约成本的落地路径。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进