ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java微信小程序宠物店商城系统:基于SSM框架的完整开发指南

Java微信小程序宠物店商城系统:基于SSM框架的完整开发指南 简介一份面向毕业设计与小程序开发学习的宠物商城项目基于SSM框架搭建后台、Vue实现管理页面微信小程序作为用户端配合MySQL数据库与JDK 1.8环境。系统包含管理员、用户、商家三类角色管理员负责用户、商家、宠物分类、宠物信息、商品与服务项目管理用户可购买宠物和用品、预约服务、发表日志并管理订单商家则提供宠物、用品及服务处理订单与预约。资源内含Java源码、数据库脚本、论文文档、环境工具包以及相同框架项目的安装教程可辅助快速部署并理解前后端数据交互。包体共1662个文件约52.19MB以png图片、js/java/vue源码、json/wxml/wxss等小程序与前端文件为主辅以sql脚本、bat启动脚本和mp4演示目录结构清晰。目前已有123人学习下载适合作为毕业设计参考或SSM微信小程序开发实践。1. 宠物店商城小程序为什么毕业设计选这个组合最稳毕业设计选型就像配环境变量版本不对后面全是坑。java微信小程序宠物店商城这套组合本质上是把SSMSpring SpringMVC MyBatis后端、微信小程序前端、MySQL数据库三层串成一条完整的业务闭环用户在小程序里看宠物、加购物车、下单支付管理员在Web端管商品、管订单、管会员。这个方向能覆盖的技术点是全的——小程序端要处理登录态和接口交互后端要写RESTful接口和事务数据库要设计表结构哪怕只是一套单体应用也能把「从数据库到手机屏幕」整条链路讲清楚。对于需要快速出成果、又要能答辩讲明白的毕业设计来说SSM源码加文档加教程这套组合是性价比最高的选择没有之一。那些直接选Spring Boot的反而在答辩时容易被追问自动配置原理问到卡壳SSM反而因为足够经典每个环节都能讲出东西来。我见过太多人选错方向要么只做一个纯前端静态页面要么后端写一堆Servlet却没有框架支撑最后在答辩时被一句「为什么用框架、框架帮你解决了什么问题」问住。SSM模式的好处是它的分层足够清晰——Controller管请求转发、Service管业务逻辑、Mapper管数据库操作你甚至可以对着分层结构讲20分钟不用看稿。这篇文章会按「环境搭建 → 核心业务实现 → 踩坑排查 → 答辩验证」的顺序把这个项目的完整落地路径拆开讲透。2. 环境搭建与架构选型先把地基打牢再谈业务2.1 技术栈选型为什么是Java SSM而不是Spring Boot如果你去翻java面试八股文你会发现SSM相关的问题占比非常高——IoC容器、AOP代理、MyBatis动态代理、SpringMVC执行流程这些几乎是一线面试的开场必问。而Spring Boot的自动配置往往被简单带过因为面试官知道大部分人只是用它根本讲不清starter背后的原理。这也解释了为什么很多java课程设计案例源码仍然坚持用SSM它更适合用来展示「我学过原理而不是只用过工具」。用SSM搭建宠物店商城核心依赖如下Spring负责管理所有Bean的生命周期和依赖注入包括Service层、Controller层、以及事务管理。SpringMVC负责HTTP层的路由分发将小程序端发来的请求映射到具体的处理逻辑。MyBatis负责数据持久化通过Mapper接口和XML映射文件操作MySQL。前端是微信小程序原生框架不引入第三方UI库减少排查问题的复杂度。数据库用MySQL 5.7JDK用1.8Tomcat用8.5——这套组合经过无数项目验证是兼容性最稳的搭配。选择这套组合还有一层考虑是排错成本低一旦出问题网上几乎能找到所有人踩过的坑不至于被一个奇怪的环境错误卡住两周。2.2 从零搭建后端骨架Maven项目结构常见的做法是先创建一个Maven的war包项目因为传统SSM最终要部署到外置Tomcat。如果你用的是IDEA新建项目时选Maven Archetype然后填好GroupId和ArtifactId。下面是一个标准的SSM项目目录骨架pet-shop-backend/ ├── pom.xml ├── src/main/java │ └── com/example/petshop │ ├── controller/ # 前端控制器接收HTTP请求 │ ├── service/ # 业务逻辑层接口 │ ├── service/impl/ # 业务逻辑实现 │ ├── dao/ # MyBatis Mapper接口 │ ├── entity/ # 实体类与数据库表字段对应 │ └── common/ # 通用返回结果、异常处理 ├── src/main/resources │ ├── spring/ # Spring和MyBatis配置 │ │ ├── spring-dao.xml │ │ ├── spring-service.xml │ │ ├── spring-mvc.xml │ ├── mapper/ # MyBatis的XML映射文件 │ └── jdbc.properties # 数据库连接配置 └── src/main/webapp └── WEB-INF/web.xml核心的pom.xml依赖如下dependencies !-- Spring核心 -- dependency groupIdorg.springframework/groupId artifactIdspring-context/artifactId version5.2.22.RELEASE/version /dependency !-- SpringMVC -- dependency groupIdorg.springframework/groupId artifactIdspring-webmvc/artifactId version5.2.22.RELEASE/version /dependency !-- MyBatis整合Spring -- dependency groupIdorg.mybatis/groupId artifactIdmybatis-spring/artifactId version2.0.7/version /dependency !-- MySQL驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId version5.1.49/version /dependency !-- 数据库连接池 -- dependency groupIdcom.alibaba/groupId artifactIddruid/artifactId version1.2.6/version /dependency /dependencies这里选择Druid连接池是可以讲出理由的它自带监控页面答辩时可以直接调出SQL执行次数和慢查询统计展示你做过的性能分析。版本号故意选5.2.22和2.0.7是因为这两个版本兼容性最成熟不要追新版本——5.3.x和6.x在部分Tomcat 8.5上会出现Jackson版本冲突属于那种排查起来极度痛苦的玄学问题。2.3 关键配置三层如何串起来spring-dao.xml负责数据库连接和Mapper扫描?xml version1.0 encodingUTF-8? beans xmlnshttp://www.springframework.org/schema/beans xmlns:txhttp://www.springframework.org/schema/tx xmlns:aophttp://www.springframework.org/schema/aop xmlns:contexthttp://www.springframework.org/schema/context xsi:schemaLocation... !-- 读取数据库配置 -- context:property-placeholder locationclasspath:jdbc.properties/ !-- 配置Druid数据源 -- bean iddataSource classcom.alibaba.druid.pool.DruidDataSource property namedriverClassName value${jdbc.driver}/ property nameurl value${jdbc.url}/ property nameusername value${jdbc.username}/ property namepassword value${jdbc.password}/ property nameinitialSize value5/ property namemaxActive value20/ /bean !-- 配置SqlSessionFactory -- bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property namedataSource refdataSource/ !-- Mapper XML文件位置 -- property namemapperLocations valueclasspath:mapper/*.xml/ /bean !-- 扫描DAO接口 -- bean classorg.mybatis.spring.mapper.MapperScannerConfigurer property namebasePackage valuecom.example.petshop.dao/ /bean !-- 事务管理 -- bean idtransactionManager classorg.springframework.jdbc.datasource.DataSourceTransactionManager property namedataSource refdataSource/ /bean tx:annotation-driven transaction-managertransactionManager/ /beans这段配置的核心逻辑是先把数据库连接交给Druid管理再让MyBatis的SqlSessionFactory使用这个数据源最后配置一个事务管理器让Service层可以正常使用Transactional注解。spring-mvc.xml负责Controller扫描和JSON转换mvc:annotation-driven mvc:message-converters bean classorg.springframework.http.converter.json.MappingJackson2HttpMessageConverter property nameobjectMapper bean classcom.fasterxml.jackson.databind.ObjectMapper !-- 处理日期格式 -- property namedateFormat bean classjava.text.SimpleDateFormat constructor-arg valueyyyy-MM-dd HH:mm:ss/ /bean /property /bean /property /bean /mvc:message-converters /mvc:annotation-driven context:component-scan base-packagecom.example.petshop.controller/有两点值得注意日期格式如果不在这里统一成yyyy-MM-dd HH:mm:ss小程序端拿到的会是一串时间戳需要额外写代码转换Controller扫描路径一定不能包含Service的实现类否则会导致Service被WebApplicationContext和RootApplicationContext重复初始化出现事务失效的严重问题。2.4 数据库表结构设计宠物店商城需要几张表一套完整的宠物店商城最少需要八张表用户表、宠物分类表、宠物表、购物车表、订单表、订单明细表、地址表、管理员表。宠物表是核心字段设计如下CREATE TABLE pet ( id INT NOT NULL AUTO_INCREMENT, name VARCHAR(50) NOT NULL COMMENT 宠物名称, category_id INT NOT NULL COMMENT 关联分类表, price DECIMAL(10,2) NOT NULL COMMENT 售价, original_price DECIMAL(10,2) DEFAULT NULL COMMENT 原价用于展示折扣, stock INT NOT NULL DEFAULT 0 COMMENT 库存, main_image VARCHAR(255) DEFAULT NULL COMMENT 主图路径, detail TEXT COMMENT 宠物描述, status TINYINT DEFAULT 1 COMMENT 1上架 0下架, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有两个容易被忽略的细节。第一price必须用DECIMAL(10,2)而不是FLOAT浮点类型在做金额比较和累加时会产生类似0.30000000000000004的精度问题这是实测翻车过无数次的坑。第二category_id必须建索引因为商城首页首先要按分类拉取宠物列表没有索引时数据量一上去就会出现明显的查询延迟。订单表需要关注的字段包括订单号、用户ID、总金额、状态待支付/已支付/已发货/已完成/已取消、创建时间和支付时间。订单号建议直接用数据库自增ID加上时间戳拼接不要用UUID因为UUID作为主键在InnoDB中会导致页分裂带来严重的写入性能下降——这是个经典的面试加分点。3. 小程序端与后端交互登录、商品列表、下单全流程3.1 微信小程序登录态设计与Session维护小程序端的核心登录机制是wx.login 后端Session存储需要完整走通流程才能保证后续所有请求都带着合法身份。第一步是小程序调用wx.login拿到临时code把这个code传给后端后端拿着code调用微信的接口换区openid再把openid存到后端Session里返回一个自定义的登录态标识给小程序。登录时序的关键代码如下小程序端// 小程序端登录逻辑 wx.login({ success: function(res) { if (res.code) { // 将临时code发送给后端换取登录态 wx.request({ url: https://yourdomain.com/api/user/login, method: POST, data: { code: res.code }, success: function(response) { // 后端返回的token存入Storage wx.setStorageSync(token, response.data.data.token); // 之后所有请求带着这个token即可 } }); } else { console.log(获取登录code失败, res.errMsg); } } });后端对应接口Controller RequestMapping(/api/user) public class UserController { Autowired private IUserService userService; RequestMapping(/login) ResponseBody public Result login(RequestBody MapString, String params) { String code params.get(code); if (StringUtils.isEmpty(code)) { return Result.error(code不能为空); } String openid userService.getOpenIdByCode(code); if (StringUtils.isEmpty(openid)) { return Result.error(微信登录失败); } // 生成token并存入Redis过期时间设为2小时 String token UUID.randomUUID().toString().replace(-, ); userService.saveToken(token, openid); return Result.success(token); } }需要注意一个容易被忽略的细节getOpenIdByCode方法的实现中需要先查库里有没有这个openid没有的话要自动创建一条新用户记录并给新用户初始化一个默认头像和昵称。由于小程序端拿不到用户头像昵称的实时更新需要用户手动触发getUserProfile所以商品浏览、加购、下单这些操作不应该依赖用户头像和昵称统一用openid作为用户唯一标识即可。3.2 小程序端请求封装统一处理Token和错误提示小程序端不能直接编写网络拦截器这种东西所以常见的做法是把wx.request封装成一个公共的request方法。封装逻辑要处理三件事在Header中带上token、在401状态码时跳转登录页、在非2xx状态码时弹出Toast提示。// utils/request.js const BASE_URL https://yourdomain.com/api; function request(path, method, data) { return new Promise(function(resolve, reject) { wx.request({ url: BASE_URL path, method: method, data: data, header: { Content-Type: application/json, token: wx.getStorageSync(token) }, success: function(res) { if (res.statusCode 401) { // token过期重新登录 wx.removeStorageSync(token); wx.navigateTo({ url: /pages/login/login }); reject(new Error(登录已过期)); } else if (res.statusCode 200 res.data.code 0) { // 业务成功 resolve(res.data.data); } else { // 业务失败提示后端返回的message wx.showToast({ title: res.data.message || 服务器繁忙, icon: none }); reject(new Error(res.data.message || 请求失败)); } }, fail: function(err) { wx.showToast({ title: 网络异常请检查网络, icon: none }); reject(err); } }); }); } module.exports { request };这个封装解决了三个真实问题不用在每个业务页面重复写header、错误提示风格统一、Promise化的代码在业务层不用回调嵌套。尤其第三个问题小程序页面里如果每个请求都用回调函数一到两个请求串联先登录再拉数据就会开始缩进地狱极其不利于答辩时展示代码。3.3 宠物列表与商品详情小程序页面的核心数据流动宠物列表页是小程序的核心场景需要用分类栏加商品卡片列表的方式呈现。页面生命周期中onLoad里先请求分类接口拿到分类后再请求默认分类下的宠物列表。// pages/index/index.js const { request } require(../../utils/request.js); Page({ data: { categories: [], currentCategory: 0, petList: [], loading: false, page: 1, hasMore: true }, onLoad: function() { this.loadCategories(); }, loadCategories: function() { request(/category/list, GET, {}).then((res) { this.setData({ categories: res, currentCategory: res[0].id }); this.loadPets(1, true); }); }, loadPets: function(page, isRefresh) { if (this.data.loading || !this.data.hasMore) return; this.setData({ loading: true }); request(/pet/list, GET, { categoryId: this.data.currentCategory, page: page, pageSize: 10 }).then((res) { if (isRefresh) { this.setData({ petList: res.list, hasMore: res.hasMore }); } else { this.setData({ petList: this.data.petList.concat(res.list), hasMore: res.hasMore }); } this.setData({ page: page, loading: false }); }).catch(() { this.setData({ loading: false }); }); }, switchCategory: function(e) { const id e.currentTarget.dataset.id; this.setData({ currentCategory: id, page: 1, hasMore: true }); this.loadPets(1, true); }, // 触底加载更多 onReachBottom: function() { this.loadPets(this.data.page 1, false); } });分页加载是展示代码规范程度的最佳场景。初学的人容易犯的错误是直接一次把全部数据拉回来在数据量测试时看不出问题但答辩演示时一旦宠物商品超过50条页面滚动会明显卡顿。这个代码里用hasMore标记控制是否还有下一页、用loading标志防止重复请求这两个标志位是老手看代码时一眼就能识别出工程素养的地方。3.4 下单流程购物车到订单的事务保证下单流程涉及多张表的写操作往订单表插一条记录、往订单明细表插N条记录每个商品一条、扣减宠物库存、清空购物车。这四步必须处于同一个事务中——如果订单插入成功但扣库存失败或者扣了库存却没能生成订单明细就会出现数据不一致。Service public class OrderServiceImpl implements IOrderService { Autowired private OrderMapper orderMapper; Autowired private OrderItemMapper orderItemMapper; Autowired private CartMapper cartMapper; Autowired private PetMapper petMapper; Override Transactional(rollbackFor Exception.class) public Order createOrder(Long userId, Long[] cartIds, Long addressId) { // 1. 查询购物车选中商品列表 ListCart carts cartMapper.selectByIds(cartIds); if (carts null || carts.isEmpty()) { throw new BusinessException(购物车为空); } // 2. 计算总金额 BigDecimal totalPrice new BigDecimal(0); for (Cart cart : carts) { Pet pet petMapper.selectById(cart.getPetId()); if (pet.getStock() cart.getQuantity()) { throw new BusinessException(商品[ pet.getName() ]库存不足); } // 累加金额 totalPrice totalPrice.add(pet.getPrice().multiply(new BigDecimal(cart.getQuantity()))); } // 3. 创建订单主记录 Order order new Order(); order.setOrderNo(generateOrderNo()); order.setUserId(userId); order.setAddressId(addressId); order.setTotalPrice(totalPrice); order.setStatus(0); // 待支付 orderMapper.insert(order); // 4. 创建订单明细并扣库存 for (Cart cart : carts) { Pet pet petMapper.selectById(cart.getPetId()); OrderItem item new OrderItem(); item.setOrderId(order.getId()); item.setPetId(pet.getId()); item.setPetName(pet.getName()); item.setPrice(pet.getPrice()); item.setQuantity(cart.getQuantity()); orderItemMapper.insert(item); // 扣减库存 petMapper.decreaseStock(pet.getId(), cart.getQuantity()); } // 5. 清空购物车 cartMapper.deleteByIds(cartIds); return order; } private String generateOrderNo() { return P System.currentTimeMillis() String.format(%04d, new Random().nextInt(10000)); } }Transactional(rollbackFor Exception.class)这个细节值得单独说明Spring默认只对RuntimeException回滚对CheckException不回滚。如果业务代码里抛出的是自定义的BusinessException且这个异常继承了Exception而非RuntimeException不加rollbackFor的话事务不会回滚会出现库存扣了但订单没生成的血泪案例。用new BigDecimal(0)而不是0来初始化金额是为了明确使用BigDecimal运算链路避开Java中浮点运算的精度坑。库存扣减这里有一个进阶做法用UPDATE pet SET stock stock - #{quantity} WHERE id #{id} AND stock #{quantity}来保证超卖不会发生。如果影响行数为0则说明库存被抢完了此时直接抛出异常让事务回滚。这个写法比先查再判断再更新更安全因为高并发下两个请求可能同时读到相同的库存值。4. 避坑指南SSM 小程序项目最常见的8个致命错误4.1 小程序请求报「url not in domain list」这几乎是每个微信小程序项目会遇到的第一个翻车点。现象是小程序真机预览时所有wx.request请求都报url not in domain list但开发者工具里却能正常请求。原因微信小程序有一套安全机制真机运行时请求的URL必须在小程序后台「开发管理-服务器域名」中配置过。开发者工具默认勾选了「不校验合法域名」所以本地调试能过真机运行就翻车。解决在小程序管理后台添加request合法域名域名必须是HTTPS且备案过的。开发阶段如果后端在本地用内网穿透工具把localhost映射成HTTPS域名能大幅缩短调试循环时间。另一个建议是用IP地址加端口直接访问在局域网真机调试——不过注意正式版小程序里IP地址同样不能通过校验只有开发版可以开启「不校验合法域名」绕过。4.2 后端返回的JSON日期变成时间戳现象小程序端拿到订单创建时间显示为一串数字如1700000000000不是预期的2024-11-01 12:30:00。原因Java侧的Date类型序列化为JSON时Jackson默认将其转换为毫秒时间戳这是为了让JavaScript端方便处理而设计的默认行为。解决在SpringMVC配置中加入全局的ObjectMapper定制统一日期格式。高版本的Jackson还支持spring.jackson.date-format配置项但SSM模式下额外引入jackson-datatype-jsr310再配置ObjectMapper更稳定一劳永逸。4.3 微信小程序进入页面白屏且Console里没有报错现象小程序页面加载后白屏Console只有几条warning没有任何error提示。原因最常见的情况是onLoad里的请求参数中含有undefined值导致请求URL或body里出现非法字符。因为请求是异步的报错信息被吞掉后页面数据一直是空数组。解决在request.js中增加一层参数校验function request(path, method, data) { // 过滤data中的undefined字段 Object.keys(data).forEach(key { if (data[key] undefined || data[key] null) { delete data[key]; } }); return new Promise(function(resolve, reject) { // 原有wx.request逻辑 }); }调试时优先看Network面板能看到实际发出的请求URL和参数比盯着Console效率高一倍。4.4 Maven项目启动报ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet现象Tomcat启动后立即报ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet但IDEA里依赖明明是有的。原因这是一个非常经典的问题。IDEA的Eclipse模式下Maven依赖默认是provided状态不会打包进最终部署的war包。更常见的情况是maven打的war包没有执行mvn package而是直接把项目根目录当Web应用部署了导致lib目录下没有任何jar包。解决用IDEA的Maven面板执行package确认target目录下生成了fat war包后再配置Tomcat部署war包。如果SSM项目里引用了少量额外的jar包如阿里的fastjson需要手动把jar放到WEB-INF/lib下。还有一个隐蔽版本mvn package后lib目录里找不到spring-webmvc的jar说明pom里包装的是scope为provided在pom里把它改为compile重新打包即可。4.5 spring-dao.xml 无法创建 SqlSessionFactory现象启动日志出现Error creating bean with name sqlSessionFactory细化原因可能是Invalid bound statement (not found)也可能是Cause: org.xml.sax.SAXParseException。原因前者说明Mapper接口和XML文件没有正确关联可能是namespace没有写全限定名或者mapperLocations路径和XML实际位置不匹配后者是XML文件开头没有从http://mybatis.org/dtd/mybatis-3-mapper.dtd引入或者文件编码不是UTF-8。解决优先检查resources/mapper目录下XML的namespace是否等于DAO接口的全限定名再检查XML中的id是否与接口中的方法同名。这是自检优先级最高的两个位置。4.6 小程序真机调试正常但扫码体验版白屏现象用开发者工具真机调试一切正常但通过体验版二维码进入时白屏。原因区别在于体验版走的是真实服务器域名Https。如果你的后端服务器没配HTTPS证书或者用了自签名证书真机调试因为开了「不校验合法域名」所以能连上体验版则直接拒绝请求。解决确认服务器上的HTTPS证书是有效且被信任的CA签发的。自签名证书只能用于开发不能用于体验版。需要检查Nginx配置的SSL证书路径是否正确、443端口是否被防火墙拦截。排查时用Chrome直接访问接口域名如果浏览器提示证书无效小程序端一定也会出问题。4.7 数据源配置了MySQL但启动总连不上数据库现象Tomcat启动后报Could not get JDBC Connection在浏览器访问项目时报500。原因绝大多数原因是MySQL的时区问题。MySQL 8.x的连接URL中必须配置serverTimezoneAsia/Shanghai否则驱动默认尝试使用服务器的时区系统如果服务器时区不是UTC就会报错。另一个高发原因是驱动版本和MySQL版本不匹配——MySQL 8用5.1.x的驱动会直接报class not found。解决MySQL 8.0对应使用com.mysql.cj.jdbc.DriverURL中加上时区参数jdbc.drivercom.mysql.cj.jdbc.Driver jdbc.urljdbc:mysql://localhost:3306/petshop?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/ShanghaiuseSSLfalse jdbc.usernameroot jdbc.password你的密码4.8 购物车库存扣减后没有订单明细现象下单完成后订单存在但订单明细表里查不到任何记录。原因订单明细的insert语句如果失败了但异常没有抛出很可能是因为订单明细表中某个字段指定了NOT NULL约束而代码里没有赋值。例如pet_image字段可能定义为NOT NULL而实体类中对应的属性为null插入时数据库报了约束异常但由于异常被catch吞掉了事务已经标记rollback回滚也完成了所以订单也没了。解决在createOrder方法的循环中逐一打印订单明细的赋值过程确认每个字段都有值。信息量更大的自检方式是打开MyBatis的SQL日志在applicationContext.xml里加入bean idsqlSessionFactory classorg.mybatis.spring.SqlSessionFactoryBean property nameconfigurationProperties props prop keylogImplSTDOUT_LOGGING/prop /props /property /bean这样每一条SQL都能在控制台打印出来一眼定位到是哪条insert失败比盯着Java堆栈高效太多。5. 源码改造与文档协同从能跑到能毕业的加分路径5.1 在别人源码基础上做差异化改造的三个方向很多同学拿到的SSM源码包是一个完整的宠物商城但在答辩时如果只是复述一遍功能很容易被评委判定为「没有工作量」。三个建议的改造方向按工程量从低到高排第一给商品列表加入多条件筛选——按价格区间、按热度排序、按上架时间排序。这看起来改动小但涉及SQL的ORDER BY和WHERE动态拼接能体现对MyBatis动态SQL的理解。第二给订单模块加入简单的销量统计按日统计总营业额、按商品统计销量Top10。这个需要新增聚合查询SQL接口在后端管理页展示技术上是典型的GROUP BY和SUM应用。第三也是工作量最大的——增加一个收藏模块。用户可以在宠物详情页点击收藏需要新增收藏表、后端接口、小程序页面三个部分整体改动涉及全栈做完后无论是技术展示还是工作量展示都足够亮眼。无论选哪个方向都要在文档的「需求分析」和「系统设计」章节同步补充对应的用例图、E-R图和数据字典——比如收藏表的结构说明。评委通常不只盯着代码问还会对比文档和系统的描述是否一致。5.2 文档配合设计文档怎么和源码互相对应毕业设计文档的核心不是事无巨细罗列功能而是向评委展示你的设计思路和技术决策过程。如果源码是参考来的文档中最容易被追问的就是「你做了什么改动」。所以文档里的「系统实现」一章要刻意强调你自己加入的那部分功能写清楚设计思路——比如你改造的价格区间筛选为什么用MyBatis的动态SQL拼装而不是直接用Java拼SQL因为你考虑到字符串拼接有SQL注入风险用if标签可以把dynamic条件安全地交给MyBatis预编译。5.3 答辩演示从「能跑通」到「能讲清」的验证流程答辩演示的质量取决于你对交互链路是否了如指掌。建议按下面的顺序走一遍完整的演示脚本打开小程序走登录流程点开Network面板展示请求头里的token变化。进入首页切换分类展示列表刷新逻辑。进入商品详情加入购物车再次进入购物车页出示数量变化。点击结算选择地址提交订单。打开后端管理页面展示订单列表出现新订单。在小程序端模拟取消订单切换Web端订单状态展示状态同步。这六个步骤覆盖了登录、商品、购物车、订单、库存这五个核心域评委大概率会在这些地方追问。5.4 进阶验证用JMeter做一个毛坯性能测试很多毕业设计只停留在「能跑」加一个简单的性能测试会显著提高技术含量。用JMeter创建一个线程组模拟50个并发用户同时请求商品列表接口观察响应时间的中位数和95分位。如果响应时间中位数超过500ms说明SQL可能存在N1查询问题需要排查Mapper的关联表查询。这一步的价值是给论文的测试章节填充真实数据。由于SSM项目自带Druid连接池你还能顺便在Druid监控页面截图SQL执行次数展示数据库层面的监控能力——这在答辩中是个极其加分的细节。我对自己的要求一直是在本地跑通完整链路之后再单独测试一次「库存扣减超卖」的并发场景。因为只有真正让它出过问题答辩时被问到这类异常处理才能给出真实且自然的回答。希望这篇拆解和踩坑记录能帮到你少走几段弯路。本文还有配套的精品资源点击获取
返回列表