ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

UniApp开发家教中介微信小程序:从订单状态机到支付上线全解析

UniApp开发家教中介微信小程序:从订单状态机到支付上线全解析 最近手头做了一个家教中介平台的小程序从技术选型到上线前后花了一个多月用的就是UniApp。简单说这个项目解决的是家长找家教和大学生老师接单这个双向匹配问题家长在小程序里发布需求、浏览老师简历、下单付费老师端完成接单、约课、查看课酬结算。整套系统从零开始搭涉及微信登录、支付、订阅消息、订单状态机这些常规但绕不开的模块。如果你正准备用UniApp开发微信小程序或者想了解家教中介这类平台型小程序从设计到上线的完整链路这篇内容可以当一份实操参考。1. 项目设计先把家教中介的底子打对1.1 业务角色和核心流程家教中介本质是双边平台同时服务家长/学生端和老师端。家长端的核心诉求是快速找到靠谱的老师查看老师的教龄、学校/专业、试讲评价按次或按课时付费。老师端的核心诉求是展示自己的授课能力、接单、管理上课时间、结算课酬。中间的平台方也就是家教中介需要控制订单流程和资金流向。我在设计时把整个业务拆成了五条主线找老师和发需求家长可以浏览老师列表也可以发布一条带科目、年级、上课时长的需求单。接单老师看到需求大厅里的单子或收到订阅消息推送确认接单。支付家长下单后先支付课时费平台作为第三方托管。授课与确认线下或线上授课后家长确认完成课时费解冻并结算给老师。评价与售后家长对老师评价产生纠纷时可申请退款。这五条主线最后都落到“订单”这个核心实体上所以开发前把订单状态机和数据关系理清楚比急着写UI重要得多。我见过不少项目前期图省事订单状态随手定义几个字符串后面接单、退款、结算全在业务代码里散着写改一个需求就要翻遍所有接口代价非常大。1.2 为什么选UniApp而非原生微信小程序其实纯做微信小程序原生开发也完全可行这次选UniApp是有几层考虑。第一客户明确提到后续可能要做App和H5不想每端都养一套代码。UniApp用Vue语法一套代码编译到微信小程序、H5、App团队里前端继续写Vue不用额外学小程序那套自定义组件语法和WXML模板。第二UniApp对微信小程序的兼容性已经相当成熟uni.login、uni.requestPayment这些API在小程序端会自动映射成微信的wx.login、wx.requestPayment不用自己判断平台差异。第三HBuilderX的开箱即用体验不错新建项目选中“默认模板”里面已经配好了pages.json和manifest.json直接在微信开发者工具里CtrlR刷新就能看到效果。当然UniApp也有代价如果用到比较偏门的小程序原生能力比如某些硬件SDK、高级画布能力框架这层有时会成为瓶颈。但家教中介这种CRUD加支付类业务UniApp完全够用省下来的开发时间可以直接投入业务逻辑打磨。1.3 数据模型与订单状态设计我用了一个比较朴素但很稳定的数据库设计核心就几张表用户表、老师信息表、订单表、评价表、退款表。字段列一下方便你照着建表。用户表user字段类型说明idint主键openidvarchar微信openidunionidvarchar微信unionidroletinyint1家长/2老师/3管理员nicknamevarchar用户昵称avatar_urlvarchar头像地址phonechar手机号statustinyint1正常/0禁用create_timedatetime注册时间老师信息表teacher_info字段类型说明idint主键user_idint关联user.idsubjectvarchar授课科目schoolvarchar就读/毕业院校introtext个人简介pricedecimal(10,2)单课时价格total_lessonsint累计授课次数audit_statustinyint0待审核/1通过/2驳回订单表order字段类型说明idint主键order_novarchar订单号parent_idint家长用户IDteacher_idint老师用户IDsubjectvarchar科目lesson_countint课时数amountdecimal(10,2)实付金额statustinyint状态pay_timedatetime支付时间create_timedatetime下单时间订单状态我定义成了7个数字后面所有接口逻辑都围着这组状态转0 待支付1 已支付待接单2 老师已接单3 授课中第一次课开始后4 已完成家长确认5 已取消6 退款中7 已退款这里最关键的一个决定是“资金托管”的思路家长付款后钱先进平台的商户号而不是直接打给老师等家长确认完成某次课后平台再把对应课酬结算给老师。这样做的好处是降低交易纠纷代价是后端要单独维护一个“可结算余额”字段逻辑比直接打款复杂一些。跟客户解释这个方案时我用了一个类比就像在电商平台买东西钱先由平台保管签收后商家才能拿到钱。家长一听就理解了业务上也更稳。2. 核心功能实现登录、下单、支付一条线2.1 微信登录与用户角色绑定家教中介和普通内容类小程序不一样用户注册完必须明确自己的身份是家长还是老师因为两边看到的首页和功能完全不同。所以我做登录时没有一上来就让用户选角色而是先静默登录拿到openid生成token再把角色选择放在注册资料页。登录流程核心代码大致是这样// pages/login/login.vue uni.login({ provider: weixin, success: (loginRes) { const code loginRes.code uni.request({ url: ${baseUrl}/api/auth/login, method: POST, data: { code }, success: (res) { if (res.data.code 0) { uni.setStorageSync(token, res.data.data.token) uni.setStorageSync(userInfo, res.data.data.userInfo) // 根据userInfo.role跳转不同页面 if (res.data.data.userInfo.role 0) { uni.navigateTo({ url: /pages/register/register }) } else { uni.switchTab({ url: /pages/index/index }) } } } }) } })后端拿着code去微信的jscode2session接口换openid和session_key这一步注意两点code只能用一次前端重复触发登录后端要做好幂等不能因为网络重试就报错openid是每个小程序加每个微信用户唯一的同一个用户在“家教小程序”和另一个小程序里的openid不一样所以如果后续要做App、公众号数据打通最好同时存储unionid。我这次项目里就存了unionid后面客户要开通公众号会员卡时直接用上了。再说新版头像昵称获取。2022年后微信调整了规则wx.getUserProfile拿不到真实头像昵称现在推荐的做法是让用户手动填头像用button的open-typechooseAvatar昵称用input的typenickname。微信会自动带出用户微信昵称用户点了就能回填体验很顺。如果还按旧资料去写wx.getUserProfile真机会直接返回灰色头像和一串“微信用户”占位昵称。这个坑我调了整整一个下午印象非常深。2.2 需求发布与订单状态流转家长端发布需求是一个大表单科目、年级、老师性别偏好、预算、上课方式线上/上门、期望上课时间段提交后生成一条“找老师需求单”。老师端首页通过“推荐老师”和“需求大厅”两个入口做匹配需求大厅按发布时间倒序展示老师可以筛选科目和年级。订单状态流转我用了状态机来管理。因为家教订单和电商订单不一样它不是“下单-发货-收货”的简单线性流程而是支持多次课、部分退款、老师拒绝接单等分支。我在后端写了一个OrderStateMachine所有订单操作支付、接单、取消、确认完成都走同一个处理器按“当前状态操作事件”决定下一步状态和是否触发消息通知。状态流转核心关系操作/当前状态待支付已支付待接单授课中已完成支付已支付待接单---接单-授课中--确认完成--已完成-取消订单已取消已取消不允许不允许这个状态机避免了大量散落在业务代码里的if else后来查线上问题效率高了不少。订阅消息这里要特别提一下小程序为了防骚扰消息推送要用订阅消息模板每次发送前都要用户主动订阅一次一次性模板只能发一条。我的实现是家长下单成功后弹窗让家长授权“订单进度通知”和“老师接单通知”同时老师端在接单前也授权“新订单通知”。模板ID在微信公众平台申请并审核通过后以常量配置在代码里不要写死在请求URL里。2.3 微信支付的接入与退款处理支付是家教中介这类业务最绕不开的环节。小程序内支付的流程是前端拿订单号调后端创建支付单接口后端调微信支付统一下单接口传入openid、订单号、金额得到prepay_id和paySign参数前端用uni.requestPayment拉起收银台用户输入密码完成支付最后微信支付异步通知后端支付结果后端更新订单状态。前端代码核心就这几行uni.requestPayment({ provider: wxpay, timeStamp: payParams.timeStamp, nonceStr: payParams.nonceStr, package: payParams.package, signType: RSA, paySign: payParams.paySign, success: () { uni.showToast({ title: 支付成功 }) }, fail: (err) { // 用户取消或支付失败 console.error(支付失败, err) } })注意这里的package字段在uni-app里是保留字传给requestPayment时直接写package但如果你在某些组件或模板里用了同名变量可能触发编译报错。我给项目组定的规范是一律叫payPackage避免踩坑。退款部分建议前端只做“申请退款”不直接调退款接口。因为退款涉及资金安全必须由后端校验订单状态、计算可退金额再调用微信支付的退款接口。如果家长只上了一次课要退剩余课时后端要做一次按比例退的计算并把退款记录写到退款表方便记账和对账。支付回调还要做幂等处理因为微信支付的通知有可能重复推送后端必须判断订单是否已处理过避免重复更新。3. 从HBuilderX到微信开发者工具开发调试与上线3.1 HBuilderX里创建项目和appid配置用HBuilderX新建项目选择“默认模板uni-app”框架选Vue 3然后打开项目根目录的manifest.json在“微信小程序配置”这一栏填上微信小程序的AppID。有个坑非常多的人问明明在HBuilderX里改了appid为什么运行到微信开发者工具里模拟器上报错还带着旧的小程序标识原因在于HBuilderX运行微信小程序时会生成一个unpackage/dist/dev/mp-weixin目录里面有一套project.config.json如果这个文件里保留了旧的appid微信开发者工具会用这个旧值覆盖HBuilderX同步过来的配置。解决办法很简单每次修改appid后先清理unpackage/dist/dev/mp-weixin目录再重新运行或者直接在微信开发者工具里点“详情-基本信息”把AppID改成新的。如果是用cli方式创建的项目还要检查src/manifest.json和根目录project.config.json两个文件都要改。提示HBuilderX运行到微信开发者工具时如果开发者工具的端口被占用或版本不一致会频繁报“编译失败”或“上传失败”。建议把HBuilderX和微信开发者工具都升级到正式版并在微信开发者工具“设置-安全设置”里开启服务端口。3.2 微信小程序的特殊配置与提审小程序上线前有几步必须提前做不然后端接口会全部请求失败。第一配置request合法域名。在微信公众平台的开发管理-开发设置-服务器域名里把https接口域名加进去。开发阶段可以勾选“不校验合法域名”但上传正式版后这个勾选无效必须走真实域名配置。第二开通支付。小程序后台要关联微信支付商户号然后把商户号mch_id、APIv3密钥写入后端配置文件。这一步如果没做前端调uni.requestPayment会一直报“商户号未关联”。第三小程序上传代码后在微信公众平台提交审核。家教中介这种平台类小程序审核时容易因为涉及“在线交易教育服务”被要求补充类目资质我这次在服务类目里选了“教育服务-培训机构”并把平台的营业执照和ICP备案传上去审核才顺利通过。如果只做微信小程序端到这里基本就结束了。但UniApp的好处在于如果客户后面说“我要个安卓App”你可以在HBuilderX里选择“发行-原生App云打包”填好Android包名和证书就能生成apk。代码里如果有平台差异化逻辑用条件编译// #ifdef MP-WEIXIN console.log(这一段只在微信小程序里执行) // #endif // #ifdef APP-PLUS console.log(这一段只在App里执行) // #endif3.3 WebView、地图等原生能力的兼容差异我做家教中介时有一部分老师需要上传试讲视频或展示教学场地我用了web-view嵌套一个视频详情页。在小程序里打开web-view页面会有过渡白屏原因是web-view加载H5页面需要时间尤其首次冷启动时明显。优化办法是先加载一个带loading动画的原生页面等web-view的加载完成事件触发后再切换显示。如果加载速度依然太慢可以考虑直接用小程序原生video组件替代页面跳转。另外有些页面要用地图标注老师的上门授课范围。UniApp编译到微信小程序时map组件是原生的H5端只能用腾讯地图JS这种差异要用条件编译分别处理否则在H5端直接白屏。当时为了让两端都能用我把地图封装成了一个公共组件内部用条件编译区分调用方不需要感知差异。4. 开发中踩过的坑与排查记录4.1 登录态失效获取登录后的微信用户失败开发中后台会报错“获取微信用户失败”小程序端提示信息里有时会带类似wx1cb4398e1413dce7这样的小程序标识当时我排查了很久。这种问题有几种典型成因manifest.json里配的还是测试appid后端实际用的却是另一个小程序的appid导致前端拿到的code在后端解析时和前端对不上后端调jscode2session接口返回session_key为空通常是小程序没有做服务器域名配置或appsecret配错前端在非用户主动操作时调了uni.login微信对这种静默请求有频率限制。排查方法是先在前端打印uni.login拿到的code后端拿到code后手动用REST工具调用一次jscode2session返回的openid如果和数据库对不上基本就是appid和appsecret的问题不是代码逻辑的问题。定位到这一步问题通常就解决了一半。4.2 真机调试报net::ERR_CONNECTION_RESET这个问题是客户在验收时遇到的电脑模拟器一切正常真机扫码一打开就报请求失败错误信息是net::ERR_CONNECTION_RESET。排查了一遍最可能的原因是开发环境的后端地址是http的局域网IP真机和小程序要求必须是https且配置合法域名。我在开发阶段用了一个小技巧本地起一个支持https的反向代理然后把这个https域名临时加到微信公众平台的工作台域名里再把手机和电脑连同一个网段调试。线上环境因为域名本来就合法所以不存在这个问题。如果只是本机调试也可以临时在微信开发者工具里勾选“不校验合法域名”但注意这只对开发者工具有效真机预览时还是要靠配置解决。4.3 下拉刷新与滚动冲突、导航栏高度适配小程序页面开启下拉刷新后如果页面里同时有scroll-view或较长list很容易出现“手指往下滑时先触发了页面级下拉刷新而不是列表滚动”的问题。解决办法是在scroll-view上监听scrolltoupper当scrollTop为0时再调用uni.startPullDownRefresh同时在页面onPullDownRefresh里做数据刷新或者在scroll-view外层用一个普通view包着避免两套滚动叠加。顶部导航栏的高度在自定义导航栏时也要单独算。用uni.getSystemInfoSync()可以拿到statusBarHeight安卓和iOS不一样刘海屏也不一样计算整个导航栏高度时用statusBarHeight 44px44px是微信小程序默认导航栏高度。如果直接用固定px写死部分机型上按钮会被刘海遮住。我封装了一个工具函数const systemInfo uni.getSystemInfoSync() export const navBarHeight systemInfo.statusBarHeight 444.4 常见问题速查表问题原因解决参考运行后小程序标识还是旧的unpackage目录里项目配置文件残留旧appid清理mp-weixin目录再运行或在微信开发者工具详情中修改AppID真机请求报net::ERR_CONNECTION_RESET请求域名不是https或不在合法域名列表配置合法域名或临时勾选不校验域名页面白屏页面路由不存在、组件未注册或基础库版本过低检查pages.json路由升级微信开发者工具基础库按钮在刘海屏上错位自定义导航栏用了固定px用statusBarHeight 44计算下单支付失败商户号未关联或参数格式错误检查商户号绑定和RSA签名参数用户头像昵称为灰色占位仍在使用旧版wx.getUserProfile改用chooseAvatar和typenickname输入框结尾我做完这个项目最大的体会是家教中介这种平台型小程序技术本身的难点并不在于某个单点功能而在于把用户角色、订单状态、资金流和消息通知串成一条完整且一致的链路。微信登录、支付、订阅消息这些能力单独看都不难但一旦订单状态设计得模棱两可后面接单、退款、结算每个环节都会连环出问题。所以动手之前建议先把状态机画清楚用纸笔画就行然后再写业务代码。最后再分享一个小技巧尽量把业务逻辑往“服务端驱动”靠前端只负责渲染和用户交互。比如订单状态是否可取消、退款金额怎么算这种逻辑放在后端统一处理前端拿到状态后按条件渲染按钮就行。这样哪怕以后你多端发布App、H5复用同一套后端逻辑不至于每一端都要重写一遍业务规则。希望这篇内容对准备用UniApp做微信小程序家教中介项目的朋友有点帮助。
返回列表