简介:本资源是一套完整的微信小程序毕业设计项目——宠物店商城系统,面向计算机相关专业本科生及初学者,解决课程设计与毕业设计中前后端协同开发、多角色业务建模等典型实践难题。压缩包含1659个文件,总大小48.38MB,涵盖250个JavaScript前端逻辑文件、184个Java后端服务代码、177个Vue组件、130个WXML页面结构及132个WXSS样式文件,辅以MySQL建表SQL、演示视频MP4及完整功能说明文档,技术栈清晰覆盖小程序+Spring Boot+MySQL全链路。已有175人学习下载,资源提供管理员、用户、商家三端完整功能实现:包括宠物/商品/服务管理、订单全流程处理、日志发布与预约调度,并附带3个批处理脚本(install/run/build)和备份文件(.bak),便于快速部署与调试。目录结构规范,模块划分明确,适合作为教学案例、二次开发基础或求职项目参考。
1. 这不是“套模板改个名”的毕业设计:一个真实跑通「宠物商城」闭环的微信小程序源码包,含 Java 后端 + MySQL + 完整角色权限体系
你见过太多标着“微信小程序毕业设计”的压缩包——点开全是 uni-app 壳子、空路由、mock 数据、连登录都跳过、订单状态永远是“待支付”。但这个.rar包不一样:它真在2-run.bat里启动了 Spring Boot 内嵌 Tomcat,真用mysql-connector-java连了本地数据库,真让管理员审核商家入驻、用户下单后触发库存扣减、商家接单后更新服务预约时间。它不是教学 Demo,而是一套能部署到学生实训服务器、经得起答辩老师现场扫码测试的最小可行系统(MVP)。核心价值在于:三端角色(管理员/用户/商家)权限隔离清晰、商品+宠物+服务三类实体共存且关联合理、订单状态机完整(创建→支付→发货/履约→完成/退款)、所有 Vue 页面组件命名规范(如IndexMain.vue.bak是主布局备份,非冗余文件)。适合计算机/软件工程专业大三下至大四上学生直接复现、调试、二次开发——尤其适合作为“Java Web + 微信小程序”双栈课程设计载体,或毕设中“前端交互+后端业务逻辑+数据库建模”三模块的落地锚点。
2. 从解压到可运行:环境准备、依赖安装与项目结构破译
2.1 环境清单与版本对齐:为什么必须用 JDK 8 和 MySQL 5.7?
这个项目不是“最新技术栈炫技”,而是面向高校实验室老旧机房和学生笔记本的务实选型。后端pom.xml中明确依赖spring-boot-starter-web:2.1.18.RELEASE(对应 Spring Boot 2.1.x),该版本官方要求 JDK 8u144+,且与mysql-connector-java:5.1.47兼容性最佳。若强行升级 JDK 17 或 MySQL 8.0,你会在application.yml的url配置处卡住——因为jdbc:mysql://localhost:3306/petshop?useSSL=false&serverTimezone=GMT%2B8中的serverTimezone参数在 MySQL 8.0+ 需改为Xxx/xxx格式(如Asia/Shanghai),而原包未适配。
提示:解压后先打开
pom.xml,搜索<java.version>和<mysql.version>标签,确认值为1.8和5.1.47;再检查application.yml中spring.datasource.url是否含serverTimezone=GMT%2B8——这是判断能否直连的关键信号。
2.2 三步启动法:.bat脚本背后的执行链路
项目根目录下三个批处理文件不是摆设,而是经过生产环境简化后的部署流水线:
# 1-install.bat 实质执行(需手动补全路径) mvn clean install -Dmaven.test.skip=true # 2-run.bat 实质执行(关键!) java -jar target/petshop-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev # 3-build.bat 实质执行(生成最终 jar) mvn clean package -Dmaven.test.skip=true注意:2-run.bat中的--spring.profiles.active=dev指向src/main/resources/application-dev.yml,该文件定义了本地数据库账号密码(默认root/root),切勿直接运行2-run.bat而不检查此文件——否则会因密码错误导致连接池初始化失败,控制台报Access denied for user 'root'@'localhost'。
2.3 前端结构解密:.bak文件不是垃圾,是历史迭代痕迹
IndexMain.vue.bak、IndexAsideStatic.vue.bak等带.bak后缀的文件,是开发者用 VS Code 的“文件自动备份”功能生成的(设置中files.autoSave为afterDelay时触发)。它们并非冗余,而是关键布局组件的原始版本:
IndexMain.vue.bak包含未删减的<router-view>嵌套逻辑和顶部 TabBar 切换事件;IndexAsideStatic.vue.bak保留了左侧菜单的v-for渲染原始数据结构(menuList: [{name:'用户管理',path:'/admin/user'},{name:'订单管理',path:'/admin/order'}]);BreadCrumbs.vue.bak中有动态面包屑生成函数getBreadCrumb(),通过this.$route.matched获取当前路由层级。
提示:若修改
IndexMain.vue导致导航失效,立即用.bak文件覆盖恢复——这是比 Git 更快的后悔药。
2.4 后端核心包解析:com.petshop.*下藏着什么?
反编译target/classes/或查看src/main/java目录,可定位四大业务包:
com.petshop.admin:管理员控制器(AdminController.java),含@PostMapping("/user/delete")等 REST 接口;com.petshop.merchant:商家控制器(MerchantController.java),处理POST /merchant/goods/add商品上架;com.petshop.user:用户控制器(UserController.java),实现GET /user/order/list订单查询;com.petshop.common:通用工具(JwtUtil.java生成 Token、PageUtils.java分页封装)。
特别注意com.petshop.entity中的Order.java:其status字段为Integer类型(0=待支付,1=已支付,2=已发货,3=已完成,4=已取消),所有状态变更必须走 Service 层事务方法(如OrderService.updateStatus(orderId, 2)),而非直接 Mapper 更新——这是避免并发超卖的核心设计。
3. 数据库建模与初始化:MySQL 表结构、ER 关系与初始化脚本实操
3.1 核心表 ER 图关键约束:为什么pet_goods表没有外键指向pet_info?
项目采用“逻辑外键”而非物理外键的设计哲学。查看pet_goods.sql初始化脚本,你会发现:
pet_info表主键id是BIGINT;pet_goods表中pet_id字段为BIGINT,但无FOREIGN KEY (pet_id) REFERENCES pet_info(id)语句;order_item表中goods_id同样无外键约束。
原因:微信小程序场景下,宠物信息(如品种、年龄)常需独立编辑,而商品(如“金毛幼犬”)可能关联多个宠物记录(不同个体),物理外键会限制删除灵活性。开发者选择在 Service 层用petInfoMapper.selectById(petId)主动校验,既保证数据一致性,又避免 MySQL 外键锁表风险。
3.2 初始化脚本执行顺序:petshop.sql必须分三步导入
直接执行petshop.sql会失败!正确顺序为:
- 先建库:
CREATE DATABASE petshop CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - 再建表:依次执行
pet_info.sql→pet_goods.sql→merchant.sql→user.sql→order_master.sql→order_item.sql; - 最后插数据:
insert_admin_user.sql(插入管理员账号admin/123456)、insert_merchant_demo.sql(插入测试商家)、insert_pet_demo.sql(插入金毛、布偶等宠物示例)。
注意:
insert_admin_user.sql中password字段存储的是 BCrypt 加密后的密文$2a$10$...,不可直接写明文123456——若手动 INSERT,请先用BCryptPasswordEncoder.encode("123456")生成密文。
3.3 关键字段类型深挖:order_master表的pay_time为何用datetime而非timestamp?
order_master表中pay_time datetime DEFAULT NULL,而非timestamp DEFAULT CURRENT_TIMESTAMP。这是为兼容微信支付回调时区问题:微信支付通知中的time_end字段格式为20230101120000(yyyyMMddHHmmss),后端解析后存入pay_time时需保持原始时区(东八区),而timestamp类型在 MySQL 中会自动转换为 UTC 存储,读取时再转回系统时区,易导致 8 小时偏差。用datetime可确保时间值“所见即所得”。
3.4 权限表设计陷阱:role_permission表的permission_id如何映射前端按钮?
role_permission表结构为(role_id, permission_id),其中permission_id并非自增 ID,而是字符串编码的权限标识,如:
"user:list"→ 用户管理列表页"order:detail"→ 订单详情页"goods:add"→ 商品新增按钮
前端IndexAsideStatic.vue中通过v-if="hasPermission('goods:add')"调用全局方法hasPermission,该方法从 Vuex store 的user.permissions数组中查找匹配项。若新增按钮权限,必须同时修改三处:①role_permission表插入新记录;② 后端PermissionService.getPermissionsByRoleId()返回新permission_id;③ 前端store/modules/user.js中permissions数组同步更新。
4. 前端页面与接口联调:微信开发者工具真机调试与跨域配置
4.1 微信开发者工具配置要点:如何让http://localhost:8080接口被允许?
微信小程序默认禁止http协议请求(仅支持https),但开发阶段必须绕过。正确做法是在project.config.json中添加:
{ "setting": { "urlCheck": false, "es6": true, "enhance": true, "postcss": true, "minified": true, "newFeature": true, "coverView": true, "nodeModules": true, "babelSetting": { "ignore": [], "disablePlugins": [], "outputPath": "" } } }关键在"urlCheck": false——这是关闭合法域名校验的开关。切勿在app.json中加"networkTimeout"或"debug"字段试图解决,那是无效的。
4.2 接口请求封装:utils/request.js中的拦截器逻辑
utils/request.js定义了统一请求方法:
// utils/request.js const request = (options) => { return new Promise((resolve, reject) => { wx.request({ url: 'http://localhost:8080' + options.url, method: options.method || 'GET', data: options.data || {}, header: { 'Content-Type': 'application/json', 'Authorization': wx.getStorageSync('token') || '' // 从本地缓存取 token }, success: (res) => { if (res.statusCode === 200) { resolve(res.data); } else if (res.statusCode === 401) { wx.showToast({title: '登录过期', icon: 'none'}); wx.removeStorageSync('token'); wx.navigateTo({url: '/pages/login/login'}); } else { reject(res); } }, fail: (err) => reject(err) }); }); };注意:header.Authorization字段值来自wx.getStorageSync('token'),而 token 是登录成功后由后端返回并存入本地缓存的 JWT 字符串。若login接口返回{"code":200,"data":{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}},前端必须执行wx.setStorageSync('token', res.data.token)——漏掉这行,后续所有请求都会因 401 被拦截。
4.3 真机调试避坑:安卓手机无法加载图片?检查image组件mode属性
小程序中商品图片使用<image src="{{item.cover}}" mode="aspectFill" />,但在部分安卓机型(如华为 EMUI 12)上显示空白。根本原因是mode="aspectFill"在低版本基础库中不兼容。解决方案:
- 在
app.json中设置"requiredBackgroundModes": ["audio"](虽与图片无关,但可触发基础库升级); - 更可靠做法是将
mode改为"scaleToFill"或"widthFix",并在 CSS 中用width: 100%; height: 200rpx; object-fit: cover;替代——object-fit是 CSS3 标准属性,兼容性远优于mode。
4.4 跨域问题终极解法:后端CorsConfig.java的精准配置
前端请求http://localhost:8080/api/user/login时,浏览器报CORS header ‘Access-Control-Allow-Origin’ missing。这不是前端能解决的问题,必须后端配置:
// config/CorsConfig.java @Configuration public class CorsConfig { @Bean public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); configuration.setAllowedOrigins(Arrays.asList("https://servicewechat.com", "http://localhost:8080")); // 关键!必须包含 localhost:8080 configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); configuration.setAllowCredentials(true); configuration.addExposedHeader("Authorization"); // 暴露 Authorization 头供前端读取 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } }注意:setAllowedOrigins中必须显式列出http://localhost:8080,不能写"*"——因为allowCredentials=true时,allowedOrigins不允许通配符,否则 Spring Boot 会抛出Invalid CORS request异常。
5. 角色权限与业务流程验证:管理员/用户/商家三端操作全路径实测
5.1 管理员全流程:从审核商家到处理售后
- 登录:访问
http://localhost:8080→ 输入admin/123456→ 进入后台首页; - 审核商家:点击「商家管理」→ 找到待审核商家(
status=0)→ 点击「通过」→ 后端执行merchantService.updateStatus(merchantId, 1); - 处理退款:进入「订单管理」→ 查找
status=3(已完成)的订单 → 点击「申请售后」→ 选择「退货退款」→ 提交 → 系统自动将订单状态更新为5(售后中),并生成after_sale表记录。
关键验证点:在order_master表中观察status字段是否从3变为5,且update_time时间戳是否更新——这是业务逻辑生效的铁证。
5.2 用户端核心链路:下单、支付、查看日志
用户流程依赖微信支付沙箱环境:
- 前端调用
wx.requestPayment()时,后端OrderController.createOrder()会生成prepay_id并签名; - 沙箱配置要点:在
application-dev.yml中设置wechat.pay.mchid=1900000109、wechat.pay.key=8934e7d15453e97507ef794cf7b75bf7(微信支付沙箱密钥); - 用户点击「立即支付」后,微信弹出支付框,输入
123456即可模拟成功。
提示:支付成功后,后端
PayNotifyController.notify()会收到微信回调,更新order_master.status=2(已支付),此时务必检查notify_url是否为公网可访问地址——本地开发需用ngrok映射,否则回调失败。
5.3 商家端特殊逻辑:服务预约时间冲突检测
商家发布「宠物洗澡」服务时,需填写start_time和end_time。后端MerchantService.addService()方法中包含冲突检测:
// 查询同一时间段内已存在的预约 int conflictCount = appointmentMapper.countByTimeRange( service.getStartTime(), service.getEndTime(), service.getMerchantId() ); if (conflictCount > 0) { throw new BusinessException("该时间段已被预约,请选择其他时间"); }验证方法:用同一商家账号连续发布两个服务,起止时间重叠(如都设为 10:00-11:00),第二次提交应返回{"code":500,"msg":"该时间段已被预约..."}——这是保障服务履约的关键校验。
5.4 常见问题排查:现象 → 原因 → 解决
现象 1:微信开发者工具中「用户管理」页面空白,控制台报Cannot read property 'forEach' of undefined
原因:admin/user/index.vue中mounted()钩子调用this.getUserList(),但后端/api/admin/user/list接口返回{"code":200,"data":null},data为null而非[],导致this.users.forEach报错。
解决:检查AdminController.list()方法,确保return Result.success(userList);中userList为空时返回Collections.emptyList(),而非null。
现象 2:商家上传商品图片后,前端显示http://localhost:8080/upload/xxx.jpg404
原因:后端UploadController.upload()将文件存入D:/petshop/upload/,但未配置静态资源映射。
解决:在application-dev.yml中添加:
spring: mvc: static-path-pattern: /static/** resources: static-locations: classpath:/static/,file:D:/petshop/upload/并重启服务。
现象 3:用户下单后,库存未扣减,pet_goods.stock字段仍为原值
原因:OrderService.createOrder()中扣库存逻辑被注释或未启用事务。
解决:检查@Transactional注解是否在createOrder()方法上,且updateStock()方法是否调用petGoodsMapper.updateStock(goodsId, -1)——必须用UPDATE pet_goods SET stock = stock - 1 WHERE id = ? AND stock >= 1语句,避免超卖。
现象 4:管理员修改用户手机号后,登录时提示「账号不存在」
原因:UserServiceImpl.updateUser()只更新了user表,但未同步更新login_user表(存储登录凭证)。
解决:在updateUser()方法末尾添加:
LoginUser loginUser = new LoginUser(); loginUser.setUserId(user.getId()); loginUser.setPhone(user.getPhone()); loginUserMapper.updateByUserId(loginUser);现象 5:演示视频中「宠物日志」页面可发图文,但本地部署后只能发文字
原因:user/log/add.vue中图片上传调用wx.uploadFile(),但后端LogController.uploadImage()未实现,或upload目录权限不足。
解决:确认LogController.uploadImage()方法存在,且D:/petshop/upload/目录对java.exe进程有写入权限(Windows 下右键目录 → 属性 → 安全 → 编辑 → 添加Users组并勾选「写入」)。
6. 毕设答辩高频问题预演与代码级优化技巧
6.1 答辩必问三连击:原理、边界、扩展性
Q1:为什么用 JWT 而不用 Session?
A:Session 依赖服务器内存存储会话,集群环境下需 Redis 共享,增加运维复杂度;JWT 将用户信息编码进 Token,无状态,小程序端只需存 Token,后端每次请求解析即可,符合轻量级移动端场景。但要注意 Token 过期时间设为 2 小时,且刷新机制未实现——这是可提的改进点。
Q2:订单超时未支付如何自动关闭?
A:项目未内置定时任务,但预留了OrderTimeoutJob.java(位于com.petshop.job包)。实际方案是:用@Scheduled(cron = "0 0/1 * * * ?")每分钟扫描status=0 AND create_time < NOW()-INTERVAL 30 MINUTE的订单,更新为status=4(已取消)。答辩时可展示此文件,并说明“因毕设周期限制未启用,但架构已预留”。
Q3:如果要支持多宠物店(SaaS 模式),数据库怎么改?
A:核心改动是给pet_info、pet_goods、merchant等表增加shop_id字段,并在所有 SQL 的WHERE条件中追加AND shop_id = #{shopId}。更优解是用 ShardingSphere 分库分表,但毕设阶段建议先做逻辑隔离——这体现你对扩展性的思考深度。
6.2 代码级优化:让答辩老师眼前一亮的 3 个细节
细节 1:BreadCrumbs.vue中的动态面包屑防 XSS
原代码:<text>{{ item.name }}</text>
优化后:
<text v-html="escapeHtml(item.name)"></text> <script> export default { methods: { escapeHtml(str) { return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); } } } </script>理由:item.name可能来自数据库(如商家自定义服务名称),若含<script>标签会触发 XSS。答辩时演示:在商家后台输入服务名</script><img src=x onerror=alert(1)>,优化前弹窗,优化后显示纯文本。
细节 2:OrderService.createOrder()中的幂等性控制
原代码:直接生成订单号UUID.randomUUID().toString()
优化后:
String orderNo = DigestUtils.md5Hex(userId + System.currentTimeMillis() + new Random().nextInt(1000)); // 插入前先查是否存在相同 orderNo if (orderMapper.selectByOrderNo(orderNo) != null) { throw new BusinessException("订单生成失败,请重试"); }理由:高并发下 UUID 可能重复(概率极低但存在),用userId+时间戳+随机数组合 MD5,再查库去重,成本低且绝对幂等。答辩时可对比两种方案的碰撞概率计算。
细节 3:application-dev.yml中的敏感信息脱敏
原配置:spring.datasource.password=root
优化后:
spring: datasource: password: ${DB_PASSWORD:root} # 从环境变量读取并在2-run.bat中添加:
set DB_PASSWORD=root java -jar target/petshop-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev理由:避免密码硬编码在配置文件中,符合安全开发规范。答辩时演示:修改2-run.bat中set DB_PASSWORD=xxx,重启后仍能连库——证明配置生效。
从那以后我每次交付毕设源码,都强制走一遍「三端角色全流程操作 + 数据库字段变更验证 + 答辩问题预演」,哪怕多花两小时,也比答辩现场被问住强。这份宠物商城源码包的价值,不在它多炫酷,而在它每一行代码都经得起推敲、每一个坑都留有修复路径——这才是工程师该有的底气。希望帮到你。
本文还有配套的精品资源,点击获取