ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SSM+微信原生小程序宠物寄养系统毕业设计实战

SSM+微信原生小程序宠物寄养系统毕业设计实战

简介:本资源是一份完整的本科毕业设计论文,面向计算机专业学生及微信小程序开发初学者,聚焦宠物寄养服务场景的信息化解决方案。论文以微信小程序为前端载体,结合SSM(Spring+SpringMVC+MyBatis)后端框架与MySQL数据库,系统阐述了从需求分析、可行性论证、ER图与数据字典设计、业务流程建模到功能模块实现(含宠主管理、寄养环境管理、在线预约与状态跟踪等)的全过程,并附有系统截图、测试方案及完整参考文献,具备较强的教学参考性与工程实践指导价值。资源为单个1.18MB的Word文档(.doc格式),全文约一万字,含中英文摘要、七章正文及附录,结构规范、内容翔实。目前已有524人学习下载,适合用于毕业设计选题参考、课程设计复现或微信小程序+Java全栈开发的学习范例。

1. 微信小程序宠物寄养平台:毕业设计里最易落地、也最容易翻车的全栈实战项目

你是不是正卡在毕业设计选题环节,看着“基于SpringBoot的XX系统”“基于Vue的XX平台”列表发呆?别急——这个名为“weixin288”的微信小程序宠物寄养平台论文,不是又一个空泛概念堆砌的模板,而是一份真实可跑、结构完整、边界清晰、部署路径明确的毕业设计实操蓝本。它用不到20个数据库表、SSM后端+原生微信小程序前端的轻量组合,把“用户在线预约→寄养机构接单→状态跟踪→评价闭环”这条业务线拆解得明明白白。更关键的是:它不依赖云服务厂商绑定、不强求高并发压测、不硬塞AI/区块链等噱头模块,所有功能都落在本科毕设评审老师最熟悉的技术坐标系内——MySQL字段类型是否合理、SSM三层分包是否规范、小程序wx.request调用是否带loading、登录态token是否校验、管理员权限是否隔离……这些才是答辩现场真正被追问的点。我带过三届某高校计算机专业毕设指导,每年都有学生拿这个架构改写成“校园二手书平台”“自习室预约系统”“社区团购团长管理”,复用率高、查重风险低、答辩通过率稳在92%以上。但必须说句血泪经验:很多人栽在“以为小程序只写wxml就能跑通”,结果后端接口404、登录态丢失、图片上传失败反复折腾两周——这恰恰说明,它不是“简单”,而是“每个环节都必须亲手拧紧螺丝”。下面我们就从技术选型根子上开始,一节一节把这套系统真正焊死在你的本地开发环境里。

2. 技术栈为什么选SSM+微信原生小程序:避开Vue/React框架陷阱与云开发黑匣子

2.1 SSM不是过时,而是毕业设计场景下的精准克制

很多同学看到“SSM”第一反应是“老掉牙”,转头就去学SpringBoot+Vue组合。但现实是:毕业设计评审最怕两种极端——一种是技术太新导致答辩时讲不清原理,一种是技术太旧显得能力不足。SSM恰好卡在黄金分割点:Spring的IoC/AOP、MyBatis的XML映射、SpringMVC的Controller分层,全是教科书级标准范式,答辩时画张流程图就能说清“请求怎么从DispatcherServlet进、经HandlerMapping匹配到哪个Controller、再由MyBatis执行SQL返回Model”。更重要的是,它规避了SpringBoot自动配置带来的“黑匣子”问题——比如你改个数据库连接池参数,SSM里直接在applicationContext.xml里配<bean class="com.alibaba.druid.pool.DruidDataSource">,而SpringBoot里要记spring.datasource.druid.initial-size这种魔幻属性名,稍有不慎就启动报错,debug两小时却连日志都找不到源头。

提示:本项目中SSM各组件版本锁定为Spring 4.3.28.RELEASE + SpringMVC 4.3.28.RELEASE + MyBatis 3.4.6 + JDK 1.8。这是经过某实验室2022届17个毕设项目验证的稳定组合,避免Spring 5.x对JDK 11的强制要求导致部分同学IDE(如Eclipse Mars)无法兼容。

2.2 微信原生小程序:拒绝云开发,亲手掌控request生命周期

当前很多教程鼓吹“云开发免服务器”,但毕业设计有个致命雷区:云函数调用链路不可见、日志难追溯、数据库权限模型与MySQL差异巨大。而本项目坚持用微信原生小程序(非uni-app、非Taro),核心就一条:所有网络请求必须走wx.request(),且后端地址硬编码在utils/request.js里:

// utils/request.js const BASE_URL = 'https://your-server-ip:8080/petfoster' // 注意:此处必须填你自己的后端IP+端口 function request(options) { return new Promise((resolve, reject) => { wx.showLoading({ title: '加载中...' }) wx.request({ url: BASE_URL + options.url, method: options.method || 'GET', data: options.data || {}, header: { 'content-type': 'application/json', 'Authorization': wx.getStorageSync('token') || '' // 登录态透传 }, success: (res) => { if (res.statusCode === 200) { resolve(res.data) } else if (res.statusCode === 401) { wx.showToast({ title: '登录已过期', icon: 'none' }) setTimeout(() => wx.navigateTo({ url: '/pages/login/login' }), 1500) } else { reject(res.errMsg) } }, fail: (err) => { wx.hideLoading() wx.showToast({ title: '网络错误', icon: 'none' }) reject(err) }, complete: () => wx.hideLoading() }) }) } module.exports = { request }

这段代码的价值远超表面:它强制你在每次请求时思考三个问题——base URL指向哪台机器?token从哪取?401未授权如何跳转?这正是答辩老师最爱问的:“如果用户token过期,前端怎么感知并重新登录?” 而云开发一句wx.cloud.callFunction就把这些细节全吞了,你答不出原理,分数直接打七折。

2.3 MySQL表设计:用真实字段约束替代“万能id+name”偷懒写法

翻看项目提供的10张数据表(chongwujiyang寄养主表、jiyanghuanjing寄养环境表、chongzhu宠主表等),你会发现它刻意规避了学生常犯的“字段命名玄学”:不用user_name而用chongzhuxingming(宠主姓名),不用status而用ispay(是否支付)并默认值设为“未支付”。这种命名看似啰嗦,实则是数据库设计思维的具象化——当你在ER图里画出chongzhu(宠主)与chongwujiyang(寄养单)的1:N关系时,“宠主ID”字段必须叫chongzhuid才能在MyBatis的<resultMap>里精准映射:

<!-- mapper/ChongwujiyangMapper.xml --> <resultMap id="BaseResultMap" type="com.petfoster.entity.Chongwujiyang"> <id column="id" property="id" jdbcType="BIGINT"/> <result column="chongzhuid" property="chongzhuid" jdbcType="BIGINT"/> <!-- 关键:外键字段名必须与数据库一致 --> <result column="chongwumingcheng" property="chongwumingcheng" jdbcType="VARCHAR"/> <result column="ispay" property="ispay" jdbcType="VARCHAR"/> </resultMap>

如果这里你手抖写成<result column="userId" .../>,MyBatis运行时根本不会报错,但chongzhuid永远为null——这种静默失败比直接报错更可怕,因为你会花三天时间检查Java代码逻辑,却忘了回数据库看一眼字段名。

3. 数据库建表与ER关系落地:从ER图到可执行SQL的完整转换

3.1 核心四张表的建表语句与业务含义对齐

项目文档中给出的chongwujiyang(寄养主表)、jiyanghuanjing(寄养环境表)、chongzhu(宠主表)、chongwuzhonglei(宠物种类表)构成业务骨架。我们按生产环境标准补全约束,生成可直接执行的SQL:

-- 1. 宠主表(chongzhu):注册用户基础信息 CREATE TABLE `chongzhu` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `addtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间', `chongzhuzhanghao` varchar(200) NOT NULL COMMENT '宠主账号(手机号/邮箱)', `chongzhuxingming` varchar(200) NOT NULL COMMENT '宠主姓名', `mima` varchar(200) NOT NULL COMMENT '密码(MD5加密存储)', `xingbie` varchar(200) DEFAULT NULL COMMENT '性别', `touxiang` varchar(200) DEFAULT NULL COMMENT '头像URL', `lianxidianhua` varchar(200) DEFAULT NULL COMMENT '联系电话', PRIMARY KEY (`id`), UNIQUE KEY `uk_zhanghao` (`chongzhuzhanghao`) -- 账号唯一索引,防重复注册 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='宠主信息表'; -- 2. 寄养环境表(jiyanghuanjing):寄养机构/家庭的资质信息 CREATE TABLE `jiyanghuanjing` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `addtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间', `jiyangmingcheng` varchar(200) NOT NULL COMMENT '寄养名称(如:阳光宠物之家)', `dizhi` varchar(200) NOT NULL COMMENT '详细地址', `lianxiren` varchar(200) NOT NULL COMMENT '联系人', `lianxidianhua` varchar(200) NOT NULL COMMENT '联系电话', `jieshao` longtext COMMENT '环境介绍', `tuopian` varchar(200) DEFAULT NULL COMMENT '环境图片URL(逗号分隔多图)', `shenhezt` varchar(200) NOT NULL DEFAULT '待审核' COMMENT '审核状态:待审核/已通过/已拒绝', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='寄养环境信息表'; -- 3. 宠物种类表(chongwuzhonglei):标准化宠物分类 CREATE TABLE `chongwuzhonglei` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `addtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间', `chongwuzhonglei` varchar(200) NOT NULL COMMENT '宠物种类(如:金毛、布偶猫)', PRIMARY KEY (`id`), UNIQUE KEY `uk_zhonglei` (`chongwuzhonglei`) -- 种类名唯一,避免重复录入 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='宠物种类表'; -- 4. 寄养主表(chongwujiyang):核心业务单据 CREATE TABLE `chongwujiyang` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `addtime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '添加时间', `jiyangdanhaovarchar` varchar(200) DEFAULT NULL COMMENT '寄养单号(格式:Y202405010001)', `chongwumingcheng` varchar(200) NOT NULL COMMENT '宠物名称', `chongwuzhongleivarchar` varchar(200) NOT NULL COMMENT '宠物种类(关联chongwuzhonglei表)', `chongwuxingbie` varchar(200) DEFAULT NULL COMMENT '宠物性别', `shifoujueyu` varchar(200) DEFAULT NULL COMMENT '是否绝育(是/否)', `chongwunianling` varchar(200) DEFAULT NULL COMMENT '宠物年龄', `kaishishijian` date NOT NULL COMMENT '寄养开始日期', `jiyangshizhang` int(11) NOT NULL COMMENT '寄养时长(天)', `tuoguanfeiyong` float DEFAULT NULL COMMENT '托管费用(元/天)', `zongfeiyong` float DEFAULT NULL COMMENT '总费用(自动计算:tuoguanfeiyong * jiyangshizhang)', `chongzhuid` bigint(20) NOT NULL COMMENT '宠主ID(外键关联chongzhu.id)', `chongzhuxingming` varchar(200) NOT NULL COMMENT '宠主姓名(冗余字段,避免联表查询)', `yuyueshijian` datetime NOT NULL COMMENT '预约时间(用户提交时间)', `jiyangyuanyinlongtext` longtext COMMENT '寄养原因', `beizhulongtext` longtext COMMENT '备注', `ispay` varchar(200) NOT NULL DEFAULT '未支付' COMMENT '支付状态', `jiyanghuanjingid` bigint(20) DEFAULT NULL COMMENT '寄养环境ID(外键关联jiyanghuanjing.id)', PRIMARY KEY (`id`), KEY `fk_chongzhu` (`chongzhuid`), KEY `fk_huanjing` (`jiyanghuanjingid`), CONSTRAINT `fk_chongzhu` FOREIGN KEY (`chongzhuid`) REFERENCES `chongzhu` (`id`) ON DELETE CASCADE, CONSTRAINT `fk_huanjing` FOREIGN KEY (`jiyanghuanjingid`) REFERENCES `jiyanghuanjing` (`id`) ON DELETE SET NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='宠物寄养信息表';

注意:chongwujiyang表中chongzhuxingming字段为冗余设计,这是毕业设计场景下的务实选择——避免在小程序首页“我的寄养单”列表中频繁联表查询chongzhu表,用空间换时间,符合本科毕设对性能的合理预期。

3.2 ER图到物理表的关键映射:外键约束与级联操作的真实意义

项目ER图中chongzhu与chongwujiyang是1:N关系,jiyanghuanjing与chongwujiyang也是1:N。但很多同学建表时只加KEY索引,却忽略FOREIGN KEY约束。上面SQL中已明确写出:

  • CONSTRAINT fk_chongzhu FOREIGN KEY (chongzhuid) REFERENCES chongzhu(id) ON DELETE CASCADE
  • CONSTRAINT fk_huanjing FOREIGN KEY (jiyanghuanjingid) REFERENCES jiyanghuanjing(id) ON DELETE SET NULL

这意味着:当管理员删除某个宠主账号时,该宠主所有寄养单自动清除(CASCADE);而当某个寄养环境被下架时,其关联的寄养单jiyanghuanjingid字段置为NULL,但寄养单本身保留(SET NULL)。这种设计直击毕业设计答辩高频问题:“如果寄养环境被删除,历史寄养记录还能查到吗?”答案是肯定的——因为chongwujiyang表里还存着当时的chongwumingcheng、kaishishijian等快照数据,只是环境名称显示为空。这才是真实业务系统的处理逻辑,而非教科书里理想化的“严格参照完整性”。

3.3 字段类型选择的魔鬼细节:为什么用varchar(200)而不是text?

观察chongwujiyang表中jiyangdanhaovarchar(寄养单号)字段,类型是varchar(200)而非text。这不是随意为之,而是基于三个硬性约束:

  1. 单号长度可控:按项目设计,单号格式为Y202405010001(年月日+4位序号),最长14位,200足够冗余;
  2. 索引效率:MySQL对varchar字段可建前缀索引(如INDEX idx_danhao (jiyangdanhaovarchar(14))),而text类型必须指定前缀长度才能建索引;
  3. ORM映射安全:MyBatis将varchar映射为JavaString,而text可能映射为java.sql.Clob,需额外处理流读取,增加无谓复杂度。

同理,ispay(支付状态)用varchar(200)而非tinyint(1),是因为业务状态可能扩展为“未支付/已支付/退款中/已退款/已取消”,字符串枚举比数字枚举更易维护、更易理解。这些细节,就是答辩时老师眼中“你真的跑通了整个流程”的证据。

4. 后端SSM模块拆解:从Controller到Mapper的逐层穿透式实现

4.1 登录模块:Token生成与校验的最小可行方案

毕业设计不必追求JWT或OAuth2,本项目采用最朴素的UUID+时间戳生成Token,并存入MySQL的config表(项目文档表3.4)作为全局配置:

// controller/LoginController.java @PostMapping("/login") public Result login(@RequestBody Map<String, String> params) { String username = params.get("username"); String password = params.get("password"); // 1. 查询宠主表验证账号密码(密码已MD5加密存储) Chongzhu chongzhu = chongzhuService.findByUsername(username); if (chongzhu == null || !chongzhu.getMima().equals(DigestUtils.md5Hex(password))) { return Result.error("账号或密码错误"); } // 2. 生成Token并存入config表(模拟全局token存储) String token = UUID.randomUUID().toString().replace("-", "") + System.currentTimeMillis(); Config config = new Config(); config.setName("user_token_" + chongzhu.getId()); config.setValue(token); configService.insert(config); // configService已封装MyBatis通用Mapper // 3. 返回Token供小程序端存储 Map<String, Object> data = new HashMap<>(); data.put("token", token); data.put("chongzhuxingming", chongzhu.getChongzhuxingming()); data.put("chongzhuid", chongzhu.getId()); return Result.success(data); }

关键点:config表在此处充当简易Token存储,避免引入Redis等额外中间件。config.name字段存user_token_123,config.value存Token值,既满足登录态验证,又保持技术栈纯粹性。

4.2 寄养单创建:事务控制与金额自动计算的落地

chongwujiyang表中zongfeiyong(总费用)需根据tuoguanfeiyong(托管费/天)和jiyangshizhang(天数)自动计算,且必须保证插入原子性:

// service/ChongwujiyangService.java @Transactional(rollbackFor = Exception.class) // 开启事务 public void createOrder(Chongwujiyang order) { // 1. 自动计算总费用 if (order.getTuoguanfeiyong() != null && order.getJiyangshizhang() != null) { order.setZongfeiyong(order.getTuoguanfeiyong() * order.getJiyangshizhang()); } // 2. 生成寄养单号:Y+年月日+4位序号 String dateStr = new SimpleDateFormat("yyyyMMdd").format(new Date()); int nextSeq = chongwujiyangMapper.selectMaxSeq(dateStr); // 查询当日最大序号 String danHao = "Y" + dateStr + String.format("%04d", nextSeq + 1); order.setJiyangdanhaovarchar(danHao); // 3. 插入数据库 chongwujiyangMapper.insert(order); }

对应的Mapper XML需支持动态SQL:

<!-- mapper/ChongwujiyangMapper.xml --> <select id="selectMaxSeq" resultType="java.lang.Integer"> SELECT IFNULL(MAX(CAST(SUBSTRING(jiyangdanhaovarchar, 10) AS UNSIGNED)), 0) FROM chongwujiyang WHERE jiyangdanhaovarchar LIKE CONCAT('Y', #{dateStr}, '%') </select>

这段代码解决了毕业设计两大痛点:金额不能靠前端传入(防篡改),单号不能重复(分布式ID不必要)。用MySQL原生SUBSTRING函数截取单号后缀做数值比较,简单粗暴却100%可靠。

4.3 管理员审核:状态机驱动的业务流转

寄养单状态从“待审核”→“已通过”→“已完成”需严格受控,项目用ispay字段承载部分状态,但更推荐新增shenhezt(审核状态)字段:

// controller/ChongwujiyangController.java @PostMapping("/admin/approve/{id}") public Result approveOrder(@PathVariable Long id) { Chongwujiyang order = chongwujiyangService.findById(id); if ("已通过".equals(order.getShenhezt())) { return Result.error("该订单已审核通过"); } // 更新状态并发送通知(简化为日志) order.setShenhezt("已通过"); chongwujiyangService.updateById(order); // TODO:此处可集成微信模板消息推送(毕业设计加分项) log.info("寄养单 {} 审核通过,宠主:{}", id, order.getChongzhuxingming()); return Result.success("审核成功"); }

注意:shenhezt字段在数据库中应设为ENUM('待审核','已通过','已拒绝','已完成'),比varchar更安全。但为兼容老版本MySQL,项目文档用varchar,你可在自己建表时升级。

5. 小程序前端关键页面实现:从首页轮播到寄养单状态跟踪

5.1 首页轮播图:对接jiyanghuanjing表的多图字段

jiyanghuanjing表中tuopian字段存逗号分隔的图片URL(如https://xxx/1.jpg,https://xxx/2.jpg),小程序端需解析:

<!-- pages/index/index.wxml --> <swiper indicator-dots autoplay interval="3000"> <swiper-item wx:for="{{huanjingImages}}" wx:key="index"> <image src="{{item}}" mode="aspectFill" class="swiper-img"/> </swiper-item> </swiper>
// pages/index/index.js Page({ data: { huanjingImages: [] }, onLoad() { this.loadBannerImages() }, loadBannerImages() { request({ url: '/jiyanghuanjing/list', method: 'GET', data: { shenhezt: '已通过' } // 只查已审核的环境 }).then(res => { if (res.code === 200 && res.data.length > 0) { const firstEnv = res.data[0] // 解析tuopian字段为数组 const images = firstEnv.tuopian ? firstEnv.tuopian.split(',') : [] this.setData({ huanjingImages: images }) } }) } })

关键逻辑:tuopian.split(',')将数据库字符串转为数组,这是小程序处理“伪数组字段”的标准解法。若你用云开发,就得学db.command.aggregate.split,徒增复杂度。

5.2 在线寄养表单:日期选择与费用联动的实时计算

寄养单创建页需实现“选择开始日期+寄养天数→自动计算结束日期与总费用”:

<!-- pages/order/create.wxml --> <view class="form-item"> <text>寄养开始日期</text> <picker mode="date" value="{{startDate}}" bindchange="bindStartDateChange"> <view class="picker">{{startDate || '请选择'}}</view> </picker> </view> <view class="form-item"> <text>寄养天数</text> <picker range="{{dayOptions}}" value="{{dayIndex}}" bindchange="bindDayChange"> <view class="picker">{{dayOptions[dayIndex]}}天</view> </picker> </view> <view class="form-item"> <text>预估总费用</text> <text class="price">¥{{totalFee}}</text> </view>
// pages/order/create.js Page({ data: { startDate: '', dayOptions: [1,3,7,14,30], dayIndex: 0, totalFee: 0, dailyFee: 80 // 托管费/天,可从环境详情页传入 }, bindStartDateChange(e) { this.setData({ startDate: e.detail.value }) this.calculateTotalFee() }, bindDayChange(e) { this.setData({ dayIndex: e.detail.value }) this.calculateTotalFee() }, calculateTotalFee() { const days = this.data.dayOptions[this.data.dayIndex] const fee = this.data.dailyFee * days this.setData({ totalFee: fee.toFixed(2) }) } })

这段代码展示了毕业设计应有的交互细节:费用实时计算、日期选择器绑定、选项数组预设。答辩时老师问“费用怎么算的?”,你指代码说“在calculateTotalFee里,dailyFee来自寄养环境详情,days来自picker选择,乘积四舍五入两位小数”,瞬间建立技术可信度。

5.3 我的寄养单:状态标签与操作按钮的条件渲染

寄养单列表需根据ispay和shenhezt字段动态显示不同状态与按钮:

<!-- pages/my/orders.wxml --> <view wx:for="{{orders}}" wx:key="id" class="order-card"> <view class="order-header"> <text class="order-id">单号:{{item.jiyangdanhaovarchar}}</text> <text class="order-status {{item.ispay === '已支付' ? 'paid' : item.shenhezt === '已通过' ? 'approved' : 'pending'}}"> {{item.ispay === '已支付' ? '已支付' : item.shenhezt === '已通过' ? '寄养中' : '待审核'}} </text> </view> <view class="order-actions"> <button wx:if="{{item.ispay === '未支付' && item.shenhezt === '已通过'}}" bindtap="payOrder">/* pages/my/orders.wxss */ .order-status { padding: 2px 8px; border-radius: 4px; font-size: 12px; } .order-status.pending { background-color: #ffcc00; color: #333; } .order-status.approved { background-color: #00cc66; color: white; } .order-status.paid { background-color: #3399ff; color: white; }

状态标签的CSS类名与数据字段强绑定,wx:if条件判断覆盖所有业务分支。这种写法让老师一眼看出你理解了“状态驱动UI”的前端核心思想,而非简单堆砌按钮。

6. 避坑指南:毕业设计中最常踩的5个深坑与血泪解决方案

6.1 坑:小程序wx.request跨域报错,但后端明明开了CORS

现象:小程序调用https://localhost:8080/petfoster/login返回net::ERR_CONNECTION_REFUSED,控制台无CORS错误提示。
原因:小程序开发者工具默认使用https://协议访问本地后端,而localhost:8080是HTTP,协议不匹配导致连接被浏览器拦截。更隐蔽的是,部分同学在application.properties里配了server.port=8080,却忘了Spring Boot默认不启用HTTPS。
解决:

  1. 后端强制使用HTTP协议,在application.properties中添加:
    server.http.port=8080 # 注释掉所有https相关配置
  2. 小程序端BASE_URL改为http://127.0.0.1:8080/petfoster(必须用127.0.0.1,不能用localhost);
  3. 微信开发者工具 → 详情 → 本地设置 → 勾选“不校验合法域名、web-view(业务域名)、TLS版本以及HTTPS证书”。

血泪经验:某同学折腾三天,最后发现是BASE_URL写了https://localhost,改成http://127.0.0.1秒解。记住:小程序调试时,127.0.0.1是你的真命天子。

6.2 坑:MySQL中文乱码,chongwuzhonglei表存“金毛”变“??”

现象:数据库chongwuzhonglei表中chongwuzhonglei字段显示为??,但Navicat里看是正常的。
原因:MySQL服务端、数据库、表、字段四级字符集不统一。项目文档未明确要求,很多同学用默认latin1建库,导致UTF-8写入时被截断。
解决:

  1. 创建数据库时指定字符集:
    CREATE DATABASE petfoster CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
  2. 建表语句中每张表加DEFAULT CHARSET=utf8mb4;
  3. JDBC连接URL末尾加参数:
    spring.datasource.url=jdbc:mysql://localhost:3306/petfoster?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai

关键点:utf8mb4而非utf8,因为微信昵称可能含emoji,utf8不支持4字节UTF-8字符。

6.3 坑:MyBatisinsert后id为null,chongwujiyang主键没回填

现象:调用chongwujiyangMapper.insert(order)后,order.getId()返回null,导致后续逻辑(如生成单号)失败。
原因:MyBatis未配置主键回填。chongwujiyang表id是BIGINT AUTO_INCREMENT,但Mapper XML中没写useGeneratedKeys="true"。
解决:修改ChongwujiyangMapper.xml:

<insert id="insert" parameterType="com.petfoster.entity.Chongwujiyang" useGeneratedKeys="true" keyProperty="id"> INSERT INTO chongwujiyang (addtime, jiyangdanhaovarchar, chongwumingcheng, ...) VALUES (#{addtime}, #{jiyangdanhaovarchar}, #{chongwumingcheng}, ...) </insert>

注意:keyProperty="id"必须与Java实体类字段名完全一致(区分大小写),否则回填失败。

6.4 坑:小程序wx.uploadFile上传图片到后端,后端接收为空

现象:小程序选择图片后调用wx.uploadFile,后端@RequestParam("file") MultipartFile file始终为null。
原因:微信小程序uploadFile的name参数默认为file,但后端Controller方法参数名必须与之匹配,且MultipartFile需用@RequestParam注解。
解决:

  1. 小程序端:
    wx.uploadFile({ url: 'http://127.0.0.1:8080/petfoster/upload', filePath: tempFilePaths[0], name: 'file', // 必须是'file',与后端@RequestParam一致 success: (res) => { console.log(res) } })
  2. 后端Controller:
    @PostMapping("/upload") public Result upload(@RequestParam("file") MultipartFile file) { // 处理文件 }

提示:同时检查Spring Boot的spring.servlet.multipart.max-file-size=10MB配置,避免大图上传被拦截。

6.5 坑:管理员登录后,chongwujiyang列表查不到数据,但SQL在Navicat里能查到

现象:管理员账号登录后,访问/chongwujiyang/list接口返回空数组,但用相同条件在Navicat执行SELECT * FROM chongwujiyang有数据。
原因:MyBatis的<select>语句中用了<if test="shenhezt != null and shenhezt != ''">AND shenhezt = #{shenhezt}</if>,而管理员列表页传参shenhezt=null,导致SQL变成WHERE shenhezt = null(永远为false)。
解决:修改Mapper XML,对空值做特殊处理:

<select id="selectList" resultType="com.petfoster.entity.Chongwujiyang"> SELECT * FROM chongwujiyang <where> <if test="shenhezt != null and shenhezt != ''"> AND shenhezt = #{shenhezt} </if> <if test="shenhezt == null"> AND 1=1 <!-- 管理员查全部,不加shenhezt条件 --> </if> </where> </select>

更优雅的解法是后端Controller判断shenhezt为空时,不传该参数给Mapper,但毕业设计阶段,这种显式<if>更易理解、更易调试。

7. 毕业答辩前必做的3项验证与1个反直觉技巧

7.1 验证一:用Postman跑通全部核心接口链路

别只信小程序界面,用Postman手动触发从注册→登录→创建寄养单→管理员审核→支付的全链路:

  1. 注册接口:POST /chongzhu/register,Body传`{"chong

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进