PHP房屋租赁系统毕业设计实战:从数据库到订单状态全解析
写PHP房屋租赁系统这套毕业设计其实挺有意思的。它不像电商、社交平台那样烂大街业务场景又足够复杂——用户角色有两种交易的标的物是非标准化的房子还有线上线下混合的看房流程要处理。做完一套下来数据库设计、权限控制、文件上传、搜索排序、状态机流转这些基本功全都能覆盖到用来做毕业设计或者项目经验展示性价比很高。这套系统的核心逻辑其实不复杂房东发布房源租客搜索、浏览、预约看房、下单、签约付款管理员在后台做审核和统计。但真正动手写代码的时候你会发现细节多到数不清房子信息的字段要不要拆表、图片到底存服务器还是存数据库、订单状态怎么流转才不乱、用户权限怎么控制才不越权……这篇文章我就按实际开发顺序把一套能跑通的PHP房屋租赁系统拆开讲清楚。如果你正准备做类似的毕设或者想自己练手可以直接照着实操。1. 项目整体设计与技术选型1.1 需求来源与功能拆分动手之前先想清楚做一个什么东西。房屋租赁系统是个典型的双边平台一个用户既可以当租客去找房子也可以当房东去发布房源。所以角色模型上要做成普通用户租客、房东发布者、管理员后台维护者三种身份。常见做法是让一个用户表统一管理靠level字段区分角色这样做的好处是登录逻辑简单后续如果一个租客想发布房源也不需要单独再注册一次。核心功能模块拆下来有这么几个用户模块注册、登录、找回密码、个人中心、房源模块发布、编辑、上下架、图片上传、房源列表、房源详情、搜索模块关键词搜索、区域筛选、租金区间、户型筛选、排序、订单模块预约看房、发起租赁、确认成交、取消订单、状态变更、收藏模块收藏房源、取消收藏、后台管理用户管理、房源审核、订单管理、统计报表。别觉得功能多每个模块拆开看本质上都是增删改查加一点业务逻辑。真正需要花心思的地方是几个关键点房源图片怎么存储、订单状态怎么流转、搜索SQL怎么写才能兼顾效率和准确率。这些点我会在后面的章节逐个展开这里先说一下为什么要这么设计。1.2 技术栈为什么选PHP而不是Java、Python、Node毕设选PHP首先得承认一个现实你的核心目标是快速、稳妥地把系统跑通把论文和答辩准备好而不是折腾框架选型。PHP在这方面优势很明显开发效率极高环境和部署成本低随便装一个集成环境就能跑。哪怕你导师要求你用MVC架构现在的主流框架也都帮你把这些事情做好了。这里我推荐用ThinkPHP 6或者Laravel选这两个的原因很简单首先中文文档和社区资料非常丰富遇到问题搜一下就有答案其次它们自带数据库ORM、模板引擎、路由解析、CSRF防护这些功能自己从零写既浪费时间又容易出安全漏洞。你要是问我原生的PHP行不行也行但前提是你得有足够的时间去处理SQL拼接、文件上传过滤、模板渲染这些非常琐碎的细节。做过一次之后你会发现框架省下来的时间足够你把系统多打磨几轮。如果非要在ThinkPHP和Laravel里选一个我的习惯是毕业设计推荐ThinkPHP 6理由很实在它在国内校园场景下的参考资料更多很多问题别人都踩过坑了你遇到报错直接搜基本都能找到对应解法。Laravel的门槛略高composer依赖、artisan命令行、Eloquent ORM这些概念对新手不算友好。技术选型这种问题上别意气用事选你最能掌控的。1.3 分层架构MVC不是老古董很多同学一听到MVC就觉得是老掉牙的东西实际上对于一个带界面的Web系统MVC的分层思想依然是最容易理解、最容易维护的。Controller层负责接收请求和响应Model层负责数据操作View层负责展示逻辑清晰别人看你代码的时候也能很轻松地顺着调用链路走。这套系统的目录结构我习惯这样做application / controller存放控制器按模块分组比如Portal前台、Admin后台application / model存放数据模型比如UserModel、HouseModel、OrderModelapplication / view存放模板文件按控制器建子目录public / static存放CSS、JS、图片等静态资源写控制器的时候有一个很重要的原则控制器只做参数接收、校验和结果返回不要把业务逻辑全堆在控制器里。比如下单这个操作正确做法是写一个OrderModel的createOrder方法里面把库存校验、状态判断、订单生成这些逻辑封装起来控制器只调用这一个方法代码可读性和可维护性会提升好几个档次。这一点做得好导师评阅代码的时候观感会明显不一样。2. 数据库设计与核心表结构2.1 用户、房源、订单三大主表设计数据库设计是这套系统最见功力的地方。房屋租赁系统涉及的核心实体不算太多但表与表之间的关联关系要理清楚。我一共设计了六张核心表用户表、房源表、房源图片表、收藏表、订单表、管理员表。用户表的核心字段要覆盖用户名、密码注意存的是哈希值不是明文、手机号、邮箱、角色等级、实名认证状态、注册时间。有个容易被忽略的点是用户在成为房东前是否要补全个人信息这里我加了字段identity_status0为未认证1为已认证发布房源时做校验。房源表是最复杂的一张表字段包括发布者ID、标题、描述、租金、押金、面积、户型几室几厅、所在城市、区域、具体地址、经纬度留给地图用的、租赁方式整租/合租、状态待审核/已上架/已出租/已下架、点击量、发布时间。地址存到“区/街道”这一粒度就够了具体门牌号建议只用文本字段保存不要做结构化拆分减少复杂度。订单表需要理清楚租赁的业务流用户浏览房源、预约看房、线下看房满意、线上下单、房东确认、签约付款、合同生效。我设计的订单表主要字段有订单号、房源ID、租客ID、房东ID、起租日期、结束日期、月租金、押金、总金额、状态、创建时间、更新时间。注意这里把房东ID也冗余进来了虽然通过房源表能关联到房东但订单查询时直接靠房东ID过滤效率更高这就是典型的空间换时间。2.2 状态机设计订单流转如何避免脏数据订单状态这块特别容易写崩因为状态之间的迁移是有方向的不是想改就改。我定义的状态集是pending待确认、reserved已预约、signed已签约、active履约中、finished已结束、cancelled已取消、expired已失效。听起来很多但摸清规律之后发现其实就三条线正向走、反向取消、超时未处理。正向走是正常流程租客预约看房订单进入reserved看房满意后租客确认租赁意向状态改为signed双方线下签合同或者走线上电子合同房东在后台确认状态改为active租期届满状态改为finished。反向走是各种取消租客未支付或主动取消状态改为cancelled房源在留定期内被其他租客抢先下单原预约订单自动变为expired。实现上有一个关键细节状态变更不能只写一个UPDATE语句而是要用“条件更新”来保证并发安全。也就是说SQL语句的WHERE条件里必须带上当前期望的状态值比如要把pending改成reserved就写UPDATE order SET status reserved WHERE id ? AND status pending。如果影响行数为0说明订单状态已经被其他人改过了这时候再做后续逻辑处理。这招对并发场景下的数据安全非常有效比先查再改然后再写靠谱得多。2.3 数据冗余与索引优化毕设阶段的数据量不大没必要过度优化但有几处设计习惯从开始就养好。第一用户表、房源表、订单表的主键都用自增ID不做联合主键保持简单。第二所有外键关系的字段都要建索引比如house表的user_id、order表的house_id、order表的renter_id因为查询基本都是靠这些字段过滤的。第三搜索场景要建联合索引。比如搜索房源最常见的过滤条件是citystatusrent那么就可以建一个(city, status, rent)的联合索引。要注意索引顺序最左前缀原则决定了条件字段的顺序要和索引顺序一致。实际操作中我发现毕设的数据量下索引实战意义不大但把这个概念写进论文和答辩中是加分项导师都会认可你考虑到了这个问题。关于数据表的一个经验是不要为了省事把图片URL拼成一个长字符串存在房源表的一个字段里。正确做法是单独建一张house_image表一个房源多条图片记录字段包括id、house_id、image_url、sort_order。这样做的好处是后续做轮播图、封面图、图片管理都很灵活SQL拆分组装起来也直观。3. 核心功能模块解析与实现细节3.1 登录认证与权限控制登录认证这部分我的建议是使用PHP框架自带的Session机制而不是自己去写JWT。原因很简单Session在传统PHP项目中天然得好服务端存储浏览器的Cookie自动带上Session ID配合框架的Auth组件可以很方便地实现用户状态的保持。JWT虽然在这几年很流行但它在无状态接口服务上更有优势这种传统的页面表单的Web系统用Session就够了。如果用的ThinkPHP 6内置的Session功能非常完善我把登录流程串一下用户提交用户名和密码控制器先做基础校验字段是否为空、格式是否正确然后调用UserModel的checkLogin方法方法内部用哈希校验密码成功之后把用户ID和角色信息写入Session同时更新用户的最后登录时间。前端页面通过判断Session里的user信息来决定是否显示登录入口。权限控制分两层第一层是页面级的通过控制器基类里加一个初始化方法比如admin模块的控制器都继承同一个AdminBase控制器只要请求进入到这个模块先检查当前Session里有没有管理员的信息没有就跳转到登录页。第二层是操作级的比如普通用户不能直接调用后台接口修改其他用户的数据这需要在关键业务方法里做归属校验如果数据当前的owner_id不等于Session里的uid直接返回错误。3.2 房源发布与图片上传发布房源是整个系统最核心的写操作牵扯到的字段多、关联表多最容易出问题。我把这个流程拆成三步表单提交基础信息、上传房源图片、保存关联数据。前端用了一个多字段表单包含标题、租金、面积、户型、地址、描述等图片上传用异步方式选择完图片之后立即上传到服务器返回图片URL最后提交表单时把URL数组一起提交。图片上传有个点要非常注意PHP默认的upload_tmp_dir路径下上传的文件是临时文件必须在请求结束前用move_uploaded_file函数移动到指定目录否则就会被自动清理。安全方面要做几个校验一是扩展名白名单只允许jpg、jpeg、png、gif二是MIME类型检查三是文件大小限制我建议单张不超过2MB在php.ini的upload_max_filesize里设置。还要注意生成新的文件名不要用用户上传的原始文件名直接时间戳加随机数拼接防止路径穿越和重名覆盖。存储路径我建议放到public/static/upload目录下并且在保存数据库时只存相对路径比如/static/upload/20250612_xxxx.jpg。这样做的原因是一旦部署上线把整个项目放到Web根目录图片就能直接通过URL访问不需要额外的静态文件路由。如果你把图片存在了application目录下的某个自定义目录里部署的时候很容易出现路径不对、无法访问的情况。3.3 搜索筛选与分页搜索模块看起来简单实际上是最容易写出“面条代码”的地方。用户选的筛选项可能是组合的城市、区域、租金范围、户型、租赁方式、排序方式。如果每多一个条件就拼一个字符串代码会变得又长又乱。我习惯用条件数组的方式来拼SQL核心思想就是把所有筛选条件统一装进一个数组里最后统一构造WHERE条件。还是以ThinkPHP为例先初始化一个查询对象然后逐个条件追加如果传了city就where city $city如果传了min_price就where rent $min_price如果传了max_price就where rent $max_price户型条件可能是几室那就where layout like %三室%。排序方面预留几个选项默认按发布时间倒序也支持按租金升序、降序。注意租金排序时数据库里存的是整数类型不要用字符串字段存金额否则排序结果会非常怪。分页用框架自带的分页类就够了传入每页条数和当前页码查询结果返回一个包含数据列表和分页HTML的数据结构模板里直接输出分页链接。有个小坑是分页链接在搜索结果页会丢失查询参数所以生成分页链接时一定要把当前的搜索条件拼进去比如page/2/type/1/area/xxx这种格式否则点击第二页之后搜索条件就全部丢了。这个细节很多人第一次写都没注意。3.4 订单流程与合同生成订单流程是这套系统的业务核心我前面说了状态机这里展开讲具体实现。用户看中一套房首先发起预约看房请求系统自动生成一个预约订单状态为reserved同时给房东发一条站内消息。之后租客实地看房满意后确认租赁订单状态改为signed此时系统要做一次并发检查确认房源状态仍是“可租”如果房源已经被其他人抢先下单就友好提示租客重新选择。房东端接收到已签约订单后可以确认并生成合同。合同这块我建议直接生成PDF文件技术方案选PHPExcel库的扩展或者TCPDF库都行模板里拼上租客姓名、身份证号、房源地址、租金、押金、租期这些字段生成后保存到本地目录同时在订单表里记录合同文件的路径。生成PDF的好处是答辩演示的时候直接把合同导出来展示效果比口头讲好很多。订单列表页要多加一个筛选功能按状态标签分页显示。租客端可以看到自己发起的订单和各自的状态房东端可以看到所有与自己房源相关的订单。注意列表里不要一次性把房源的所有字段都查出来只需要冗余的关键字段比如封面图、标题、租金、地址这种冗余字段在生成订单的时候就直接复制到订单表里面存一份后续订单状态再怎么变这些快照信息都不会受影响。3.5 后台管理功能后台管理模块是区别于普通前端页面的独立子系统我的做法是把后台路由统一加上admin前缀比如/admin/login、/admin/index、/admin/house/list然后整个admin模块的控制器都继承AdminBase控制器做权限控制。后台功能不需要很花哨但必须把关键的几个管理入口打通用户管理、房源管理、订单管理、数据统计。房源管理里最要紧的是审核功能。会员发布的房源默认是待审核状态管理员在后台查看房源详情后选择通过或驳回驳回时需要填写原因前端页面会显示这个原因让发布者知道该改哪里。订单管理主要是一个可搜索的订单列表管理员能看到所有订单的状态可以对个别异常订单进行手动关闭处理。数据统计稍微做几个简单的指标就行总用户数、总房源数、总订单数、本月新增用户数、本月成交订单数画个柱状图或者折线图。图表不建议用复杂的前端库用ECharts就够了引入一个JS文件渲染一下效果已经非常好看。4. 实操过程从零搭建到跑通4.1 开发环境准备在本地开发我推荐直接用集成环境把PHP、MySQL、Apache/Nginx一次性装好Windows上用phpstudyMac上用MAMP非常省事。PHP版本选7.4或者8.0都行ThinkPHP 6要求PHP 7.2.5Laravel 9要求PHP 8.0选个目前主流的版本就行。MySQL这里用5.7注意MySQL 8.0的认证插件跟PHP的老版本连接方式有兼容性问题如果非要装8.0记得把认证规则改成mysql_native_password否则连数据库会报错。数据库用phpMyAdmin或者Navicat都行我习惯先用SQL语句把数据库和表结构创建出来再往里面灌测试数据。测试数据很重要房源数据至少准备20条用户数据至少10条不然搜索结果页空荡荡的演示效果很糟糕。写个PHP脚本循环插入随机数据也行手动分批插入也行关键是字段之间逻辑保持一致比如房屋状态是已租就不要再出现在可租列表里。4.2 项目初始化与目录结构如果你选了ThinkPHP 6初始化项目很简单composer create-project topthink/think tp6这样的命令就能拉下来一个空项目。如果你对composer有点抗拒也可以直接从官网下载完整的源码包解压就能用。初始化完成后先把数据库连接配置改好在.env文件里填入数据库名、用户名、密码然后确认项目能正常访问首页这一步跑通了后面所有开发都在这个地基上。接下来要规划好目录结构。Controller目录下面按模块分子目录Portal放前台控制器Admin放后台控制器每个控制器对应一个主要的业务实体。Model目录下面放和表对应的模型类一个表对应一个模型模型里写这个表相关的业务逻辑方法。View目录下面按控制器建目录一个控制器对应一个文件夹里面的HTML文件按方法名命名。这样的目录规划虽然简单但让我后面找人帮我维护代码或者自己隔了一周再看代码都不会迷路。4.3 前端页面与PHP混合开发的实现这套系统的前端我没有用前后端分离直接用ThinkPHP的模板引擎在服务端渲染HTML。原因很简单前后端分离意味着你要同时维护一套前端工程和一套接口服务对毕设来讲工作量翻倍而且答辩时你还需要解释为什么引入这么复杂的架构。PHP混编的方式代码都在一个项目里部署只需要一份展示逻辑也直白。模板文件里可以用框架自带的模板语法比如遍历房源列表就用foreach循环填充表单的默认值就用value{$house.title}。这种模式下的页面刷新是整页刷新交互体验肯定不如Vue、React这类前端框架流畅但作为一个管理系统需求完全够用。要注意页面的统一风格我建议把头部导航栏、底部版权信息抽成公共模板文件用模板继承或者include的方式引入比如layout.html放公共头部和尾部各个页面只写主体部分的内容。4.4 接口联调与数据填充虽然没做前后端分离但前后台交互的过程中仍然涉及到一些接口调用比如异步图片上传、注册时检测用户名是否重复、收藏房源时不用跳页。这些功能我用jQuery的Ajax实现控制器返回JSON格式数据前端根据返回的code字段判断成功还是失败弹出对应的提示信息。注意控制器返回JSON之前要设置好响应头并且关闭模板渲染否则框架可能返回一个HTML页面而不是JSON结构。数据填充这一环节别偷懒我前面说过至少要保证演示时页面是丰满的。除了手动准备数据还可以写一个数据填充脚本放在database/seed目录下直接调用模型批量生成测试数据。这样你电脑重装环境或者数据库被我搞崩了之后一条命令就能把演示数据全部恢复。尤其是答辩前万一数据库出了意外有这个脚本能救你一把。5. 常见问题与排查技巧实录5.1 中文乱码问题中文乱码是最常见、最容易排查的一个问题但很多新手绕不明白。这个问题的本质是字符集不一致到处都在用编码最关键的三个环节保持一致就不会乱数据库表字符集是utf8mb4、PHP文件保存编码是UTF-8、页面输出字符编码是UTF-8。其中两个点特别容易被忽略一是连接数据库的DSN里要指定charsetutf8mb4二是HTML页面的meta标签写的是charsetutf-8但PHP文件本身可能是ANSI编码保存这时候输出到浏览器必然乱码。排查思路很简单用浏览器开发者工具看响应头里的Content-Type再看数据库里的数据到底有没有变成乱码先定位是存储环节还是读取环节出了问题。5.2 图片上传失败图片上传失败的原因无非几种文件过大被php.ini的upload_max_filesize限制、上传目录没有写权限、文件扩展名校验不通过、move_uploaded_file函数执行时目标目录不存在。前两个问题都可以通过检查php.ini配置和目录权限来排查第三个问题通过打印上传错误码来定位第四个问题是很多人容易错过的上传目录要提前创建好TP框架里一般用递归创建目录的方法保证目录存在后再移动文件。我建议在upload方法里加上对应的错误码日志记录上传失败时把错误码写进日志排查效率高很多。5.3 SQL注入与安全防护这个话题我觉得就是“渐进式”的先把框架提供的基础防护做扎实再在关键环节手动加固。使用ThinkPHP这类框架的查询构造器和ORM方法内部已经做了参数绑定基本的注入风险已经被挡掉了但有几个坑是框架管不了的一是原生SQL查询如果你因为某些场景用了query方法执行SQL字符串里面的参数一定要手动加引号处理二是搜索模块的动态排序如果把用户传进来的排序字段直接拼进SQL攻击者可以传一个奇怪的order字段虽然危害有限但属于代码缺陷。安全习惯上后台登录要加验证码接口要加CSRF Token校验文件上传要过滤MIME类型密码不能明文存储可以用password_hash函数处理。5.4 分页参数错误分页参数出错主要发生在两个地方一是页码参数传成字符串导致SQL报错这个只需要做一次强制类型转换(intval)就能解决二是搜索结果分页丢失查询条件这个我前面提过要手动把查询参数拼到分页链接中。还有一个小细节是分页跳转到超出范围页码时要判断一下总页数如果当前页大于总页数就强制跳转到第一页或者最后一页不要让用户看到空白页。5.5 部署到生产环境时容易踩的坑本地跑得好好的一到线上就出问题这是所有新手都会经历的过程。我总结下来主要有几个原因第一文件目录权限。线上服务器的Web用户默认是www-data或者nginx你需要保证storage目录、upload目录、runtime目录对Web用户可写。第二伪静态配置。ThinkPHP默认的URL模式需要服务器把请求重写到入口文件的index.phpApache要开启mod_rewriteNginx要配置try_files规则这个不配好访问除首页之外的任何页面都会404。第三环境变量。线上部署一定要把.env文件里的debug模式改为false关闭错误堆栈显示否则SQL报错信息会直接暴露给访客样子也不好。6. 个人经验总结与扩展建议6.1 做完这套系统的几点心得体会如果你也在做PHP相关的毕设我觉得有几个经验提前知道能少走很多弯路。第一项目能跑通只是起点业务逻辑的正确性和代码的可读性才是拉开差距的地方。比如订单状态机设计、上传文件的校验、查询条件的动态拼装这些点做得稳答辩时导师问起来你能解释就是加分项。第二数据库设计阶段多花时间比代码阶段反复改SQL要划算得多。我一开始设计房源表的时候忘了把收藏功能和订单表的关联字段想清楚后面补字段、改数据接口浪费了将近一周的精力。第三演示数据一定要丰富。答辩的时候导师觉得房源列表有20多条数据、搜索能筛选、分类能看到不同频道、订单能流转起来直接锤实了系统的完成度。如果列表页只有两三条假数据哪怕后端逻辑再严谨观感上也像一个半成品。6.2 后续扩展方向这套系统的核心骨架已经足够稳了想继续扩展可以考虑几个方向。如果对前端感兴趣可以把前台首页和房源列表页改成Vue API的方式把PHP端改造成纯粹的API服务这就算完成了一次前后端分离升级简历里能写的东西又多一条。如果想把这个项目做成一个真实的SaaS产品可以继续加支付功能接入第三方支付的电脑网站支付、H5支付、电子合同在线签署、在线客服、定期自动生成账单、房源VR看房这些模块。我个人更建议的是业务层面做深比如增加一个“房东评分”体系用户在交房之后可以对房东进行评价然后把这部分数据结构加入现有的订单流程中。原因是这个扩展会让系统的数据模型更复杂业务闭环更完整也更能体现你对系统全局的把控能力。最后再分享一个写这类管理系统的小技巧每完成一个模块的功能就顺手把对应的SQL语句、关键配置文件、session交互流程整理成一个文档放到项目的docs目录下。写论文的时候你会发现这些东西直接就能用答辩准备PPT的时候也能直接拉出流程图和数据字典不用重新翻代码去回忆当初是怎么设计的。