简介:基于微信小程序的智慧家政系统毕业设计项目,采用 Vue、SpringBoot 与 MySQL 构建,面向需要完成 Java 毕业设计的学生,以及希望快速上手家政服务类小程序开发的工程师。项目围绕家政服务线上管理场景,覆盖地址管理、订单管理、家政分类、家政服务、用户反馈等核心模块,并集成用户管理、部门管理、角色管理、菜单管理、日志管理、数据字典等基础功能,支持基于角色的访问控制,权限可精确到按钮级别,适合需要自定义角色并严格分配权限的毕业设计题目。资源为 zip 压缩包,共 409 个文件,大小约 1.9MB,以 161 个 Java 源码文件、72 个 Vue 组件、72 个 JS 脚本为主体,另有 SQL 数据库脚本、YML 环境配置、小程序页面结构与样式文件、PNG 图片素材等,后端、前端、数据库与静态资源覆盖完整,目录结构清晰,便于按需检索和二次开发。已有 543 人学习下载,可直接作为选题参考和功能拆解对象,也可通过部署运行观察订单流转、服务分类与权限控制的实际效果,串联起小程序端、接口层和数据表设计之间的协作关系。
1. 基于微信小程序的智慧家政:让排单、派单、催进度从打电话变成看手机
排单靠微信群里吼一嗓子,阿姨上工全靠电话确认,用户催进度只能一遍遍打电话——这是我接到「基于微信小程序的智慧家政系统」这个入口时最先想解决的三个问题。这个项目用 Vue 写小程序前端,后端用 JAVA 配 SpringBoot,数据落在 MySQL 里,核心就是把「找服务 → 下单 → 派单 → 上门 → 完结」这条链路从线下搬进手机。它适合做毕业设计时想练全栈的学生,也适合家政公司老板想低成本做信息化。下面我按自己做这类系统时的顺序,先讲设计边界,再给数据库和两端代码,最后把最容易翻车的地方挨个点一遍。
2. 技术选型与数据库设计:为什么是 Vue+SpringBoot+MySQL,三张核心表怎么定
2.1 选型理由:这套组合在毕业设计里到底图什么
微信小程序是前端容器,用户不用下载 App,扫码或搜索就能进,对家政这种低频但刚需的场景来说获客成本低。Vue 用来组织小程序页面:列表、表单、状态切换都是典型的前端交互,用 Vue 的数据绑定比原生小程序写起来顺手很多。后端选 SpringBoot,是因为它内置了 Web 服务器和自动配置,一个 Main 方法就能起服务,不需要像传统 SSH 那样堆一堆 XML 配置。MySQL 则是数据落地的稳定选择,订单、服务项目、阿姨信息这些结构化数据用它管理最方便,而且招聘市场上熟悉 MySQL 的人多,后续维护不愁没人接。
如果你只是应付毕业设计,这套组合还有个隐性好处:前端、后端、数据库三层拆分得很清楚,答辩时每一层都能单独讲出设计思路。常见做法是用一个支持 Vue 语法的小程序框架先把页面跑通,后端用 SpringBoot 暴露 JSON 接口,小程序端通过 wx.request 去调。把每一层的边界划清楚,后面写代码和写论文都省力。
2.2 MVP 功能边界:先砍掉「抢单大厅」这类伪需求
我第一次做这个项目时,列了一堆功能:用户评论、阿姨抢单、自动派单、优惠券、积分商城,结果数据库建了十几个表,最后只完成了下单和列表。后来我把功能砍到最小可用版本,反而一周就打通了全流程。如果你也在拿这个题目做毕业设计,建议先锁住三个角色:用户端能浏览服务项目、提交订单、查看订单状态;管理端能维护服务项目和阿姨信息、手动派单、更新订单状态;系统层面必须有明确的订单状态流转,这是整个项目的骨架。
砍功能的标准很简单:没有它,订单无法从下单走到完结,就不算核心功能。优惠券、评分、积分全部往后放。等到核心链路跑通了,再加这些作为亮点功能,比一开始就铺开要稳妥得多。
2.3 三张核心表:把业务钉死在数据结构上
数据库里最能看出设计功力的不是表多,而是订单表的状态设计。这里给出最小可用的三张表:服务项目表 service_item、订单表 orders、服务人员表 employee。状态字段我单独抽出来设计,不搞成「已接单所以就不保存取消时间」这种隐含逻辑。
CREATE TABLE service_item ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '服务项目ID', name VARCHAR(50) NOT NULL COMMENT '服务名称,如日常保洁', price DECIMAL(10,2) NOT NULL COMMENT '单价,单位元', unit VARCHAR(10) DEFAULT '次' COMMENT '计费单位:次/小时', cover_url VARCHAR(255) COMMENT '封面图URL', description VARCHAR(500) COMMENT '服务描述', status TINYINT DEFAULT 1 COMMENT '1上架 0下架', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='服务项目表'; CREATE TABLE employee ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '服务人员ID', name VARCHAR(30) NOT NULL COMMENT '姓名', phone VARCHAR(20) NOT NULL COMMENT '联系方式', service_type BIGINT COMMENT '关联service_item.id,擅长项目', order_count INT DEFAULT 0 COMMENT '累计接单数', status TINYINT DEFAULT 1 COMMENT '1可接单 0休息', create_time DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='服务人员表'; CREATE TABLE orders ( id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '订单ID', order_no VARCHAR(32) NOT NULL UNIQUE COMMENT '业务订单号,全局唯一', user_id BIGINT NOT NULL COMMENT '下单用户ID', service_item_id BIGINT NOT NULL COMMENT '服务项目ID', employee_id BIGINT COMMENT '服务人员ID,派单后写入', service_date DATE NOT NULL COMMENT '预约上门日期', service_time_slot VARCHAR(20) COMMENT '时间段,如09:00-11:00', address VARCHAR(255) NOT NULL COMMENT '服务地址', amount DECIMAL(10,2) NOT NULL COMMENT '订单金额', order_status TINYINT NOT NULL DEFAULT 0 COMMENT '0待支付 1待派单 2待服务 3服务中 4已完成 5已取消', pay_status TINYINT NOT NULL DEFAULT 0 COMMENT '0未支付 1已支付 2已退款', cancel_reason VARCHAR(255) COMMENT '取消原因', create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '下单时间', update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';逻辑说明:orders 表把 order_status 和 pay_status 分开,是因为「支付了但还没派单」和「派单了但还没支付」是完全不同的业务状态;如果只用一个状态字段,查询时要把两个维度揉在一起,写 SQL 容易出错。employee 表里的 service_type 是逻辑外键,没有创建物理外键约束,因为实际项目里外键约束会影响插入性能和后续分表,业务层面用应用代码保证一致性更灵活。
参数说明:order_no 设置为 UNIQUE,这是防止重复下单的第一道闸门,生成策略在后面接口里讲。service_date 用 DATE 类型而非 DATETIME,因为预约只精确到天,时间段用单独的字符串字段存,避免把日期和时间混在一个字段里导致索引失效。amount 用 DECIMAL(10,2),不用 FLOAT,家政项目金额虽然小,但涉及结算时浮点数精度问题属于自作孽。
3. SpringBoot 后端接口:下单流程、状态机与支付回调的最小实现
3.1 项目骨架:分层建好,后面写接口不迷路
我用 SpringBoot + MyBatis-Plus 搭后端,结构按 controller → service → mapper 三层拆。MyBatis-Plus 能少写很多单表 CRUD,这对毕业设计来说节省的时间非常可观。目录结构大致是这样:
src/main/java/com/example/housekeeping ├── controller │ ├── OrderController.java │ └── ServiceItemController.java ├── service │ ├── OrderService.java │ └── impl/OrderServiceImpl.java ├── mapper │ ├── OrderMapper.java │ └── ServiceItemMapper.java ├── entity │ ├── Orders.java │ └── ServiceItem.java └── common ├── Result.java └── GlobalExceptionHandler.java启动类不用额外配置,SpringBoot 的自动装配会把内嵌 Tomcat 启起来,你只需要在 application.yml 里配好端口和数据源。常见的做法是生成一个全局返回体 Result,让所有接口返回 {code, message, data} 的统一格式,前端小程序拦截起来才方便。
3.2 下单接口:生成订单号、校验服务项、写库一步到位
下单是整个系统最关键的一个接口,用户从小程序点「立即预约」后,前端会把服务项目 ID、预约时间、地址传给后端。后端要做三件事:校验服务项目是否上架、生成唯一订单号、把订单记录插入数据库。
@RestController @RequestMapping("/api/order") public class OrderController { @Autowired private OrderService orderService; @PostMapping("/create") public Result<String> create(@RequestBody @Valid CreateOrderDTO dto) { // dto: serviceItemId, serviceDate, serviceTimeSlot, address String orderNo = orderService.createOrder(dto); return Result.success(orderNo); } }@Service public class OrderServiceImpl implements OrderService { @Autowired private ServiceItemMapper serviceItemMapper; @Autowired private OrderMapper orderMapper; @Override public String createOrder(CreateOrderDTO dto) { ServiceItem item = serviceItemMapper.selectById(dto.getServiceItemId()); if (item == null || item.getStatus() != 1) { throw new BizException("服务项目不存在或已下架"); } String orderNo = generateOrderNo(); Orders order = new Orders(); order.setOrderNo(orderNo); order.setServiceItemId(dto.getServiceItemId()); order.setServiceDate(dto.getServiceDate()); order.setServiceTimeSlot(dto.getServiceTimeSlot()); order.setAddress(dto.getAddress()); order.setAmount(item.getPrice()); order.setOrderStatus(0); // 初始状态:待支付 order.setPayStatus(0); orderMapper.insert(order); return orderNo; } private String generateOrderNo() { return "HK" + System.currentTimeMillis() + RandomUtil.randomNumbers(4); } }逻辑说明:generateOrderNo 用时间戳加 4 位随机数,配合 orders 表的 UNIQUE 约束来保证不重复。如果极端情况下随机数撞了,插入时会抛 DuplicateKeyException,全局异常处理器捕获后返回「请重试」即可,不用在业务代码里加分布式锁,毕业设计的数据量完全不需要引入 Redis 那套复杂度。
参数说明:CreateOrderDTO 用 @Valid 注解做参数校验,serviceDate 要求格式 yyyy-MM-dd,address 限制长度不超过 255。前端传过来的服务时间段我只做存储,不做合法性校验——因为时间的合法性应该由你在管理后台维护服务时间模板时控制,而不是在下单接口里硬编码。
3.3 支付回调:验签、幂等、更新状态,少一个都是隐患
用户在小程序里支付后,微信支付会往你配置的回调地址发 POST 请求。很多新手在这里犯的错是:回调里直接改订单状态为「已支付」,然后返回 success。这看似简单,但漏了两个关键点——验签和重复通知处理。
@PostMapping("/api/pay/callback") public String payCallback(@RequestBody String notifyData) { // 1. 验签:用微信支付平台证书验证通知签名 boolean valid = WxPayUtil.verifySignature(notifyData); if (!valid) { return "failure"; } // 2. 解析通知,拿到订单号和支付结果 String orderNo = WxPayUtil.parseOrderNo(notifyData); String tradeStatus = WxPayUtil.parseTradeStatus(notifyData); if ("SUCCESS".equals(tradeStatus)) { // 3. 幂等更新:只有待支付状态才更新为已支付 LambdaUpdateWrapper<Orders> wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(Orders::getOrderNo, orderNo) .eq(Orders::getPayStatus, 0) .set(Orders::getPayStatus, 1); orderMapper.update(null, wrapper); } return "success"; }逻辑说明:第 3 步是整段代码的命门。回调可能因为网络抖动被微信重复发送多次,如果不用 pay_status=0 作为条件,第二次回调会重复执行「待支付转已支付」的更新。加了条件后,第二次回调影响行数为 0,不会产生副作用。验签必须放在最前面,否则任何人都可以伪造回调把你订单改成已支付,这是资金安全红线。
注意:个人开发者的微信小程序无法直接开通微信支付商户号。我在做模拟项目X时,本地和演示阶段先写了一个假的支付接口,前端点击「立即支付」后直接跳转到模拟支付页,后端收到一个测试回调把订单置为已支付。到答辩演示时,把回调地址切到真实环境即可,整套流程不用大改。这样既跑通了业务链路,又不会被商户号审批卡住进度。
3.4 订单状态机:用一张字典把状态流转写清楚
订单状态我定义成 5 个数字:0 待支付、1 待派单、2 待服务、3 服务中、4 已完成、5 已取消。合法流转是:0 → 1 → 2 → 3 → 4,其中 0 可以在用户主动取消时跳到 5,3 之后管理员可以标记完成。任何非法的跳跃,比如从 1 直接跳到 4,都应该被写死在代码的校验逻辑里。
我一般在管理端的接单接口里加一个前置校验:只有当前状态是 1(待派单)才允许指派阿姨,并把状态改成 2(待服务)。这样做的好处是,无论前端怎么调接口,数据状态都不会被破坏,哪怕有人绕过前端直接抓包改数据也无济于事。把这个状态机画成表格放进论文里,答辩老师一眼就能看出你考虑了业务边界。
4. 小程序端 Vue 页面:请求封装、服务列表与一键预约下单
4.1 小程序框架与页面结构:用 Vue 的思维写小程序
小程序端最常见的做法是采用支持 Vue 语法的小程序框架,页面写法与 Vue SFC 基本一致,上手成本低。如果你只想练 Java 后端,用原生小程序写页面也能实现同等功能,只是 Vue 风格的组件化写法在维护体验上会好很多。我在页面结构上按 TabBar 分成首页、订单、我的三块,核心页面只有五个:首页服务列表、服务详情、预约下单、订单列表、订单详情。
src/pages ├── index/index.vue ├── service-detail/detail.vue ├── order-create/create.vue ├── order-list/list.vue └── order-detail/detail.vue目录结构里的每个页面只做一件事:index 负责展示服务项目,order-create 负责收集预约信息并提交。别把页面拆得太细,但也不能把列表、详情、下单全塞进一个文件里,后面改需求时你会想抽自己。
4.2 请求封装:把 wx.request 变成可复用的 Promise
小程序原生的 wx.request 写起来啰嗦,而且不支持 Promise,每个页面都要处理 success / fail 两个回调,代码重复度很高。我的习惯是封装一个 request.js,统一管理接口地址和 token,顺便把登录态失效跳转也处理了。
const BASE_URL = 'https://your-domain.com/api' function request({ url, method = 'GET', data = {} }) { return new Promise((resolve, reject) => { wx.request({ url: BASE_URL + url, method, data, header: { 'Content-Type': 'application/json', // 如果有登录态,自动带上 token 'Authorization': wx.getStorageSync('token') || '' }, success: (res) => { if (res.statusCode === 200 && res.data.code === 0) { resolve(res.data.data) } else if (res.statusCode === 401) { wx.navigateTo({ url: '/pages/login/login' }) } else { wx.showToast({ title: res.data.message || '请求失败', icon: 'none' }) reject(res.data) } }, fail: (err) => { wx.showToast({ title: '网络异常', icon: 'none' }) reject(err) } }) }) } export default request逻辑说明:统一在 Promise 的 resolve 里只返回业务数据 data,页面里拿到的直接就是用了,不用反复解包 result 里的 data。code === 0 是我在 Result 里定义的业务成功码,接口返回非 0 时统一弹 toast 提示。401 的处理很关键——用户登录态过期后,任何接口都会返回 401,统一跳登录页而不是在十几个页面里各写一套。
参数说明:BASE_URL 在开发阶段可以指向本地电脑的局域网 IP,比如 http://192.168.1.100:8080/api,但真机预览时小程序必须用「合法域名」。你可以在开发者工具里勾选「不校验合法域名」来本地调试,这个坑后面专门说。
4.3 首页服务列表:一页看懂前后端链路
首页从后端拉服务项目列表并渲染成卡片,按类别展示。这一页麻雀虽小五脏俱全,覆盖了「页面加载 → 请求后端 → 渲染数据」的完整链路。
<template> <view class="service-list"> <view v-for="item in serviceList" :key="item.id" class="card" @click="goDetail(item.id)"> <image :src="item.coverUrl" class="cover" mode="aspectFill" /> <view class="info"> <text class="name">{{ item.name }}</text> <text class="price">¥ {{ item.price }}/{{ item.unit }}</text> </view> </view> </view> </template> <script> import request from '../../utils/request' export default { data() { return { serviceList: [] } }, onLoad() { this.fetchServiceList() }, methods: { async fetchServiceList() { this.serviceList = await request({ url: '/service/item/list', method: 'GET' }) }, goDetail(id) { uni.navigateTo({ url: `/pages/service-detail/detail?id=${id}` }) } } } </script>逻辑说明:onLoad 在页面初始化时只调用一次,适合做首屏数据拉取。模板里 v-for 渲染列表,点击卡片带 id 跳详情页,下一页通过 onLoad 的 query 参数拿到 id 再请求详情接口。注意这里的/service/item/list后端接口返回的字段是驼峰格式 coverUrl、unit,因为我在后端做了全局 Jackson 配置把下划线转驼峰,前端代码不用写一堆字段映射。
参数说明:mode="aspectFill" 让图片按比例裁剪填充,避免家政服务封面图被拉伸变形。price 保留两位小数是后端格式化的,前端不要用 toFixed 再去截断,容易在不同小程序基础库上显示不一致。
4.4 预约下单页:把用户选择组装成下单参数
下单页是参数密集的页面:服务项目、预约日期、时间段、地址,每一项都要做校验,提交时一键下单。
<template> <view class="create-order"> <picker mode="date" @change="onDateChange"> <view class="field">预约日期:{{ serviceDate || '请选择' }}</view> </picker> <picker :range="timeSlots" @change="onTimeChange"> <view class="field">服务时间:{{ serviceTimeSlot || '请选择' }}</view> </picker> <input v-model="address" placeholder="请输入服务地址" /> <button @click="submitOrder">立即支付</button> </view> </template> <script> export default { data() { return { serviceItemId: '', serviceDate: '', serviceTimeSlot: '', address: '', timeSlots: ['09:00-11:00', '14:00-16:00', '19:00-21:00'] } }, onLoad(query) { this.serviceItemId = query.id }, methods: { async submitOrder() { if (!this.serviceDate || !this.serviceTimeSlot || !this.address.trim()) { wx.showToast({ title: '请完善预约信息', icon: 'none' }) return } const orderNo = await request({ url: '/order/create', method: 'POST', data: { serviceItemId: this.serviceItemId, serviceDate: this.serviceDate, serviceTimeSlot: this.serviceTimeSlot, address: this.address.trim() } }) wx.navigateTo({ url: `/pages/pay/pay?orderNo=${orderNo}` }) } } } </script>逻辑说明:submitOrder 先做前端基础校验,再调后端下单接口,成功后带着 orderNo 跳转到支付页。支付页拿到 orderNo 后发起微信支付;在模拟支付阶段,支付页可以直接放一个「模拟支付成功」按钮,调用后端测试接口触发回调,把订单置为已支付。这套设计的好处是支付环节和后端回调解耦,不会因为商户号没申请下来就阻塞整体开发。
参数说明:timeSlots 目前是写死在前端的。如果以后要做后台可配置的服务时间,这个数组应该从后端拉取,并且下单接口需要校验传入的时间段在允许的模板内。毕业设计阶段写死没问题,答辩时你说得出「这里预留了动态配置扩展点」就够了。
5. 避坑与排查:5 个最常见的翻车点与修复路径
5.1 本地一切正常,真机白屏:request 合法域名是个黑匣子
现象:开发者工具里预览服务列表、下单都很流畅,一用手机真机预览,页面转圈后直接空白。控制台报的就是 request 的 url 不在合法域名列表中。
原因:小程序真机环境强制校验 request 域名必须是 HTTPS,且要在小程序管理后台配置。开发者工具默认勾选了「不校验合法域名」,所以本地没问题,真机上直接拦截。
解决:开发调试阶段不要慌,在开发者工具「详情 → 本地设置 → 勾选不校验合法域名」,先用局域网 IP 调通功能和 UI。要上真机完整演示,就得准备一个已备案的 HTTPS 域名,后端用 Nginx 反向代理到 SpringBoot 端口,然后在微信公众平台「开发管理 → 开发设置 → 服务器域名」里加 request 合法域名。这是微信的规则,不是你的代码问题,别在代码里找原因。
5.2 小程序包体积超限:主包超过 2M,构建直接失败
现象:前端框架构建产物超过 2M,上传代码时提示主包大小超出限制,真机加载异常缓慢。
原因:我把服务列表的封面图全部放进了小程序静态资源目录,又引入了整个 UI 组件库,构建产物直接膨胀。图片资源才是元凶,代码本身通常没多少。
解决:第一步,将项目按 TabBar 页面和分包页面拆成 subpackage,下单页、订单详情这些非首屏页面放到分包里,主包只留首页和公共组件。第二步,封面图全部改成网络图片 URL,由后端接口返回,小程序端只在云存储或服务器上保存压缩后的 80KB 以内缩略图。第三步,UI 组件库按需引入,别一键引入全部组件。
5.3 支付回调重复通知导致状态错乱
现象:用户支付成功后,服务端收到两条回调通知,订单最终状态停留在「待派单」而不是「已支付」,后台查流水才发现是回调处理里覆盖了状态。
原因:微信支付回调在网络抖动时会重发通知。我在回调处理里直接 update 订单状态,没有校验「当前状态是否为待支付」,导致第二次回调把已经派单的订单状态又覆盖了。
解决:回调更新永远带上状态前置条件,用乐观锁思路。比如:
wrapper.eq(Orders::getOrderNo, orderNo) .eq(Orders::getPayStatus, 0) .in(Orders::getOrderStatus, 0, 1)同时,处理完回调后立即返回 success,微信收到 success 后不会再重发。如果业务处理失败,返回 failure,微信会按策略重试,你的接口必须保证幂等,这个逻辑怎么强调都不过分。
5.4 模拟器显示正常,真机时间差 8 小时
现象:订单列表页显示的下单时间比实际时间晚了 8 个小时,但开发者工具里完全正常。
原因:后端从 MySQL 查出 DATETIME 后,Jackson 序列化默认转成 UTC 时间字符串,小程序端 new Date() 解析时按本地时区转换,真机时区设置和模拟器不一致就出现了偏差。
解决:前后端统一用时间戳传输。后端在 Jackson 配置里把 LocalDateTime 序列化成 epoch 毫秒值,前端直接 new Date(timestamp) 渲染,完全绕开时区问题。数据库里存什么格式不重要,传输层格式统一才是关键。
5.5 线上接口突然全部超时:数据库连接池被慢查询拖垮
现象:家政公司实际使用一周后,某个下午所有接口集体卡死,重启 SpringBoot 后恢复,但没过多久又超时。
原因:订单列表页做了一个多表 join 查询,用户反复刷新时,每次请求都要扫描全表,MySQL 连接数被慢查询占满,后续请求全部排队。
解决:第一,SQL 里去掉多余的 join,改为两次简单查询并在内存中组装数据;第二,在常用查询字段上建索引,比如 orders 表的 user_id 和 order_status 联合索引、service_item 表的 status 索引;第三,给 HikariCP 连接池设置最大连接数和连接超时时间,防止极端情况下连接被耗尽。排查时先在 MySQL 执行 SHOW PROCESSLIST,看到大量长时间未完成的 select 基本就能锁定是慢查询问题。
6. 进阶:让订单从「查得到」变成「推得动」
到这里,核心链路已经通了,但用户还是会问:「阿姨什么时候到?」「订单做完没有?」你再做个页面让他来查,是一种方案;另一种方案是让系统把状态主动推给他。小程序有个能力叫订阅消息,用户授权后,后端可以在订单状态变化时,通过微信服务器给用户发一条服务通知。
订阅消息有个前提:用户必须主动点击授权并且是一次性的——每发一条消息就要授权一次。最简单的做法是在用户下单成功后,立刻弹出一个授权框,让他勾选「同意接收服务进度通知」,把订阅消息的调用放在 wx.requestSubscribeMessage 里:
// 下单成功后触发 wx.requestSubscribeMessage({ tmplIds: ['TEMPLATE_ID'], success(res) { if (res['TEMPLATE_ID'] === 'accept') { wx.showToast({ title: '已开启通知', icon: 'none' }) } } })后端收到这个授权结果后,不需要同消息内容存库。等阿姨确认接单、开始服务、完成服务时,调用微信的统一消息发送接口把状态变化推给用户。模板内容建议配置成「订单号 + 当前状态 + 温馨提示」三段,比如「您预约的日常保洁服务已完成,感谢使用」——用户看一条消息就懂进度,不用再进小程序翻订单详情。
我在做模拟项目X时,把订阅消息只接在「服务完成」这一个节点上,没有做全部状态推送。原因很简单:每个用户一个订单只有一次授权,如果中间状态推太多,用户会关掉通知,最后一条关键完成通知反而推不出去。留最关键的一条,转化效果最好。
再往后可做的扩展就多了:订单完成后让用户对阿姨做评价,评价数据回流到 employee 表作为派单参考;给管理端做一个按日维度的营收统计接口,前端用图表展示;阿姨端小程序单独拆出来做接单和开工打卡。这些方向都建立在核心链路稳定之上。
这个项目做完后,我最大的教训是:毕业设计或小公司信息化,功能堆得越多死得越快。用心把下单、派单、完成这条主链路打磨顺,把状态设计得经得起推敲,比做十个半成品功能有用得多。希望帮到你。
本文还有配套的精品资源,点击获取