简介围绕微信小程序房屋租赁管理系统的毕业设计完整资料包面向计算机专业学生在课程设计、毕业答辩或SSM框架实践中的需求覆盖房源管理、租房订单、账单、用户及中介角色等核心功能实现房屋租赁业务的系统化流程。压缩包共1069个文件大小约59.06MB以java/vue/js源码、png/svg界面素材、sql数据库脚本、mp4视频演示为主另有doc/txt文档与bat运行脚本目录划分清晰。已有43人学习浏览资源完整度较高。内含SSM后端、Vue管理后台、微信小程序前端及数据库初始化脚本并附毕业论文与操作录屏便于对照设计文档理解功能模块与数据库表结构安装批处理与配置文件可帮助快速启动项目无论是毕业设计参考还是小程序的二次开发都能提供直接的落地支撑。1. 房东还在用 Excel 和微信群管房微信小程序房屋租赁系统到底解决什么问题一个中介手上有 40 套房房源信息躺在 Excel 里租客在微信群里喊“水管漏了”“门锁坏了”租金到期全靠手机闹钟和笔记本上的铅笔标记。这不是个别现象而是中小公寓运营方的常态。基于微信小程序的房屋租赁管理系统做的是把房源发布、租客登记、合同签署、账单生成和到期提醒全部装进一个不用下载、扫码即开的小程序里。这套系统的成本和维护门槛比 App 低一截用户不需要安装开放给租客后几乎没有学习成本适合两类人一类是被表格和群消息折腾烦的中介、二房东和公寓运营方另一类是正在选毕业设计或外包课题、想避开常见坑的开发者。做之前有两个问题必须先想清楚一是微信平台侧的规则约束二是租约状态怎么建模才不会在退租时算错账。2. 从 AppID 到后端框架房屋租赁小程序的技术选型与最小可跑架构2.1 前端原生小程序还是 uni-app按交付场景选如果只做微信端我建议直接上原生小程序。原生在真机调试、微信支付、订阅消息这些能力上都是最直接的不需要等第三方框架适配。如果你的业务后续要扩展到支付宝小程序、抖音小程序或者要打包成 Android/iOS/鸿蒙 App那用 uni-app 更划算同一套 Vue 语法可以多端发布代价是部分微信专有能力要写条件编译。前端最需要注意的不是语言本身而是小程序的运行模型逻辑层和渲染层是分开的页面数据靠setData从逻辑层传到渲染层这个操作是异步且按 JSON 序列化传输的。很多刚上手的人把setData当this.data xxx用一次推几百条数据页面直接白屏这在第 5 章会展开讲。最小可跑的项目结构大致是这样{ pages: [ pages/index/index, pages/house/list/list, pages/house/detail/detail, pages/contract/list/list, pages/user/login/login ], window: { navigationBarTitleText: 租房管理, navigationBarBackgroundColor: #ffffff, navigationBarTextStyle: black }, tabBar: { list: [ { pagePath: pages/index/index, text: 首页 }, { pagePath: pages/house/list/list, text: 房源 }, { pagePath: pages/user/login/login, text: 我的 } ] } }这个配置的意图是把小程序切成三个主入口首页放运营概览和待办事项房源页负责房源浏览和筛选个人页承载登录、合同列表和管理员入口。tabBar只放三个 Tab避免把“合同管理”“账单管理”都塞进一级导航那样在手机屏幕上会显得拥挤。2.2 后端Spring Boot MySQL 是成本最低的常见组合后端我一般推荐 Spring Boot MyBatis-Plus MySQL。原因是这套组合的资料密度极高遇到问题基本都能搜到现成案例团队招人也容易。接口统一走 RESTful JSON小程序端用wx.request发起 HTTPS 请求。如果做毕设或内部管理系统也可以考虑微信云开发不需要自己买服务器、配 HTTPS 域名云函数直接写逻辑云数据库自动带权限控制。但要提醒一点云开发的项目后期如果要迁移到自建服务器几乎等于重写后端。如果题目是“设计与实现”通常评审会看完整的后端代码和数据库设计这时自建后端更占优势。后端选型还要提前定三件事登录态用 JWT 还是 session文件存本地还是对象存储定时任务用什么触发。登录态我建议用 JWT小程序端每次请求带上 token后端拦截器统一校验。文件存储如果房源图片量大用云存储或 OSS不然后端服务器带宽会很紧张。定时任务常见做法是 Spring Boot 自带的Scheduled配合cron表达式租约到期提醒、账单生成这类任务直接用就行。2.3 通信链路上的三个“微信规定”决定你能跑多顺微信小程序和普通 Web 项目最大的区别在合规约束。第一wx.request、wx.uploadFile、wx.downloadFile的域名必须在微信公众平台配置合法域名而且必须是 HTTPS不能直接填 IP。开发调试阶段可以在开发者工具里勾选“不校验合法域名”但真机体验版不勾就白屏。第二登录必须走wx.login获取临时 code再由后端用 code 换 openid 和 session_key。小程序端没有 cookie 机制不能像 Web 一样靠 session 维持登录所以需要后端自己发 token 给前端存起来。第三给租客发提醒只能用订阅消息且订阅消息是一次性的——用户每次点击“允许”只对应一次下发机会。这意味着“到期前 3 天提醒租客交租”这种功能在用户没有主动订阅的情况下无法实现。设计业务时要让租客在签合同、下单时顺手完成订阅这是一件影响系统体验的隐性决策。提示如果后端要跑在本地让小程序连开发者工具里可以勾选“不校验合法域名”并开启本地设置但演示时要用http://localhost或局域网 IP手机真机访问不到电脑的 localhost需要把接口地址改成电脑的局域网 IP。3. 把房源、租约和账单落成表数据库设计与状态流转3.1 六张核心表的设计与字段说明房屋租赁系统的数据模型不算复杂但容易漏表。常见的设计失误是只建“房源表”和“租客表”合同、账单靠手写管理结果退租的时候租金押金对不上账。我建议至少拆成六张表用户表、房源表、房源图片表、租约表、账单表、通知记录表。先看用户表和房源表CREATE TABLE t_user ( id bigint NOT NULL AUTO_INCREMENT, openid varchar(64) NOT NULL COMMENT 微信openid唯一, nickname varchar(50) DEFAULT COMMENT 昵称, phone varchar(20) DEFAULT COMMENT 联系电话, role tinyint NOT NULL DEFAULT 0 COMMENT 0-租客 1-管理员, status tinyint NOT NULL DEFAULT 1 COMMENT 1-正常 0-禁用, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_openid (openid) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表; CREATE TABLE t_house ( id bigint NOT NULL AUTO_INCREMENT, title varchar(100) NOT NULL COMMENT 房源标题如XX小区3室2厅, address varchar(200) NOT NULL COMMENT 详细地址, area decimal(6,2) NOT NULL COMMENT 面积单位平米, rent decimal(10,2) NOT NULL COMMENT 月租金, deposit decimal(10,2) NOT NULL COMMENT 押金, house_type varchar(20) DEFAULT COMMENT 户型如3室2厅, status tinyint NOT NULL DEFAULT 0 COMMENT 0-空置 1-已租 2-已预订 3-下架, landlord_id bigint NOT NULL COMMENT 所属管理员用户id, remark varchar(500) DEFAULT COMMENT 备注, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_status_rent (status, rent) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT房源表;这里的t_user用 openid 做唯一键是关键。微信登录换到的 openid 对每个小程序是固定的哪怕用户换手机号openid 不会变所以它是天然的关联键。role字段区分管理员和租客管理员负责发房、录合同租客只看房和收账单。t_house表单独拆了landlord_id这个字段对应的是管理员的t_user.id。有人会问为什么不在房源表里直接放一个owner_name字符串——因为后续要按管理员统计业绩、按管理员分配待办存 id 可以用 join 查出来的名字存字符串则无法做关联统计。status字段用 0/1/2/3 四位数字表示房源状态过渡状态“已预订”很重要能避免两个租客同时看上同一套房。3.2 租约表和账单表状态设计决定退租时算不算得清账CREATE TABLE t_contract ( id bigint NOT NULL AUTO_INCREMENT, contract_no varchar(32) NOT NULL COMMENT 合同编号如Z20240601001, 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-已解约, create_time datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_contract_no (contract_no), KEY idx_house_id (house_id), KEY idx_tenant_id (tenant_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT租约表; CREATE TABLE t_bill ( id bigint NOT NULL AUTO_INCREMENT, bill_no varchar(32) NOT NULL COMMENT 账单编号, contract_id bigint NOT NULL COMMENT 租约id, bill_type tinyint NOT NULL COMMENT 1-租金 2-押金 3-水费 4-电费 5-其他, amount decimal(10,2) NOT NULL COMMENT 金额, due_date date NOT NULL COMMENT 应缴日期, status tinyint NOT NULL DEFAULT 0 COMMENT 0-待支付 1-已支付 2-逾期 3-已退款, pay_time datetime DEFAULT NULL COMMENT 支付时间, remark varchar(200) DEFAULT , PRIMARY KEY (id), KEY idx_contract_id (contract_id), KEY idx_due_date (due_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT账单表;租约表里特意保存了monthly_rent和deposit的快照值而不是和房源表实时联动。原因是租金会变半年后续签时可能涨了 200 块如果账单金额直接去t_house.rent查历史账单全都会变成新价格对账就乱了。账单表的status是业务逻辑最重的字段。常见做法是每月 1 号定时任务扫描生效中的租约按start_date到end_date的月份生成租金账单水电费则按抄表数录入后台手动生成。逾期判定不要靠定时任务每天扫——那样改了截止日期就漏了——正确做法是每次查询账单列表时如果due_date 今天 且 status 0就标记为逾期。3.3 查询索引与列表接口筛选条件和分页顺序决定数据库撑不撑得住房源列表是访问量最大的接口筛选条件通常是区域、户型、租金范围、状态。idx_status_rent这个联合索引能覆盖大部分场景因为列表默认按“在租状态 租金排序”展示。分页建议用经典的LIMIT offset, size数据量在几千条以内完全够用。不要一上来就搞游标分页、深度分页优化那是几万条以后的事。但有一个细节要注意租约列表的排序字段不是create_time而是start_date DESC——用户关心的是当前有哪些合同在生效而不是哪条最先录入。我见过有人把“合同编号”设计成自增 id打印出来的合同单号是“14”完全没有辨识度。合作过的一个项目里合同编号用Z 年月日 三位序号例如Z20240601001后面客服打电话报单号用户一听就能确认这比一长串无意义 id 好用得多。4. 登录、发房、催租三件事的代码实现照着能跑的微信小程序写法4.1 登录链路wx.login 换取 openid后端签发 token小程序端登录不能直接用wx.getUserInfo拿用户信息那是早期版本的错误做法。现在标准链路是wx.login拿 codecode 只能一次性使用后端拿 code 调微信的code2Session接口换取 openid 和 session_key。前端登录按钮的完整实现// pages/user/login/login.js Page({ handleLogin() { wx.login({ success: async (res) { if (!res.code) { wx.showToast({ title: 登录失败请重试, icon: none }); return; } // 把 code 发给自己的后端 const loginRes await new Promise((resolve) { wx.request({ url: https://api.example.com/auth/login, method: POST, data: { code: res.code }, success: resolve, fail: () resolve({ data: { code: 500, msg: 网络异常 } }) }); }); // 期望后端返回 { code: 0, data: { token, role, nickname } } if (loginRes.data.code 0) { wx.setStorageSync(token, loginRes.data.data.token); wx.setStorageSync(role, loginRes.data.data.role); wx.showToast({ title: 登录成功, icon: success }); } else { wx.showToast({ title: loginRes.data.msg, icon: none }); } } }); } });这个链路的关键点有三个wx.login的 code 有效期只有 5 分钟不能让用户填写表单后再去调wx.request的 url 必须是已配置的合法域名否则在真机上直接失败后端返回的 token 必须存到本地缓存后续所有请求的 header 里都要带上它。后端对应的 Java 实现PostMapping(/auth/login) public Result login(RequestBody LoginRequest req) { // 1. 用 code 换 openid String url https://api.weixin.qq.com/sns/jscode2session?appid appid secret secret js_code req.getCode() grant_typeauthorization_code; // 实际用 RestTemplate / OkHttp 发起请求这里省略HTTP调用细节 WxSessionResp resp restTemplate.getForObject(url, WxSessionResp.class); if (resp null || resp.getOpenid() null) { return Result.error(登录失败); } // 2. 查用户表不存在则自动注册 User user userMapper.selectByOpenid(resp.getOpenid()); if (user null) { user new User(); user.setOpenid(resp.getOpenid()); user.setRole(0); // 默认租客 userMapper.insert(user); } // 3. 生成 JWT包含 userId 和 role String token JwtUtil.createToken(user.getId(), user.getRole()); return Result.ok(new LoginVO(token, user.getRole(), user.getNickname())); }这段代码包含系统里最核心的“自动注册”逻辑第一次登录的用户直接建一条记录不需要单独走注册页。实际项目中微信接口的 appid 和 secret 要配在配置中心或环境变量里不能明文出现在代码中。4.2 房源发布图片上传与表单提交分开做房源发布是最容易接口设计混乱的地方。常见错误是图片和房源信息在一个请求里提交图片 base64 编码后塞进 JSON后端解析巨大字符串性能很差。正确姿势是先调wx.uploadFile逐张传图拿到文件 id/URL 后再把房源信息连同图片 id 列表一次提交。// pages/house/edit/edit.js async uploadImages(filePaths) { const uploadedUrls []; for (let i 0; i filePaths.length; i) { const res await new Promise((resolve, reject) { wx.uploadFile({ url: https://api.example.com/house/upload, filePath: filePaths[i], name: file, header: { Authorization: Bearer ${wx.getStorageSync(token)} }, success: resolve, fail: reject }); }); // 后端返回 { code: 0, data: { url: https://.../xxx.jpg } } const data JSON.parse(res.data); if (data.code 0) { uploadedUrls.push(data.data.url); } } return uploadedUrls; }这里有一个不能偷懒的点wx.uploadFile的返回值是字符串不是对象必须手动JSON.parse。很多项目挂就挂在忘了解析。后端接口接收MultipartFile保存到本地磁盘或 OSS再返回可访问的 URL。wx.uploadFile的name字段必须和后端RequestParam(file)的参数名一致否则后端收不到文件。文件大小限制在微信侧默认单次上传不能超过 10MB房源图片通常控制在 1MB 以内前端可以先wx.compressImage压缩再上传省流量也省后端带宽。房源信息提交用普通表单就行注意必填校验放在前后端各做一次前端防手滑后端防绕过小程序直接调接口。4.3 账单生成与订阅消息定时任务和一次性订阅的组合账单生成我见过两种方案。方案 A 是在租约创建时就把未来所有月份的账单全部生成优点是查询快、逻辑简单缺点是改租约日期时旧账单要批量改。方案 B 是每月 1 号定时生成当月账单灵活但对定时任务的稳定性有要求。我一般推荐方案 B配合Scheduled每月的0 0 1 * * ?执行一次。Component public class BillGenerateTask { Scheduled(cron 0 0 1 * * ?) // 每月1号凌晨1点执行 public void generateMonthlyBills() { ListContract contracts contractMapper.selectActive(); // status0 AND end_date 今天 for (Contract contract : contracts) { Bill exist billMapper.selectByContractAndMonth( contract.getId(), YearMonth.now()); if (exist ! null) continue; // 防止重复生成 Bill bill new Bill(); bill.setContractId(contract.getId()); bill.setAmount(contract.getMonthlyRent()); bill.setBillType(1); // 租金 bill.setDueDate(LocalDate.now().plusDays(5)); // 5天内交租 bill.setStatus(0); billMapper.insert(bill); } } }这个定时任务的核心经验是“幂等判断”同一个合同同一个月份不允许生成两条账单。没有这个判断定时任务被手动触发两次就会产生重复账单租客看到两笔待支付体验直接崩了。订阅消息的发送必须由用户主动触发授权。常见做法是租客在签约成功页点击“开启缴费提醒”按钮后后端保存用户的订阅授权记录账单生成时后端检查该用户是否有可用授权有则调subscribeMessage.send。注意订阅消息每天有次数限制测试阶段经常触发 43101 错误用户拒绝授权生产环境一定要做失败日志。4.4 租客看房流程分页加载和状态筛选的配合租客端房源列表的核心性能瓶颈在setData。正确写法是分页拉取每页 20 条onReachBottom触底加载下一页。列表页的筛选组件建议用单选框承载“区域”“户型”“租金区间”搜索框做模糊匹配小区名。分页接口设计时前端传pageNum和pageSize后端返回{ total, list }。小程序端第一次进入页面加载第一页下拉刷新时重置页码重新拉取。这里最容易被忽略的是页面onUnload时要清除加载状态否则页面返回再进入时会出现旧数据闪烁。5. 上线必踩的 5 个坑从域名白名单到 setData 卡顿的排查手册5.1 真机预览白屏、request 全部失败request:fail url not in domain list现象开发者工具里接口调用正常页面数据都能渲染一点预览真机打开后页面空白控制台打印request:fail url not in domain list。原因小程序真机环境强制校验request合法域名。开发者工具勾选了“不校验合法域名”后可以绕过但真机不认这个设置。如果后端接口是 IP 地址或者用了自签名证书也会触发同样的问题。解决在微信公众平台「开发管理 → 开发设置 → 服务器域名」里配置request 合法域名必须是 HTTPS且证书是正规 CA 签发的。排在前面、最省事的开发期方案是真机调试时在微信开发者工具里开启“真机调试”模式也就是“不校验合法域名”的二维码节奏但这只是临时方案上线前必须配好正式域名。5.2 开发者工具一切正常真机登录却拿不到 openid现象登录按钮在开发者工具里点击后能正常跳转换到手机预览就一直停在登录页后端日志里根本没有收到/auth/login请求或者收到了但 openid 为空。原因这是开发者工具和真机的差异。开发者工具的wx.login会返回一个模拟 code后端拿这个 code 调code2Session也能成功因为它走的是开发者工具自己的模拟逻辑。真机上如果填的是测试号 AppID或者 AppID 没有关联到正确的密钥code 就会失效。解决第一确认小程序后台的 AppID 和代码里的 appid 完全一致第二真机预览不要用测试号用开发版/体验版对应的正式 AppID第三后端日志把 code2Session 接口的原始返回打出来看是40013appid 错误还是40163code 已使用错误码比猜原因高效得多。5.3 房源图片传上去了页面里却裂图一堆现象后台传图片显示成功打开房源详情页图片一半能显示一半裂图有的房子图片是灰的点开也加载不出来。原因wx.uploadFile传文件用的是 uploadFile 合法域名但页面里image标签加载图片走的是 downloadFile 合法域名。很多项目只配了 request 和 uploadFile漏配了 downloadFile导致真机上图片无法加载。如果图片存在本地服务器且走 HTTP也会被拦。解决在服务器域名配置里补上downloadFile 合法域名。如果图片使用的是云存储 CDN 地址也要确认 CDN 域名已加白名单。另一个自查项是图片 URL 是不是后端拼接错了——http://localhost:8080/upload/1.jpg这种地址在小程序真机上永远打不开要换成公网可访问地址。5.4 订阅消息发送失败报错 errCode 43101现象后台点“发送提醒”按钮接口返回成功但租客收不到任何消息。查订阅消息记录发现errcode为43101。原因43101 是“用户拒绝订阅消息授权”。小程序订阅消息是一次性授权用户每次点击“允许”只代表当次有效如果用户之前点了“总是保持以上选择不再询问”并且选了拒绝后续就无法再弹窗。另一个原因可能是在wx.requestSubscribeMessage里传的模板 ID 和后台subscribeMessage.send用的模板 ID 不是同一个。解决把订阅引导放在业务强相关的位置比如租客提交“立即签约”按钮时同时弹出订阅授权这时候用户最容易同意。发送前在服务端记录该用户是否还有剩余授权次数用完就不调发送接口避免大量 43101 告警。调试阶段可以用测试模板但上线前必须换成正式模板 ID否则直接发送失败。5.5 setData 一次塞 500 条房源页面白屏卡顿现象房源列表接口一次返回全部房源小程序端setData后页面白屏滚动和点击退出都没响应有的页面能显示但滑动掉帧。原因setData是逻辑层到渲染层的全量数据推送数据量越大序列化时间越长。500 条房源加上图片 URL 和描述JSON 体积可能到 200KB 以上渲染层一次接不住直接卡死。解决接口改成强制分页每页 20 条onReachBottom时追加下一页。如果数据量仍然大列表页只渲染房源标题、首图缩略图和租金详情页再请求完整字段。setData的优化原则是“一次只传当前页要显示的字段”永远不要把整个对象倒进页面。提示翻车最多的永远是环境问题不是代码问题。遇到诡异现象先确认三个版本微信开发者工具的版本、基础库版本、后端运行的代码分支。三个版本串了什么问题都可能出现。6. 上线前按这套清单过一遍验收、压测与业务闭环检查上线前我会花一整天走一遍全流程验收重点不是功能有没有而是“业务闭环”。租房管理系统的闭环是什么是房源上架 → 租客看房 → 签合同 → 生成账单 → 收租金 → 到期退租 → 释放房源为“空置”。这条链路有一个环节断了整个系统就只是摆设。第一个必验场景是“退租再租”。A 租客合同到期后管理员把租约状态改为“已退租”房源状态自动从“已租”变“空置”然后 B 租客可以签约。这个流程设计时容易漏掉“退租时是否生成押金账单”和“已租状态房源不可再签约”两个约束验一次就能暴露。第二个必验场景是“跨月账单”。现在时间是 6 月 28 日新建一份 7 月 1 日起租的合同定时任务在 7 月 1 日凌晨跑完后账单是否正确生成、租客能不能在小程序里看到。建议手动把服务器时钟临时改到月底或直接调一次任务的方法验证幂等性。第三个必验场景是“权限越权”。普通租客能不能调用管理员接口如果你用的是 JWT后端拦截器有没有校验role字段租客 A 能不能把合同编号改成租客 B 的合同编号去查详情这几个问题靠前端隐藏入口是挡不住的必须后端加权限判断。我做这类系统有一个习惯先把租约状态流转图画在纸上状态节点写清楚“由哪个角色、在什么条件下、改成哪个状态”再动数据库设计。状态机画清楚了后面的接口和页面都只是搬运工。这个方法帮我避过很多次“上线后才发现退租逻辑没做”的尴尬希望帮到你。本文还有配套的精品资源点击获取
