ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

基于ThinkPHP的小区停车场车辆信息管理系统开发实战

基于ThinkPHP的小区停车场车辆信息管理系统开发实战 开篇先把这个项目说透这是一个基于 ThinkPHP 框架开发的小区停车场车辆信息管理系统核心解决的是车牌登记、进出场记录、收费统计、月租车管理这几类日常刚需。简单讲就是给小区物业或者停车场管理员一套能用的 Web 管理工具替代原来的纸质登记和 Excel 表格。整个系统不算大但涉及的功能链路非常完整很适合用来学习和二次开发。文章会从需求拆解、数据库设计、核心功能实现到 ThinkPHP 路由配置的坑一步步把整个系统的设计思路和落地过程讲清楚。我自己在做这类系统的时候最大的体会是停车场管理系统难点不在业务复杂度而在“状态一致性”。车辆进和出是两件事但必须关联在一个逻辑闭环里否则就会出现车还在场内但系统已计费、或者月租车到期但仍能抬杆这类尴尬问题。所以下面这篇内容会重点讲我对这些问题的处理方式包括数据库怎么设计、进出场逻辑怎么写、路由地址怎么配置才不容易出错。1. 项目概述与需求拆解1.1 这类管理系统到底在解决什么问题先明确一下定位我们要做的不是城市级智慧停车平台而是面向“温馨小区”这种中小型社区的车牌管理系统。核心使用场景是小区大门和地下车库入口管理员在电脑上操作对每一辆车的进出做记录并自动计算需要缴纳的停车费用。它的用户角色很清晰通常有三种管理员拥有全部权限管理车辆信息、查看记录、设置收费标准。业主登记自己的车辆信息申请月租或购买固定车位。临时车来访车辆进出场时按小时计费。需求拆解下来系统需要具备以下功能车辆信息管理、进出场登记、收费规则配置、月租车和临时车的区分、停车记录查询统计。这些功能听起来不多但是要在一个后台管理系统里顺畅跑起来从前端表单到后端逻辑再到数据库查询链路其实不短。因为用的是 ThinkPHP 框架涉及内容也包含了路由配置、模型关联、控制器编写、模板渲染这些常规操作。如果你是 ThinkPHP 新手这个项目刚好能把框架的核心用法串一遍。1.2 为什么选 ThinkPHP 而不是其他方案很多人会纠结做这类管理系统该用 Laravel 还是 ThinkPHP甚至要不要用 Flask 或 Spring Boot。我的建议很直接如果项目周期短、部署环境是普通虚拟主机或轻量服务器、团队里 PHP 经验居多ThinkPHP 是性价比极高的选择。ThinkPHP 有几个优势在小区管理系统这个场景特别实用。首先是部署简单ThinkPHP 对运行环境的要求低PHP 7 以上配 Nginx 或 Apache 就能跑不需要额外装复杂组件。其次是上手门槛低它的 MVC 结构非常直观控制器里写业务逻辑、模型里写数据库交互、视图里写页面模板对刚接触框架的人来说比 Laravel 的 Service Provider 机制友好得多。另外ThinkPHP 自带的路由解析、验证码、分页、数据库迁移等基础能力基本覆盖了管理系统的大多数需求。比如后面要讲的停车场进出场记录列表ThinkPHP 的分页类只需要一行代码就能搞定省了很多重复劳动。当然这不是说 Laravel 不好而是说在类似“温馨小区停车场管理系统”这种体量的项目里ThinkPHP 带来的开发效率提升更直接。选择一个团队熟悉、文档丰富、生态成熟的框架能少踩很多坑。2. 功能模块设计与数据建模2.1 功能模块划分拿到需求后先别急着写代码先把功能模块画出来。停车场车辆信息管理系统按我的经验可以拆成这几个功能块基础信息管理小区楼栋信息、车位信息、收费规则设置。车辆管理车辆登记、车牌修改、车辆状态维护正常、挂失、注销。进出场管理入场登记、出场登记、自动计算停车时长和费用。月租管理月租车办理、续费、到期提醒。记录查询与统计进出场明细、收费记录、每日/每月营收统计。系统管理管理员账号、操作日志、数据备份。每个模块之间是有依赖关系的。比如“进出场管理”里的出场计费需要读取“车辆管理”里的车辆类型判断是月租车还是临时车还需要读取“基础信息管理”里的收费规则才能算出金额。这种依赖关系在数据库表设计阶段就要留好关联字段。2.2 数据库表设计数据库是整个系统的地基表结构设计得不好后面实现功能的时候就处处难受。我实际开发时设计了这几张核心表车辆信息表car_info字段id, plate_number, owner_name, owner_phone, car_type, status, create_timecar_type 区分月租车/临时车/业主车辆status 控制是否有效。车位信息表parking_space字段id, space_no, space_type, statusspace_type 区分固定车位和临时车位status 表示占用还是空闲。进出场记录表parking_record字段id, plate_number, car_id, space_id, entry_time, exit_time, duration, amount, record_status这条表是系统的核心每一条记录对应一次完整的停车行为。收费规则表fee_rule字段id, rule_name, car_type, first_hour_fee, hourly_fee, daily_max_fee, month_fee临时车按小时计费月租车按月固定费用规则分开配置。月租续费记录表renewal_record字段id, car_id, fee_rule_id, start_date, end_date, amount, create_time管理员表admin_user字段id, username, password, real_name, last_login_time字段设计上有几个细节值得注意。一是所有金额字段比如停车费用和月租费用我都建议用 decimal(10,2) 而不是 float因为浮点数在计算金额时容易出精度问题。二是时间字段建议直接用 datetime 类型虽然用 int 存时间戳也可以但是在做区间查询和维护数据的时候datetime 更直观ThinkPHP 的查询构造器对 datetime 的支持也更好。2.3 关联查询的便利性设计ThinkPHP 的模型关联功能在做这类系统时特别能提效。比如查询一条进出场记录时我们希望同时知道车辆对应的车主姓名和联系电话如果没有建立关联就得手动写两次查询然后在 PHP 代码里拼接数据。我在设计表的时候把 parking_record 表和 car_info 表通过 plate_number 字段做了关联虽然冗余但在停车场业务里用车牌做关联查询会更自然。在 ThinkPHP 模型中可以这样定义// 进出场记录模型中定义关联车辆信息 public function carInfo() { return $this-belongsTo(CarInfo::class, plate_number, plate_number); }这样在控制器里查询记录时直接调用-with([carInfo])就能把车辆信息一起查出来。代码简洁而且生成的 SQL 是高效的 JOIN 查询不用一层层嵌套循环去补数据。这个经验对后面写列表页特别管用。3. 核心功能实现与关键代码3.1 车辆登记与进出场管理的实现车辆登记是最基础的功能重点在于车牌号的规范处理。用户输入车牌时特别容易多打空格、把英文字母 O 打成数字 0或者直接用中文输入法输成了全角字符。这里必须要做清洗和校验。public function register(Request $request) { $plateNumber strtoupper(trim($request-post(plate_number))); // 将全角字符转为半角 $plateNumber ik2ascii($plateNumber); $validator Validate::rule([ plate_number require|unique:car_info ]); if (!$validator-check([plate_number $plateNumber])) { return json([code 0, msg 车牌号已存在或格式错误]); } $car CarInfo::create([ plate_number $plateNumber, owner_name $request-post(owner_name), owner_phone $request-post(owner_phone), car_type $request-post(car_type), status 1, ]); return json([code 1, msg 登记成功, data $car]); }进出场管理是核心中的核心。入场时系统记录当前时间和车牌出场时根据入场记录计算出停车时长再根据车辆类型和对应的收费规则计算出应收费用。时效性和状态一致性是这里的难点。比如车辆进场后如果管理员误操作连续点两次“入场”就会生成两条入场记录导致出场时算错费用。我在设计中加了一道“该车牌是否已在场内”的检查public function entry(Request $request) { $plateNumber strtoupper(trim($request-post(plate_number))); // 检查当前是否有未完成(在场内)的记录 $exists ParkingRecord::where(plate_number, $plateNumber) -where(record_status, 1) -find(); if ($exists) { return json([code 0, msg 该车辆已在场内请勿重复入场]); } $record ParkingRecord::create([ plate_number $plateNumber, entry_time date(Y-m-d H:i:s), record_status 1, ]); return json([code 1, msg 入场成功, data $record]); }出场逻辑稍微复杂一点。出场时要更新车位状态、计算停车费用、如果涉及临时缴费可能还要生成一条收费记录。我把计费逻辑单独抽了一个方法方便以后改写收费规则。public function exit(Request $request) { $plateNumber strtoupper(trim($request-post(plate_number))); $record ParkingRecord::where(plate_number, $plateNumber) -where(record_status, 1) -orderBy(id, desc) -find(); if (!$record) { return json([code 0, msg 未找到该车辆的入场记录]); } $exitTime date(Y-m-d H:i:s); $duration (strtotime($exitTime) - strtotime($record-entry_time)) / 3600; // 根据车辆类型计算费用 $amount $this-calculateFee($record-plate_number, $duration); $record-exit_time $exitTime; $record-duration round($duration, 2); $record-amount $amount; $record-record_status 2; $record-save(); return json([code 1, msg 出场成功, data $record]); }3.2 收费规则与费用结算逻辑停车收费是最容易被需求方“临时改规则”的地方。一开始可能只说“每小时3元不足一小时按一小时算”但实际运营中还会出现“前半小时免费”“单日封顶30元”“过夜车加收”等规则。所以收费规则表不能写死在代码里必须让管理员在后台能灵活配置。我在小额场景下用了一个规则匹配的思路先根据车辆类型找到适用的收费规则再根据停车时长选择计费方式。比如临时车规则可能是前 30 分钟免费首小时 5 元之后每小时 3 元单日封顶 20 元按照这个规则停车 3 小时 20 分钟的费用计算逻辑是先减去免费时段首小时收 5 元剩余 2 小时 20 分按照 3 小时计算不足一小时按一小时收 9 元合计 14 元未达到封顶所以最终收费 14 元。如果超过单日封顶就直接取封顶值。private function calculateFee($plateNumber, $duration) { $car CarInfo::where(plate_number, $plateNumber)-find(); $carType $car ? $car-car_type : 2; // 未登记车辆默认视为临时车 if ($carType 1) { // 月租车不按小时收费直接判定是否在有效期内 $renewal RenewalRecord::where(car_id, $car-id) -where(end_date, , date(Y-m-d)) -orderBy(end_date, desc) -find(); return $renewal ? 0.00 : -1; // -1 表示月租已过期 } $feeRule FeeRule::where(car_type, $carType)-find(); if (!$feeRule) { return 0; } $freeMinutes $feeRule-free_minutes ?? 0; $firstHourFee $feeRule-first_hour_fee ?? 0; $hourlyFee $feeRule-hourly_fee ?? 0; $dailyMax $feeRule-daily_max_fee ?? 0; $totalMinutes ceil($duration * 60); // 减去免费时段 if ($totalMinutes $freeMinutes) { return 0; } $billableMinutes $totalMinutes - $freeMinutes; $billableHours ceil($billableMinutes / 60); if ($billableHours 1) { $amount $firstHourFee; } else { $amount $firstHourFee ($billableHours - 1) * $hourlyFee; } // 单日封顶判断 if ($dailyMax 0 $amount $dailyMax) { return $dailyMax; } return round($amount, 2); }这段逻辑里面有一个容易踩的坑封顶判断是基于“单日”的如果车辆停了跨天封顶应该重新计算。为了避免算法过于复杂我在实现时限定单次停车超过 24 小时的当天封顶值乘以天数再加剩余小时费用。虽然不算绝对精确但对大多数小区场景已经够用了。真正要实现全精确的跨天封顶逻辑可以按“每天零点拆分”重新核算但那样代码复杂度会高很多我个人觉得没太必要。3.3 月租车与固定车位管理月租车在小区停车场里占比非常高处理不好很容易被投诉。月租车管理的核心是有效期控制。虽然停车场系统可以联动道闸自动抬杆但如果月租过期了还放行营收就会受损如果到期前不提醒业主又会觉得物业服务不到位。我采用的方案是在车辆详情页显示“月租到期时间”到期前 7 天在列表里显示醒目的“即将到期”状态标签。每次出入场时在接口里实时校验有效期。这样即便管理员忘记续费系统也会在车辆出场时明确提示。月租续费记录表 RenewalRecord 的设计是支持同一辆车多条记录的这样做的原因是可以看到每一笔续费的历史。也可以用“车辆表上只保存一个到期时间”的做法但那样会丢掉历史数据财务对账的时候不方便。更合理的设计是当前有效状态根据 end_date 最新的记录来判断而历史记录全部保留。public function renew(Request $request) { $carId $request-post(car_id); $months intval($request-post(months)); $car CarInfo::find($carId); if (!$car || $car-car_type ! 1) { return json([code 0, msg 该车辆不是月租车]); } $lastRenewal RenewalRecord::where(car_id, $carId) -orderBy(end_date, desc) -find(); // 如果之前没有续费记录生效日期从今天开始否则从上次到期日期的次日开始 $startDate $lastRenewal ? date(Y-m-d, strtotime($lastRenewal-end_date . 1 day)) : date(Y-m-d); $endDate date(Y-m-d, strtotime($startDate . {$months} months)); $feeRule FeeRule::where(car_type, 1)-find(); $amount $feeRule ? $feeRule-month_fee * $months : 0; RenewalRecord::create([ car_id $carId, start_date $startDate, end_date $endDate, amount $amount, create_time date(Y-m-d H:i:s), ]); return json([code 1, msg 续费成功]); }固定车位管理和月租车类似区别在于固定车位绑定了具体的车位编号比如“A栋负一层 A-012”。在车辆入场时系统会自动匹配该车辆是否绑定了固定车位绑定的话就直接分配对应的车位没有绑定的话只能停临时车位。3.4 数据统计与报表展示管理系统光有数据不够还得能从数据里看出经营状况。我这边做的是三张核心报表今日进出场统计进多少辆、出多少辆、目前场内剩余多少辆。今日收费统计临时车收费、月租车续费各收了多少。月度趋势图把每天收费总额折线展示出来方便物业看营收趋势。这里最需要注意的是“在场车辆数”的计算。由于实际运行中会有断电、误操作等特殊情况单纯用入场数 - 出场数结算很简单但可能不准确。我的做法是在统计时直接查询record_status 1的记录数用当前在库数据来确保准确。public function dashboard() { $today date(Y-m-d); $entryCount ParkingRecord::where(entry_time, like, $today . %)-count(); $exitCount ParkingRecord::where(exit_time, like, $today . %)-count(); $insideCount ParkingRecord::where(record_status, 1)-count(); $todayIncome ParkingRecord::where(exit_time, like, $today . %) -sum(amount); return json([ code 1, data [ entry_count $entryCount, exit_count $exitCount, inside_count $insideCount, today_income $todayIncome, ] ]); }这里有个小技巧统计时如果用whereBetween来做时间范围筛选会稍微复杂一点直接用like匹配前缀在 MySQL 的索引下也是高效的因为yyyy-mm-dd这种日期格式可以走前缀索引。对于中小型系统来说完全够用也更容易读懂。4. 路由配置实操与地址跳转排查4.1 ThinkPHP 路由基础与配置方式这部分内容我单独拿出来讲因为在实际开发中路由和地址跳转问题占了我排查 Bug 时间的一大部分。很多人写 ThinkPHP 项目时明明控制器和方法都写好了一访问就 404 或者跳到了错误页面基本都是路由配置没弄对。ThinkPHP 的路由有两种模式一种是默认的 PATH_INFO 模式URL 形如index.php?s/admin/car/index另一种是伪静态模式URL 形如/admin/car/index.html。开发和部署时建议开启伪静态URL 更美观也方便以后做 SEO。在route/app.php里可以定义路由别名规则比如把后台的车辆管理首页指向具体的控制器方法use think\facade\Route; Route::get(car_list, admin.Car/index); Route::post(car_add, admin.Car/add); Route::post(car_edit, admin.Car/edit); Route::post(car_delete, admin.Car/delete);定义了这些路由规则后在视图模板里跳转时不要硬编码 URL 字符串尽量使用url()辅助函数来生成地址。这样做的目的是万一后面改了路由规则不需要在模板里到处找链接来改。a href{php echo url(admin/car/index);}车辆管理/a4.2 常见地址跳转失效的场景与解决办法地址跳转失效或者跳到了奇怪页面最常见的场景我总结了一下基本就是这几类第一类是伪静态没有配置好。ThinkPHP 的伪静态规则在 Nginx 下需要特别配置。很多人把项目部署到服务器上之后发现除了首页之外的所有链接都打不开页面报 404原因就是 Nginx 配置里没有把请求重写到index.php。Nginx 下可以这样配置location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; } }配置完成之后必须nginx -s reload重载一次才能生效。我在这个坑上踩过两回每次都是因为配置文件改了但没 reload。第二类是控制器方法名是驼峰命名但 URL 访问时用了下划线导致方法找不到。ThinkPHP 默认 URL 是严格区分控制器和方法名的比如控制器里写的是public function carList()URL 就得写car_list或者carList取决于你的路由配置。建议在开发前就统一一个命名规范我习惯全用小写加下划线彻底避免这类问题。第三类是使用了模板跳转redirect()或$this-success()时判断的 URL 参数需要符合路由规则。比如你想在添加车辆成功后跳转到车辆列表直接写redirect(/admin/car/index)可能就报错。正确应该写成return redirect((string) url(admin/car/index));用url()函数动态生成地址这样无论项目部署在子目录还是根目录都能跳对位置。4.3 前后端分离与普通模板场景下的路由选择做这种管理系统有人喜欢前后端分离用 Vue 写前端ThinkPHP 只提供 API 接口有人更愿意用 ThinkPHP 自带的模板引擎直接渲染页面。我在这个项目里的建议如果只有一两个管理员使用并且追求开发速度和维护方便直接用服务端模板渲染就够了。但如果以后要扩展小程序端或 App 端那接口化的思路更有优势。这里提供一个折中方案ThinkPHP 路由分两份后台管理端走模板渲染对外 API 走 JSON 接口。// 后台页面路由 Route::get(admin/car/index, admin.car/index); Route::get(admin/car/add, admin.car/add); // 对外接口路由加 api 前缀 Route::group(api, function () { Route::post(entry, api.Parking/entry); Route::post(exit, api.Parking/exit); Route::get(records, api.Parking/records); });接口统一用return json()返回数据前端拿到数据后自行渲染。这样既保留了传统开发的效率也留出了未来扩展的空间。实际项目中我倾向用这个混合方案因为小区停车场后期基本都会对接车牌识别摄像机、微信公众号或者小程序提前把 API 设计好能省掉后面很大的重构成本。5. 常见问题与排查技巧实录5.1 问题速查表我在开发这套系统时遇到的几个典型问题整理成了下面的表格方便你直接对照查找问题现象可能原因解决方案访问后台子页面 404Nginx 伪静态未配置或未 reload检查location /配置重载 Nginx表单提交后跳转停留在原页面没反应路由规则中缺少对应请求类型的定义检查route/app.php确认 POST 路由已定义车辆出场金额显示为 0收费规则表中未配置对应车辆类型的规则在后台为临时车添加收费规则月租车续费后到期时间没有更新续费查找的是最新一条记录的 end_date检查 RenewalRecord 数据是否按 end_date 倒序排车牌号重复登记成功唯一校验没生效确认验证器规则里写了unique:car_info页面显示缓存数据刷新才有新内容模板缓存未清理运行php think clear清缓存上传的图片或文件 404public 目录重定向配置错误确保静态资源通过 public 目录访问5.2 几个容易踩的坑和相关心得第一个坑是“时间字段的时区问题”。这个在开发环境常常遇不到因为开发机一般和服务器在同一时区。但是把项目部署到云服务器上之后如果服务器时区设置成 UTC你存进去的时间和实际时间会差 8 个小时。尤其是进出场记录和计费时间差了 8 小时计费逻辑瞬间就崩了。我的建议是在config/app.php里明确设置默认时区// config/app.php default_timezone Asia/Shanghai,另外在数据库连接配置里也可以加一个timezone 8:00双保险。这样即使服务器系统时区不对PHP 层和 MySQL 层的时间也能保持一致。第二个坑是“状态字段的语义比想象中重要”。parking_record 表里的 record_status我用 1 表示“停车中”2 表示“已完成”。这个设计本身没问题问题在于后续加需求时如果新增了一个“已取消”的状态老代码里的where(record_status, 1)还有以状态判断是否在场的逻辑都要仔细排查一遍。否则统计在场车辆数就可能会把已取消的单子也算进去。建议在开发时写一个枚举类或者常量类把所有状态值集中管理不要散落在各个控制器里。第三个坑是“车牌号的不规范输入”。字段长度要留够新能源车牌比传统车牌要长一位。车牌号用 varchar(10) 已经够用但为了兼容未来的特殊情况用 varchar(20) 会更稳妥。同时在入库前统一转为大写字母这样在查询和统计时才不会出现同一辆车的大小写不一致而查不到记录的情况。第四个坑是“金额运算的精度”。前面已经提到不要用 float 存金额这里还要再强调一下在 PHP 代码里做金额计算时尽量用整型单位分运算最后输出时再转成元。这样可以完全避开浮点数运算误差带来的“1 元停车费变成了 0.9999999 元”的尴尬。比如计算每小时 3 元停车 2 小时直接 3.00 * 2 用浮点算可能没问题但如果是 3.00 * 3.33 这种场景误差就会出现。稳妥的办法是全程用整数“分”计算。5.3 实操过程中最值得做的一步接口日志这个系统上线之后我非常建议给所有写操作的接口加上日志记录。ThinkPHP 本身有日志机制在控制器里调用Trace::log()或者直接写file_put_contents都可以。我在系统中加了一张操作日志表记录管理员在后台的每一次增删改操作比如谁在几点修改了哪辆车的车牌号、谁删除了哪条停车记录。出现数据问题时可以通过日志快速定位到是哪个操作导致的。// 写入操作日志 OperationLog::create([ admin_id session(admin_id), action car_edit, detail 修改车辆 . $carId . 的车牌号为 . $plateNumber, ip $request-ip(), create_time date(Y-m-d H:i:s), ]);这一步在项目初期可能觉得“没什么用”但一旦系统在真实环境中跑起来管理员之间如果出现操作失误日志是你排查问题最可靠的依据。我甚至建议不要只记录“成功”操作把那些“被拒绝的修改尝试”也记录下来比如“试图将车牌号修改为已存在的号码”这能帮你发现一些不够规范的使用习惯。6. 上线部署与运行环境建议6.1 本地开发环境搭建开发时我用的环境是 PHP 8.0 MySQL 5.7 NginxThinkPHP 6 完全兼容。本地开发建议用集成环境就行但调试阶段注意开启 debug 模式这样出错了能直接看到详细的异常堆栈。需要在config/database.php里配置好数据库连接return [ default mysql, connections [ mysql [ type mysql, hostname 127.0.0.1, database parking_system, username root, password 123456, hostport 3306, charset utf8mb4, prefix ps_, debug true, ], ], ];表前缀用ps_是为了避免和其他系统共用数据库时表名冲突。这个细节在多人协作或多系统部署时非常有用。6.2 生产环境部署要点生产环境我的建议是PHP 用 7.4 或者 8.0 都行但 MySQL 一定要 5.7 以上因为要用 utf8mb4 字符集支持手机号等场景下可能出现的特殊字符。部署时先把debug改成false否则服务器会把数据库连接密码、SQL 语句直接显示在页面上这个安全风险是很严重的。Nginx 配置的关键点是把项目根目录指向public目录其他目录不能让外部直接访问否则控制器代码、配置文件都可能被下载。常规配置如下server { listen 80; server_name parking.example.com; root /var/www/parking/public; index index.php index.html; location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; } } location ~ \.php$ { include fastcgi_params; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_pass 127.0.0.1:9000; } }相关的安全配置还有关闭目录浏览、限制上传目录的执行权限、数据库账号不要用 root单独创建一个只有业务库权限的账号。这些事不复杂但是漏掉一个就可能出问题。6.3 性能优化与后续扩展停车场管理系统的数据量以每天几百条进出场记录来算一年也就几万条MySQL 完全没压力。但如果未来要接入多个小区的停车场数据或者要做实时车位引导就必须考虑性能优化了。目前阶段我做的最有效的优化是给数据库加了几个关键索引ALTER TABLE ps_parking_record ADD INDEX idx_plate (plate_number); ALTER TABLE ps_parking_record ADD INDEX idx_status (record_status); ALTER TABLE ps_parking_record ADD INDEX idx_entry_time (entry_time);索引并不是越多越好但是这三个字段是查询最频繁的按车牌查历史记录、按状态查在场车辆、按时间查收费统计。加了索引后数据量到几十万条时查询依然很快。未来如果想扩展可以考虑接入车牌识别摄像头的 API实现无感进出场。那部分需要在入场时调用摄像头接口获取车牌号再通过本系统的接口自动创建停车记录逻辑并不复杂接口设计在开发初期留好扩展位就可以了。这个项目完全可以作为整个智慧社区系统的一个基础模块。写在后面的一点心得这个项目从开发到落地我自己跑通了一遍后最大的收获是不要一开始就追求所谓的“完美架构”先把核心业务闭环跑通再一步步完善细节。比如收费规则模块第一版就是写死的后面有临时需求才抽成独立的表月租管理一开始也只是在车辆表里加了一个到期时间字段后来财务需要历史记录才扩展出续费记录表。系统是慢慢变好的。如果你现在也正在做类似的管理系统我建议你先把车辆进出场和收退费这条主线走通再去做那些加分项功能比如报表导出、导入车牌、批量续费。主线通了系统就能用加分项则是提升使用体验和运营效率的关键可以根据实际需要逐步补上。最后再提醒一次路由配置和时区设置是 ThinkPHP 项目最容易踩坑的地方开发前先把它搞定后面会省心很多。
返回列表