简介:这是一份基于微信小程序的房屋租赁管理毕业设计资源,面向计算机专业学生及需要掌握SSM框架与小程序整合开发的开发者,适合毕业设计、课程设计或项目实训场景。系统包含管理员、中介、用户三类角色,覆盖房源管理、租房订单、账单生成、用户信息维护等业务模块,并提供可行性分析与数据库设计说明,能帮助读者理解租赁管理系统的工程化实现。压缩包共1069个文件,以Java后端源码、Vue管理端页面、微信小程序wxml/wxss文件、MySQL数据库脚本为主,同时含大量png界面截图与mp4演示视频,整体约59MB,目录结构清晰,适合按模块阅读。已有45人学习下载,资源附带一键安装、运行、构建的bat脚本,结合毕业论文与视频演示,可降低环境搭建和二次开发门槛,用于毕业设计参考或SSM小程序实战练习都很合适。
1. 房屋租赁小程序不是展示页:先分清租客、房东、管理员三个角色
很多同学拿着“基于微信小程序的房屋租赁管理系统”这个题目开工,第一步就去画首页、做房源列表,最后做出来的是一个能看不能交易的展示页。问题在于:租赁系统真正难的不是页面,而是三个角色对着同一套房看到的状态不一样——租客看到的是“可预约”,房东看到的是“审核中、已上架、已出租”,管理员看到的是“待审核、违规下架”。这套状态流转才是“管理系统”的骨架。本文按微信小程序原生框架 + Spring Boot + MySQL 的常见技术栈,讲清楚表结构怎么设计、登录与签约接口怎么写、小程序端怎么接,以及上线前哪些坑必须提前趟平。适合毕业设计、课程设计,也适合接私活快速交付的第一版。
2. 数据模型先行:房屋租赁系统的四张核心表与状态机设计
这一章是整篇的定音锤。接口写错了可以改,表结构错了要迁移数据重写 SQL。做房屋租赁管理系统,我一般先不碰代码,先画四张表:user、house、appointment、contract。很多项目组喜欢往一张表里堆字段,或者把“预约”和“合同”合并成一个订单表,短期看起来省事,一旦要算“看房转化率”“合同续租率”,数据就拆不开了。下面把四张表的设计和原因一起说清。
2.1 三种角色的权限边界:租客、房东、管理员分别能做什么
角色权限是这套系统的第一道分界线,建议在写登录接口前就定死,不然后面每个接口都要加一层“if 身份判断”,改起来极其痛苦。按我落地的习惯,权限矩阵是下面这样的:
| 操作域 | 租客(role=2) | 房东(role=1) | 管理员(独立后台) |
|---|---|---|---|
| 房源浏览 | 只看已上架且审核通过 | 看自己全部房源 | 看全部含待审核 |
| 房源操作 | 无 | 发布、编辑、上下架 | 审核、强制下架 |
| 预约看房 | 提交预约、取消预约 | 确认、拒绝、完成预约 | 查看全部预约记录 |
| 合同 | 确认合同、查看合同、发起退租 | 起草合同、确认合同 | 结束合同、处理纠纷 |
实现上,一张 user 表加 role 字段就够了,不要给三种角色建三张用户表。三张表在“用户信息同步”上非常麻烦,而且现实中一个房东自己租房住的情况也很常见——你自己有房出租,同时也租别人的房,双表结构直接崩。管理员不要塞进小程序端,后台管理页面用 Web 做,接口复用同一套 API,在网关层按角色控制访问。小程序端只做租客和房东两种视图,这是最省事的切法。
2.2 核心数据表结构:用户、房源、预约、合同四张表
直接用我最常用的一套建表 SQL,字段按业务最小集来,不堆冗余:
-- 用户表 CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `openid` varchar(64) NOT NULL COMMENT '微信openid,唯一标识', `nickname` varchar(50) DEFAULT '' COMMENT '微信昵称', `phone` varchar(20) DEFAULT '' COMMENT '手机号,签约时必填', `role` tinyint(4) NOT NULL DEFAULT '2' COMMENT '1房东 2租客', `avatar` varchar(255) DEFAULT '', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `deleted` tinyint(1) DEFAULT '0', PRIMARY KEY (`id`), UNIQUE KEY `uk_openid` (`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 房源表 CREATE TABLE `house` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `landlord_id` bigint(20) NOT NULL COMMENT '房东user.id', `title` varchar(100) NOT NULL, `cover` varchar(255) NOT NULL COMMENT '封面图URL', `images` json DEFAULT NULL COMMENT '轮播图URL数组', `area` decimal(6,2) DEFAULT NULL COMMENT '面积㎡', `layout` varchar(20) DEFAULT '' COMMENT '户型,如两室一厅', `rent` decimal(10,2) NOT NULL COMMENT '月租金', `deposit` decimal(10,2) DEFAULT NULL COMMENT '押金', `address` varchar(255) NOT NULL COMMENT '小区+楼栋+门牌', `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0草稿 1上架 2已出租 3下架', `audit_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0待审核 1通过 2拒绝', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_landlord` (`landlord_id`), KEY `idx_status` (`status`,`audit_status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 预约看房表 CREATE TABLE `appointment` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `house_id` bigint(20) NOT NULL, `tenant_id` bigint(20) NOT NULL, `visit_time` datetime NOT NULL COMMENT '计划看房时间', `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0待确认 1已确认 2已取消 3已完成', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_house` (`house_id`), KEY `idx_tenant` (`tenant_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 合同表 CREATE TABLE `contract` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `appointment_id` bigint(20) NOT NULL COMMENT '签约关联的预约记录', `house_id` bigint(20) NOT NULL, `tenant_id` bigint(20) NOT NULL, `landlord_id` bigint(20) NOT NULL, `start_date` date NOT NULL, `end_date` date NOT NULL, `rent` decimal(10,2) NOT NULL, `deposit` decimal(10,2) NOT NULL, `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0待签署 1生效中 2已结束 3已解约', `file_url` varchar(255) DEFAULT '' COMMENT '已生成的合同PDF地址', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;这里有几个字段取舍值得展开。第一个是 house.images 用 json 存数组,不要为“多图”单独建一张附件表。小程序端一次最多传 9 张图,json 完全够用,还能避免一次房源详情要查两张表的麻烦。第二个是 status 和 audit_status 必须分开,这是两根完全独立的轴:审核是“能不能对外展示”,上下架是“房东是否愿意出租”,合在一个字段里,会出现“审核拒绝”和“手动下架”互相覆盖的脏数据。第三个是 deleted 逻辑删除字段,房源下架不等于删除,管理员误下架要能一键恢复,物理删除在这类系统里是给自己找麻烦。
2.3 状态流转:房源、预约、合同三条状态机
表结构定完后,最要紧的是把状态流转规则写明白,否则前端按钮的“置灰/可点”完全没法判断。下面是三条状态机:
| 对象 | 状态值与含义 | 触发动作 |
|---|---|---|
| 房源 | 0草稿→1上架→2已出租→3下架 | 提交审核 / 审核通过 / 签约成功 / 房东手动或管理员违规下架 |
| 预约 | 0待确认→1已确认→2已取消→3已完成 | 房东确认 / 双方任一方取消 / 看房结束 |
| 合同 | 0待签署→1生效中→2已结束→3已解约 | 双方确认签署 / 租期到期 / 违约终止 |
重点关注一点:预约和合同不要合并成一张订单表。很多同学觉得“预约看房成功后就直接签约”,其实是两步业务——看房是意向,签约是成交,中间还隔着线下谈价。把预约和合同拆开,好处是看房转化率、合同履约率可以分开统计,而且预约表的状态只管到“看房完成”,后续合同表的生命周期独立演进,不会被一个订单对象绑死。我见过最难受的改法,是后来想加“租金减免”字段,结果发现合同和预约混在同一张表里,连加字段都不知道加在哪一端。
3. 后端接口实现:微信登录、房源发布与签约下单的 Spring Boot 落地
表结构定了,后端就顺了。这一章挑三个最影响交付的接口讲:登录、图片上传、签约。一是这三个接口最容易在小程序端出问题,二是它们的写法决定了后面所有接口的骨架。技术栈用 Spring Boot + MyBatis-Plus,这是目前做这类管理系统最常见的组合。
3.1 微信登录的完整链路:code 换 openid,再下发自定义 token
小程序端 wx.login 拿到的 code 是五分钟有效的一次性凭证,后端拿 code 去微信接口换 openid。注意 openid 是用户在小程序里的唯一身份,但后端不要直接把 session_key 下发到前端,那东西一旦泄露,理论上可以伪造会话。正确做法是后端自己发一个 token,放到 Redis 里设 7 天过期:
@PostMapping("/login") public Result login(@RequestBody LoginDTO dto) { String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + appSecret + "&js_code=" + dto.getCode() + "&grant_type=authorization_code"; String resp = restTemplate.getForObject(url, String.class); JSONObject obj = JSONObject.parseObject(resp); if (obj.getString("openid") == null) { return Result.fail("登录失败:" + obj.getString("errmsg")); } String openid = obj.getString("openid"); User user = userMapper.selectOne(new LambdaQueryWrapper<User>() .eq(User::getOpenid, openid)); if (user == null) { user = new User(); user.setOpenid(openid); user.setRole(2); // 默认租客 userMapper.insert(user); } String token = UUID.randomUUID().toString().replace("-", ""); redisUtil.set("login:token:" + token, String.valueOf(user.getId()), 7 * 24 * 3600); return Result.ok(LoginVO.builder() .token(token) .role(user.getRole()) .nickname(user.getNickname()) .phone(user.getPhone()) .build()); }逻辑很简单:先拿 code 换 openid,查 user 表,没有就自动注册,有就更新登录状态,最后返回自定义 token。前端后续请求都带这个 token,后端根据 token 从 Redis 取出 userId,再去查角色。这里我一般会在LoginVO里把 role 一起返回,前端才能决定跳转到“租客首页”还是“房东工作台”。
提示:wx.getPhoneNumber 目前要求小程序已完成企业主体认证,个人开发者的小程序调用会直接报错。所以这个系统里手机号我用表单录入,签约前校验手机号非空,不做一键授权。
3.2 房源发布与图片上传:文件存储的大小、格式与命名规范
房源发布的第一步是传图片。小程序端用 wx.chooseMedia 拿到临时文件路径,再通过 wx.uploadFile 上传。后端接口按下面的写法,能挡住 90% 的脏数据:
@PostMapping("/file/upload") public Result upload(@RequestParam("file") MultipartFile file) throws IOException { if (file.isEmpty()) { return Result.fail("文件不能为空"); } if (file.getSize() > 5 * 1024 * 1024) { return Result.fail("图片不能超过5MB"); } String suffix = StringUtils.getFilenameExtension(file.getOriginalFilename()); if (!Arrays.asList("jpg", "jpeg", "png", "webp").contains(suffix)) { return Result.fail("不支持的图片格式"); } String key = "house/" + UUID.randomUUID() + "." + suffix; ossClient.putObject("rental", key, file.getInputStream()); return Result.ok("https://your-bucket.oss-cn-hangzhou.aliyuncs.com/" + key); }参数设计的重点有三个:大小限制 5MB 对应小程序端 wx.chooseMedia 的 sizeType 参数,超过这个值上传耗时会长到用户直接放弃;格式限定 jpg/png/webp,gif 这类动图在列表页非常吃性能,建议直接拒绝;文件命名用 UUID 而不是原名,否则两个用户都传1.jpg,后传的会覆盖先传的,这是文件服务最常见的翻车点。上传成功后返回的永久 URL 要存到 house.images 字段里,发布房源接口只需要接收 cover 和 images 两个字符串,cover 取第一张图。
3.3 预约与签约接口:条件更新才能防止重复下单
预约接口本身不复杂,插入一条 appointment 记录即可,但前置校验要做扎实:
@PostMapping("/appointment") public Result createAppointment(@RequestBody AppointmentDTO dto) { House house = houseMapper.selectById(dto.getHouseId()); if (house == null || !"1".equals(String.valueOf(house.getStatus()))) { return Result.fail("房源不可预约"); } Appointment apt = new Appointment(); apt.setHouseId(dto.getHouseId()); apt.setTenantId(currentUserId()); apt.setVisitTime(dto.getVisitTime()); apt.setStatus(0); appointmentMapper.insert(apt); return Result.ok(apt.getId()); }这里 status 只允许为 1(上架且审核通过)的房源被预约。签约接口才是真正考验功底的地方,两个租客同时点“签约”,如果代码先selectById看 status 再updateById改成已出租,两个请求都读到“可租”,最后就会生成两份合同。正确写法是条件更新:
@PostMapping("/contract/sign") public Result sign(@RequestBody SignDTO dto) { // 先把房源从“上架”改成“已出租”,条件带上 status=1 int rows = houseMapper.update(null, new LambdaUpdateWrapper<House>() .eq(House::getId, dto.getHouseId()) .eq(House::getStatus, 1) .set(House::getStatus, 2)); if (rows == 0) { return Result.fail("房源已被租出,请刷新后再看"); } // 房源抢占成功后,再创建合同记录 Contract contract = new Contract(); contract.setHouseId(dto.getHouseId()); contract.setTenantId(dto.getTenantId()); contract.setLandlordId(dto.getLandlordId()); contract.setAppointmentId(dto.getAppointmentId()); contract.setStartDate(dto.getStartDate()); contract.setEndDate(dto.getEndDate()); contract.setRent(dto.getRent()); contract.setDeposit(dto.getDeposit()); contract.setStatus(0); contractMapper.insert(contract); return Result.ok("签约成功,等待房东确认"); }condition update 的精髓在于数据库层面的原子性——UPDATE house SET status=2 WHERE id=? AND status=1这条语句在上架房源只有一条时,并发请求只会有一个影响行数为 1,另一个是 0,从源头堵住超卖。如果还想更稳,可以在 contract 表给 house_id 加唯一索引,双保险。这个写法同样适用于预约的取消、管理员的下架,凡是涉及“状态跳变”的接口,都别用先查后改。
4. 小程序前端落地:请求封装、房源列表与预约表单的核心代码
后端接口就绪后,小程序端主要做三件事:统一请求封装、列表分页、表单提交。很多毕设代码把 wx.request 写在每一个页面里,token 要重复传,出错要重复处理,改一个接口地址要全局搜索替换。这一章直接给你一套能复用的骨架。
4.1 request.js 封装:token、401 与 baseUrl 的统一管理
utils/request.js 是全项目最值得先写的文件:
const BASE_URL = 'https://api.example.com' function request(path, method = 'GET', data = {}) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + path, method: method.toUpperCase(), data, header: { 'Content-Type': 'application/json', 'token': wx.getStorageSync('token') || '' }, success(res) { if (res.statusCode === 401) { wx.removeStorageSync('token') wx.navigateTo({ url: '/pages/login/login' }) reject(res.data) return } if (res.data.code !== 0) { wx.showToast({ title: res.data.msg, icon: 'none' }) reject(res.data) return } resolve(res.data) }, fail(err) { wx.showToast({ title: '网络异常,请重试', icon: 'none' }) reject(err) } }) }) } module.exports = { request, BASE_URL }这段代码做了几件小事:所有请求自动带 token;遇到 401 清掉登录态并跳转登录页;后端约定的 code=0 为成功,业务错误统一 toast。这里有个经验:token 一定要从 storage 读,不要从 globalData 读。小程序冷启动时 globalData 是空的,而 storage 是持久化的,从 storage 读才能保证用户重启小程序后登录态还在。
4.2 首页与房源列表:分页加载与触底刷新
首页房源列表不做分页,一次拉全量列表,房源稍微多一点 setData 的数据量就会让页面卡顿。标准做法是 page 和 pageSize 分页,触底加载下一页:
Page({ data: { list: [], page: 1, pageSize: 10, loading: false, finished: false }, onLoad() { this.loadList(true) }, onReachBottom() { if (!this.data.loading && !this.data.finished) { this.loadList(false) } }, loadList(reset) { const page = reset ? 1 : this.data.page + 1 this.setData({ loading: true }) request(`/house/list?page=${page}&pageSize=${this.data.pageSize}`) .then(res => { const records = reset ? res.data.records : this.data.list.concat(res.data.records) this.setData({ list: records, page, finished: records.length >= res.data.total }) }) .finally(() => { this.setData({ loading: false }) }) } })参数说明:reset 为 true 时拉第一页并重置列表,适用于下拉刷新;为 false 时追加下一页。finished 的判断用总条数 total 和当前列表长度比较,防止最后几页反复请求空数据。这个模式所有列表页都能套,收藏列表、我的房源、合同列表都复用同一套逻辑。
4.3 房源详情与预约表单:picker 日期、radio 租期与导航栏高度
详情页的预约表单涉及三个小程序常用控件。日期选择用 picker mode="date",租期选择用 radio-group,这是最顺手的组合:
<picker mode="date" start="{{today}}" bindchange="onDateChange"> <view class="picker-value">{{visitDate || '请选择看房日期'}}</view> </picker>日期选择拿到的是2024-06-01这种字符串,但 iOS 的 JavaScriptCore 不认这个格式,直接new Date('2024-06-01')会得到 Invalid Date。正确的做法是回填前先替换:
onDateChange(e) { // iOS 不认 yyyy-MM-dd,统一转成 yyyy/MM/dd const date = e.detail.value.replace(/-/g, '/') this.setData({ visitDate: date }) }这段代码就是“微信小程序顶部导航栏高度”这个老问题之外,第二个 iOS 适配的典型坑。导航栏高度如果用了自定义导航栏,也别写死,不同机型胶囊按钮位置不同,正确计算方式是读取胶囊布局信息:
const rect = wx.getMenuButtonBoundingClientRect() const windowInfo = wx.getWindowInfo ? wx.getWindowInfo() : wx.getSystemInfoSync() const navHeight = (rect.top - windowInfo.statusBarHeight) * 2 + rect.height租期选择用 radio-group,年付、月付、押一付三这类选项都适合单选:
<radio-group class="rent-type" bindchange="onRentTypeChange"> <label class="radio-item"> <radio value="12" checked />年付 </label> <label class="radio-item"> <radio value="1" />月付 </label> </radio-group>radio 在小程序里默认样式很丑,一般会通过 cover 或自定义样式把圆点和文字间距拉开。业务上建议给 radio 的 value 直接传数字字符串,后端接收后强转,避免“年付”这种中文值传到数据库还要再映射一次。
4.4 我的页面:按角色渲染不同菜单
登录接口返回 role 字段后,前端就要做分支渲染。我的页面用 wx:if 按角色显示不同入口,比写两套页面省事太多:
<view class="menu-group" wx:if="{{userInfo.role === 1}}"> <cell title="发布房源" bindtap="goPublish"></cell> <cell title="我的房源" bindtap="goMyHouse"></cell> <cell title="预约管理" bindtap="goAppointmentManager"></cell> </view> <view class="menu-group" wx:if="{{userInfo.role === 2}}"> <cell title="我的预约" bindtap="goMyAppointment"></cell> <cell title="我的合同" bindtap="goMyContract"></cell> <cell title="我的收藏" bindtap="goFavorite"></cell> </view>这里有一个容易被忽略的细节:wx:if 判断的是变量类型,后端返回的 role 是 number,但某些情况下 JSON 解析会变成字符串,role === 1永远为 false。我在联调时吃过这个亏,后来统一在后端返回 int,前端用Number(userInfo.role) === 1做判断,彻底根除类型问题。角色分支的登录态校验也要做在 onShow 里,因为用户可能在小程序后台切换账号,onLoad 只在首次加载执行一次,onShow 每次进入页面都会执行。
5. 避坑记录:房屋租赁小程序最常见的 5 个翻车现场
这一章写我做过、带人做过这类项目后沉淀下来的踩坑记录,全部是真实复现过的场景。按“现象 → 原因 → 解决”三条写,方便你对照排查。
5.1 环境与文件类:真机联调最容易踩的两个坑
坑 1:开发工具里接口正常,真机一打开就 request:fail
现象:开发者工具里预览、调试、网络面板都正常,扫码真机运行后白屏,控制台打 request:fail。
原因:接口地址写成了http://localhost:8080。开发者工具的 localhost 指向你电脑,真机的 localhost 指向手机自己,手机当然连不上。这是“微信小程序开发工具接口访问正常 真机接口访问失败”最常见的原因。
解决:把 BASE_URL 改成电脑的局域网 IP,比如http://192.168.1.100:8080,手机和电脑连同一个 WiFi 再试。在开发者工具“详情 → 本地设置”里勾选“不校验合法域名”,否则真机请求会被拦。正式上线前再去公众平台配置 HTTPS 的 request 合法域名,这一步别拖到审核前一天。
坑 2:图片上传成功后列表全裂图
现象:发布房源时缩略图能预览,发布成功后回到列表,封面图全是灰色占位块。
原因:上传时没有处理后端返回的永久 URL,而是把wx.chooseMedia返回的tempFilePath直接存进了 house 表。tempFilePath 是小程序运行期间的临时文件路径,页面关掉或小程序退出后就失效了。
解决:wx.uploadFile 成功后,用返回的永久 URL 覆盖临时路径,等所有图片传完再提交房源表单。封面图和轮播图统一存后端返回 URL 数组的 JSON 字符串,列表页不要再拼接任何本地路径。
5.2 数据与状态类:登录态、并发签约与日期解析
坑 3:登录成功后重启小程序又回到登录页
现象:用户首次登录后填完预约信息,退出小程序再进来,要求重新登录。
原因:登录成功后的 token 只存在当前页面的 data 里,没有写入 storage。小程序冷启动后 token 丢失,request 拦截器拿不到有效凭证,被 401 打回登录页。
解决:登录接口返回 token 后,立刻wx.setStorageSync('token', token)。request.js 里从 storage 读取而不是从 globalData 读取,见 4.1 节代码。另外,登录页要做静默登录:进入小程序先调一次 wx.login 换 code,后端自动注册,用户无感知地完成登录,而不是一进来就弹授权框。
坑 4:一套房被两个租客同时签约成功
现象:两个租客同时点“签约”,房东后台看到两份合同,房源状态却只有一个。
原因:签约接口用了“先查状态再更新”的写法。两个请求都查到了 status=1,然后分别执行 updateById,把状态从 1 改成 2,数据库层面没有任何约束拦住第二次写入。
解决:改用条件更新,UPDATE house SET status=2 WHERE id=? AND status=1,影响行数为 0 就直接提示“房源已被租出”。合同表再给 house_id 加唯一索引,双重保险。具体实现见 3.3 节代码,这个写法同样适用于预约确认和管理员强制下架,凡是状态跳变接口都必须条件更新。
坑 5:日期字段在 iOS 上显示 Invalid Date
现象:微信开发者工具里一切正常,iPhone 上合同开始日期显示 NaN 或 Invalid Date。
原因:picker mode="date" 返回的是2024-06-01格式字符串,iOS 的 JavaScriptCore 不认这种格式,new Date('2024-06-01')解析失败。
解决:取值后先e.detail.value.replace(/-/g, '/')再处理,数据库和接口层面统一用时间戳或yyyy/MM/dd格式。日期比较也不要直接拿字符串比大小,转成时间戳再比。后端返回日期给前端时,建议统一返回yyyy-MM-dd HH:mm:ss字符串,前端展示前再格式化。
提示:还有一个更隐蔽的日期坑是时区。数据库存的是北京时间,接口返回给小程序后没转时区,合同起止日会平白少 8 小时。如果你用时间戳存 contract 的 start_date 和 end_date,记得在服务端先转成东八区再返回。
6. 上线前验证:真机联调、体验版与审核注意的三个细节
系统功能写完后,最怕的不是逻辑 bug,而是“开发者工具里好好的,一上真机就露馅”。这一章给你三个上线前必做的动作,省得审核被驳回两次才反应过来说明书写得再好也没用。
发布体验版前跑通三个场景。第一个是弱网场景:开发者工具里把网络切到弱网,看房源列表是否出现加载兜底,接口超时有没有提示文案。第二个是多角色联调:准备一个房东账号一个租客账号,完整走一遍“发布房源 → 管理员审核 → 租客预约 → 房东确认 → 双方签约”的链路,中间任何一步卡住,先查状态字段是否符合预期。第三个是新设备首启:用一个之前没登录过的手机号扫码,确认静默登录能自动完成,不要第一步就弹授权框把人吓跑。
审核容易被拒的两个点要提前处理。类目选择上,房屋租赁类目属于房产行业,个人主体往往缺少资质文件。常见做法是把类目选到“生活服务”,服务描述写“个人房东房源信息发布与管理”,轻量很多。内容审核上,测试账号里不要放“月租 500 市中心精装”这类明显虚假的房源,审核员会逐条看数据真实性,最好用几套带真实地址脱敏的房源做演示。整个业务流程的按钮都不能是“开发中”占位,哪怕功能简单,也要有一个可用版本。
状态变更通知是提升体验的小技巧。房源被下架、预约被拒绝、合同即将到期,这些状态变化用户自己发现不了。最简单的方案是小程序内轮询关键状态接口,配合服务端判断后通过订阅消息推送。个人开发者申请订阅消息模板时,选择“预约提醒”和“业务通知”两个模板足够覆盖租赁场景。注意订阅消息是有次数限制的,别把模板消息当营销通道刷。
我之前上线过一版带后台管理的租赁小程序,最大的教训就是上真机前没做新设备全流程测试。当时图片上传成功但列表页的 src 拼接漏了一段路径,审核员打开看到全部裂图,连拒两次。后来养成一个习惯:每次发布体验版前,用一个新手机号从头走一遍完整流程,专门盯图片、日期、按钮置灰这三个细节,从此再没在审核环节翻过车。这条习惯今天也分享给你,希望帮到你。
本文还有配套的精品资源,点击获取