资讯动态

无中介租房系统设计与实现:微信小程序+Spring Boot全栈实战

发布时间:2026/9/11 16:52:43 来源:尧图企业网站定制
简介面向计算机类专业毕业设计/课程设计场景这套基于微信小程序与Java后端的无中介租房系统覆盖管理员、房东、租客三类角色实现房屋信息发布、租赁合同、租金管理、交流发帖等核心功能可用于快速搭建完整项目并理解前后端分离开发流程。压缩包内含1239个文件共41.34MB主要类型包括Java后端源码、Vue后台管理页面、微信小程序wxml/wxss/json文件、数据库SQL脚本以及png/jpg界面素材和演示视频目录划分清晰便于按模块查阅。已有347人学习下载。资源不仅提供可直接运行的完整源码还附带操作演示视频、说明文档与数据库备份能帮助毕业设计者缩短环境搭建和编码调试时间适合需要参考完整项目结构、学习小程序端与Java后端交互的读者。1. 无中介租房系统到底在做什么拿到这个项目标题先别急着看代码。一套无中介租房系统的核心不是“小程序界面做得像贝壳”而是房东、租客、订单这三条线怎么在数据库里闭合。标题里“无中介”三个字意味着业务上没有经纪人角色没有带看流程没有佣金计算取而代之的是房东直发房源、租客自主筛选、线上预约与合同签订。比较反直觉的一点是这个项目最简单的部分是 CRUD最难的部分是订单状态。房源可以随便增删改但“待看房、已看房、已签约、已退租”之间的流转一旦设计不严租客和房东就会在履约环节产生纠纷这在毕业设计答辩里也是最容易被追问的地方。全文会顺着“需求拆解 → 数据库建模 → 后端接口 → 小程序端对接 → 交付打磨”这条路径展开重点落在转账逻辑、状态约束和并发控制上。适合准备 Java 全栈岗位面试的人也适合想把手上的前后端分离项目从“能跑”提升到“能讲”的人。2. 需求拆解与技术选型理由2.1 无中介租房系统的功能边界毕业设计的功能清单往往会被学生写到十五项以上但实际评审时面试官关心的核心只有三件事房源怎么发布与检索、租客怎么预约看房、双方怎么完成签约与退租。围绕这三件事再把用户角色拆开系统的功能就会非常聚焦租客端需要注册登录、浏览房源、收藏房源、提交看房申请、在线签约房东端需要发布房源、管理房源状态、查看申请列表、同意或拒绝看房、发起合同系统后台则需要用户管理、房源审核和基础的运营数据统计。“无中介”的字面要求体现在角色模型上——系统里不应该有“经纪人”这张表也不需要带看排期表。对比市面上的商业租房平台会发现它们的最大成本在线下服务而毕业设计要想在有限代码量里做出亮点正确的做法是把服务环节全部“线上化”看房申请、确认时间、签约、退租提醒全部通过订单状态驱动。这套设计思路可以在答辩时直接说成“用状态机收敛了房东与租客之间的履约路径”比堆砌十几个模块听起来扎实得多。2.2 微信小程序 Java 后端搭配的合理性微信小程序在这个项目里的角色是 C 端入口。选择它不是因为“微信生态热门”而是因为它解决了两个其他端很难解决的问题微信授权登录免去了用户名密码体系微信支付可以支撑在线押金与租金流转。换句话说小程序端天然具备身份与支付能力后端只需要专注业务逻辑这一点在校验单体架构的 Java 后端时是很大的加分项。后端的技术栈现在的主流方案是 Spring Boot MyBatis-Plus MySQL缩略地说就是一顿标准的 Java 后端组合。如果有同学的课设要求里带“前后端分离项目实战”这个说法那用这种组合最省心。管理后台可以选 ruoyi 框架也可以自己写一个轻量的 Vue 管理端但如果说目标是快速通过验收建议后端直接 Spring Boot 单服务承载小程序 API 与后台 API避免在部署环境里再搭一套网关和注册中心。把精力留给房源搜索、订单并发和合同生成远比空搭微服务架构更有性价比。2.3 工程骨架与目录规划后端建议按模块分包而不是按“Controller/Service/Mapper”三层贯穿到底。一个适合毕业设计的包结构能让你在写代码和论文时都更省力同时比较容易应对“请介绍一下你的项目架构”这类面试问题。常见做法是把业务模块作为顶层目录com.example.rental ├── common // 通用返回体、异常处理、工具类 ├── config // 微信配置、拦截器、跨域配置 ├── controller // 小程序端 API │ ├── HouseController.java │ ├── OrderController.java │ └── WxAuthController.java ├── entity // 数据库实体 ├── mapper // MyBatis-Plus Mapper ├── service // 业务逻辑层 ├── vo // 视图对象返回给前端的数据结构 └── utils // 工具类如WxUtil、DistanceUtil小程序的目录规划也一样原生开发时按页面职责分目录/pages ├── index // 首页与房源列表 ├── houseDetail // 房源详情 ├── publish // 房东发布房源 ├── order // 看房申请与订单列表 ├── contract // 合同签署与查看 └── my // 个人中心后端可以做成一个标准的 Spring Boot 应用内置 Tomcat打包成 jar 后部署管理后台若时间紧张可以不做用数据库手工加字段代替。但小程序端是必须的因为“基于微信小程序”是这个题目里明确限定的交付形态这一部分不要用 H5 套壳糊弄小程序原生组件编译后的运行体验和审核通过率都明显更可控。3. 数据库设计与核心表结构3.1 用户、房源、订单三张核心表数据库设计是毕业设计里最容易拉开档次的部分。面试官翻开你的.sql文件先看三样东西字段类型是否合理、主键策略是否统一、状态字段是否用了tinyint而不是varchar存中文。以下给出一个可以直接落地的核心表结构。先看用户表CREATE TABLE user ( id bigint(20) NOT NULL AUTO_INCREMENT, openid varchar(64) NOT NULL COMMENT 微信openid唯一, nickname varchar(50) DEFAULT COMMENT 昵称, avatar varchar(255) DEFAULT COMMENT 头像url, phone varchar(20) DEFAULT NULL COMMENT 手机号, role tinyint(1) NOT NULL DEFAULT 0 COMMENT 0租客 1房东, real_name varchar(30) DEFAULT NULL COMMENT 真实姓名, id_card varchar(18) DEFAULT NULL COMMENT 身份证号签约用, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;逻辑说明openid是微信体系下识别用户的唯一标识后端拿到微信登录凭证后换取不需要用户注册。role用tinyint(1)区分租客与房东一个用户在一个系统里只承担一种角色如果后续想扩展“既是房东又是租客”的场景可以拆成user_role关联表但毕业设计保持单字段即够。id_card只有在签约时才需要所以允许为空。房源表要承载的是搜索与展示的核心字段。注意租金用DECIMAL(10,2)不要用DOUBLE——金额用浮点数存储会出现 0.1 0.2 不等于 0.3 的问题这在涉及押金退还时是小概率但致命的问题。看房时间和起租日期用日期时间类型不要用字符串否则范围查询会非常痛苦。CREATE TABLE house ( id bigint(20) NOT NULL AUTO_INCREMENT, landlord_id bigint(20) NOT NULL COMMENT 房东用户id, title varchar(100) NOT NULL COMMENT 房源标题, cover varchar(500) DEFAULT COMMENT 封面图, images text COMMENT 轮播图json数组, province varchar(30) DEFAULT NULL, city varchar(30) DEFAULT NULL, district varchar(30) DEFAULT NULL, address varchar(255) DEFAULT NULL COMMENT 详细地址, latitude decimal(10,6) DEFAULT NULL COMMENT 纬度, longitude decimal(10,6) DEFAULT NULL COMMENT 经度, rent decimal(10,2) NOT NULL COMMENT 月租金, deposit decimal(10,2) DEFAULT NULL COMMENT 押金, area int(11) DEFAULT NULL COMMENT 面积平方米, bedrooms tinyint(2) DEFAULT NULL COMMENT 几室, hall tinyint(2) DEFAULT NULL COMMENT 几厅, status tinyint(1) NOT NULL DEFAULT 0 COMMENT 0未出租 1已出租 2下架 3待审核, description text COMMENT 房源描述, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_city_status (city,status), KEY idx_landlord (landlord_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT房源表;逻辑说明images用text存 JSON 数组字符串是因为小程序端读取后可以直接从缓存里解析省去联表查询图片表的成本这种做法在数据量小时没有问题当单表图片达到万级时需要拆表。status字段在房东后台的展示逻辑是“上架/下架”在用户端看的是“未出租/已出租”两套状态不要混用一个字段。3.2 看房订单与合同表的状态设计订单表是整个系统状态的载体。它要同时关联房源和两个用户状态字段则要覆盖从预约申请到退租完成的全部流程。先看订单表设计CREATE TABLE rental_order ( id bigint(20) NOT NULL AUTO_INCREMENT, order_no varchar(32) NOT NULL COMMENT 业务单号, house_id bigint(20) NOT NULL COMMENT 房源id, tenant_id bigint(20) NOT NULL COMMENT 租客id, landlord_id bigint(20) NOT NULL COMMENT 房东id, status tinyint(1) NOT NULL DEFAULT 0 COMMENT 0待确认 1待看房 2已完成 3已取消 4已退租, appointment_time datetime DEFAULT NULL COMMENT 预约看房时间, contract_id bigint(20) DEFAULT NULL COMMENT 关联合同Id, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_tenant (tenant_id), KEY idx_house (house_id), CONSTRAINT fk_order_house FOREIGN KEY (house_id) REFERENCES house (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT看房订单表;订单状态机可以用一张状态转换表做约束说明这也是答辩时会被要求现场画的东西当前状态触发动作下一状态备注0 待确认房东同意预约1 待看房租客可取消0 待确认房东拒绝3 已取消记录取消原因1 待看房租客点击“已看房”2 已完成此时可发起签约2 已完成合同到期退租4 已退租触发押金退还任意状态双方协商取消3 已取消已签订合同后取消需冻结订单order_no建议用yyyyMMddHHmmss 4位随机数生成不要在数据库里用自增 id 直接展示给用户。状态流转必须由后端校验不能信任小程序传来的状态值。比如租客提交“已看房”时后端要确认当前订单状态确实是1 待看房否则拒绝更新。3.3 按距离排序的 SQL 写法房源列表页通常会做“按距离排序”这个需求在一个市区级毕业设计数据集里很常见。MySQL 没有内置地理距离函数常见做法是用球面距离公式计算。SQL 写起来并不复杂SELECT h.id, h.title, h.rent, h.cover, ROUND( 6371 * 2 * ASIN( SQRT( POWER(SIN(RADIANS((h.latitude - #{lat}) / 2)), 2) COS(RADIANS(#{lat})) * COS(RADIANS(h.latitude)) * POWER(SIN(RADIANS((h.longitude - #{lng}) / 2)), 2) ) ), 1 ) AS distance_km FROM house h WHERE h.status 0 AND h.city #{city} ORDER BY distance_km ASC LIMIT 20逻辑说明6371 是地球半径单位千米。公式计算的是球面两点间的大圆距离在同一个城市范围内误差可以忽略。注意WHERE先按城市和房源状态过滤再对结果集做距离计算而不是全表先算距离再过滤——后者的性能在房源表过万后会出现明显裂化。distance_km在SELECT里算出并ORDER BYMySQL 会在临时表中完成排序数据量不夸张时没有问题如果后续要做复杂筛选再加上HAVING distance_km 5而不是在WHERE里引用别名。4. 后端接口设计与小程序端对接4.1 微信登录与自定义登录态小程序端的登录流程不能只用微信的wx.login返回的 code 换取 openid之后就不再管理登录态。正确做法是小程序调用wx.login()获取临时 code后端拿去请求微信接口拿到 openid 后生成自己的token返回给小程序小程序后续请求都带着这个 token后端通过拦截器校验。下面是关键流程的 Java 实现。RestController RequestMapping(/api/wx) public class WxAuthController { PostMapping(/login) public Result login(RequestBody MapString, String params) { String code params.get(code); String url String.format( https://api.weixin.qq.com/sns/jscode2session?appid%ssecret%sjs_code%sgrant_typeauthorization_code, appId, appSecret, code); // 发起HTTP请求获取sessionKey和openid此处代码省略 String openid response.getOpenid(); User user userMapper.selectOne( new LambdaQueryWrapperUser().eq(User::getOpenid, openid)); if (user null) { user new User(); user.setOpenid(openid); user.setRole(0); userMapper.insert(user); } String token UUID.randomUUID().toString().replace(-, ); redisTemplate.opsForValue().set(wx:token: token, user.getId().toString(), 7, TimeUnit.DAYS); return Result.success(new LoginVO(token, user.getRole(), user.getNickname())); } }逻辑说明jscode2session接口是小程序登录的凭证换取入口参数必须保持官方命名appid、secret、js_code三个参数值从小程序后台获取。拿到 openid 后先查库再决定插入还是直接登录。返回的token是自定义会话标识存在 Redis 里并设置 7 天过期比把 openid 直接返回给前端安全得多。拦截器在处理请求时从 Header 中取Authorization比对 Redis 中是否存在即可。面试时这个问题经常以“微信小程序登录流程”出现能讲清楚“code 换 openid、openid 换 token”这个链路比背 java 八股文里那些内存模型题目更容易让面试官记住你的项目深度。4.2 房源列表与房源详情接口后端对外暴露的接口要遵循一个小原则列表接口不返回全字段。比如房源列表只需要展示封面、标题、租金、面积、户型、距离这几个字段详情接口才返回完整信息包括描述、轮播图、房东联系方式等。这样做既省流量也让小程序端渲染更快。接口设计参考如下接口方法路径参数说明获取房源列表GET/api/house/listcity, page, size, sort支持按城市筛选、分页获取房源详情GET/api/house/detailid完整字段发布房源POST/api/house/publishJSON body需房东权限修改房源状态PUT/api/house/statusid, status上下架切换提交看房申请POST/api/order/applyhouseId, time生成订单房东确认订单PUT/api/order/confirmorderId状态从0到1房源列表的Service层实现要点是把查询条件封装成LambdaQueryWrapper所有过滤条件做动态拼接避免写死多条 SQLpublic PageResultHouseVO listHouse(HouseQueryVO query, int page, int size) { LambdaQueryWrapperHouse wrapper new LambdaQueryWrapper(); wrapper.eq(StringUtils.isNotBlank(query.getCity()), House::getCity, query.getCity()) .eq(query.getStatus() ! null, House::getStatus, query.getStatus()) .like(StringUtils.isNotBlank(query.getKeyword()), House::getTitle, query.getKeyword()) .orderByDesc(House::getCreateTime); PageHouse p new Page(page, size); PageHouse result houseMapper.selectPage(p, wrapper); // 将Entity转为VO覆盖字段截断、时间格式化等逻辑 return convertToPageResult(result); }这段代码里eq方法的第一个参数是布尔条件条件为true时才拼上这段 SQL。这里的一个常见误用是直接构造 XML 动态 SQL其实 MyBatis-Plus 的LambdaQueryWrapper就能覆盖大多数场景而且代码可读性更好。列表接口返回的 VO 里不应该出现landlord_id的原始值可以转成房东昵称和头像前端直接展示。4.3 看房预约与状态更新逻辑看房预约是整个系统最容易出现并发问题的接口两个租客同时看到一套“未出租”的房子同时提交预约申请如果不加控制两个订单都会创建成功。解决这个问题最直接的方式是在创建订单前对房源状态做一次条件更新而不是查询后更新Transactional(rollbackFor Exception.class) public Long applyOrder(OrderApplyVO vo, Long tenantId) { // 1. 查询房源基本信息 House house houseMapper.selectById(vo.getHouseId()); if (house null || house.getStatus() ! 0) { throw new BizException(房源不存在或已被预订); } // 2. 检查租客是否已有未完成订单 Long count orderMapper.selectCount(new LambdaQueryWrapperRentalOrder() .eq(RentalOrder::getTenantId, tenantId) .in(RentalOrder::getStatus, Arrays.asList(0, 1))); if (count 0) { throw new BizException(您有待处理的看房订单); } // 3. 创建订单 RentalOrder order new RentalOrder(); order.setOrderNo(generateOrderNo()); order.setHouseId(house.getId()); order.setTenantId(tenantId); order.setLandlordId(house.getLandlordId()); order.setStatus(0); order.setAppointmentTime(vo.getAppointmentTime()); orderMapper.insert(order); // 4. 房源置为“已被预约”状态可选 house.setStatus(2); // 自定义状态码表示已被预约 houseMapper.updateById(house); return order.getId(); }这段代码里Transactional保证订单创建与房源状态更新要么都成功、要么都失败。一个容易忽略的细节是第 2 步的“租客是否已有未完成订单”检查它解决的是同一个租客重复约同一套房的问题而“两个租客同时抢同一套房”的问题还需要在第 4 步之前加一层乐观锁也就是在house表加一个version字段更新时检查版本号。毕业设计阶段用UPDATE house SET status2, versionversion1 WHERE id? AND version?即可解决。4.4 小程序端页面流转与请求封装小程序端不要在每个页面里直接写wx.request这会让代码维护成本翻倍。常见做法是在utils/request.js里封装请求方法统一携带 token统一处理过期与报错const request (url, method, data) { return new Promise((resolve, reject) { wx.request({ url: BASE_URL url, method: method || GET, data: data || {}, header: { Authorization: wx.getStorageSync(token) }, success: (res) { if (res.data.code 200) { resolve(res.data.data); } else if (res.data.code 401) { wx.removeStorageSync(token); wx.navigateTo({ url: /pages/login/login }); } else { wx.showToast({ title: res.data.msg, icon: none }); reject(res.data); } }, fail: reject }); }); };页面跳转时从列表页进详情页用wx.navigateTo并携带id参数从详情页跳到签约页需要把订单 id 与房源信息一起传到下一个页面。这里有一个高频小坑页面路径参数如果包含中文或特殊字符需要先encodeURIComponent。另一个常见适配问题是自定义导航栏。如果项目里用了自定义顶部导航需要调用wx.getMenuButtonBoundingClientRect()获取胶囊按钮位置才能正确计算导航栏高度原生导航栏虽然简单但样式上不够统一这个细节会在演示视频里被导师看到。5. 从毕业设计到可上线项目要补的代码细节5.1 乐观锁与防重复提交前面提到了乐观锁这里给出它的完整落地方式。在house表增加version int字段后更新代码判断影响行数int rows houseMapper.update(null, new LambdaUpdateWrapperHouse() .set(House::getStatus, 2) .set(House::getVersion, house.getVersion() 1) .eq(House::getId, house.getId()) .eq(House::getVersion, house.getVersion())); if (rows 0) { throw new BizException(房源状态已变化请刷新后重试); }逻辑说明rows 0表示当前版本号被其他事务抢先修改此时直接返回失败提示避免同一套房源被重复预约。这也是后端防并发最轻量的方案不用引入分布式锁。5.2 索引设计的两条建议只要总量过了万列表接口的索引就要重点设计。毕业设计的数据量通常不大但加分点在索引命名和设计思路上。不要为所有查询字段都建索引那样会拖慢插入速度。优先保证三个方向用户表的openid唯一索引订单表的tenant_id与landlord_id联合索引房源表的city status组合索引。查看执行计划用EXPLAIN SELECT ...观察type是否为ref或range避免ALL全表扫描。5.3 演示视频里应该重点展示的流程如果这个项目配有演示视频视频里必须覆盖一个完整业务闭环而不是把每个页面都点一遍。建议按这条链路录租客登录 → 浏览房源列表 → 进入详情页 → 提交看房申请 → 切到房东账号登录 → 确认看房申请 → 切回租客账号确认已看房 → 双方在线签订合同 → 后台查看订单状态变化。整个视频时长控制在 5 分钟左右这段内容最能让评审者理解你的状态机设计与角色权限控制。weixin://dl/business这类微信内部跳转协议在租房场景里并不适用小程序内页跳转统一用wx.navigateTo即可。需要注意的另一点是小程序后台的服务器域名必须配置 HTTPS 白名单否则真机预览时所有请求都会失败不要在开发工具里关闭域名校验后就忘掉这一步换一台手机演示时就会露馅。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价