
简介本资源是一套基于Python Django框架开发的停车场预约与计费系统完整源码案例面向Web开发初学者及Django进阶学习者聚焦真实业务场景中的用户管理、车位调度、动态计费与在线支付等核心问题。压缩包共2000个文件含1632个JavaScript前端交互脚本支撑预约表单、时间选择器、状态实时更新等功能、249个HTML模板页面覆盖登录、车位列表、预约确认、订单支付等全流程界面、54个CSS样式文件含bootstrap、font-awesome、datetimepicker等主流UI组件以及5个核心Python后端逻辑文件models、views、urls等整体大小为13.19MB。已有129人下载学习资源结构清晰、模块解耦明确提供从数据库建模、REST式路由设计到第三方支付对接的全链路实现参考特别适合用于课程设计、毕业项目或Django工程化实践训练。1. 项目概述与整体设计思路1.1 项目核心需求解析停车场预约管理这个需求我相信只要是写过一段时间业务系统的朋友都不会陌生。前几天有个做物业系统的朋友跟我聊起来说他们新接了一个商业综合体停车场项目需要一个支持线上预约、按时段计费的停车管理系统我一听就明白了——这不是典型的预约计费场景么。市面上的停车场管理系统不少但大部分要么是纯硬件厂商绑定的要么是SaaS平台按月收费的真正能拿源码自己改、自己定制部署的反而不多。这也是为什么我一直觉得Django特别适合这类业务系统开发效率高、ORM操作数据库省心、自带Admin后台能快速搭建管理界面最关键的是Python生态在业务逻辑处理上特别顺手。这个项目的核心需求拆开来看其实就三块车位预约用户选择停车场、选择时间段提交预约订单系统锁定对应车位。计费管理根据停车时长、时段规则、预约类型计算费用支持入场离场时间记录。后台管理停车场管理员要能看车位状态、管理订单、设置费率规则。听起来不复杂但真正实现起来几个关键细节是会坑到新手的比如车位状态在预约锁定期间怎么流转、计费时段怎么精确切割、跨天停车怎么算超时费这些都是在实际写代码前必须想清楚的。1.2 为什么选Django来实现可能有人会问停车场系统用Java、Go甚至PHP都行为什么偏偏选Python Django我的理由比较实际。第一Django自带的ORM可以把数据模型直接映射成数据库表开发阶段不用花时间写SQL建表脚本改模型跑个迁移就同步了。第二Django Admin算得上Django的杀手级应用停车场管理员需要的车位管理、订单查询、费率配置这些功能通过Admin稍微定制一下就能用根本不需要从零写一堆重复的CRUD页面。第三Django的MTV架构把业务逻辑层和展示层天然隔离了后面就算要加API接口比如给小程序端用Django REST Framework直接补上就行。还有一个很现实的原因——Python的招聘市场大。大部分新团队上手Django项目的门槛远低于Java全家桶后续维护起来也容易招到人。再加上Django在国内虽然不是最热门的选择但确实有不少中大型项目在生产环境跑得很稳文档资源也全出了问题搜索引擎一查基本都有解决方案。1.3 系统架构与项目目录结构我把整个系统的模块划分成四层简单画个结构说明Web端用户模块面对普通车主的预约入口实现注册、登录、车位选择、预约下单、支付/取消、订单查询。计费模块核心业务逻辑包含费率规则配置、费用计算、超时补缴、跨天计费处理。后台管理模块基于Django Admin定制实现车位状态管理、订单管理、费率设置、停车记录导出。数据存储层车辆/用户表、车位表、预约订单表、计费规则表、停车记录表。项目目录上我采用的是标准Django项目结构parking_system/ ├── manage.py ├── requirements.txt ├── config/ # 全局配置settings、urls ├── apps/ │ ├── users/ # 用户模块 │ ├── parking/ # 车位管理 │ ├── reservation/ # 预约模块 │ └── billing/ # 计费模块 ├── static/ # 静态资源 ├── templates/ # 页面模板 └── docs/ # 接口文档和部署说明之所以按业务领域拆成多个app而不是全塞在一个app里主要是考虑到后续扩展。比如之后要增加会员系统只要新增一个app而不需要动现有代码保证各模块之间的边界清晰。2. 数据库设计与核心表结构2.1 关键业务表及字段设计数据库是整个系统的地基。我在设计表结构时遵循一个原则每个业务实体一张表每张表的主键用自增ID状态字段用整数存而不是字符串方便后续加枚举。核心表一共五张用户表字段类型说明idbigint主键usernamevarchar用户名password_hashvarchar密码哈希phonevarchar手机号plate_numbervarchar默认车牌号created_atdatetime注册时间停车场表字段类型说明idbigint主键namevarchar停车场名称addressvarchar地址total_slotsint总车位数量open_timetime开放时间close_timetime关闭时间hourly_ratedecimal基础小时费率车位表字段类型说明idbigint主键lot_idbigint所属停车场外键slot_numbervarchar车位编号statusint状态0空闲、1锁定、2占用、3维护current_order_idbigint当前关联订单可空预约订单表字段类型说明idbigint主键order_novarchar订单号唯一user_idbigint下单用户slot_idbigint预约的车位plate_numbervarchar本次停车车牌start_timedatetime预约开始时间end_timedatetime预约结束时间statusint状态详见状态机total_feedecimal实付金额created_atdatetime下单时间停车记录表字段类型说明idbigint主键order_idbigint关联预约订单entry_timedatetime进场时间exit_timedatetime离场时间actual_durationint实际停车时长分钟final_feedecimal最终结算金额2.2 车位状态与预约订单的状态机设计这个部分是我觉得最容易写崩的地方很多踩坑案例都是在状态流转逻辑上出问题。车位状态我定义了四种空闲、锁定、占用、维护。空闲表示可以被预约锁定是用户预约成功后车位被预留占用是实际车辆已经进场停在位子上维护则用于后台标记故障车位。预约订单的状态更复杂一些我用一个整型字段表示定义如下0 待支付用户提交预约后等待支付此时车位被锁定但有时效限制。1 已确认支付成功或管理员确认车位锁定完成等待用户到场。2 已进场车辆扫码/输入车牌进场车位从锁定变为占用。3 已离场车辆离场费用结算完成车位释放为空闲。4 已取消用户主动取消或者超时未到场系统自动取消释放车位。5 已超时预约时间结束但用户未到场订单被标记为异常。状态之间的转换必须经过用户端或后台触发我用一个统一的状态迁移函数做校验禁止非法的状态跳转。比如待支付状态不能直接跳到已离场已取消的订单不能再进场。2.3 索引设计和数据一致性考虑大数据量场景下查询性能和大表操作很容易被忽略等数据一多就发现查询卡顿。主要查询场景有两个用户查询自己的预约订单后台按状态筛选订单。所以我给这两块建了联合索引user_id created_at、status start_time。停车记录表则建立plate_number单列索引应对查车牌历史记录的场景。数据一致性上我用了事务 锁的双重机制。预约下单时先查询车位状态然后用select_for_update()锁定该车位记录再执行预约订单插入。这样能有效防止并发情况下两个用户同时约到同一个车位。这个点后面我会专门展开说因为并发是停车场预约系统最容易被攻破的软件防线。3. 核心业务模块实现细节3.1 预约流程的关键代码逻辑预约功能是整个系统的门脸用户体验好不好全看这块。我把它拆成了五个步骤选择停车场、选择车位、提交预约、支付、生成订单。用户提交预约时的核心代码如下from django.db import transaction from django.utils import timezone transaction.atomic def create_reservation(user, slot_id, start_time, end_time, plate_number): 创建预约订单需要保证车位在这个时间段内不被重复预约 # 先锁住车位记录 try: slot ParkingSlot.objects.select_for_update().get( idslot_id, statusParkingSlot.STATUS_AVAILABLE ) except ParkingSlot.DoesNotExist: raise ValueError(车位不可预约) # 检查时间段重叠 conflict Reservation.objects.filter( slot_idslot_id, status__in[Reservation.STATUS_PENDING, Reservation.STATUS_CONFIRMED, Reservation.STATUS_ENTERED], start_time__ltend_time, end_time__gtstart_time ).exists() if conflict: raise ValueError(该车位在此时间段已被预约) # 生成订单号带上日期和随机数保证唯一 order_no PK timezone.now().strftime(%Y%m%d%H%M%S) str(random.randint(1000, 9999)) reservation Reservation.objects.create( order_noorder_no, useruser, slotslot, plate_numberplate_number, start_timestart_time, end_timeend_time, statusReservation.STATUS_PENDING, ) # 锁定车位 slot.status ParkingSlot.STATUS_LOCKED slot.current_order reservation slot.save(update_fields[status, current_order]) return reservation这里有个细节我想多提一句时间段重叠判断用的是start_time__ltend_time和end_time__gtstart_time两个条件这是区间重叠的标准判断方式不管新预约在已有预约的前面、后面还是穿插在中间都能正确检测出来。别用单纯的大于小于会漏掉边界情况。3.2 入场、离场流程与计费触发机制入场逻辑相对简单用户到场后在入口终端输入订单号或者扫二维码系统核验订单状态是已确认然后更新车位状态为占用写一条停车记录记录入场时间。真正复杂的是离场计费。预约的结束时间只是一个预定时间实际可能的场景有几种提前离场按实际停车时长计费不足一小时按一小时或按规则可配置。准时离场按订单金额结算。超时离场预约时间之外的部分额外加收超时费用。迟到进场预约时间段已经开始了但用户没在预约时段内进场。我设计了一个独立的计费函数核心逻辑是先解析费率配置基础小时费率、超时费率、单日封顶价然后根据入场时间和离场时间的差值按实际分钟数计算总费用。如果是预约用户订单金额内的部分按预约价计算超出部分按临时停车费率计算。def calculate_fee(entry_time, exit_time, reservation_fee0, hourly_rate10, max_daily60): 计算停车费用支持跨天和超时 duration_minutes (exit_time - entry_time).total_seconds() / 60 total_fee reservation_fee # 预约订单基础费 # 如果实际停车时长超过预约时长超出部分按小时计费 reserved_minutes (reservation.end_time - reservation.start_time).total_seconds() / 60 if reservation else 0 if duration_minutes reserved_minutes: return reservation_fee, duration_minutes over_minutes duration_minutes - reserved_minutes over_hours math.ceil(over_minutes / 60) over_fee min(over_hours * hourly_rate, max_daily) total_fee over_fee return total_fee, duration_minutes这里有一个我当时踩过的坑跨天计费。如果停车时间跨过零点有些系统会错误地按两天分别计算导致多收费。我在计算时长时统一用绝对时间差不按自然日拆分然后单日封顶价是控制总费用上限的另一道保险。3.3 后台管理模块的定制Django Admin可以快速搭建管理界面但是直接裸用的体验不太行需要定制。我主要做了三方面定制字段列表与搜索订单列表显示订单号、用户手机号、车牌、车位号、金额、状态加搜索框允许按订单号、手机号、车牌搜索。字段过滤状态筛选、日期范围筛选。操作按钮在列表页增加确认订单、取消订单的自定义按钮后台值班人员点击即完成操作不需要进入详情页。class ReservationAdmin(admin.ModelAdmin): list_display [order_no, user_phone, plate_number, slot_number, start_time, end_time, status, total_fee] list_filter [status, start_time] search_fields [order_no, user__phone, plate_number] actions [confirm_reservation, cancel_reservation] admin.action(description确认选中的订单) def confirm_reservation(self, request, queryset): for obj in queryset: if obj.status Reservation.STATUS_PENDING: obj.status Reservation.STATUS_CONFIRMED obj.save()3.4 费率规则的可配置化设计费率规则不能写死在代码里因为停车场运营方随时可能调价。我在后台开放了费率配置表运营人员可以直接修改基础小时费率、超时费率、单日封顶价、免费停车时长等参数。计费模块每次计算费用时从费率表读取最新的有效配置保证修改后立即生效。这里的实现并不复杂一个RateRule表存费率数值和生效时间计费时取生效时间最新的一条。但是要注意一点已经产生的订单不能用新费率所以订单结算时要把当时使用的费率快照保存到订单表里。我是直接存在订单表的rate_snapshot字段存JSON字符串。这个小设计在财务对账的时候特别有用。4. 系统部署与源码使用指南4.1 本地环境搭建与依赖安装拿到源码包之后第一步是搭环境。我建议用虚拟环境不污染系统Python环境每个项目独立依赖。首先是确保本机有Python 3.9然后执行# 创建虚拟环境macOS/Linux python3 -m venv venv # Windows下用 # python -m venv venv # 激活虚拟环境 source venv/bin/activate # macOS/Linux # venv\Scripts\activate # Windows # 安装项目依赖 pip install -r requirements.txtrequirements.txt里的核心依赖就这几个Django建议4.x3.x也可以、django-crispy-forms美化表单、pillow处理验证码和图片。其他小依赖都是Django自带的。4.2 数据库初始化与超级用户创建环境装好后先初始化数据库。默认我用的是SQLite零配置直接跑适合项目和教学演示。上生产再切MySQL或PostgreSQL只需改settings里的数据库配置。# 执行迁移创建数据库表 python manage.py makemigrations users parking reservation billing python manage.py migrate # 创建超级管理员 python manage.py createsuperuser # 启动开发服务器 python manage.py runserver 0.0.0.0:8000启动后在浏览器访问http://127.0.0.1:8000/admin登录后台。第一次登录后先不要急着操作去费率规则里设置好收费标准再去停车场里添加停车场、车位然后系统就能正常预约了。4.3 Settings配置的关键调整点源码包里自带的settings默认是开发环境的配置部署到生产环境前有几个地方必须改# settings.py 关键调整 DEBUG False # 生产环境必须关掉调试模式 ALLOWED_HOSTS [yourdomain.com, www.yourdomain.com] # 改成实际域名或IP DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: parking_db, USER: parking_user, PASSWORD: your_password, HOST: 127.0.0.1, PORT: 3306, } } # 时区建议设为本地时区避免时间显示偏差 TIME_ZONE Asia/Shanghai USE_TZ True # 静态文件收集 STATIC_ROOT /var/www/parking/static/这里特别提醒一点如果你部署在公网服务器千万不要开着DEBUG跑不光是性能问题更重要的是DEBUG开启时会暴露完整的报错堆栈和配置信息这是安全大忌。4.4 使用Gunicorn Nginx部署建议开发环境下runserver够用了但生产环境上runserver是绝对不能用的它是单进程开发服务器性能和稳定性都不行。我通常用Gunicorn跑DjangoNginx做反向代理和静态文件服务。# 安装gunicorn pip install gunicorn # 启动4个worker进程 gunicorn config.wsgi:application -w 4 -b 127.0.0.1:8000 --daemonNginx配置一个反代到8000端口静态文件交给Nginx直接托管server { listen 80; server_name yourdomain.com; location /static/ { alias /var/www/parking/static/; } location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }5. 常见问题与排查技巧实录5.1 并发预约同一个车位的问题这是预约系统在线运营后第一个暴露的问题。最开始我的代码没有加select_for_update压测的时候发现两个用户同时提交预约后端查询车位状态都是空闲结果两个预约订单都创建成功了车位被重复分配。排查的过程让我印象很深我去翻数据库记录发现两笔订单同一时间锁定了同一个车位。后来加上了数据库行锁并且把所有订单创建逻辑放到一个事务里问题就解决了。这里要注意select_for_update()必须在事务内使用才有效如果不在transaction.atomic()块里锁会立即释放等于白锁。这是个非常隐蔽的坑很多新手踩进去不知道怎么排查。5.2 预约超时未支付自动释放用户提交预约后如果不支付车位一直被锁定会影响运营效益。我在设计里加了超时释放机制预约创建后15分钟内未支付的系统自动取消订单并释放车位。实现方式有两种一是用Celery定时任务扫描二是懒处理——用户查看车位或预约时先顺带清理超时订单。我在项目里用的是第二种因为逻辑简单不依赖额外组件对中小项目更友好。def clean_expired_reservations(): 清理15分钟内未支付的预约 expire_time timezone.now() - timezone.timedelta(minutes15) expired Reservation.objects.filter( statusReservation.STATUS_PENDING, created_at__ltexpire_time ) for order in expired: order.status Reservation.STATUS_CANCELLED order.save() # 释放车位 slot order.slot slot.status ParkingSlot.STATUS_AVAILABLE slot.current_order None slot.save(update_fields[status, current_order])我是在每个需要展示车位状态的视图里调用这个函数的相当于用的时候顺手清理一把没必要维护一个常驻定时任务。5.3 时间字段的时区陷阱时间字段的坑我踩得最深。Django设置了USE_TZTrue后写入数据库的时间会被转成UTC读取时再转回本地时区。如果前端页面展示直接用查出来的时间因为浏览器时区不同显示的预约时间可能有偏差。解决方法是所有前端展示时间都通过模板过滤器或者JS格式化统一转成Asia/Shanghai时区再显示。后端计算计费时长时必须使用同一时区我统一用timezone.localtime()转后再做差值计算避免夏令时之类的问题。5.4 Admin后台搜索和过滤的性能优化订单量上万之后Admin后台的订单列表加载变慢了尤其搜索的时候数据库要全表扫描。查询优化方面我做了两个调整在search_fields里不用user__phone这种跨表关联搜索而是在订单表冗余一个phone字段搜索直接走本表索引避免JOIN。列表页加list_per_page限制每页显示数量默认Django是一次性加载全部记录一多就卡。class ReservationAdmin(admin.ModelAdmin): list_per_page 20 ordering [-created_at]5.5 实际问题速查表现象可能原因解决方案预约成功后车位仍显示空闲缓存未刷新或事务回滚检查缓存策略确认视图使用的数据源计费金额异常大时区问题导致时长计算错误统一使用localtime转换后再做差值后台增删车位后页面不更新数据库迁移没执行运行makemigrations migrate同一车位被重复预约缺少行锁或事务没生效加select_for_update并包在事务中部署后静态文件404未收集静态文件执行python manage.py collectstatic并发下单卡顿SQLite写锁限制上生产切换到MySQL/PostgreSQL写在最后做这个项目的过程我最大的一个体会是业务系统的难度从来不在功能实现而在边界情况的处理。停车预约看起来就是选车位、下单、计费、离场这几个动作但一旦进入真实运营并发抢单、超时释放、跨天计费、时区转换每一个细节都可能让你半夜爬起来修bug。源码包里的这套系统整体逻辑是完整可跑的我建议你拿到代码后不要直接部署到生产环境先用它做二次开发的底子。把基础的表结构、状态机、计费逻辑吃透然后根据自己的业务场景去扩展——比如接入微信支付、增加会员折扣、对接车牌识别摄像头这些都是可以继续做的方向。最后分享一个调试技巧预约系统的并发问题很难靠肉眼排查可以在本地用django-test-plus写几个并发测试用例模拟多个用户同时抢单个车位验证你的锁逻辑是否正确。我每次改状态流转相关的代码都会跑一遍这个测试能省下大量线上排查的时间。本文还有配套的精品资源点击获取