SSM+微信小程序房屋租赁系统:从数据库设计到联调交付全流程
简介这是一套面向高校计算机相关专业毕业设计的房屋租赁管理小程序完整项目资料聚焦微信小程序前端与SSM框架、MySQL数据库的整合开发。系统覆盖管理员、中介、用户三类角色包含房屋信息发布、租房订单、账单管理、个人中心等核心模块可直接用于毕业设计选题落地、课程实训或Java全栈入门实践。资源包共891个文件大小约59.15MB主体包含java后端代码、vue管理端页面、wxml/wxss小程序页面、sql数据库脚本、xml映射配置以及png/jpg界面截图、mp4演示视频和毕业论文文档。文件类型丰富目录结构完整便于按前后端分层与功能模块检索其中sql脚本与源码配合可快速复现系统环境毕业论文和演示视频则有助于理解设计思路与答辩准备。目前已有200人学习下载适合需要快速搭建同类管理系统、撰写设计文档或完成系统演示的开发者。 房屋租赁管理小程序是毕业设计里特别常见的一类选题微信端承担找房、收藏、预约与下单SSM 后端处理业务逻辑MySQL 存全量数据。它不像电商项目那样必须接在线支付也不像推荐系统那样依赖算法效果主链路清晰但又有足够多的表、接口和页面撑起论文、答辩和演示视频。我见过不少同学把精力全放在页面好不好看上结果联调时接口一个接一个断也有一上来就写 CRUD最后论文里连一张关系图都画不出来。这篇内容就按“系统设计 → 数据库 → 后端 → 联调排错 → 演示交付”的顺序把房屋租赁管理小程序从零跑通的一套可靠路径拆开讲。2. 架构拆分与技术选型小程序、SSM、MySQL 各自负责什么一套完整的房屋租赁管理小程序跑起来涉及三条链路微信小程序负责界面展示和用户交互SSM 后端负责接收请求、处理业务、读写数据库MySQL 负责把用户、房源、订单、合同这些数据落地。三个部分之间通过 HTTP 接口通信数据格式统一用 JSON。这个结构本身不复杂但每一层的工作边界要划清楚后面写代码和论文时才不会乱。2.1 三个参与角色租客、房东、管理员的功能边界先把系统里的角色定下来租客、房东、管理员。很多选题会把房东和租客做成两个完全独立的表实际做的时候我一般建议只在一个用户表里用 role 字段区分0 是租客1 是房东管理员单独一张表。这样注册登录逻辑只要写一套论文里画角色用例图也更清爽。三个角色的功能边界是这样的角色核心诉求主要负责的功能租客找到合适的房子并签约搜索房源、查看详情、收藏、预约看房、提交租赁订单房东把房子租出去并管理租约发布房源、上下架房源、确认租客订单、查看合同管理员保证平台内容合规审核房源、管理用户状态、查看全平台订单与合同角色边界清楚之后接口设计就跟着出来了。比如发布房源和上下架房源只允许 role1 的用户调用收藏和下单只允许登录用户调用房源审核只允许管理员调用。这个控制不放在前端页面隐藏上而是放在后端拦截器里做否则别人直接调接口就能绕过页面限制。2.2 SSM 与小程序之间的 HTTP 交互模型小程序和后端的交互模型很简单小程序通过 wx.request 发起 HTTP 请求请求落到 Tomcat 里的 SpringMVC ControllerController 调 Service 处理业务Service 调 MyBatis Mapper 操作数据库处理完成后以 JSON 形式返回给小程序。整个链路是同步的每一步都有明确的代码位置这也是 SSM 适合做毕设的原因——每一层都能在答辩时指给老师看。接口风格我习惯用 REST 风格返回体统一包一层 Result 结构public class Result { private Integer code; // 200 成功401 未登录500 业务异常 private String msg; // 提示信息直接给前端弹 toast 用 private Object data; // 业务数据列表或详情对象 }前端所有接口都按这个结构解析判断 code 再决定接下来的动作。接口清单不需要很多核心的就是这几类模块接口路径方法说明登录/user/loginPOST用微信 code 换 openid自动注册房源/house/listGET分页查询支持关键词和价格区间房源/house/publishPOST房东发布房源收藏/favorite/togglePOST收藏与取消收藏共用一个接口订单/order/createPOST租客提交租赁订单订单/order/confirmPOST房东确认订单合同/contract/generatePOST管理员生成合同2.3 选型理由毕设场景下 SSM 比 Spring Boot 更划算现在很多同学上来选 Spring Boot因为配置少、启动快。但作为毕业设计我反而建议选 SSM。原因很实际SSM 的配置是显式的spring-mvc.xml 里配了哪些拦截器、spring-mybatis.xml 里配了哪个数据源在答辩时直接打开文件讲就行Spring Boot 的自动装配把这些都藏起来了被问到“请求怎么从 HTTP 走到数据库的”的时候容易答不上来。另外 SSM 对 MySQL 的支持非常稳定jdbc 连接串、驱动依赖、事务配置都是现成的写法网上资料也最多遇到问题搜索基本都有对应答案。不是说 Spring Boot 不好而是房屋租赁管理小程序这种业务型毕设SSM 的“展开性”更好每写一行配置都在为论文和答辩积累素材。3. 数据库设计租赁业务表结构与建表 SQL数据库是这套系统里最先要落地的部分。表结构一旦建错后面写代码全是补丁缺字段要加列关系错了要改查询状态不统一要对数据。我一般建议先花一个晚上把表和字段定下来再开始写后端代码。房屋租赁业务的核心数据模型并不复杂它的主链路是用户发布房源 → 租客收藏/预约 → 租客下单 → 房东确认 → 管理员生成合同。3.1 核心表关系用户、房源、订单、合同的关联设计整个系统的表我建议控制在 6 张以内用户表 user、房源表 house、收藏表 favorite、订单表 rental_order、合同表 contract再加一张管理员表 admin。不要一开始就加会议室预约、物业缴费、消息通知这些扩展表毕设的评分点是“功能完整、流程闭环”不是表多。它们之间的关系是这样user 表与 house 表一对多一个房东可以发布多套房源house 表里用 owner_id 外键指向 user.id。user 表与 house 表多对多通过收藏表 favorite 中间表实现favorite 同时存 user_id 和 house_id。rental_order 表同时关联租客和房东renter_id 指向租客landlord_id 指向房东再冗余一个 house_id 方便按房源查订单。合同表 contract 关联 rental_order一个订单在房东确认后由管理员生成一份合同contract 表里存 order_id。外键在表设计里要加但在实际业务代码里不建议依赖数据库级联删除删除操作用逻辑删除或者状态字段控制更安全。3.2 建表 SQL五张核心表的落地写法用户表和房源表是最先建的直接看 SQLCREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT COMMENT 用户ID, openid VARCHAR(64) DEFAULT NULL COMMENT 微信openid, username VARCHAR(32) NOT NULL COMMENT 登录账号, password VARCHAR(64) NOT NULL COMMENT 登录密码, nickname VARCHAR(32) DEFAULT NULL COMMENT 昵称, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, role TINYINT NOT NULL DEFAULT 0 COMMENT 0租客 1房东, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; CREATE TABLE house ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(64) NOT NULL COMMENT 房源标题, address VARCHAR(128) NOT NULL COMMENT 详细地址, price DECIMAL(10,2) NOT NULL COMMENT 月租金, area DECIMAL(6,2) DEFAULT NULL COMMENT 面积单位平米, description TEXT COMMENT 房源描述, cover VARCHAR(255) DEFAULT NULL COMMENT 封面图URL, owner_id INT NOT NULL COMMENT 房东ID, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待审核 1上架中 2已下架 3已出租, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_owner (owner_id), CONSTRAINT fk_house_user FOREIGN KEY (owner_id) REFERENCES user(id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT房源表;这里有两个设计细节值得注意。第一price 用 DECIMAL(10,2) 而不是 FLOAT舍入误差在合同金额场景不能接受。第二status 用 TINYINT 并用注释标明含义比直接存“上架中”这种中文串更省空间、查询更快后续做状态统计也方便。create_time 用 DEFAULT CURRENT_TIMESTAMP 自动填充程序里不用手动维护。接下来是订单表和合同表它们是业务闭环的关键CREATE TABLE rental_order ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 业务订单号, renter_id INT NOT NULL COMMENT 租客ID, house_id INT NOT NULL COMMENT 房源ID, landlord_id INT NOT NULL COMMENT 房东ID冗余便于查询, status TINYINT NOT NULL DEFAULT 0 COMMENT 0待房东确认 1已确认待签合同 2已签合同 3已结束 4已取消, start_date DATE DEFAULT NULL COMMENT 租期开始日, end_date DATE DEFAULT NULL COMMENT 租期结束日, rent DECIMAL(10,2) DEFAULT NULL COMMENT 月租金快照, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_order_no (order_no), KEY idx_renter (renter_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT租赁订单表; CREATE TABLE contract ( id INT PRIMARY KEY AUTO_INCREMENT, contract_no VARCHAR(32) NOT NULL COMMENT 合同编号, order_id INT NOT NULL COMMENT 关联订单, renter_name VARCHAR(32) NOT NULL, landlord_name VARCHAR(32) NOT NULL, lease_start DATE NOT NULL, lease_end DATE NOT NULL, rent DECIMAL(10,2) NOT NULL, deposit DECIMAL(10,2) NOT NULL COMMENT 押金金额, sign_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_contract_no (contract_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT租赁合同表;订单表里把 landlord_id 冗余出来是为了房东端查询“我的订单”时不用先关联 house 表再查归属一个 where 条件就能搞定。contract 表里的 rent 和 deposit 是合同签定时点的快照即使以后房源涨价合同里的金额仍然有效。收藏表的唯一约束是防重复收藏的兜底CREATE TABLE favorite ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, house_id INT NOT NULL, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_user_house (user_id, house_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT收藏表;收藏表加上(user_id, house_id)唯一约束后前端连续点击收藏按钮第二次 insert 会直接被数据库拦截后端代码里抓 DuplicateKeyException 再转成“已取消收藏”即可。3.3 状态字段设计订单状态与房源状态怎么枚举状态字段是整个系统的隐藏核心。房源有四个状态0 待审核、1 上架中、2 已下架、3 已出租。订单有五个状态0 待房东确认、1 已确认待签合同、2 已签合同、3 已结束、4 已取消。状态流转必须单向推进取消操作只允许在待确认和已确认这两个阶段发生。两个设计经验第一状态值从 0 开始而不是从 1 开始这样所有新数据的默认状态都是“待处理”即使插入时忘记赋值也不会直接上架第二前端展示状态时做一层格式化不要直接显示数字在小程序端用 map 映射成“待审核”“上架中”等文案。状态字段后期肯定会加枚举值加的时候在注释里同步更新不然三个月后自己都看不懂 4 代表什么。4. 后端 SSM 链路搭建从 pom.xml 到房源发布接口数据库结构定好后开始搭后端工程。SSM 项目的骨架比较固定Maven 管理依赖Spring 管 BeanSpringMVC 管接口路由MyBatis 管 SQL。第一次做的同学不建议自己从零写配置文件按下面的骨架建工程能省掉大部分初始化时间。4.1 项目结构与 pom.xml 关键依赖标准的 SSM 工程目录结构是这样的src/main/java/com/example/rent ├── controller # 接口层 ├── service # 业务层 ├── mapper # MyBatis 接口 ├── entity # 实体类 ├── interceptor # 登录拦截器 └── config # Spring 和 SpringMVC 配置 src/main/resources ├── mapper # MyBatis XML 文件 ├── spring-mvc.xml ├── spring-mybatis.xml ├── jdbc.properties └── web.xmlpom.xml 里最关键的依赖是下面这几个dependencies dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version5.1.20.RELEASE/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis/artifactId version3.5.6/version /dependency dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.6/version /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version8.0.29/version /dependency dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.11.3/version /dependency dependency groupIdcommons-fileupload/groupId artifactIdcommons-fileupload/artifactId version1.3.3/version /dependency /dependencies这套版本组合我用了很多次互相兼容没有坑。spring-webmvc 负责接口路由和参数绑定mybatis-spring 是 MyBatis 与 Spring 集成的桥没有它 Mapper 无法注入到 Servicejackson-databind 负责把 Controller 返回的对象序列化成 JSONcommons-fileupload 是小程序上传房源图片时后端接收文件用的。4.2 房源发布接口与小程序的调用三层实现 wx.request 对接以“发布房源”这个接口为例把三层代码完整走一遍。先是 Controller 层Controller RequestMapping(/house) public class HouseController { Autowired private HouseService houseService; ResponseBody RequestMapping(value /publish, method RequestMethod.POST) public Result publish(RequestBody House house, HttpSession session) { Integer userId (Integer) session.getAttribute(loginUserId); if (userId null) { return Result.error(401, 请先登录); } house.setOwnerId(userId); house.setStatus(0); // 新发布房源默认待审核 houseService.insert(house); return Result.ok(); } }Service 层和 Mapper 层就简洁得多Service public class HouseService { Autowired private HouseMapper houseMapper; public void insert(House house) { houseMapper.insert(house); } }insert idinsert parameterTypecom.example.rent.entity.House useGeneratedKeystrue keyPropertyid INSERT INTO house (title, address, price, area, description, cover, owner_id, status, create_time) VALUES (#{title}, #{address}, #{price}, #{area}, #{description}, #{cover}, #{ownerId}, #{status}, NOW()) /insertController 里有两个关键做法一是用 RequestBody 直接接收 JSON 对象后端实体类 House 的字段名需要和小程序端传的参数名完全一致二是 ownerId 和 status 不让前端传服务端从 session 里取登录用户并强制赋默认值。这样即使有人绕过程序直接调接口伪造数据也改不了归属和审核状态。小程序端对应的调用代码是wx.request({ url: http://localhost:8080/rent/house/publish, method: POST, header: { Content-Type: application/json }, data: { title: this.data.form.title, address: this.data.form.address, price: this.data.form.price, area: this.data.form.area, description: this.data.form.description, cover: this.data.coverUrl }, success(res) { if (res.data.code 200) { wx.showToast({ title: 发布成功等待审核 }) wx.navigateBack() } else { wx.showToast({ title: res.data.msg, icon: none }) } } })这里最容易犯的错是漏掉 header 里的Content-Type: application/json。如果不设置小程序默认以表单格式提交后端 RequestBody 接到的直接是空对象。4.3 登录与 token 鉴权拦截器里的三个配置细节微信小程序的登录流程是wx.login 拿到临时 code传给后端后端拿 code 向微信接口换 openid再用 openid 去 user 表查用户查到就返回登录成功查不到就自动注册。业务接口里不直接传 userId而是通过登录态识别用户身份。SSM 里最常用的实现是拦截器。HandlerInterceptor 的代码不复杂Component public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session request.getSession(); Object userId session.getAttribute(loginUserId); if (userId null) { response.setStatus(401); response.setContentType(application/json;charsetUTF-8); response.getWriter().write({\code\:401,\msg\:\未登录\}); return false; } return true; } }配置放行规则时注意三个细节。第一登录接口、房源列表、房源详情这类“游客也能看”的接口要显式排除放行路径配置在 spring-mvc.xml 里mvc:interceptors mvc:interceptor mvc:mapping path/**/ mvc:exclude-mapping path/user/login/ mvc:exclude-mapping path/house/list/ mvc:exclude-mapping path/house/detail/ mvc:exclude-mapping path/upload/**/ /mvc:interceptor /mvc:interceptors第二上传图片的路径必须放行不然小程序加载图片时也会被拦截返回 401。第三前端要处理 401 状态码在 wx.request 的 complete 回调里判断 statusCode统一跳转到登录页。这个拦截器只需要拦截“写操作”读操作尽量放开能少踩很多联调的坑。5. SSM 与小程序联调踩坑记录五个真坑的排查过程前后端联调是毕设里最耗时间的阶段很多问题看起来玄学其实原因都很具体。下面这五条是我实际排查中遇到最多的按“现象 → 原因 → 解决”记录清楚。5.1 真机调试请求失败域名校验与开发者工具设置现象在微信开发者工具里模拟器请求接口正常换到真机预览所有 wx.request 直接走 fail 回调报错信息是request:fail url not in domain list。原因微信对小程序请求的域名有白名单校验。模拟器可以勾选“不校验合法域名”但真机上这个选项不生效请求必须是配置过合法域名的地址。解决开发联调阶段打开微信开发者工具的“详情 → 本地设置”勾选“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”。如果要在真机上调试且后端跑在本地电脑需要把本机接口映射成一个临时 HTTPS 域名再把域名填到小程序后台的 request 合法域名里。注意排查时先确认手机和电脑在同一个局域网内。5.2 图片上传成功但列表不显示相对路径与回显地址现象上传图片接口返回 success数据库里也有记录但小程序端image组件加载时一片空白。原因后端保存图片时存的是相对路径比如/upload/1.jpg。在小程序里这个路径会被解析成当前小程序包的路径而不是后端服务器的地址自然加载不出来。解决后端返回图片地址时拼成完整 URL。用 request 对象动态拼接协议、域名和端口不要写死这样部署时换域名不用改代码String baseUrl request.getScheme() :// request.getServerName() : request.getServerPort(); String fullUrl baseUrl /upload/1.jpg;5.3 JSON 返回中文乱码SpringMVC 的消息转换器配置现象后端接口返回的 JSON 里所有中文都变成???或者类似ä¸Â的乱码英文数字正常。原因SpringMVC 默认的字符串消息转换器使用 ISO-8859-1 编码不满足中文字符集要求。解决在 spring-mvc.xml 里显式配置消息转换器强制使用 UTF-8mvc:annotation-driven mvc:message-converters bean classorg.springframework.http.converter.StringHttpMessageConverter property namesupportedMediaTypes list valuetext/plain;charsetUTF-8/value valueapplication/json;charsetUTF-8/value /list /property /bean /mvc:message-converters /mvc:annotation-driven5.4 数据库时间差 8 小时时区与 JDBC 连接串现象程序里new Date()插入数据库后再查出来发现少了 8 个小时比如插入时间是 20:00查出来是 12:00。原因MySQL 服务端和 JDBC 驱动之间的时区不一致。mysql-connector-java 8.x 对时区敏感连接串里没有指定时区时默认按服务器本地时区处理导致时间和系统时间偏差。解决在 jdbc.properties 的连接串里显式指定时区和编码jdbc.urljdbc:mysql://localhost:3306/rent?useSSLfalseuseUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai5.5 MyBatis 条件查询翻车动态 SQL 的 if 判空细节现象前端传 status0 查询待审核房源结果后台把 status1 的记录也查出来了条件看起来没生效。原因XML 里写了if teststatus ! null and status ! status 是 Integer 类型。在 MyBatis 的 OGNL 表达式中Integer 类型的 0 和空字符串比较时会被转成 false导致条件被跳过退化成全表查询。解决Integer 类型的判空只要判断 null 就行不要加! if teststatus ! null AND status #{status} /if这条很隐蔽如果没遇到可能第二天才反应过来。建议所有 Mapper 里的 if 判空都遵循一个原则String 类型才判空串数值类型只判 null。6. 项目验证与交付技巧让演示和论文都立得住把功能跑通只是第一步演示效果和论文质量决定了最终评分。很多同学功能全做了演示时却因为数据太少、页面空白显得项目没完成。提前准备种子数据很关键。6.1 数据初始化演示前备好覆盖状态的种子数据演示前尽量在数据库里准备充足的模拟数据3 个房东用户、2 个租客用户、8 套房源覆盖待审核、上架中、已出租、已下架四种状态、5 条订单覆盖待确认、已确认、已签合同、已结束、已取消五种状态、2 份合同。每种状态都让列表页有内容可展示避免演示到某一步时出现空列表。订单种子数据可以这样批量插入INSERT INTO rental_order (order_no, renter_id, house_id, landlord_id, status, create_time) VALUES (R202507010001, 2, 1, 3, 0, NOW()), (R202507010002, 5, 2, 3, 1, NOW()), (R202507010003, 2, 4, 8, 2, NOW()), (R202507010004, 5, 6, 3, 3, NOW()), (R202507010005, 2, 7, 8, 4, NOW());订单号规则用日期加序号论文里可以顺带说明这个生成策略。6.2 演示路径设计一条完整的业务闭环演示时不要跳着点功能按真实业务流程走一条闭环最好租客登录 → 搜索筛选房源 → 查看详情 → 收藏 → 预约看房 → 房东确认订单 → 管理员生成合同 → 合同列表查看。每一步对应一个页面、一个接口、一张表的数据变化答辩时老师顺着你的演示就能把系统结构看明白。我习惯在演示前把数据库重置一遍所有状态归零然后从管理员登录开始审核房源、确认订单、生成合同再换租客视角走一遍流程。这样做的好处是每个操作都有实际的数据变化反馈不会出现“这里我已经提前点过了”这种尴尬。6.3 交付物整理源码、数据库脚本和演示视频的对应关系交付时把源码按后端前端分目录整理数据库脚本单独导出一份纯 SQL 文件。演示视频录制时先展示数据库里数据的变化再录前端操作这样看视频的人能直观看到数据从无到有的过程。论文里的 ER 图直接对应第 3 章的表关系用例图对应第 2 章的角色功能表时序图就用 4.2 的房源发布流程画。框架定了写起来会顺畅很多。我的习惯是交付前把整个流程完整走三遍第一遍验证功能第二遍验证数据准确性第三遍模拟答辩边演示边讲解。走过三遍之后源码和数据库脚本的完整性基本就有保障了。这套流程做完房屋租赁管理小程序从开发到交付的每个环节都立得住希望帮到你。本文还有配套的精品资源点击获取