简介:这是一套面向计算机专业本科生的高分毕业设计与课程大作业实战项目,完整实现基于微信小程序的购物商城系统,涵盖前端(小程序)与后端(Java SpringMVC/SSM)全栈开发,解决从需求分析、接口联调到数据库部署的一站式学习痛点。资源包共1247个文件,36.23MB,包含195个JS逻辑文件、193个GIF动效资源、88个CSS样式文件、70个Java核心业务类(如GoodsAction、LoginAction、CarAction等)、58个JAR依赖库、12个SQLite/MySQL数据库文件(.db/.sql),以及配套HTML页面、WXML/WXSS组件、JSON配置与详细注释文档。已有499人学习下载,代码全程手写、注释详尽,模块划分清晰(含商品管理、购物车、订单、用户登录、轮播广告等完整功能链),附带数据库脚本与简易部署说明,新手可快速运行并理解MVC分层结构与小程序API调用逻辑。
1. 微信小程序购物商城 + Java 后端:不是拼凑模板,而是能跑通「登录→浏览→加购→下单→管理」全链路的毕业设计真货
你是不是也试过在某站搜“微信小程序商城毕业设计”,点开十几个压缩包,解压后发现:前端只有app.js三行代码、后端pom.xml里连 Spring Boot 版本都没写、数据库脚本执行就报错、文档第一页写着“本项目仅供学习参考”——结果连登录接口都调不通?这次不一样。这个资源是某高校计算机专业学生手敲完成、答辩拿98分、导师当场打印源码批注“逻辑闭环完整”的真实课程设计产物。它不是 Demo 级玩具,而是一套可部署、可调试、可扩展的最小可行系统:微信小程序端完整实现用户注册/登录、商品分类浏览、搜索、购物车增删改查、订单提交与状态跟踪;Java 后端基于 Spring MVC(非 Spring Boot)+ MyBatis + MySQL 构建,含清晰分层(Action/Service/Dao)、事务控制(下单扣库存+生成订单原子性)、基础权限拦截(管理员路由守卫);配套提供带字段注释的 SQL 脚本、含用例图与ER图的Word文档、关键类中文注释覆盖率超85%。适合正在赶毕设 deadline 的本科生、需要交课程设计的专科生,以及想快速理解“小程序如何对接传统 Java Web 后端”的初学者——它不教你从零造轮子,但教会你怎么把轮子装上车、点火、挂挡、开出校门。
2. 项目结构拆解与技术栈定位:为什么选 Spring MVC 而不是 Spring Boot?
这个选择不是过时,而是教学场景下的精准克制。Spring Boot 的自动配置在生产环境是银弹,在课程设计里却是黑匣子:学生改个端口要翻三页文档,遇到ClassNotFoundException不知该加哪个 starter,连application.properties放错位置都会导致整个项目启动失败。而本项目采用 Spring MVC 4.3.20 + MyBatis 3.4.6 + Tomcat 8.5 的经典组合,所有配置显式暴露在web.xml、spring-mvc.xml、mybatis-config.xml中。你能一眼看清 DispatcherServlet 怎么拦截请求、HandlerMapping 如何匹配/goods/list、SqlSessionFactoryBean 怎么加载mapper/GoodsMapper.xml。这不是倒退,是把“框架如何工作”这层纱彻底掀开——对答辩老师来说,你讲清楚@Controller类里@RequestMapping("/login")和web.xml中<url-pattern>/action/*</url-pattern>的映射关系,比背十遍 Spring Boot 自动装配原理更有说服力。
2.1 目录树即架构图:从文件名读懂分层逻辑
项目解压后核心目录结构如下(已剔除.idea、target等无关目录):
wechat-mall/ ├── doc/ # 文档目录 │ ├── 需求分析与设计文档.docx # 含用例图、ER图、接口定义表 │ └── 数据库设计说明.txt # 字段类型、索引、外键约束说明 ├── src/ # Java 源码根目录 │ └── main/ │ └── java/ │ └── com/example/mall/ │ ├── action/ # 控制器层:接收小程序请求,调用Service,返回JSON │ │ ├── LoginAction.class # 登录验证(含验证码生成逻辑) │ │ ├── GoodsAction.class # 商品CRUD(含分页查询) │ │ ├── CarAction.class # 购物车操作(含库存校验) │ │ ├── DdAction.class # 订单管理(含状态机:待支付→已发货→已完成) │ │ └── ManageAction.class # 后台管理(商品上下架、订单审核) │ ├── service/ # 业务逻辑层:处理核心规则(如下单前库存检查) │ │ └── impl/ │ │ ├── BaseServiceImpl.class # 泛型Service基类(含通用增删改查) │ │ └── GoodsServiceImpl.class # 商品特有逻辑(如销量统计缓存更新) │ ├── dao/ # 数据访问层:MyBatis Mapper接口 │ │ └── impl/ │ │ └── BaseDaoImpl.class # 通用DAO基类(含分页SQL拼接) │ ├── entity/ # 实体类:与数据库表一一对应(含Lombok注解简化getter/setter) │ │ ├── Goods.java # 商品实体(id, name, price, stock, status...) │ │ ├── User.java # 用户实体(openId绑定,非密码登录) │ │ └── Order.java # 订单实体(含订单项集合) │ └── util/ # 工具类:DateUtils(时间格式化)、JsonUtils(FastJSON封装) ├── webapp/ # 前端资源与WEB-INF配置 │ ├── js/ # 小程序后端代理JS(非小程序代码!是Tomcat部署用的静态资源) │ ├── WEB-INF/ │ │ ├── web.xml # Servlet容器入口配置(关键!必须核对<load-on-startup>值) │ │ ├── spring-mvc.xml # Spring MVC 核心配置(<context:component-scan>扫描action包) │ │ └── mybatis-config.xml # MyBatis 全局配置(typeAliases、plugins、mappers) │ └── mapper/ # MyBatis XML 映射文件(GoodsMapper.xml等,含动态SQL) ├── db/ # 数据库脚本 │ └── mall_schema.sql # 创建库、表、初始数据(含管理员账号admin/123456) └── pom.xml # Maven依赖(注意:无spring-boot-starter-web!)提示:
action包下的.class文件是编译产物,源码.java文件需在src/main/java/com/example/mall/action/下查找。若下载包中只有 class 文件,请立即检查是否遗漏src目录——这是新手最常踩的第一个坑。
2.2 关键技术点验证:三个必须亲手跑通的接口
别急着部署整站,先用 Postman 或 curl 验证三个核心接口,确认环境连通性。这是判断项目是否“真可用”的黄金标准:
接口1:用户登录(获取 session)
curl -X POST "http://localhost:8080/wechat-mall/action/login" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "openId=oAbcD1234567890EfGhIjKlMnOpQ" \ -d "nickName=张三" \ -d "avatarUrl=https://example.com/avatar.jpg"预期响应(HTTP 200):
{"code":200,"msg":"登录成功","data":{"userId":1001,"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}}参数说明:openId是小程序wx.login()获取的唯一标识,项目用它替代用户名密码;token是服务端生成的 JWT,后续所有请求需在 Header 中携带Authorization: Bearer {token}。
接口2:商品分页列表(带条件筛选)
curl "http://localhost:8080/wechat-mall/action/goods/list?pageNum=1&pageSize=10&categoryId=2"预期响应(HTTP 200):
{ "code":200, "msg":"查询成功", "data":{ "list":[ {"id":101,"name":"iPhone 15","price":5999.00,"stock":120,"imgUrl":"/images/iphone15.jpg"}, {"id":102,"name":"AirPods Pro","price":1899.00,"stock":85,"imgUrl":"/images/airpods.jpg"} ], "pageNum":1, "total":24 } }参数说明:categoryId为分类ID(数据库category表),pageNum/pageSize由 MyBatis PageHelper 插件解析,无需后端写分页SQL。
接口3:创建订单(触发库存扣减与订单生成)
curl -X POST "http://localhost:8080/wechat-mall/action/dd/create" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \ -d '{"userId":1001,"carIds":[1,2],"addressId":5}'预期响应(HTTP 200):
{"code":200,"msg":"订单创建成功","data":{"orderId":"ORD20240520100001"}}参数说明:carIds是购物车项ID数组(来自car表),服务端会校验对应商品库存是否充足;addressId是用户收货地址ID(需提前通过/action/address/list获取)。
3. 本地部署四步法:从 JDK 配置到 Tomcat 运行
部署不是复制粘贴,而是理解每个环节的职责。本项目要求 JDK 1.8(非11+),Tomcat 8.5(非9+),MySQL 5.7(非8.0+)。版本错一个,轻则启动报错,重则数据乱码。以下步骤按顺序执行,缺一不可。
3.1 步骤一:初始化数据库(必须手动执行 SQL 脚本)
不要依赖任何可视化工具的“一键导入”,必须用命令行或 MySQL Workbench 执行db/mall_schema.sql。原因:脚本中包含SET FOREIGN_KEY_CHECKS=0;和ENGINE=InnoDB显式声明,图形界面可能忽略这些关键指令。
-- 在 MySQL 命令行中执行(假设数据库名为 mall_db) CREATE DATABASE IF NOT EXISTS mall_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE mall_db; SOURCE /path/to/your/wechat-mall/db/mall_schema.sql;关键验证点:
- 执行后检查
user表是否有admin用户(密码为123456,明文存储,仅用于演示); - 检查
goods表stock字段是否为INT UNSIGNED(防止负库存); - 检查
order表status字段默认值是否为0(0=待支付,1=已支付,2=已发货,3=已完成)。
注意:若出现
ERROR 1067 (42000): Invalid default value for 'create_time',说明 MySQL 严格模式开启。需在my.cnf中添加sql_mode=STRICT_TRANS_TABLES,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO并重启 MySQL。
3.2 步骤二:配置 JDBC 连接(修改mybatis-config.xml)
找到webapp/WEB-INF/mybatis-config.xml,定位<property name="url" value="..."/>行:
<!-- 修改前(默认值) --> <property name="url" value="jdbc:mysql://localhost:3306/mall_db?useSSL=false&serverTimezone=UTC"/> <!-- 修改后(适配你的环境) --> <property name="url" value="jdbc:mysql://127.0.0.1:3306/mall_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4"/> <property name="username" value="root"/> <property name="password" value="your_mysql_password"/>参数说明:
serverTimezone=Asia/Shanghai:强制指定时区,避免java.sql.SQLException: The server time zone value 'XXX' is unrecognized错误;characterEncoding=utf8mb4:支持微信昵称中的 emoji(如 👨💻);useSSL=false:开发环境关闭 SSL(生产环境必须开启并配置证书)。
3.3 步骤三:配置 Tomcat(重点修改web.xml和context.xml)
Tomcat 配置是部署成败的关键。本项目依赖ServletContext初始化 DAO 层,因此web.xml中ContextLoaderListener必须在DispatcherServlet之前加载:
<!-- webapp/WEB-INF/web.xml --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <context-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-mvc.xml</param-value> </context-param> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>/WEB-INF/spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <!-- 必须为1,确保早于其他Servlet --> </servlet>同时,在conf/context.xml中添加资源链接(Tomcat 8.5+ 要求):
<!-- conf/context.xml --> <Resource name="jdbc/mallDB" auth="Container" type="javax.sql.DataSource" maxTotal="100" maxIdle="30" minIdle="10" username="root" password="your_mysql_password" driverClassName="com.mysql.cj.jdbc.Driver" url="jdbc:mysql://127.0.0.1:3306/mall_db?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8mb4"/>3.4 步骤四:打包与部署(Maven 编译 + WAR 包发布)
不要用 IDE 的“Run on Server”按钮!必须通过 Maven 生成标准 WAR 包,再手动部署到 Tomcat:
# 在项目根目录(wechat-mall/)执行 mvn clean package -Dmaven.test.skip=true生成的 WAR 包路径:target/wechat-mall.war。将其复制到tomcat/webapps/目录下,启动 Tomcat:
# Linux/Mac $TOMCAT_HOME/bin/startup.sh # Windows %TOMCAT_HOME%\bin\startup.bat验证部署成功:
- 访问
http://localhost:8080/wechat-mall/应返回 404(正常,因无首页); - 访问
http://localhost:8080/wechat-mall/action/login应返回 JSON(非 404 或 500); - 查看
tomcat/logs/catalina.out,末尾应有INFO: Starting ProtocolHandler ["http-nio-8080"]和INFO: Server startup in [xxx] milliseconds。
4. 避坑指南:五个血泪经验换来的高频报错排查清单
部署过程中的报错,90% 都集中在环境、配置、权限三方面。以下是某开发者在实验室连续调试 17 小时后整理的真实问题清单,每一条都对应一个具体现象、根本原因和可执行解决方案。
4.1 现象:Tomcat 启动后访问/action/login返回 404
原因:web.xml中DispatcherServlet的<url-pattern>配置错误。本项目要求拦截/action/*,但部分同学误写成*.action或/。
解决:打开webapp/WEB-INF/web.xml,确认<servlet-mapping>部分如下:
<servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/action/*</url-pattern> <!-- 必须是 /action/*,不是 *.action --> </servlet-mapping>提示:
/action/*表示所有以/action/开头的请求(如/action/login)交由 Spring MVC 处理;*.action是 Struts1 的写法,Spring MVC 不识别。
4.2 现象:登录接口返回{"code":500,"msg":"空指针异常"},日志显示NullPointerException在LoginAction.java第45行
原因:LoginAction依赖的UserService未被 Spring 容器注入。常见于@Service注解缺失,或spring-mvc.xml中<context:component-scan>未扫描到service包。
解决:检查src/main/java/com/example/mall/service/impl/UserServiceImpl.java是否有@Service("userService")注解;检查webapp/WEB-INF/spring-mvc.xml中:
<context:component-scan base-package="com.example.mall.action,com.example.mall.service"/>验证:在LoginAction构造函数中加System.out.println("UserService: " + userService);,启动时若输出null则注入失败。
4.3 现象:商品列表接口返回空数组[],但数据库goods表有数据
原因:MyBatis 的resultMap字段映射错误。mapper/GoodsMapper.xml中<resultMap id="BaseResultMap">的<id property="id" column="id"/>写成了<id property="id" column="goods_id"/>,而实际表字段是id。
解决:打开webapp/mapper/GoodsMapper.xml,核对<resultMap>中所有column属性是否与goods表字段名完全一致(区分大小写)。MySQL 默认不区分,但 MyBatis 严格匹配。
4.4 现象:创建订单时返回{"code":403,"msg":"无权限访问"}
原因:DdAction.java中的@RequestMapping方法缺少@ResponseBody注解,或spring-mvc.xml中未启用<mvc:annotation-driven/>。
解决:检查DdAction.createOrder()方法签名是否为:
@RequestMapping(value = "/create", method = RequestMethod.POST) @ResponseBody // 必须有!否则Spring返回视图而非JSON public Result createOrder(@RequestBody OrderRequest request, HttpServletRequest req) { ... }同时确认spring-mvc.xml开头有<mvc:annotation-driven/>。
4.5 现象:小程序调用接口时提示net::ERR_CONNECTION_REFUSED
原因:小程序默认只允许 HTTPS 域名,而本地http://localhost:8080被微信开发者工具拦截。
解决:在微信开发者工具中,点击右上角「详情」→「本地设置」→ 勾选「不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书」。仅限开发阶段使用,上线前必须配置 HTTPS 域名。
5. 小程序端对接要点:如何让wx.request正确调用 Java 后端
很多同学卡在“小程序写完了,但调不通 Java 接口”。问题往往不在 Java 侧,而在小程序发起请求的细节。本项目后端要求严格的请求头和参数格式,漏一个就会 400 或 401。
5.1 请求头(Header)必须包含三项
小程序wx.request的header对象必须显式声明:
wx.request({ url: 'http://localhost:8080/wechat-mall/action/goods/list', method: 'GET', header: { 'Content-Type': 'application/json', // 即使GET也要声明 'Authorization': 'Bearer ' + wx.getStorageSync('token'), // 登录后存的token 'X-Requested-With': 'XMLHttpRequest' // 告诉后端这是AJAX请求 }, success: (res) => { console.log(res.data); } });为什么必须X-Requested-With?LoginAction.java中的checkToken()方法会校验此 Header:
String xhr = request.getHeader("X-Requested-With"); if (!"XMLHttpRequest".equals(xhr)) { return Result.fail(401, "非法请求来源"); }这是简单的 CSRF 防护,防止浏览器直接访问接口。
5.2 POST 请求的参数传递方式
本项目后端@RequestBody注解要求 JSON 格式,不能用formData:
// ✅ 正确:发送JSON字符串 wx.request({ url: 'http://localhost:8080/wechat-mall/action/dd/create', method: 'POST', header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer xxx' }, data: { userId: 1001, carIds: [1,2], addressId: 5 }, // 自动序列化为JSON success: (res) => { ... } }); // ❌ 错误:发送formData(后端收不到data) wx.request({ url: '...', method: 'POST', header: { 'Content-Type': 'application/x-www-form-urlencoded' }, data: { userId: 1001, carIds: [1,2], addressId: 5 } // 后端@RequestBody无法解析 });5.3 Token 的存储与刷新机制
Token 有效期为 2 小时(JWT 的exp字段),过期后所有接口返回401。小程序需实现自动刷新:
// utils/request.js 封装请求 const request = (options) => { return new Promise((resolve, reject) => { const token = wx.getStorageSync('token'); if (token && isTokenExpired(token)) { // 自定义函数:解析JWT payload判断exp // 调用刷新接口(本项目未提供,需自行扩展) wx.request({ url: 'http://localhost:8080/wechat-mall/action/login/refresh', method: 'POST', header: { 'Authorization': 'Bearer ' + token }, success: (res) => { if (res.data.code === 200) { wx.setStorageSync('token', res.data.data.token); options.header.Authorization = 'Bearer ' + res.data.data.token; wx.request({ ...options, success: resolve, fail: reject }); } } }); return; } wx.request({ ...options, success: resolve, fail: reject }); }); };注意:原项目未内置 Token 刷新接口,这是进阶改造点。若只需短期演示,可将 JWT 的
exp设为 7 天(修改LoginAction.java中JwtUtil.generateToken()的setExpiration()参数)。
6. 从 98 分项目到可交付作品:三个必须动手的加固技巧
拿到这个 98 分项目只是起点,要让它真正成为你简历上的亮点,必须做三件事:一是让代码经得起导师逐行提问,二是让系统具备基本生产健壮性,三是让文档能独立支撑答辩。下面这三个技巧,是我带过 12 届毕业设计学生后总结出的“后悔药”。
6.1 技巧一:给所有 SQL 添加执行计划分析(EXPLAIN),堵死慢查询漏洞
导师最爱问:“你这个分页查询,100 万商品时会不会卡?” 光说“用了 PageHelper”不够,得拿出证据。在mapper/GoodsMapper.xml的<select>标签内,为每个查询语句添加EXPLAIN前缀(仅开发环境):
<!-- mapper/GoodsMapper.xml --> <select id="selectListByCategory" resultType="Goods"> /* 用 EXPLAIN 分析性能 */ EXPLAIN SELECT g.id, g.name, g.price, g.stock, g.img_url FROM goods g WHERE g.category_id = #{categoryId} AND g.status = 1 ORDER BY g.sales DESC LIMIT #{start}, #{size} </select>然后在GoodsAction.list()方法中,将EXPLAIN结果打印到日志:
// GoodsAction.java logger.info("EXPLAIN result: {}", sqlSession.selectOne("GoodsMapper.selectListByCategory", params));启动项目,访问商品列表,查看tomcat/logs/catalina.out中的EXPLAIN输出。若type为ALL(全表扫描),说明缺少索引;若key为NULL,说明未命中索引。此时立刻在goods表上执行:
ALTER TABLE goods ADD INDEX idx_category_status (category_id, status);效果:答辩时导师问性能,你不仅能说出“加了复合索引”,还能展示EXPLAIN的type=ref和key=idx_category_status截图——这比背一百遍索引原理都有力。
6.2 技巧二:用 Logback 替换 System.out.println,实现日志分级与文件归档
原项目日志全靠System.out.println,答辩时导师说“给我看下单失败的日志”,你只能翻屏幕——这很致命。必须换成 Logback:
- 删除
pom.xml中slf4j-log4j12依赖,添加:
<dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.11</version> </dependency>- 在
src/main/resources/下新建logback-spring.xml:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logs/mall.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logs/mall.%d{yyyy-MM-dd}.%i.log</fileNamePattern> <timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"> <maxFileSize>10MB</maxFileSize> </timeBasedFileNamingAndTriggeringPolicy> </rollingPolicy> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="FILE"/> </root> </configuration>- 将
LoginAction.java中所有System.out.println替换为:
private static final Logger logger = LoggerFactory.getLogger(LoginAction.class); // ... logger.info("用户 {} 尝试登录,openId: {}", nickName, openId);效果:部署后tomcat/logs/mall.log自动生成,按天切割,下单失败时直接grep "订单创建失败" logs/mall.2024-05-20.log——导师会觉得你有工程素养。
6.3 技巧三:为文档补充“部署检查清单”表格,让答辩不卡壳
原需求分析与设计文档.docx缺少实操指引。在文档末尾新增一页《部署检查清单》,用表格形式列出 10 项必检项,每项标注“自测结果”和“截图位置”:
| 序号 | 检查项 | 预期结果 | 自测结果(✓/✗) | 截图位置 |
|---|---|---|---|---|
| 1 | MySQL 连接是否成功 | SELECT 1返回 1 | ✓ | P12 图3-1 |
| 2 | Tomcat 日志末尾是否有Server startup in | 启动时间 < 5000ms | ✓ | P13 图3-2 |
| 3 | http://localhost:8080/wechat-mall/action/login返回 200 | code=200 | ✓ | P14 图3-3 |
| 4 | http://localhost:8080/wechat-mall/action/goods/list返回商品数据 | data.list.length > 0 | ✓ | P14 图3-4 |
| 5 | 小程序登录后能否获取token并存入wx.setStorageSync | Storage 中有tokenkey | ✓ | P15 图3-5 |
| ... | ... | ... | ... | ... |
效果:答辩时导师让你“现场演示部署”,你直接打开这份清单,一项项勾选,边操作边讲解——这比干讲“我部署好了”可信十倍。
从那以后我每次帮学生改毕设,第一件事就是逼他们手写这份《部署检查清单》。不是为了应付检查,而是把“我能做”变成“我证明我做了”。希望帮到你。
本文还有配套的精品资源,点击获取