毕业设计这东西,每年选题都像在开盲盒。市面上那些库存管理系统、商城系统早就被做烂了,你答辩时老师看一眼题目就知道你用了什么模板。相比之下,“留守宠物喂养管理系统”是这几年我一直推荐给学生的选题:需求真实存在、功能边界清晰、技术栈覆盖全面,而且做出来之后是真的能拿去用的东西。
先说一下这个系统到底解决了什么问题。养猫养狗的人出差、回老家、临时加班,宠物没法带在身边,放在宠物店寄养不仅贵,还得担心宠物应激反应。所以一个能远程查看宠物状态、按计划定时喂食、记录进食情况的微信小程序,就成了刚需。这个项目恰好卡在了一个非常合适的位置:微信小程序做前端,后端用Java或者Node.js都行,数据用MySQL存,再加上硬件侧的定时喂食器(如果要做硬件联动的话),整个链路做下来,一个完整的毕设项目闭环就有了。
我之前带过几个学生做这个方向,今天就把整个项目的设计思路、关键代码、踩坑记录全部分享出来。
1. 项目定位与整体设计思路
1.1 为什么选微信小程序而不是App
这可能是你要做的第一个决定。我的建议非常直接:选微信小程序,别犹豫。
从用户角度看,微信小程序的打开成本几乎为零。用户不会为了喂个猫专门去应用商店下载一个App,但他们会为了等外卖顺手在微信里搜一下小程序。从开发角度看,小程序前端上手门槛低,WXML和WXSS的语法接近HTML和CSS,只要是写过网页的人都能快速进入状态。再加上微信自带的登录体系和消息推送机制,省掉了自己搭用户系统和短信服务的麻烦。
从毕设评审的角度看,微信小程序这个关键词本身就带着关注度。答辩老师看到你做的是小程序,第一反应是“这学生跟得上技术趋势”,而不是“又是传统Web那套”。
1.2 功能模块拆解:需求从哪来
做毕设最容易犯的错就是一上来就写代码。正确做法是先画清楚功能边界。我当时给学生的建议是分四个核心模块:
- 宠物档案模块:添加、编辑、删除宠物信息(名字、品种、年龄、体重、饮食习惯备注)
- 喂食计划模块:设定每日喂食时间、单次出粮量、喂食器状态反馈
- 监控与记录模块:查看宠物的进食记录、(可选)视频画面或摄像头抓拍
- 个人中心模块:用户登录、设备绑定、消息通知设置
这四个模块覆盖了从“创建宠物信息”到“查看喂食结果”的完整闭环。每一块拿出来都能在文档里写出一章,加起来内容量足够撑起一篇合格的毕业论文。
1.3 技术选型的底层逻辑
整个项目的主流技术栈是这个组合:
| 层级 | 技术选型 | 选择理由 |
|---|---|---|
| 前端 | 微信小程序原生(或uni-app) | 原生语法稳定、调试方便,uni-app可兼顾多端 |
| 后端 | Spring Boot | Java生态成熟,毕业答辩被追问的概率低 |
| 数据库 | MySQL | 关系型数据库的经典选择,表结构设计好讲 |
| 接口协议 | RESTful API + JSON | 前后端分离的标准姿势 |
| 硬件联动 | ESP8266/ESP32(可选) | 如果做硬件方向,这是最便宜的方案 |
后端我推荐Spring Boot是有私心的。不是因为Java性能多好,而是因为答辩时老师大概率会问“为什么选这个框架”“Spring的IoC和AOP怎么体现的”,你用Spring Boot可以很自然地引出依赖注入、自动配置这些知识点,回答起来有东西可说。如果你更熟Node.js,改成Express或Egg也没问题,关键是自己弄得明白。
2. 数据库设计与接口规范
2.1 表结构怎么设计才合理
这套系统的表结构比一般的商城系统要简单,但有几张表的设计直接决定了后面功能好不好扩展。我建议核心表设计如下:
用户表(user):openid是微信小程序的唯一身份标识,必须加唯一索引。nickname、avatar_url用于展示个人资料,绑定设备编号字段可空,方便后面做设备解绑再绑定。
宠物表(pet):user_id关联用户表,pet_name、pet_type(猫/狗)、pet_weight、breed、birthday用于档案展示,feeding_note是自由文本字段,存“猫粮过敏”“每天加一次化毛膏”这类备注。
喂食计划表(feeding_plan):计划创建时一次性生成未来N天的计划记录,或者只在用户编辑时更新当天及之后的记录。每条记录包含计划时间、出粮克数、状态字段(待执行、已执行、已跳过),状态由后端定时任务更新。
喂食记录表(feeding_record):设备上报喂食完成后写入一条记录,包含实际喂食时间、出粮克数、剩余粮量、执行来源(自动/手动)。这张表的粒度决定了你之后能做多细的统计。
设备表(device):device_id作为物理设备的唯一标识,status字段表示在线/离线,last_online_time用于展示设备状态。如果你不做硬件,这表可以简化成用户直接维护一个“喂食器状态”。
2.2 接口怎么设计才清晰
接口设计遵循一个原则:让前端调用起来不用想太多。以下是核心接口清单:
- POST /api/user/login:小程序wx.login获取code,后端用code换取openid,首次登录自动注册
- GET /api/pet/list:获取当前用户的宠物列表
- POST /api/pet/add:新增宠物资料
- PUT /api/pet/update:修改宠物资料
- DELETE /api/pet/delete:删除宠物
- GET /api/plan/list?petId=xxx&date=xxx:按日期获取喂食计划
- POST /api/plan/save:创建或修改喂食计划
- POST /api/plan/execute:手动触发一次喂食
- GET /api/record/list?petId=xxx&pageNum=1&pageSize=10:分页查询喂食记录
- GET /api/device/status:获取绑定设备状态
注意接口命名要语义化,参数要校验,响应格式保持统一。我习惯统一返回Result对象,包含code(200成功,500失败)、message和data三个字段。这样前端处理异常就只需要判断code,省心很多。
3. 微信小程序前端实现的核心细节
3.1 登录流程别再用老一套了
现在很多教程还在让你用wx.getUserInfo去拿用户头像昵称,这已经行不通了。微信官方早就调整了规则, getUserInfo弹窗被砍掉,现在是默认获取灰色头像和“微信用户”昵称。
正确的做法是:
// app.js 中封装登录逻辑 const login = () => { wx.login({ success: (res) => { if (res.code) { // 将code发送到后端,后端通过code换openid wx.request({ url: 'http://localhost:8080/api/user/login', method: 'POST', data: { code: res.code }, success: (response) => { const { token, userId } = response.data.data wx.setStorageSync('token', token) wx.setStorageSync('userId', userId) } }) } } }) }用户头像和昵称要单独引导用户去完善:点击头像时弹出wx.chooseMedia选择图片,上传到自己的对象存储,再把URL存到数据库。这也是一个可以写进文档的细节:为什么不能用open-type="getUserInfo"直接拿头像,因为隐私协议收紧后,很少会有用户愿意授权。
3.2 定时任务的“坑”与解决方案
这里说一个小程序端的经典坑。很多学生会想用小程序的定时器setInterval去定时触发喂食,但这在微信小程序里根本不靠谱:小程序在后台时定时器会被挂起。你的宠物喂食计划必须由后端执行。
后端的定时任务方案也很简单,在Spring Boot里用@Scheduled注解:
@Component public class FeedingTask { @Autowired private FeedingPlanMapper planMapper; @Autowired private FeedingRecordMapper recordMapper; // 每分钟执行一次,检查是否有需要执行的喂食计划 @Scheduled(cron = "0 * * * * ?") public void checkPlans() { List<FeedingPlan> plans = planMapper.selectByStatusAndTime( "WAITING", DateUtil.format(new Date(), "yyyy-MM-dd HH:mm") ); for (FeedingPlan plan : plans) { // 发送HTTP请求到喂食器端 DeviceResponse resp = deviceClient.executeFeed(plan.getDeviceId(), plan.getFeedAmount()); if (resp.isSuccess()) { plan.setStatus("EXECUTED"); // 记录喂食信息 recordMapper.insert(...); } else { plan.setStatus("FAILED"); // 发送告警通知给用户 wxMessageService.send(plan.getUserId(), "喂食失败,请检查设备"); } planMapper.updateById(plan); } } }这个定时任务的粒度设置为“每分钟检查一次”,相比每秒轮询大大降低了对数据库的压力。计划表里的执行时间精确到分钟就足够了。
3.3 前端页面开发顺序建议
页面开发顺序直接决定了效率。我的建议按这个顺序来:
先做宠物列表页,这是用户进入小程序后的首页,需要显示宠物卡片(头像、名字、品种、体重)和“添加宠物”按钮。接着做宠物档案编辑页,把表单组件调稳定了,后面套用别的表单就顺畅了。
然后做喂食计划页,这是整个系统的核心功能页。这里有个设计要点:喂食计划的展示建议按周视图排布,周一到周日,每天可以单独设置喂食时间。用表格布局做这个比时间轴更清晰。
最后做动静分离的页面:喂食记录列表用滚动列表,每行显示时间和出粮克数;我的页面用普通卡片布局,放设置入口、设备绑定、关于页面。
3.4 网络请求封装
原生小程序请求有点繁琐,建议封装一个request工具:
// utils/request.js const request = (url, method, data) => { return new Promise((resolve, reject) => { wx.request({ url: `https://your-api-server.com${url}`, method, data, header: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${wx.getStorageSync('token')}` }, success: (res) => { if (res.data.code === 200) { resolve(res.data.data) } else { wx.showToast({ title: res.data.message, icon: 'none' }) reject(res.data) } }, fail: (err) => { // 网络异常时的统一处理 wx.showToast({ title: '网络连接失败', icon: 'none' }) reject(err) } }) }) }别小看这个封装,它会让你的页面代码干净得多。每个页面里只需要写:
const list = await request('/api/pet/list', 'GET') setData({ petList: list })而不是每次都去写一遍wx.request的回调、错误处理和loading状态。
4. 后端关键逻辑与硬件联调(可选方案)
4.1 用户认证的完整链路
微信小程序后端的用户认证流程是固定套路:
- 前端调用wx.login拿到临时凭证code
- 前端把code传给后端
- 后端通过HTTPS请求微信接口:https://api.weixin.qq.com/sns/jscode2session,带上appid、secret和code
- 微信返回openid和session_key
- 后端查数据库,有则更新登录信息,无则注册新用户
- 后端生成自定义token(可以用jwt或session),返回给前端
这里要注意的坑是:appid和secret绝对不能写到前端代码里,否则任何人反编译小程序都能拿到你的secret,理论上可以冒充你的小程序做坏事。正确做法是通过后端转发,由后端来存secret。
4.2 硬件联调的最后一步:串口通信能不做就不做
如果你选择做“硬件联动”,最省心的方式是:买一个带WiFi的智能喂食器(市场价大约100到200元),通过它的开放API接口对接你的后端,而不是自己去写ESP8266的固件做串口通信。
原因很现实:纯软件方向的学生,搞单片机要看很多焊接和电烙铁知识;纯硬件方向的学生,又会卡在后端接口上。用现成的智能喂食器,把对接的重点放在“你的后端如何通过HTTP请求操作设备”,一样可以讲清楚整个物联网闭环,而且精力能集中在毕业设计真正考核的软件工程能力上。
如果你一定要自己用ESP8266做硬件,联调时的关键代码大致是这样的思路:
#include <ESP8266WiFi.h> #include <HTTPClient.h> void setup() { WiFi.begin("your-wifi-ssid", "your-wifi-password"); while (WiFi.status() != WL_CONNECTED) { delay(500); } // 每10秒轮询一次后端,查询是否有新的执行指令 } void loop() { if (WiFi.status() == WL_CONNECTED) { HTTPClient http; http.begin("http://your-server.com/api/device/command"); int httpCode = http.GET(); if (httpCode > 0) { String payload = http.getString(); // 解析指令,触发舵机转动出粮 } http.end(); } delay(10000); }这种轮询方案虽不优雅,但胜在简单稳定。真正生产级的做法是让后端通过MQTT协议推送指令到设备,这个对于毕设来说有点超纲,能写进文档里提一下就行。
4.3 消息通知怎么实现
用户最关心的是“喂食是否成功”这个结果。微信小程序里推送订阅消息是合适的方案,不过需要解决模板ID和用户授权的问题。
// 小程序端请求订阅消息授权 wx.requestSubscribeMessage({ tmplIds: ['模板ID'], success: (res) => { if (res['模板ID'] === 'accept') { // 用户同意授权,可以发送订阅消息 request('/api/subscribe/register', 'POST') } } })服务端在计划执行完成或异常时,调用微信的订阅消息接口:
curl -X POST "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token=ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "touser": "OPENID", "template_id": "模板ID", "page": "pages/record/record", "data": { "thing1": { "value": "小乖" }, "time2": { "value": "12:30" }, "thing3": { "value": "出粮15克,剩余粮量充足" } } }'有一个实际的限制需要提前知道:微信订阅消息是一次性的。用户授权一次只能收到一条消息,不能像公众号模板消息那样无限制推送。因此对于“喂食成功”这种高频场景,不能每次都推送。实际设计中,我建议只推送“异常告警”和“手动执行的结果”,计划成功执行时不做推送,而是在小程序首页展示,让用户自行点开查看。
5. 常见问题与排查技巧实录
5.1 制作流程中会遇到的典型问题
先盘点一下学生在做这个题目时最常见的问题:
| 问题场景 | 现象 | 排查思路与解决办法 |
|---|---|---|
| 后端启动了但小程序请求失败 | 提示“request:fail -2:net::ERR_FAILED” | 开发阶段后端地址要加 https,但在本地调试必须勾选“不校验合法域名”,小程序开发者工具右上角详情-本地设置里打开 |
| 微信登录拿不到openid | 后端日志显示code无效 | 检查appid和secret是否填写正确,确认不是用的测试号来对接正式小程序后台 |
| 计划保存了但到点不执行 | 数据库状态没有更新 | 检查@Scheduled是否启用(启动类要加@EnableScheduling),确认服务器时间与本地时间一致 |
| 请求头跨域报错 | 浏览器控制台CORS报错 | 后端加@CrossOrigin注解,或在配置类中注册全局CORS配置 |
| 数据库时间字段差8小时 | 存储时间比实际时间晚8小时 | 连接池URL里加serverTimezone=Asia/Shanghai,同时检查服务器时区设置 |
| 订阅消息发送时报43101 | 用户拒绝了授权或已超过一次性限制 | 引导用户重新在小程序内授权,或换一个模板场景重新触发 |
第一个问题概率最高。很多学生第一次用开发者工具,不知道默认情况下小程序不能请求http接口。这里再次强调:正式发布的小程序只能访问HTTPS接口且域名必须备案;但开发和真机调试阶段,可以在工具里勾选“开发环境不校验合法域名”。
5.2 微信开发者工具的3个实用姿势
这3个技巧是提升调试效率的关键,常规教程很少提到:
- 利用“模拟操作”面板里的“编译模式”:可以自定义启动页面的参数,比如直接进入编辑宠物资料页,而不用每次从首页点好几层才能到。非常适合调试某个深层次页面时使用。
- “真机调试”选择“USB调试”或“远程调试”:当你发现模拟器上一切正常,但真机上数据加载出来是空白时,可以优先通过真机调试模式看Console面板的输出,能直接看到具体报错是接口挂了还是渲染出问题。
- “缓存”面板可以一键清除全部缓存:开发时经常遇到小程序代码更新了,但运行结果还是旧数据,多半是Storage缓存没清。通过“清缓存”一键完成,省得反复手动删Storage。
5.3 有没有必要做多端兼容(uni-app版本)
这个问题的答案取决于你要不要参加比赛或后续继续维护。如果你做的是纯毕业设计,这个不是刚需,微信小程序原生自己做一遍就够了。但如果你要参加“互联网+”之类的创新创业比赛,多端发布(微信小程序、支付宝小程序、H5、App)会显著加分,这时候考虑用uni-app重写前端是有价值的。
用uni-app的代价是:你可能会遇到一些编译层面的问题(比如某些原生组件在uni-app里需要特殊适配),以及很多教程都是针对原生写法的,迁移过去需要一定的排查能力。建议是:毕业设计就原生写,省时省力;参加比赛可以冲一下uni-app,作为创新点写进文档。
6. 毕业设计过程中自己踩过的坑
6.1 需求膨胀是一个巨大的坑
这个项目管理系统的方向很容易让人上头:要不要加视频监控?要不要加语音呼唤?要不要加自动铲屎?我的建议很明确:除非你的毕设题目明确要求必须包含这些,否则统一砍掉。你首先需要保证的是核心流程(登录-建档-设置计划-执行记录-结果展示)完整跑通。
做减法是我带学生时反复强调的点。一个能稳定跑完核心流程的毕设,拿到的评分一定高于一个做了5个功能但每一个都Bug连篇的毕设。核心流程做好了,有余力再锦上添花。视频监控功能牵扯到视频流、对象存储、小程序live-player组件兼容性,复杂度是以指数级增长的,不是一星期能磨出来的。
6.2 数据库的初始化脚本一定要留好
很多学生喜欢直接在Navicat里面手动点着建表,进程到了最后忘了留存SQL脚本,换一台电脑就要重头建表。正确做法是写一个schema.sql,存放在项目代码库里,里面包含建库建表语句和基础测试数据。这样不管换开发机、部署服务器、还是最后交给导师演示,都能一键初始化。
核心SQL脚本示例:
-- 用户表 CREATE TABLE `user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `openid` varchar(64) NOT NULL, `nickname` varchar(50) DEFAULT '微信用户', `avatar_url` varchar(255) DEFAULT NULL, `device_id` varchar(64) DEFAULT NULL, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_openid` (`openid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; -- 喂食计划表 CREATE TABLE `feeding_plan` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user_id` bigint(20) NOT NULL, `pet_id` bigint(20) NOT NULL, `device_id` varchar(64) NOT NULL, `plan_time` varchar(16) NOT NULL COMMENT '计划喂食时间 HH:mm', `feed_amount` int(11) NOT NULL COMMENT '出粮克数', `status` varchar(20) NOT NULL DEFAULT 'WAITING', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='喂食计划表';多花这一点时间,后期能省掉很多琐碎的麻烦。
6.3 工作量展示上留意这几个细节
最后说一个答辩和文档上的经验。
- 截图配时序图:论文里的功能展示不要只放小程序页面截图,加上接口调用时序图(比如用户发起喂食计划-后端保存-定时任务触发-设备执行-前端展示记录),会显得你对系统整体运行逻辑有清晰的认知。
- 过程文档按版本迭代命名:答辩时老师在评审表上会看你的开发日志或技术报告。如果能把过程文档做成“V1.0-需求分析→V2.0-数据库设计→V3.0-前后端联调→V4.0-功能优化”这种版本线,直观体现你的工作是在递进的。
- 演示前一定要准备一台备用手机:真机演示时,最常见的翻车情况是小程序需要重新登录、扫码调试后网络断了、或手机电量不足。用备用机提前配置好测试账号,避免现场手忙脚乱。
这个选题最大的乐趣在于,你做的东西是真的可以拿去喂家里那只猫的。我自己的那只橘猫,就是被这套系统喂胖的。当你出差在外,打开小程序看到猫主子在食盆前吃得正香的时候,那种成就感不是一纸文凭能替代的。如果你也在做这个方向的毕设,希望这篇文章能帮你少走一些弯路。