简介:这是一套面向Java Web毕业设计的商城后台管理系统源码与数据库打包,前后台功能完整。前端使用JSP、jQuery、Layui开发,后端采用Maven构建的Spring Boot整合SpringMVC、Spring、MyBatis体系,覆盖商品发布、商品浏览、购物车、订单结算、财务管理、公告管理等电商核心业务,同时区分用户、管理员、商家三类角色,并提供商品管理、订单管理、采购管理、评论管理、日志导出、按日/月销售额统计等后台功能。资源包共468个文件,包含Java源码、JSP页面、JS脚本、CSS样式、XML配置、SQL数据库脚本、GIF操作演示截图、MP4演示视频及说明文档,压缩包约8.66MB,部署轻量、便于学习研究。目前已有31人学习浏览。整体项目结构清晰,附带数据库初始化和演示录屏,适合毕业设计参考、课程答辩或作为二次开发的基础,可快速跑通商城购物和管理员管理全流程,也方便在此基础上进行功能扩展。
1. 这个 springboot 商城后台管理系统,到底是给谁准备的
如果你正在找毕设题目,或者在资源站里翻到一个叫「基于 springboot 的商城后台管理系统(源码+数据库)」的压缩包,你真正想知道的是:这东西能不能跑、好不好改、答辩时能不能讲清楚。这类项目的本质是一个典型的 Java Web 后台管理工程——Spring Boot 做服务端,MySQL 做存储,带商品管理、订单管理、用户管理这些电商后台的标配模块,压缩包里通常是一个完整的 Maven 工程加一份 SQL 数据库脚本。它解决的核心问题不是「做出一个惊艳的商城」,而是「用最低的成本把电商后台的骨架搭起来,并且能演示、能答辩、能二次开发」。适合三类人:刚学完 SSM 想找个整合练手的 Java 初学者,急需交毕设/课设的在校生,以及刚入职电商公司想快速理解后台管理业务流的新人。下面这篇笔记,我就按自己验收这类项目的顺序,从数据库设计、核心代码实现、启动步骤到常见踩坑,完整拆一遍。
2. 技术骨架与数据库设计:先看清项目再动手
这类源码包拿到手,第一件事不是点运行,而是先把技术栈和数据库结构摸清楚。因为很多项目跑不起来,不是因为代码烂,而是你根本没搞清楚它依赖什么、表之间的关系是什么。
2.1 为什么是 Spring Boot + MyBatis-Plus,而不是 SSM
老一点的商城后台会写成 SSM(Spring + SpringMVC + MyBatis),配置 XML 一大堆,光 spring 和 mybatis 的配置文件就要写几百行,新手复制错一个字段就启动失败。现在这类源码绝大多数已经换成 Spring Boot + MyBatis-Plus 的组合,原因是:Spring Boot 内嵌 Tomcat,不用再单独部署容器,mvn spring-boot:run或直接跑 main 方法就能起服务;MyBatis-Plus 把单表增删改查的 SQL 全都封装掉了,你写的核心代码量比 SSM 版本少一半以上,这对赶工期的毕设来说太关键了。
选这个组合还有一个现实理由:好答辩。面试官或评委问「你这个项目为什么用 Spring Boot」,你能答出「自动配置、内嵌容器、生态成熟、快速开发」,这比 SSM 那套繁琐的 XML 好讲得多。不过要注意一点:Spring Boot 2.x 和 3.x 差别很大,很多资源包里写的是 2.3.x、2.5.x、2.7.x,这些项目用的是javax.*包;如果你图新鲜配成 Spring Boot 3.x,JDK 版本必须 17+,而且依赖全得换成jakarta.*,后面我会专门讲这个坑。
2.2 数据库表怎么拆:用户、商品、订单三张主表的关系
拿到 SQL 脚本,先看表设计。一个标准的商城后台管理系统,核心不会超过这几张表:用户表(后台管理员和前台会员通常分两张表,或者共用一张加角色区分)、商品表、商品分类表、订单表、订单明细表、购物车表,还有权限相关的角色表和菜单表。
我最常看到的设计是下面这套,字段不追求复杂,但关系一定是完整的:
| 表名 | 核心字段 | 作用 |
|---|---|---|
sys_user | id, username, password, role_id, status | 后台管理员账号 |
sys_role | id, role_name, role_key | 角色:超级管理员 / 普通运营 |
sys_menu | id, parent_id, menu_name, perms | 后台菜单与权限标识 |
bs_category | id, name, parent_id, sort | 商品分类,支持二级分类 |
bs_product | id, category_id, name, price, stock, status | 商品基本信息与库存 |
bs_product_image | id, product_id, url, sort | 商品轮播图/详情图 |
bs_order | id, order_no, user_id, total_amount, status | 订单主表,状态字段驱动 |
bs_order_item | id, order_id, product_id, product_name, price, quantity | 订单快照明细 |
bs_cart | id, user_id, product_id, quantity | 购物车 |
这段表关系要记牢:商品表与订单明细表是"多对多"的典型拆法,订单表和订单明细表是"一对多";订单明细里的 product_name 和 price 必须冗余一份,因为商品可能改名、改价甚至删除,但历史订单不能跟着变。很多人答辩被问住就在这个点上,你要能解释「为什么订单明细要冗余商品快照」。
建表脚本一般长这样,注意字符集和引擎:
CREATE DATABASE IF NOT EXISTS mall_admin DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE mall_admin; CREATE TABLE `bs_product` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', `category_id` BIGINT DEFAULT NULL COMMENT '分类ID', `name` VARCHAR(128) NOT NULL COMMENT '商品名称', `price` DECIMAL(10,2) NOT NULL COMMENT '销售价', `stock` INT NOT NULL DEFAULT 0 COMMENT '库存', `status` TINYINT NOT NULL 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`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';这段 SQL 里有三个设计细节:价格用DECIMAL(10,2)而不是 FLOAT,避免了浮点金额误差;status用 TINYINT 做上下架标记,配合查询时WHERE status = 1;create_time和update_time交给数据库维护,代码里不用每次手动 set。这种建表习惯,是商品类后台最通用的做法。
2.3 导入 SQL 脚本前的三个检查点
很多人在导入数据库这一步就翻车,不是因为 SQL 文件有问题,而是环境不匹配。我一般导入前检查三件事:
第一,MySQL 版本。如果包里写的是 MySQL 5.7 语法,而你装了 MySQL 8.0,绝大多数情况能兼容,但要注意 8.0 默认认证插件是caching_sha2_password,老驱动连不上时会报Public Key Retrieval is not allowed,后面配置连接串时要加allowPublicKeyRetrieval=true。
第二,字符集。建库脚本如果写的是DEFAULT CHARSET=utf8,建议手动改成utf8mb4,否则商品名称里存 emoji 或者生僻字会直接报错。上面我已经用 utf8mb4 写了建表脚本,直接照抄就行。
第三,导入工具的选择。Navicat 导入 SQL 时,遇到DELIMITER $$这种存储过程脚本容易切错分隔符;我通常直接用命令行导入,最稳:
mysql -u root -p < mall_admin.sql上面这条命令会把整个 SQL 文件一次性执行完,比在图形工具里粘贴可靠。导入成功后,用USE mall_admin; SHOW TABLES;确认一下表数量,再直接跑一句SELECT COUNT(*) FROM bs_product;,看看包里自带的初始数据有没有进去。很多源码包会在商品表里预置几十条测试数据,如果一张表都是空,后面页面白屏可别怪代码。
到这里,技术栈和数据库结构已经清楚了。下一步是看核心模块代码怎么写的,因为你要改的往往就是这些地方。
3. 核心模块这样写:登录鉴权、商品与订单流转
商城后台管理系统听起来模块多,但真正决定你这个项目能不能用的,只有三块:登录怎么做权限控制、商品怎么管理、订单状态怎么流转。其他像轮播图管理、公告管理都是锦上添花。
3.1 登录鉴权:用拦截器 + Token 的轻量方案
这类源码包里,登录鉴权最常见的实现不是 Spring Security,而是拦截器 + Token。原因是 Spring Security 配置门槛高、学习成本大,对毕设项目来说是杀鸡用牛刀。轻量方案是:登录成功后生成一个 Token(一般用 UUID 或 JWT),存在 Redis 里,前端每次请求带着 Token 过来,后端用一个拦截器校验。
下面这段是拦截器的典型写法:
@Component public class AuthInterceptor implements HandlerInterceptor { @Autowired private StringRedisTemplate redisTemplate; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录接口和静态资源 String uri = request.getRequestURI(); if (uri.startsWith("/api/login") || uri.startsWith("/static/") || uri.startsWith("/images")) { return true; } // 从 Header 里取 Token String token = request.getHeader("Authorization"); if (token != null && Boolean.TRUE.equals(redisTemplate.hasKey("login_token_" + token))) { return true; } // 未登录统一返回 401,而不是重定向 response.setStatus(HttpStatus.UNAUTHORIZED.value()); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write("{\"code\":401,\"msg\":\"未登录或登录已过期\"}"); return false; } }这段代码的逻辑说明:preHandle是 Spring MVC 拦截器的入口方法,返回 true 放行、false 拦截。注意它放行了三类地址——登录接口、静态资源和图片资源,否则会出现页面开了但 CSS、JS 全被拦截的诡异情况(后面避坑章节会细说)。Authorization是前后端约定的 Header 名称,有的项目会写成token,你要先看前端代码用的是哪个。
配置拦截器注册也很重要,Spring Boot 里这样写:
@Configuration public class WebConfig implements WebMvcConfigurer { @Autowired private AuthInterceptor authInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor) .addPathPatterns("/**") .excludePathPatterns("/api/login", "/api/captcha", "/static/**", "/error"); } }这里的参数说明:addPathPatterns("/**")表示拦截所有请求,excludePathPatterns里必须把你所有不需要登录就能访问的接口列全。常见错误是漏掉/api/captcha(验证码接口),然后前端验证码图片永远加载不出来,因为它被拦截器挡掉了。
3.2 商品管理接口:分页、上下架、库存扣减
商品管理的核心是三件事:分页查询、改上下架状态、扣减库存。用 MyBatis-Plus 的话,分页查询几乎不用手写 SQL:
@Service public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService { @Override public Page<Product> pageQuery(Integer pageNum, Integer pageSize, String keyword, Integer categoryId) { Page<Product> page = new Page<>(pageNum, pageSize); LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>(); wrapper.like(StringUtils.hasText(keyword), Product::getName, keyword) .eq(categoryId != null, Product::getCategoryId, categoryId) .orderByDesc(Product::getCreateTime); return this.page(page, wrapper); } @Override @Transactional(rollbackFor = Exception.class) public boolean deductStock(Long productId, Integer quantity) { // 乐观锁方案:先查再更新,用版本号防止超卖 Product product = this.getById(productId); if (product == null || product.getStock() < quantity) { throw new BusinessException("库存不足"); } boolean updated = this.update(null, new LambdaUpdateWrapper<Product>() .eq(Product::getId, productId) .eq(Product::getStock, product.getStock()) .setSql("stock = stock - " + quantity)); if (!updated) { throw new BusinessException("库存已变化,请重试"); } return true; } }参数说明:pageNum从 1 开始,pageSize是每页条数,前端传过来后 MyBatis-Plus 的Page对象会返回 total、records 等结构。like和eq的第一个参数是个布尔值,只有条件成立时该查询条件才会加进去,这是防止用户不传关键字时拼出畸形 SQL 的通用做法。库存扣减这里值得注意:我没有直接用setSql("stock = stock - 1")一把梭,而是先用eq(Product::getStock, product.getStock())加上原库存条件,如果更新条数为 0 说明期间被别人改过库存,这就是乐观锁的思路。在单机版后台管理系统里,这种写法比悲观锁SELECT FOR UPDATE更轻量,也够用。
上下架接口更简单,就是一个按 ID 更新状态字段的请求,但要注意一点:下架商品后,购物车里的商品怎么办?成熟的方案是前端在购物车列表里实时查商品状态,如果下架了就置灰不可勾选。如果你的源码包里没做这个逻辑,二次开发时可以加上。
3.3 订单状态机:用枚举把状态流转写死
订单是商城后台里最容易写成一团乱麻的模块。常见做法是定义一个枚举,把所有合法流转列清楚,状态更新只能通过统一的 API 来做:
public enum OrderStatus { WAIT_PAY(0, "待付款"), WAIT_SHIP(1, "待发货"), WAIT_RECEIVE(2, "待收货"), FINISHED(3, "已完成"), CANCELED(4, "已取消"); private final int code; private final String desc; OrderStatus(int code, String desc) { this.code = code; this.desc = desc; } // 判断状态是否允许从 from 流转到 to public static boolean canTransit(int from, int to) { if (from == WAIT_PAY.code) { return to == WAIT_SHIP.code || to == CANCELED.code; } if (from == WAIT_SHIP.code) { return to == WAIT_RECEIVE.code; } if (from == WAIT_RECEIVE.code) { return to == FINISHED.code; } return false; } }这段枚举的逻辑说明:canTransit是整套流转的核心,它规定待付款订单只能去待发货或已取消,已取消的订单不能再回到待付款。所有改订单状态的 Service 方法第一行都要先调canTransit判断,不合法就直接抛异常。这样做的好处是,以后不管谁来维护这段代码,都不会出现「从已完成改成待发货」这种逻辑灾难。
订单列表页在后台管理里还有一个高频需求——按状态筛选和按时间范围查询。对应的 MyBatis-Plus 查询条件是.eq(orderStatus != null, Order::getStatus, orderStatus)和.ge(startTime != null, Order::getCreateTime, startTime),与商品查询同一套路。到这里,核心业务模块已经讲完了,但这个项目现在还在你电脑里躺着没跑起来,下面说怎么把它启动。
4. 把源码跑起来:导入、配置、启动三步走
拿到源码包后,最怕的不是代码有 bug,而是连第一步都迈不出去。下面这套流程是我每次验收新项目都走的固定步骤,照着做能避开八成问题。
4.1 IDEA 导入 Maven 项目与依赖镜像配置
先把压缩包解压,确认目录结构里有pom.xml。用 IntelliJ IDEA 打开时,选Open直接指向项目根目录,IDEA 识别到pom.xml后会提示你Import as Maven Project,选Trust Project并等待右下角依赖下载完成。
这个等待过程最容易卡住——因为 Maven 默认中央仓库在国外,国内网络下载依赖慢得让人怀疑人生。解决方法是改 Maven 的settings.xml,加上阿里云镜像:
<mirror> <id>aliyunmaven</id> <mirrorOf>central</mirrorOf> <name>阿里云公共仓库</name> <url>https://maven.aliyun.com/repository/public</url> </mirror>逻辑说明:mirrorOf填central表示只镜像中央仓库,其他第三方仓库不受影响。改完之后重启 IDEA,或者点 Maven 面板里的Reload All Maven Projects。如果你看到依赖列表里spring-boot-starter-web这些 jar 包还在反复爆红,把 IDEA 缓存清理一下(File -> Invalidate Caches)再重新导入,比反复 Reimport 管用。
还有一个容易忽略的点:项目里的.idea目录或.classpath文件是别人机器上生成的,不要指望它能在你电脑上直接用。我一般会先把target目录和.idea删掉,再重新导入,这样能避免编译残留带来的各种幽灵报错。
4.2 application.yml 里的连接池和端口这样配
项目跑起来之前,必须改配置文件里的数据库连接。Spring Boot 的配置文件一般叫application.yml或application.properties,放在src/main/resources下。下面是标准配置:
server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mall_admin?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true username: root password: 你的数据库密码 hikari: minimum-idle: 5 maximum-pool-size: 20 connection-timeout: 30000 redis: host: localhost port: 6379 database: 0 servlet: multipart: max-file-size: 10MB max-request-size: 20MB mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: true参数说明:driver-class-name如果是 MySQL 5.x 驱动写com.mysql.jdbc.Driver,MySQL 8.x 驱动必须写com.mysql.cj.jdbc.Driver,写反了直接启动报ClassNotFoundException。URL 里serverTimezone=Asia/Shanghai是 8.x 驱动强制要求的,不写会报时区错误;allowPublicKeyRetrieval=true解决 8.0 认证插件导致的连接失败。hikari是 Spring Boot 默认的数据库连接池,maximum-pool-size设 20 对后台管理这种低并发项目足够,不要随手改成 200,连接池太大只会白白占资源。
还有一类项目用的是 druid 连接池,配置项是spring.datasource.druid.initial-size和max-active,要看 pom 里引入的是哪个包。如果你配置了 Redis,那么本机必须启动 Redis 服务,否则项目会启动失败。很多源码包里登录用到了 Redis 存验证码或 Token,这一步逃不掉。
4.3 启动后的接口自测:curl 验证登录与商品查询
配置改完,直接运行MallAdminApplication(类名可能不叫这个,找到带@SpringBootApplication注解的类就是入口)。启动日志出现Started ... in 5.23 seconds之后,先别急着打开浏览器,用 curl 做一次接口冒烟测试最直接:
# 1. 登录测试,拿到 token curl -X POST http://localhost:8080/api/login \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"123456"}' # 2. 带 token 查询商品列表 curl -X GET "http://localhost:8080/api/product/list?pageNum=1&pageSize=10" \ -H "Authorization: 上一步返回的token"逻辑说明:第一步登录接口如果返回 JSON 里带token字段,说明登录逻辑没问题;第二步查询商品列表时,Header 里必须带Authorization,否则会被拦截器返回 401。用 curl 而不是浏览器测试有个好处:接口返回的原始 JSON 结构一目了然,能快速确认 token 字段名、分页参数名到底叫pageNum还是page,这两个命名在项目里经常不一致,前端代码和接口要对上才行。
到这里项目已经能跑起来了。但能跑通只是及格线,下面这些坑才是真正让你熬夜的元凶。
5. 排查与避坑:新手最容易翻车的 5 个问题
我把这些年看过的、自己也踩过的坑整理成下面 5 条,每条按「现象 → 原因 → 解决」来讲,你先对照排查,再动手改。
5.1 数据库连不上:驱动包名、时区与编码三连击
现象:项目启动后控制台报Cannot create PoolableConnectionFactory或Could not create connection to database server,有时候还带The server time zone value 'Öйú±ê׼ʱ¼ä'这种乱码。
原因:三个高频原因——MySQL 驱动包版本和数据库版本不匹配、JDBC URL 里没写serverTimezone、数据库密码填错。
解决:先确认 pom.xml 里mysql-connector-java的版本。MySQL 5.7 配8.0.x驱动一般也能连,但驱动类名必须写com.mysql.cj.jdbc.Driver。然后把 URL 改成我上面 4.2 节给的那串标准格式,其中characterEncoding=utf8一定保留。如果还报错,在 MySQL 命令行里执行SELECT VERSION();确认版本,再去检查密码里有没有特殊字符——密码含@或#时放在 YAML 里必须用引号包起来,否则 YAML 解析直接失败。
5.2 Spring Boot 版本太高引发的依赖崩溃
现象:mvn compile报Cannot resolve symbol 'javax.servlet'或package javax.servlet does not exist,运行时报NoClassDefFoundError: javax/servlet/HttpServlet。
原因:Spring Boot 3.x 把javax.*全部升级为jakarta.*,同时要求 JDK 17 以上。源码包里如果是按 Spring Boot 2.x 写的(javax.servlet、javax.validation),直接升到 3.x 必然爆炸。
解决:看你本机 JDK 版本。如果你装的 JDK 8,老老实实把 Spring Boot 版本降到2.7.18,这是 2.x 最后一个版本,稳定且资料多;如果你已经装了 JDK 17,又非要跑老项目,就需要把代码里所有import javax.*改成import jakarta.*,改起来很繁琐,我一般建议直接用 JDK 8 + Spring Boot 2.7.x,因为这类毕设源码十有八九是按 Java 8 写的。还有一个细节:IDEA 的 Project Structure 里Project SDK和Modules的 Language Level 要一致,否则会出现编译环境对了但语法报错的现象。
5.3 前端页面样式丢失或接口 404
现象:页面能打开但纯文字没样式,或者控制台一堆Failed to load resource: 404,请求路径是/css/style.css这种。
原因:三处可能——拦截器把静态资源拦截了;Spring Boot 的静态资源路径没匹配上;前端打包后的 dist 目录没有正确放位置。
解决:如果你用的模板是 Thymeleaf,先看templates目录下的 HTML 文件里引用路径写的是./css还是../static/css,改成相对路径或直接/static/**;如果你用的是前后端分离的 Vue 页面,把dist目录里的内容复制到src/main/resources/static,并在WebConfig里放行静态资源。排查顺序是:先用浏览器直接访问http://localhost:8080/static/css/app.css,如果 404 就是资源位置不对,如果 200 就是页面引用路径不对。
5.4 登录拦截器把登录页也拦了,形成死循环
现象:打开后台登录页,页面没出来,不断刷新后浏览器报This page isn’t working或者无限重定向。
原因:AuthInterceptor的preHandle里没有放行/login、/captcha、/static/**,导致登录页本身的 HTML、CSS 请求全部返回 401,浏览器永远加载不出页面。更隐蔽的是,很多后端会把未登录请求重定向到/login,而/login本身又被拦截,就成了死循环。
解决:按 3.1 节那样,在preHandle第一行手动判断请求 URI 是否以登录接口、验证码、静态资源开头,是就直接return true。注意用startsWith时要把干扰项排掉,比如/api/login和/api/login/xxx都会误判,最好精确匹配登录路径或统一约定/api/public/**作为免鉴权前缀。这是一个非常典型的设计习惯:所有免登录接口统一前缀,比一个个写startsWith干净得多。
5.5 MyBatis-Plus 字段映射不上与逻辑删除
现象:查询出来createTime为 null,或者删除操作变成DELETE FROM ...直接把数据物理删掉了,而项目里明明配置了逻辑删除。
原因:实体类属性createTime映射不到数据库列create_time,或者应用里没开驼峰映射;逻辑删除字段deleted没有被 MyBatis-Plus 识别。
解决:确认配置里map-underscore-to-camel-case: true,同时实体类的createTime属性上可以加@TableField("create_time")强制绑定。逻辑删除需要在实体类字段上加@TableLogic注解,在 application.yml 里配置:
mybatis-plus: global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0逻辑说明:logic-delete-field指定逻辑删除字段名,logic-delete-value和logic-not-delete-value定义删除和未删除的值。配好之后,MyBatis-Plus 的deleteById会自动变成UPDATE ... SET deleted=1 WHERE id=? AND deleted=0,查询也会自动带上deleted=0条件。每次启动日志打开StdOutImpl后,你会看到真实执行的 SQL,这个习惯能帮你少走不少弯路。
6. 从毕设到能用的后台:三个升级方向与一个验证技巧
项目跑通、bug 清完,接下来才是真正拉开差距的部分——怎么从「能演示的毕设」变成「敢写进简历的项目」。
第一个升级方向是把后台接口真正开放给前端页面。现在很多源码包的后台页面是服务端渲染的 Thymeleaf,但企业里现在普遍是 Vue + Spring Boot 前后端分离。常见做法是:前端用 Vue 打包后,把生成的dist目录里的文件复制到 Spring Boot 的src/main/resources/static下,这样部署时一个 jar 包同时搞定前后端——这就是业界常说的「vue 打包放进 springboot 中」。Spring Boot 会自动把static目录映射为根路径,所以访问http://localhost:8080/就能直接进后台首页,不需要额外配 Nginx,对毕设演示来说足够体面。
第二个升级方向是订单处理加定时任务。后台管理系统的订单超时未支付自动取消,是业务上绕不开的需求,可以用 Spring 自带的@Scheduled做,也可以引入@EnableScheduling后写个定时任务,每分钟扫描一次待付款超时订单,调用取消方法并回滚库存。这一步能让评委看到你对业务闭环的理解,而不是只会增删改查。第三个方向是把权限从「拦截器判断是否登录」升级成 RBAC 权限模型,表结构里已经有sys_role和sys_menu了,给每个接口加上权限标识@RequiresPermission("product:edit"),再配合自定义注解 + AOP 做细粒度校验。
最后给你一个验证技巧:写一个简单的自测脚本,一键验证登录、商品查询、上下架、订单发货这条主链路是否正常。脚本思路是用 curl 依次调用,把返回码和返回 JSON 里关键字段打印出来:
#!/bin/bash BASE=http://localhost:8080 TOKEN=$(curl -s -X POST $BASE/api/login -H "Content-Type: application/json" \ -d '{"username":"admin","password":"123456"}' | jq -r '.data.token') if [ "$TOKEN" == "null" ] || [ -z "$TOKEN" ]; then echo "登录失败" exit 1 fi # 主链路检查:商品列表 -> 创建订单 -> 发货 curl -s "$BASE/api/product/list?pageNum=1&pageSize=5" -H "Authorization: $TOKEN" | jq '.code' curl -s -X POST "$BASE/api/order/create" -H "Authorization: $TOKEN" \ -H "Content-Type: application/json" \ -d '{"productId":1,"quantity":1}' | jq '.msg'这里的技巧很朴实:把每个接口的返回 JSON 用jq提取关键字段做断言,失败就exit 1。这样每次改完代码,跑一遍脚本就能发现问题,而不是靠人工点页面。写完这套脚本后,我自己也养成了一个习惯:所有接手的新后台项目,第一件事是写自测脚本、看启动日志、跑通主链路,再做任何代码改动。这个习惯帮我避开了无数次「看起来没问题一上线就炸」的尴尬。希望帮到你。
本文还有配套的精品资源,点击获取