
简介这是一套基于Java语言、采用SpringBoot与MyBatis框架开发的免税商品优选购物商城前后端分离适合Java学习者、高校学生以及需要完成毕业设计或课程设计的人群。项目完整实现了网上购物商城的核心模块包含个人中心、用户管理、商家管理、商品分类管理、商品信息管理、在线客服管理、系统管理、订单管理等基本覆盖商品展示、后台维护、订单处理等电商业务链。资源包共837个文件整体大小25.08MB包含138个Java源码文件、50个Vue组件、153个JavaScript脚本、47个HTML页面、44个CSS样式以及SQL数据库脚本、项目说明文档、开题报告PPT、Markdown设计文档和启动脚本部署时结合MySQL 5.7、Maven3.3与eclipse/IDEA即可运行。当前已有33人学习下载。通过该项目可以系统掌握SpringBoot整合MyBatis的分层开发方式、RESTful接口设计、前端Vue页面构建以及数据库表结构设计等关键技能说明文档还会帮助理解系统架构、权限控制和订单流程便于在此基础上二次扩展是毕业设计、课程设计和Java Web进阶学习不可多得的完整参考。1. 免税商城源码拆解SpringBoot 单体架构为什么比微服务更适合毕设一个由 Java SpringBoot MyBatis MySQL 5.7 构成的前后端分离商城压缩包里满是 .vue.bak、.bat 和 db.sql这些.vue.bak不是损坏文件而是前端改版后留下的可恢复副本。对于正在做毕业设计或课程设计的人这套源代码的价值在于把个人中心、用户管理、商家管理、商品分类、商品信息、在线客服、订单管理都做成了完整可运行的链路。适合谁用需要快速搭出可演示项目、又不想从零写业务代码的 Java 学习者以及想研究 SpringBoot 与 MyBatis 实际配合方式的开发人员。拆完之后你会发现单体架构在这个量级下比微服务更容易讲清楚也更容易跑通。2. 拆解项目结构与启动链路从 .vue.bak 到 db.sql 再到 Maven 构建拿到压缩包先别急着导入 IDE先认清里面的文件属性。.classpath、mvnw.cmd、1-install.bat、2-run.bat、3-build.bat是同一套 Maven 项目的不同入口而index.html.bak、update-password.vue.bak、IndexAsideStatic.vue.bak、BreadCrumbs.vue.bak、IndexHeader.vue.bak这五个带 .bak 的文件其实都属于前端资源。2.1 压缩包里的文件到底对应什么文件/目录真实作用使用时机index.html.bak前端入口页面 Vue 挂载点恢复前端时去掉 .bakupdate-password.vue.bak修改密码页面的 Vue 单文件组件改版后的备份可恢复IndexAsideStatic.vue.bak后台侧边栏菜单组件菜单权限调整时参考BreadCrumbs.vue.bak面包屑导航组件路由与导航联动调试IndexHeader.vue.bak后台头部组件含用户信息与退出用户态展示逻辑1-install.bat安装依赖并初始化项目第一次部署2-run.bat启动 SpringBoot 服务日常开发3-build.bat打包构建可执行 jar部署前mvnw.cmdMaven Wrapper 启动脚本未配置全局 Maven 时使用.classpathEclipse 工程路径描述文件用 Eclipse 导入时自动识别.vue.bak是 Vue 单文件组件源码的备份不是损坏文件。遇到这种情况常见做法是把 .bak 后缀去掉后直接覆盖回原文件再重新执行前端构建。3-build.bat和2-run.bat的分工也值得注意2-run.bat面向开发期直接拉起应用方便热调试3-build.bat面向部署期产出的是可独立运行的 jar 包。2.2 先立数据库db.sql 导入 MySQL 5.7数据库是整条链路的地基。项目描述里指定 MySQL 5.7开发期使用 Navicat11 管理这个版本搭配对 SpringBoot 低版本和 JDK 1.8 最稳。导入 db.sql 有两种姿势第一种是命令行mysql -uroot -p --default-character-setutf8mb4 db.sql说明-u指定用户名-p提示输入密码--default-character-setutf8mb4强制客户端以 utf8mb4 字符集读取 SQL 文件避免中文乱码。是 shell 重定向把文件内容交给 mysql 客户端逐条执行。导入完成后用use 数据库名;和show tables;验证表是否齐全。第二种是 Navicat11 图形化导入连接本地 MySQL 后右键目标数据库选“运行 SQL 文件”选择 db.sql 执行。注意如果是新建的数据库字符集选utf8mb4 -- utf8mb4_unicode_ci不然商品名称和收货地址里的生僻字可能变成问号。这两种方式导入前都要先确认 MySQL 服务已启动Windows 下常见失败是 3306 端口被占用或 root 密码策略导致连接拒绝。2.3 Maven 构建与启动eclipse/IDEA 两种姿势数据库就位后修改配置再启动。SpringBoot 项目的数据库连接通常写在src/main/resources/application.yml或application.properties里。用 IDEA 导入时选择根目录下的pom.xml作为 Maven 工程等待依赖下载完成用 Eclipse 导入时则会读取.classpath自动识别工程结构。cd /d D:\project\your-project-dir 1-install.bat 2-run.bat逻辑说明1-install.bat内部通常执行mvn clean install -DskipTests作用是编译项目、跳过测试并把依赖打包进本地仓库2-run.bat内部执行mvn spring-boot:run启动内嵌 Tomcat。如果本机没有单独装 Mavenmvnw.cmd会按.mvn/wrapper配置自动下载对应版本的 Maven。常见启动报错集中在三处第一application.yml 里的数据库名、用户名、密码和本机不一致SpringBoot 启动时会在初始化数据源阶段直接抛异常第二MySQL 时区问题连接串上增加serverTimezoneAsia/Shanghai可解决 8 小时偏移第三项目使用 JDK 1.8 编译而本机默认 JDK 是 11 或 17需要在 IDE 里把 Project Structure 和 Maven Runner 的 JDK 都切到 1.8。3. 核心模块落地订单、商品分类与在线客服的实现逻辑前两章解决了“跑起来”的问题这一章看业务。项目描述里列出的个人中心、用户管理、商家管理、商品分类管理、商品信息管理、在线客服管理、系统管理、订单管理在实际工程里最终都会落到表和接口两个层面。3.1 业务表如何映射六大模块免税商城最核心的表一般包括用户表、商家表、商品分类表、商品信息表、订单表、订单明细表、客服消息表。商品信息表与分类表是多对一关系订单表与订单明细表是一对多关系用户表和商家表通过 role 字段区分身份。一个典型的商品查询用 MyBatis 写出来是这样SELECT p.id, p.name, p.price, p.tax_free_price, c.name AS category_name FROM product p LEFT JOIN category c ON p.category_id c.id WHERE p.status 1 AND p.category_id #{categoryId} ORDER BY p.sales_count DESC LIMIT #{offset}, #{pageSize};逻辑说明LEFT JOIN保证即使分类被删除商品仍能查出来status 1只查上架商品下架和软删除的都不进列表#{categoryId}是 MyBatis 预编译参数能防止 SQL 注入LIMIT #{offset}, #{pageSize}是 MySQL 分页的经典写法。实际项目里要注意 MyBatis 的#{}和${}区别表名、列名这种结构不能拼进#{}只能白名单校验后使用。选型上SpringBoot MyBatis 比 JPA 在这个场景更合适。商城列表页的筛选条件复杂LEFT JOIN、动态if、手动分页在 MyBatis 里都是透明可控的SQL 出问题可以直接复制到 Navicat 里跑JPA 的自动生成 SQL 在关联查询多时反而不好推断执行计划。3.2 订单状态机与库存扣减订单模块是商城里的重头戏。常见状态流是待支付 → 已支付 → 已发货 → 已完成另外还有已取消和退款中。订单状态修改不能直接update覆盖要让状态迁移是确定的否则并发下会出现重复支付或重复发货。状态值业务含义可流转目标1待支付已支付、已取消2已支付已发货、已取消3已发货已完成4已完成无5已取消无UPDATE order SET status #{newStatus}, pay_time NOW() WHERE order_no #{orderNo} AND status #{expectStatus};逻辑说明WHERE里的status #{expectStatus}是关键它要求当前状态必须先等于预期状态才能完成流转UPDATE影响行数为 1 表示成功为 0 表示状态已被其他请求修改程序要返回“订单状态已变化请刷新”。这就是一种乐观锁思路比在内存里加锁更简单可靠。库存扣减同理UPDATE product SET stock stock - #{buyCount} WHERE id #{productId} AND stock #{buyCount};当stock #{buyCount}不满足时影响行数为 0说明库存不足事务回滚。基于 Java 的免税商品优选购物商城在设计订单号时一般用时间戳加随机数或数据库序列生成避免直接暴露订单自增 ID。需要特别提醒的是整个下单过程必须在Transactional方法里完成先减库存再生成订单最后清购物车任何一个环节失败都要整体回滚否则会出现“库存扣了单子没创建”的脏数据。3.2.1 支付回调的幂等处理在线支付回调是另一个高频坑点。第三方支付平台超时重试会导致同一笔订单收到多次回调如果每次都执行“确认支付”就会出现重复发货。常见做法是用订单号加状态做幂等收到回调后先查订单只有状态为待支付时才执行更新并在更新语句里再次带上status 1条件。3.3 在线客服会话与未读消息在线客服模块在演示项目里经常被做成聊天室但更合理的设计是“用户—商家”一对一会话。表结构至少要有会话表和消息表会话表记录用户与商家的关联消息表记录每条内容的发送者、接收者、内容、时间、已读状态。查询未读消息数是客服入口最常见的接口SELECT COUNT(*) FROM message m JOIN conversation c ON m.conversation_id c.id WHERE c.receiver_id #{userId} AND m.is_read 0;逻辑说明JOIN conversation先把消息归属到会话再按接收人过滤is_read 0表示未读。这里有个实际开发中容易忽略的点未读状态最好在消息表上做而不是在会话表上存一个总数否则并发下数字会失真。已经接入 WebSocket 的项目可以在此基础上把新消息实时推给前端没接入的也可以用定时轮询兜底。3.4 权限拦截登录态与角色校验用户管理、商家管理、系统管理这些后台功能必须区分角色。SpringBoot 里最轻量的做法是拦截器加注解先写一个实现HandlerInterceptor的拦截器Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || .equals(token)) { response.setStatus(401); return false; } // 伪代码从 Redis 或数据库校验 token 并取出角色 UserRole role tokenService.getRole(token); if (role null || role.level() 2) { response.setStatus(403); return false; } request.setAttribute(userId, tokenService.getUserId(token)); return true; }逻辑说明preHandle在 Controller 方法执行前调用返回false表示拦截。Authorization头是前后端分离项目里传递登录态的通用位置401 表示未登录403 表示无权限语义要分清。拦截器注册时可以用addPathPatterns(/api/admin/**)限定只拦后台接口放行登录、注册、商品列表这类公开接口。真实项目里 token 推荐存 Redis 并设置过期时间演示项目直接用数据库表存也能跑通。4. 前后端分离调试Vue 管理端对接 SpringBoot 接口后端接口就绪前端却还躺着一堆 .bak 备份。这一步解决两件事把备份文件恢复成可运行的 Vue 工程再把 Vue 的请求正确转发到 SpringBoot。4.1 从 .vue.bak 恢复前端工程压缩包里出现index.html.bak、update-password.vue.bak、IndexAsideStatic.vue.bak等文件说明原作者的编辑器在改动前后各留了一份副本。恢复很简单把 .bak 后缀去掉就行。for /r %f in (*.bak) do ren %f %~nf逻辑说明for /r递归遍历子目录%~nf取文件名主名即去掉最后的.bak扩展在 .bat 脚本里写要换成%%f。运行前先复制一份备份因为覆盖后原文件就没了。如果工程里同时存在update-password.vue和update-password.vue.bak优先保留文件大小更大、修改时间更新的那个然后删除冗余版本再构建。恢复完成后在 vue 工程根目录执行npm install安装依赖再npm run dev启动开发服务器。这里有一个非常常见的失败场景npm install报证书过期或网络超时常见做法是把 npm 镜像源切到国内 registry再用npm cache clean --force清缓存重试。4.2 接口联调代理、Token 与跨域前端开发服务器默认跑在 8080 端口SpringBoot 也在 8080直接请求必然跨域。最干净的方式不是在后端开 CORS而是让 Vue 的 dev server 做代理// vue.config.js module.exports { devServer: { port: 3000, proxy: { /api: { target: http://localhost:8080, changeOrigin: true, pathRewrite: { ^/api: } } } } };逻辑说明/api前缀的请求都会转发到http://localhost:8080浏览器看到的是同源请求不触发 CORS。changeOrigin: true把请求头的 Host 改成目标地址pathRewrite把/api/user/login重写成/user/login具体要不要重写取决于 SpringBoot 接口的 RequestMapping 是否带/api前缀。接口对接时登录态通常通过请求拦截器统一附加axios.interceptors.request.use(config { const token localStorage.getItem(token); if (token) { config.headers[Authorization] token; } return config; }, error Promise.reject(error));逻辑说明每次请求前从 localStorage 读取 token写入Authorization请求头与后端拦截器里的request.getHeader(Authorization)对应。不要在每个业务方法里手动拼 header散落一地的 token 逻辑是后期越改越乱的根源。响应拦截器里还要统一处理 401发现 token 失效就跳回登录页并清理本地缓存。4.3 联调失败排查清单现象最常见原因处理方式前端请求 404代理路径与后端 RequestMapping 不匹配对比 pathRewrite 后路径接口返回 401token 未写入或已过期检查拦截器代码与登录逻辑页面能开但数据空白跨域被拦或接口返回非 JSON看 Network 面板的响应体中文乱码数据库连接串缺 characterEncoding增加characterEncodingutf8npm 启动报错缺模块node_modules 不完整删掉后重新 npm installSpringBoot 端口被占多个实例占用 8080修改 server.port 或杀进程排查顺序建议从前向后先看 Network 面板确认请求有没有发出再看请求路径和响应状态码最后才是看控制台报错。很多所谓“前后端分离项目实战”里的联调问题本质上都是路径重写和 token 传递没对齐按这张表逐项核对通常五分钟定位。5. 答辩前的改造把演示商城升级成可上线的雏形作者原始代码能跑通演示但距离答辩高分还差三处小改造密码存储、订单号、商品搜索。改动量不大却能明显提升代码评审的印象分。5.1 密码从 MD5 升级到 BCrypt很多毕设商城还在用MD5(password salt)这种静态盐写法对抗彩虹表的能力有限。常见做法是引入spring-security-crypto的BCryptPasswordEncoderString encoded new BCryptPasswordEncoder().encode(rawPassword); boolean match new BCryptPasswordEncoder().matches(rawPassword, encoded);逻辑说明BCrypt 每次生成的哈希都不同验证时用matches完成等价比对不需要自己保存盐。已有用户表里的 MD5 密码可以在登录方法里做一次兼容迁移新注册用户全部走 BCrypt答辩时能讲清楚“安全升级”这条线。5.2 订单号与商品搜索的实用增强订单号从自增 ID 改成yyyyMMddHHmmss userId 后四位 随机数避免订单数据被遍历。商品搜索量不大时LIKE %keyword%够用数据量上来后在 MySQL 5.7 里建全文索引ALTER TABLE product ADD FULLTEXT INDEX ft_product_name (name); SELECT id, name FROM product WHERE MATCH(name) AGAINST (免税 化妆品 IN NATURAL LANGUAGE MODE);逻辑说明中文全文索引依赖 MySQL 的 ngram 解析器需要在 my.ini 配置ngram_token_size2并重启服务。嫌配置麻烦就退回LIKE加限量分页演示也够用。验证改动时先用 curl 走一遍登录和订单查询链路再用 Navicat 执行EXPLAIN SELECT看索引是否命中。最后重新执行2-run.bat和3-build.bat确认改造没有破坏原有启动流程。本文还有配套的精品资源点击获取