简介:这是一份面向计算机相关专业毕业设计的房屋租赁管理小程序完整项目资料,基于微信小程序+SSM+MySql开发,涵盖前后端完整源码、数据库脚本、毕业论文及操作演示视频。系统按角色划分功能:管理员可管理用户、中介、房屋信息、租房订单与账单等;中介负责房源与订单;用户可浏览房屋、租房并查看账单,整体具备较好的实用性与展示价值。压缩包共891个文件,约59.15MB,主要包括Java后台源码、Vue管理端页面、微信小程序前端(wxml/wxss/js/json)、SQL数据库脚本、论文文档及mp4演示视频;png/svg等图片资源用于界面素材与说明文档,目录结构清晰,便于二次开发与答辩讲解。目前已有200人浏览学习,适合毕业设计选题、课程项目复现,或希望掌握SSM+小程序整合开发流程的学习者参考。
1. 房屋租赁管理小程序:毕业设计做到哪一步才算能交付
先说结论:以房屋租赁管理小程序作为毕业设计,真正的难点不在“写代码”,而在把微信小程序端、SSM 后端、MySQL 数据库这条完整链路跑通,并能在答辩现场用五分钟把业务讲清楚。我见过太多同学拿着源码在本地能启动,换一台电脑、换一个微信开发者工具版本就各种翻车。这个题目的聪明之处在于,它同时覆盖移动端、后端、数据库三块内容,工作量可展示、效果直观,非常适合需要结合移动端场景的毕业设计。这篇文章会按一个可复现的毕业设计方案,拆解技术选型、数据库设计、SSM 后端实现、小程序端联调,最后落到部署和答辩演示的避坑技巧,适合正在做或打算选这个题目的同学。
2. 技术选型与工程结构:为什么是微信小程序 + SSM + MySQL
2.1 技术选型:毕业设计里最“稳”的组合,没有之一
这个题目选微信小程序做前端,本质上是想避开安卓和 iOS 双端开发的成本。微信小程序自带用户体系和登录能力,不需要自己搭一套账号系统,租客、房东、管理员三类角色都能在同一个小程序里承载。后端的 SSM 是 Spring + SpringMVC + MyBatis 的组合,虽然现在新项目多用 Spring Boot,但 SSM 在课程设计、毕业设计里仍然有大量现成代码和文档可以参考,遇到问题搜索引擎随便一翻就有答案。MySQL 做数据库是因为它免费、跨平台、GUI 工具多,而且 8.0 版本对 JSON、窗口函数的支持足够应付一个租赁管理系统的全部需求。
| 技术组件 | 本题中的角色 | 选择理由 | 替代方案 |
|---|---|---|---|
| 微信小程序 | 租客/房东端界面 | 免安装、自带登录授权、演示直观 | H5 网页(但少了移动端亮点) |
| SpringMVC | 后端接口层 | 请求路由、参数绑定、JSON 返回 | Spring Boot(更省配置) |
| MyBatis | 数据持久层 | SQL 可控、动态 SQL 方便 | JPA(但 SQL 调优不够直观) |
| MySQL | 数据存储 | 成熟、开源、生态大 | PostgreSQL(功能更强但文档少) |
这套组合的劣势也明显:SSM 的 XML 配置量比较大,第一次搭容易漏配导致启动失败。所以工程结构一定要一开始就清晰,避免后面排错无从下手。
2.2 工程结构:一个能满足前后端联调的目录划分
我一般会把项目按三个模块组织:backend放 SSM 后端,miniapp放微信小程序,sql放数据库脚本。docs放毕业论文和答辩 PPT。第一次上手时建议完全照这个结构来,不要自己发明目录,否则后面写论文梳理模块会花两倍时间。
rent-miniapp/ ├── sql/ # 数据库初始化脚本与测试数据 ├── backend/ # SSM 后端工程 │ ├── pom.xml │ └── src/main/ │ ├── java/com/example/rent/ │ │ ├── controller/ # SpringMVC 接口层 │ │ ├── service/ # 业务逻辑层 │ │ ├── dao/ # MyBatis Mapper 接口 │ │ ├── entity/ # 与表结构对应的实体类 │ │ └── common/ # 统一返回体、异常处理、工具类 │ └── resources/ │ ├── mapper/ # MyBatis 的 XML SQL 映射 │ ├── spring/ # Spring、MyBatis 配置 │ ├── springmvc/ # SpringMVC 配置 │ └── jdbc.properties ├── miniapp/ # 微信小程序 │ ├── pages/ │ │ ├── index/ # 房源列表 │ │ ├── house/ # 房源详情与签约入口 │ │ ├── lease/ # 我的租约 │ │ ├── user/ # 个人中心 / 房东后台 │ │ └── login/ # 登录授权页 │ ├── utils/request.js # wx.request 封装 │ └── app.js └── docs/ # 毕业论文、答辩讲稿、演示视频脚本com.example.rent是包名占位,实际创建时改成你们学校要求的包名格式即可。注意mapper目录下的 XML 文件路径必须与 Spring 配置里的mapperLocations一致,否则启动就会报找不到statement。
2.3 数据库连接与 ORM 选型:先避开两个经典坑
第一坑是 MySQL 8.0 默认的认证插件是caching_sha2_password,而老项目的驱动还停留在com.mysql.jdbc.Driver,会导致登录数据库报认证失败。第二个坑是时区问题,连接串不写serverTimezone=Asia/Shanghai,查询出来的时间字段会差 8 小时。
jdbc.driver=com.mysql.cj.jdbc.Driver jdbc.url=jdbc:mysql://127.0.0.1:3306/rent_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai jdbc.username=root jdbc.password=你的数据库密码驱动必须用com.mysql.cj.jdbc.Driver,这是 MySQL 8.0 之后的驱动全类名。useUnicode=true&characterEncoding=utf8保证中文不乱码;serverTimezone=Asia/Shanghai解决时区差。如果你还在用 5.7,驱动名可以用旧版,但建议统一切到 8.0,教程和社区遇到问题更好查。实体类和表字段的映射,建议开启 MyBatis 的驼峰自动映射,就不用为每个字段写繁琐的resultMap。
3. 数据库先行:房屋租赁核心表设计与关键 SQL
3.1 业务梳理:先定状态机,再建表
房屋租赁管理的核心就两个对象:房源和租约。房源有“下架、上架、已租出”三种状态;租约有“申请中、生效中、已到期、已退租、已拒绝”五种状态。这两组状态决定了所有接口的行为逻辑。比如房源上架时不能有人正在申请租约,租约生效时房源必须从“上架”变成“已租出”。很多毕业设计把表建了、CRUD 写完,但状态之间没有联动,答辩时被老师一问就露馅。建表之前先把状态流转图画在草稿纸上,比直接写 SQL 更重要。这套业务的表不需要太多,用户表、房源表、租约表、账单表、收藏表五张就够,其中前四张是核心。
3.2 用户表与房源表:字段、索引和约束的设置
用户表不用存密码,因为微信小程序登录走的是wx.login拿 code,后端再向微信接口换 openid。表里只需要 openid 作为唯一标识,再冗余昵称、手机号和角色字段。角色用TINYINT存数字,不要用字符串,省空间且查询快。
CREATE TABLE user ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '用户ID', openid VARCHAR(128) NOT NULL UNIQUE COMMENT '微信openid', nickname VARCHAR(64) DEFAULT '' COMMENT '昵称', phone VARCHAR(20) DEFAULT '' COMMENT '联系电话', role TINYINT NOT NULL DEFAULT 1 COMMENT '角色: 1租客 2房东 3管理员', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';openid设置UNIQUE是必须的,同一个人反复登录不能产生多条记录。update_time使用ON UPDATE CURRENT_TIMESTAMP可以让每次更新自动刷新时间。utf8mb4一定要用,utf8mb3存不了 Emoji,也会让少量生僻字乱码。房源表要区分“面积”和“租金”用十进制,不要用FLOAT,避免浮点误差。
CREATE TABLE house ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '房源ID', landlord_id BIGINT NOT NULL COMMENT '房东用户ID', title VARCHAR(100) NOT NULL COMMENT '房源标题', address VARCHAR(200) NOT NULL COMMENT '地址', area DECIMAL(6,2) DEFAULT 0 COMMENT '面积(㎡)', rent DECIMAL(10,2) NOT NULL COMMENT '月租金(元)', deposit DECIMAL(10,2) DEFAULT 0 COMMENT '押金(元)', status TINYINT NOT NULL DEFAULT 0 COMMENT '状态: 0下架 1上架 2已租出', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, KEY idx_status (status), KEY idx_landlord (landlord_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房源表';status字段加索引是有意义的,因为房源列表页最常用的查询就是WHERE status = 1。landlord_id也加索引,因为“我发布的房源”是房东端的核心列表。这里故意不建外键约束,而是用逻辑外键。毕业设计阶段经常要批量造测试数据,物理外键会在删除数据时带来不必要的麻烦,但在论文里要把逻辑外键关系画清楚。
3.3 租约表与初始化数据:给答辩准备一套“会说话”的数据
租约表是整个系统的业务核心,也是答辩时最有的聊的一张表。它把房东、租客、房源、租金、起止日期、状态串在一起。注意start_date和end_date用DATE类型,不要用DATETIME,租约按天计算,带时分秒没有意义,反而增加校验成本。
CREATE TABLE lease ( id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '租约ID', house_id BIGINT NOT NULL COMMENT '房源ID', tenant_id BIGINT NOT NULL COMMENT '租客用户ID', start_date DATE NOT NULL COMMENT '起租日期', end_date DATE NOT NULL COMMENT '到期日期', monthly_rent DECIMAL(10,2) NOT NULL COMMENT '月租金(元)', deposit DECIMAL(10,2) NOT NULL COMMENT '押金(元)', status TINYINT NOT NULL DEFAULT 0 COMMENT '状态: 0申请中 1生效中 2已到期 3已退租 4已拒绝', create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_house_status (house_id, status), KEY idx_tenant (tenant_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='租约表';idx_house_status是联合索引,因为最常见的查询是查某套房源当前生效的租约。初始化数据不要只造几条,我给学生演示时一般造 12 套左右的房源,租约覆盖“申请中、生效中、已到期、已退租”四种状态。这样演示“房东处理申请”“租客查看账单”“退租后房源自动恢复上架”时都有现成数据可点,不用现场输入。
INSERT INTO user (id, openid, nickname, phone, role) VALUES (1, 'mock_openid_1', '张三', '13800000001', 2), (2, 'mock_openid_2', '李四', '13800000002', 1);造测试数据时注意:模拟的 openid 随便写一串不重复的字符串就行,只要和后端登录逻辑的测试账号保持一致即可。
4. SSM 后端实现:租约状态流转是核心链路
4.1 统一返回体与登录接口:从前端 code 换 openid 的完整链路
后端接口要做成什么格式?我强烈建议所有接口统一返回同一个结构,code表示业务状态,msg给提示,data放数据。前端拿到后统一判断code === 200,而不是去猜不同的字段名。
public class Result<T> { private Integer code; private String msg; private T data; public static <T> Result<T> success(T data) { Result<T> r = new Result<>(); r.code = 200; r.msg = "ok"; r.data = data; return r; } public static <T> Result<T> error(Integer code, String msg) { Result<T> r = new Result<>(); r.code = code; r.msg = msg; return r; } // getter/setter 省略 }Result泛型类可以让列表、对象、空值都走同一套返回逻辑。前端utils/request.js里只需要对code做一次判断就能决定是提示还是静默处理。登录接口是前端传code过来,后端拿着code去微信的code2Session接口换openid。这里同学们最容易踩的坑是:本地调试时没有真实的小程序 AppID,导致换不到 openid。解决方法是后端的登录接口做一个“模拟模式”,本地联调时直接拿前端传过来的一个测试标识当 openid。
@RestController @RequestMapping("/api/user") public class UserController { @Autowired private UserService userService; @PostMapping("/login") public Result<User> login(@RequestBody LoginDTO dto) { // 本地联调阶段: 不调用微信接口, 直接把测试标识当 openid String openid = userService.resolveOpenid(dto.getCode()); User user = userService.getOrCreateByOpenid(openid); return Result.success(user); } }resolveOpenid方法里用if ("test".equals(code))区分模拟环境和真实环境。真实环境用 HttpClient 调微信接口,模拟环境直接返回"mock_openid_" + code。这个开关一定要留到答辩前,因为答辩现场的投影机器没有微信开发者工具的登录态,你永远不知道什么时候网络会出问题。
4.2 房源发布与租约创建:Service 层业务规则
租约创建不是简单的 INSERT,它要连带检查房源状态。如果房源已经下架或已租出,就不能再接新的租约。这个逻辑必须放在 Service 层,而不是写在前端。Service 层的方法加@Transactional事务注解,保证“检查状态 + 创建租约 + 更新房源状态”这三步要么全成功,要么全失败。
@Service public class LeaseServiceImpl implements LeaseService { @Autowired private HouseMapper houseMapper; @Autowired private LeaseMapper leaseMapper; @Transactional(rollbackFor = Exception.class) public void applyLease(LeaseApplyDTO dto) { House house = houseMapper.selectById(dto.getHouseId()); if (house == null || house.getStatus() != 1) { throw new BizException("房源不存在或不可租"); } // 同一个租客同一房源不能重复申请 int count = leaseMapper.countActiveByHouseAndTenant(dto.getHouseId(), dto.getTenantId()); if (count > 0) { throw new BizException("已有进行中的租约申请"); } Lease lease = new Lease(); lease.setHouseId(dto.getHouseId()); lease.setTenantId(dto.getTenantId()); lease.setStartDate(dto.getStartDate()); lease.setEndDate(dto.getEndDate()); lease.setMonthlyRent(house.getRent()); lease.setDeposit(house.getDeposit()); lease.setStatus(0); // 申请中 leaseMapper.insert(lease); } }applyLease里先查房源,再查重复申请,最后插入租约。这三步缺一不可,尤其“重复申请”的校验在正式项目里是高频踩坑点。房东端处理申请是另一个事务方法,房东点击“同意”后,租约状态从“申请中”变成“生效中”,同时房源状态从“上架”变成“已租出”。
4.3 状态流转与并发控制:为什么不能直接 UPDATE
房源被租出去的那一刻,如果两个租客同时提交申请,后端可能同时读到房源“上架”,同时写出两条“生效中”的租约。这个问题在答辩时非常容易被老师追问。解决办法是状态流转使用条件 UPDATE,而不是先 SELECT 判断再 UPDATE。
<!-- HouseMapper.xml --> <update id="updateStatusWithCondition"> UPDATE house SET status = #{newStatus}, update_time = NOW() WHERE id = #{id} AND status = #{oldStatus} </update>int rows = houseMapper.updateStatusWithCondition(houseId, 2, 1); if (rows == 0) { throw new BizException("房源已被人租走,请刷新后再试"); }updateStatusWithCondition的WHERE里带上了旧状态,UPDATE 的返回值是受影响行数。如果为 0,说明执行 UPDATE 之前房源状态已经不是“上架”,就说明发生了并发竞争。这种写法叫乐观锁,毕业设计里能写出这一步,说明你真正理解了并发控制,而不是只会 CRUD。
4.4 MyBatis 配置与 XML 映射:让 SQL 与 Java 解耦
MyBatis 的 XML 映射文件是 SSM 项目里代码量最大、也最容易出错的部分。建议所有 mapper XML 放在resources/mapper目录下,namespace必须与 DAO 接口全限定名一致。
<!-- resources/mapper/LeaseMapper.xml --> <mapper namespace="com.example.rent.dao.LeaseMapper"> <select id="selectListByTenant" resultType="com.example.rent.entity.Lease"> SELECT l.*, h.title AS houseTitle, h.address AS houseAddress FROM lease l LEFT JOIN house h ON l.house_id = h.id WHERE l.tenant_id = #{tenantId} ORDER BY l.create_time DESC </select> </mapper>selectListByTenant用LEFT JOIN把房源的标题和地址冗余到租约列表结果里。#{tenantId}是预编译参数,能防 SQL 注入。注意resultType写实体类全限定名,实体类里要额外加houseTitle、houseAddress这两个非表字段,否则 MyBatis 映射会直接忽略,页面上的房源信息就是空的。
5. 避坑与排查:把“源码能跑”变成“换台电脑也能跑”
5.1 微信开发者工具报错“不在以下 request 合法域名列表中”
现象:小程序端请求后端接口,控制台报错request:fail url not in domain list,页面数据死活加载不出来。
原因:微信小程序对请求域名有白名单限制,本地开发时如果直接请求http://127.0.0.1:8080,不在合法域名列表中就会被拦截。很多同学不知道这个限制,以为是代码问题,折腾半天。
解决:开发调试阶段,在微信开发者工具右上角“详情 - 本地设置”里勾选“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”。这个选项只对开发工具有效,真机预览时如果还要调接口,可以临时在预览页面勾选“预览时校验合法域名”并关闭,或者把后端接口配成 HTTPS 并加到小程序管理后台的白名单里。毕业设计答辩通常用开发者工具的模拟器,勾选后即可正常演示。
5.2 租约状态更新了,小程序端列表不刷新
现象:房东在后台把租约状态从“申请中”改成“生效中”,租客端返回列表页再进来,看到的还是“申请中”。
原因:小程序页面默认只在onLoad里请求一次数据,从列表页跳详情页再返回时不会重新触发onLoad,而是触发onShow。接口调用函数写在onLoad里,返回页面时自然不会重新拉数据。
解决:把拉取列表的函数抽出来,在onShow里调用。注意onShow会比onLoad更早触发一次,函数内部要加防重复请求的判断。
Page({ onShow() { this.fetchLeaseList(); }, async fetchLeaseList() { const res = await request({ url: '/api/lease/list', method: 'GET' }); if (res.code === 200) { this.setData({ leaseList: res.data }); } } });onShow里重新请求,保证从详情页返回、从支付页返回时列表都是最新状态。如果页面有分页,记住把page重置为 1,否则翻页数据会重叠。
5.3 时间字段显示差 8 小时,或者序列化成一串数字
现象:租约的create_time在小程序端显示成 16:00,实际是 00:00 创建的;更诡异的是有时数据库里是2025-06-01 00:00:00,接口返回却变成1748716800000。
原因:差 8 小时是 MySQL 连接串没配serverTimezone=Asia/Shanghai,而驱动用了默认的 UTC 时区。变成一串数字是 JSON 序列化工具把Date类型转成了时间戳毫秒值,前端没有做格式化。
解决:连接串加上serverTimezone=Asia/Shanghai。JSON 序列化配置全局日期格式,SpringMVC 的MappingJackson2HttpMessageConverter里设置objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))。如果用的是@JsonFormat注解,记得timezone = "GMT+8"也要显式声明,否则注解只管格式不管时区。
5.4 换一台电脑部署时数据库连不上
现象:整套源码从 U 盘拷到另外一台电脑,Tomcat 正常启动,但一登录就报Access denied for user或Communications link failure。
原因:jdbc.properties里写的是原机器的数据库 IP、端口、账号密码,换机器后这些全变了。另外如果原机器用的 MySQL 5.7,新机器装的是 MySQL 8.0,而驱动还是老版本的com.mysql.jdbc.Driver,就会报认证插件错误。
解决:换机器部署时,第一步改jdbc.properties里的jdbc.url、jdbc.username、jdbc.password,把你当前机器的实际值填进去。第二步确认驱动 jar 是mysql-connector-java8.0 以上版本,jdbc.driver改成com.mysql.cj.jdbc.Driver。第三步检查 MySQL 服务确实启动了。这三步按顺序做,90% 的“换电脑就跑不起来”问题能解决。
5.5 Tomcat 启动成功,但所有接口返回 404
现象:后端启动日志没有报错,浏览器访问http://localhost:8080/api/house/list却返回 404,控制台也没有任何映射日志。
原因:SpringMVC 的DispatcherServlet没有拦截到/api/*路径。常见错误是web.xml里<url-pattern>误配成了/*,或者项目没有正确部署到 Tomcat 的webapps目录,导致访问的是 Tomcat 自带的 ROOT 应用。
解决:检查web.xml中DispatcherServlet的<url-pattern>是否配置为/,这样才会进入 SpringMVC 处理。还要确认部署方式,用 IDEA 的 Tomcat 集成部署时,看 “Deployment” 里的 Application context 是否设成了/,如果是/rent,那访问路径就应该是http://localhost:8080/rent/api/house/list。
6. 交付前最后三件事:造数据、定演示动线、准备被追问
6.1 给演示准备一份“有故事”的测试数据
数据不要只造几条“成功”的,要覆盖业务流程的各种分支。我会在数据库里放 12 套房源、6 个用户、10 条租约,其中至少包含一条“申请中”、一条“生效中”、一条“已到期”、一条“已退租”。这样演示时每个按钮点了都有对应反馈,老师不会看到你现场临时填表。
6.2 演示动线:从登录到退租,一条线走完
演示建议按这个顺序走:先打开数据库看一眼核心表数据量;再打开后端接口,用浏览器访问房源列表接口;然后切换到小程序端登录、浏览房源、提交租约申请;回到后端接口确认租约已创建;再到数据库执行一条查询,展示租约表状态变化。这条动线能把“前后端 + 数据库 + 业务逻辑”一次性讲清楚,比只在小程序里点来点去有力得多。
6.3 答辩最可能被追问的三个点
第一,为什么用 SSM 不用 Spring Boot?答:题目要求基于 SSM 理解三层架构,SpringMVC 做控制层、MyBatis 做持久层的分工更明确,适合展示基础功底。第二,两个租客同时申请同一套房怎么办?答:乐观锁条件更新,受影响行数为 0 时拒绝后到的请求。第三,openid 是什么?答:微信用户在小程序内的唯一标识,后端通过 code 调用微信接口获取,用于区分用户身份。这三个点准备充分,答辩环节基本不会卡壳。
最后说个我的习惯:每次改完数据库表结构或者后端接口,我都会先在微信开发者工具里完整跑一遍核心流程再提交记录,避免遗留“能编译但跑不通”的代码。做毕业设计最怕的不是功能少,而是自己都说不清每一段代码在干什么。把状态流转、并发控制、时区处理这几个细节想明白,这个题目的收获会比想象中大得多。希望这篇笔记能帮到你,祝你顺利通过答辩。
本文还有配套的精品资源,点击获取