ARTICLE DETAIL

资讯详情

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

基于Spring Boot+MyBatis-Plus的超市管理系统开发全攻略

基于Spring Boot+MyBatis-Plus的超市管理系统开发全攻略 简介一套基于SpringBoot和MySQL设计实现的小型超市管理系统毕业设计资源面向计算机专业学生、Java开发者及需要快速搭建管理类项目的初学者。系统采用B/S架构覆盖商品分类管理、采购流程优化、销售数据统计与经营报表生成等核心业务能有效支撑小型超市日常运营和经营决策。压缩包为zip格式共402个文件、约13.18MB其中158个Java文件对应后端业务逻辑58个Vue文件对应前端页面另有SQL数据库脚本、XML和yml配置、CSS/JS静态资源以及doc/docx配套设计文档目录结构清晰可直接导入开发工具运行。项目已有147人学习下载。通过学习这套代码可以完整理解SpringBoot整合MySQL、前端与后端接口联调、数据库表设计及报表统计的实现路径还能借助配套文档梳理论文撰写与答辩思路。对正在准备毕业设计或希望入门Java全栈开发的同学来说是一份具备参考和复用价值的实战案例。1. 基于Spring Boot的小型超市管理系统为什么大家都在选它做毕业设计/练手项目写毕业设计选题的时候很多人名单里都有“基于Spring Boot的小型超市管理系统”。这类题目看起来只是做一个超市后台真要动手商品、库存、进货、收银、会员、日结统计都要照顾到正好把Spring Boot MyBatis/MyBatis-Plus MySQL这条主流技术路线完整走一遍。标题里的“代码数据库LW”也说明交付物很实在除了可运行的工程还带一份SQL脚本和一篇对应论文。适合想快速跑通一个完整全栈项目、拿来做毕设或补后端落地细节的人。下面按我实际交付的顺序从选型、建表、收银扣库存讲到打包验证把步骤和坑一次说清。2. 架构与选型Spring Boot MyBatis-Plus MySQL这套组合为什么够用2.1 先把功能边界圈出来小超市的业务流是什么哪些模块可以砍小超市的日常作业其实很固定进货入库上架销售收银台开单库存不够了补货再记录一下会员折扣和日结营业额。映射到系统里就是六个核心功能登录鉴权、商品分类与商品管理、进货记录、收银开单、会员管理、营业额统计。它们之间靠商品表和库存字段串联起来业务逻辑并不复杂不需要把企业级ERP那套东西搬过来。很多第一次做这类题目的人会在选型上翻车一上来就琢磨微服务、消息队列、分布式事务。这种复杂度对一台测试服务器、一个人开发的项目完全是负担。常见做法是只保留Spring Boot作为应用框架持久层选MyBatis或MyBatis-Plus数据库用MySQL单库再配一个Redis做缓存就算顶配。权限也只需要一个管理员角色不用设计RBAC多级权限体系。我一般会把整个项目划分成六个模块商品、分类、进货、订单、会员、统计。每个模块一张到两张表Service层负责事务Controller层只做参数接收和结果返回。这个边界对毕业设计答辩和后续扩展都够用。多出来的需求比如商品图片上传、批量导入Excel都属于锦上添花不影响主流程。2.2 项目结构动手前先把package和分层骨架搭好Spring Boot项目不需要手写一大堆配置文件但包结构最好一开始就定清楚。以“supermarket”作为工程根目录常见的分层做法是下面这样supermarket/ ├── src/main/java/com/example/supermarket/ │ ├── controller/ # 接收HTTP请求对外接口都放这里 │ ├── service/ # 业务逻辑事务边界在这里控制 │ ├── mapper/ # MyBatis/MyBatis-Plus的Mapper接口 │ ├── entity/ # 数据库表对应的实体类 │ ├── config/ # 拦截器、分页插件等配置 │ └── common/ # 统一返回结果、异常处理、工具类 ├── src/main/resources/ │ ├── application.yml # 数据源、连接池、MyBatis配置 │ └── db/supermarket.sql # 建表SQL和初始数据 └── pom.xmlcontroller、service、mapper、entity四层是最常规的分层。entity里的类直接对表mapper接口继承MyBatis-Plus的BaseMapper后基本的增删改查就不用手写XML。service层放事务方法比如收银开单要同时写订单表和扣库存必须放在同一个事务里。有两点值得提前说。第一如果你用的IDEA社区版新建Spring Boot项目时没有Spring Initializr入口可以装一个Spring Assistant插件或者直接在start.spring.io上把工程生成后导入效果一样。第二不需要把某个对外接口单独拆成服务。题目里所有接口都放在controller包下拆微服务只会让答辩时被评委追问“为什么这么小的系统要拆服务”反而不好回答。2.3 依赖与配置pom.xml和application.yml两份可复现配置打开pom.xml最小可用依赖就是Web、MyBatis-Plus、MySQL驱动、Lombok四件套。常见说法是以Spring Boot 2.7.x为中心3.x也兼容代码差别不大。MyBatis-Plus的starter会把它自己的依赖带进来所以不需要额外引入mybatis和mybatis-spring网上很多旧教程会让加这两个重复引入反而容易版本冲突。parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent dependencies !-- Web服务内置Tomcat -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- MyBatis-Plus包含MyBatis核心和分页插件 -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3/version /dependency !-- MySQL 8.x驱动 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency !-- Lombok减少getter/setter样板代码 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies这段配置里最需要注意的是MyBatis-Plus版本。3.5.x之后分页插件的用法和2.x时代不完全一样后面写分页查询时会体现出来。Lombok如果不想用把实体类里的getter和setter手写也行只是代码会多三分之一。application.yml是另一个容易出问题的地方。数据库连接串、连接池参数、MyBatis的驼峰映射都集中在这里server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/supermarket?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver hikari: maximum-pool-size: 10 minimum-idle: 2 connection-timeout: 30000 mybatis-plus: configuration: map-underscore-to-camel-case: true global-config: db-config: id-type: auto连接串里的serverTimezoneAsia/Shanghai不能省MySQL 8.x下不加它启动时会报时区错误。characterEncodingutf8要放在useUnicodetrue后面才生效。driver-class-name写com.mysql.cj.jdbc.Driver这是MySQL 8的驱动类名老教程里的com.mysql.jdbc.Driver在新驱动里已经不存在。连接池我直接用了Spring Boot默认的HikariCP没有额外引入Druid。HikariCP的maximum-pool-size默认是10对这种几百条并发以下的超市后台完全够用不需要照抄大厂那些上百连接池的配置。map-underscore-to-camel-case开启后数据库里的product_name能自动映射到实体的productName字段省掉手写一大堆resultMap的功夫。3. 数据库设计从理清表关系到SQL脚本建表顺序与字段类型是核心3.1 核心数据表用户、分类、商品、会员、订单、订单明细一共六张表创业初期跑业务表结构越直白越好维护。这个系统最核心的表关系是分类表一对多商品表商品表被订单明细表引用订单明细表归属订单表会员表在收银时被订单表引用。用户表独立存管理员账号不跟业务表发生外键关系这样即使业务表清空重来也不影响登录。我用六张表覆盖整个业务链t_user管理员、t_category商品分类、t_product商品、t_member会员、t_order销售订单、t_order_item订单明细。库存就直接放在t_product.stock字段上不单独拆库存流水表。为什么要这样做因为超市进货和收银的库存变化量不算大拆流水表会让每次开单都要维护两张表对练手项目来说是过度设计。下面是建表SQL脚本注意外键我没有在表级别强制声明。原因是后续清数据、导数据时外键检查会很麻烦删除顺序错了就报错这种规模的项目靠应用层维护一致性更灵活。-- 创建数据库统一用utf8mb4 CREATE DATABASE IF NOT EXISTS supermarket DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci; USE supermarket; -- 管理员表 CREATE TABLE t_user ( id BIGINT AUTO_INCREMENT PRIMARY KEY, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(100) NOT NULL, real_name VARCHAR(50), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 商品分类表 CREATE TABLE t_category ( id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, sort INT DEFAULT 0 ); -- 商品表 CREATE TABLE t_product ( id BIGINT AUTO_INCREMENT PRIMARY KEY, category_id BIGINT NOT NULL, name VARCHAR(100) NOT NULL, price DECIMAL(10,2) NOT NULL, stock INT UNSIGNED NOT NULL DEFAULT 0, status TINYINT DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 会员表 CREATE TABLE t_member ( id BIGINT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50) NOT NULL, phone VARCHAR(20) NOT NULL, discount DECIMAL(3,2) DEFAULT 1.00, balance DECIMAL(10,2) DEFAULT 0.00 ); -- 订单表 CREATE TABLE t_order ( id BIGINT AUTO_INCREMENT PRIMARY KEY, order_no VARCHAR(32) NOT NULL, member_id BIGINT, total_amount DECIMAL(10,2) NOT NULL, pay_type TINYINT DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); -- 订单明细表 CREATE TABLE t_order_item ( id BIGINT AUTO_INCREMENT PRIMARY KEY, order_id BIGINT NOT NULL, product_id BIGINT NOT NULL, product_name VARCHAR(100), price DECIMAL(10,2) NOT NULL, count INT NOT NULL, subtotal DECIMAL(10,2) NOT NULL );几个字段类型我要专门解释。价格统一用DECIMAL(10,2)不用float和double否则累计金额会出现0.30000000000000004这种尾差。库存用INT UNSIGNED从数据库层面保证不能为负数配合后端的条件更新语句是防超卖的双保险。主键用BIGINT AUTO_INCREMENT订单量不大没问题如果以后要迁移分库BIGINT也能容纳雪花ID。MySQL里最常见的翻车点是把订单明细表建成订单表的物理外键然后在导入测试数据时因为父表还没数据导致插入失败。如果你在写项目时也这样建建议删除外键约束只保留逻辑上的关联用order_id这一列去关联就够。数据库增删改查是这个系统最频繁的操作字段设计得越扁平后面写MyBatis的查询就越省事。3.2 导入SQL脚本的三个注意点外键禁用、字符集与初始数据给用户交付的SQL脚本一定要能一键导入成功我见过很多人在这一步被卡住。先大表后小表只是基本操作更难处理的是初始化数据顺序。为了避免翻来覆去地报错脚本开头通常要加一段保护性设置SET FOREIGN_KEY_CHECKS 0; DROP TABLE IF EXISTS t_order_item; DROP TABLE IF EXISTS t_order; DROP TABLE IF EXISTS t_product; DROP TABLE IF EXISTS t_category; DROP TABLE IF EXISTS t_member; DROP TABLE IF EXISTS t_user; SET FOREIGN_KEY_CHECKS 1;这段脚本的作用是先把外键检查关掉再按从子表到父表的顺序删表。如果没有关闭外键检查删除父表时会报“Cannot delete or update a parent row”。加在SQL脚本最前面就能让脚本重复执行而不报错对反复调试的人来说算是后悔药。导入完成后别急着登录先查一下初始数据。登录账号最少得有一条INSERT INTO t_user (username, password, real_name) VALUES (admin, e10adc3949ba59abbe56e057f20f883e, 管理员); INSERT INTO t_category (name, sort) VALUES (饮料, 1), (零食, 2); INSERT INTO t_product (category_id, name, price, stock) VALUES (1, 矿泉水 550ml, 2.00, 100), (1, 可乐 500ml, 3.50, 80), (2, 薯片 原味, 6.50, 50);密码字段里存的是e10adc3949ba59abbe56e057f20f883e这是123456的MD5值。毕设和课程设计用MD5做演示没问题但生产环境不要这么干用BCrypt。这一条我会在论文里说明也算一个加分点。初始数据不要塞太多每个分类两类商品即可后续测试接口时可以自己加。导入SQL之后如果后面要调整表结构直接用ALTER TABLE在Navicat或命令行里改。比如商品想加一个barcode字段执行ALTER TABLE t_product ADD COLUMN barcode VARCHAR(20) AFTER name即可不要删表重建。这个习惯能省掉很多测试数据的重复录入时间。4. 后端落地登录鉴权、商品管理和收银扣库存的关键实现4.1 登录鉴权一个拦截器就够不需要上Spring Security很多教程一上来就推荐Spring Security但对这个小系统来说太重了。Spring Security的过滤器链、UserDetailsService、密码编码器一套配完至少多出两三百行配置还经常因为放行路径配置不对导致登录接口401。我一般做法是写一个HandlerInterceptor几十行代码解决。先看拦截器本体Component public class AuthInterceptor implements HandlerInterceptor { Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 先检查用户是否已登录这里用session保存登录状态 Object userId request.getSession().getAttribute(userId); if (userId null) { response.setStatus(401); response.getWriter().write(未登录或登录已过期); return false; } // 把用户信息放入ThreadLocal后续Service可以直接获取 UserContext.setUserId((Long) userId); return true; } Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { // 请求结束后必须清理否则线程池复用时会串数据 UserContext.clear(); } }重点是afterCompletion里的清理动作。Spring Boot的Tomcat会复用线程ThreadLocal不清掉下一个请求可能拿到上一个登录用户的ID。这是个很隐蔽的坑我见过有人上线后出现用户A看到用户B购物车数据最后定位到这个ThreadLocal没清理。然后在配置类里注册拦截器并设置放行路径Configuration public class WebConfig implements WebMvcConfigurer { private final AuthInterceptor authInterceptor; public WebConfig(AuthInterceptor authInterceptor) { this.authInterceptor authInterceptor; } Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(authInterceptor) .addPathPatterns(/**) .excludePathPatterns( /api/login, /api/register, /css/**, /js/** ); } }addPathPatterns拦截所有接口excludePathPatterns放行登录和静态资源。这里有个常见误区放行路径写错了比如漏掉/js/**前端页面加载不出来就会一直白屏。拦截器配好后登录接口里只需要执行一条userMapper.selectOne把用户ID放进session即可。Session方案对这个小系统是合适的。如果真要换Redis保存token也只是把session换成Redis操作拦截器判断逻辑不变。Spring Security留给那些真正需要角色权限区分的项目这里不硬上省下来的时间去做收银逻辑更值。4.2 商品管理MyBatis-Plus的分页查询和条件检索商品管理是系统的门面列表、搜索、上下架、改价都集中在这里。用MyBatis-Plus后基础的增删改查完全不用写SQL。以分页查询为例Controller层长这样RestController RequestMapping(/api/product) public class ProductController { private final ProductService productService; public ProductController(ProductService productService) { this.productService productService; } GetMapping(/page) public Result page(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 10) Integer size, RequestParam(required false) String name, RequestParam(required false) Integer status, RequestParam(required false) BigDecimal minPrice) { return productService.pageQuery(page, size, name, status, minPrice); } }Controller层只做参数接收业务逻辑放到Service。这里最重要的是Service里的条件构造器写法Service public class ProductService { private final ProductMapper productMapper; public ProductService(ProductMapper productMapper) { this.productMapper productMapper; } public Result pageQuery(Integer page, Integer size, String name, Integer status, BigDecimal minPrice) { PageProduct p new Page(page, size); QueryWrapperProduct qw new QueryWrapper(); // 按商品名模糊搜索 qw.like(StringUtils.hasText(name), name, name); // 按状态筛选status为空时不过滤 qw.eq(status ! null, status, status); // 价格区间查询演示最低价过滤 qw.ge(minPrice ! null, price, minPrice); // 按价格降序最新的低价商品排在前面 qw.orderByDesc(create_time); PageProduct result productMapper.selectPage(p, qw); return Result.success(result); } }QueryWrapper是MyBatis-Plus条件的核心。like(name不为空时拼接name条件空时跳过)、eq(状态)、ge(价格)这三类方法覆盖了超市里80%的筛选场景。selectPage需要提前配置分页插件否则分页不生效这是新人最容易踩的坑。分页插件配置在config包下Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }没有这段配置selectPage返回的total永远是0数据也只出前几条。MyBatis-Plus 3.5.x必须这样显式添加分页插件。商品的新增、修改、删除也都是调用BaseMapper自带的insert、updateById、deleteById不需要写一行XML。修改商品时有个细节updateById会把实体里为null的字段也更新成null如果只想更新非空字段就在实体字段上加TableField(updateStrategy FieldStrategy.NOT_NULL)或在UpdateWrapper里显式指定列。直接用updateById修改单个字段时先查一次原实体再set要改的字段能避免误清空。4.3 收银与库存事务加条件更新防止超卖收银开单是这个系统技术上最值得写的点因为涉及订单表、订单明细表、商品库存三个写操作还必须保证原子性。经典翻车写法是这样先select查库存判断库存够再update扣减。在高并发下两个请求同时查到库存为1都判断够都执行扣减库存就变成-1了。正确做法是把判断和扣减合并成一个条件更新语句Mapper public interface ProductMapper extends BaseMapperProduct { // 条件更新扣减库存只有当库存足够时才扣成功 Update(UPDATE t_product SET stock stock - #{count} WHERE id #{id} AND stock #{count}) int deductStock(Param(id) Long id, Param(count) Integer count); }先在dao层把原子扣减写好。注意这段SQL用了stock #{count}条件更新行数为0说明库存不足由Service抛业务异常回滚事务。如果把SQL写成先查库存再减行数判断就是两回事了。条件更新从数据库层面保证了并发时只有一个请求能扣减成功。然后看Service层的收银方法Service public class OrderService { private final OrderMapper orderMapper; private final OrderItemMapper orderItemMapper; private final ProductMapper productMapper; Transactional(rollbackFor Exception.class) public Result createOrder(OrderCreateDTO dto) { // 1. 生成订单号并插入订单表 Order order new Order(); order.setOrderNo(S System.currentTimeMillis()); order.setMemberId(dto.getMemberId()); order.setTotalAmount(dto.getTotalAmount()); orderMapper.insert(order); // 2. 逐条扣库存并插入订单明细 for (OrderItemDTO item : dto.getItems()) { int rows productMapper.deductStock(item.getProductId(), item.getCount()); if (rows 0) { throw new BusinessException(商品库存不足: item.getProductId()); } // 扣减成功后再组织明细数据入库 OrderItem orderItem new OrderItem(); orderItem.setOrderId(order.getId()); orderItem.setProductId(item.getProductId()); orderItem.setCount(item.getCount()); orderItem.setSubtotal(item.getPrice().multiply( BigDecimal.valueOf(item.getCount()))); orderItemMapper.insert(orderItem); } return Result.success(order.getOrderNo()); } }Transactional必须加在public方法上而且是Spring管理的Bean才生效。rollbackFor指定所有Exception都回滚因为Spring默认只对RuntimeException回滚像BusinessException这种自定义异常如果你继承了Exception但不指定rollbackFor事务不会回滚订单和明细就产生了脏数据。事务边界也要注意事务方法内部做远程调用、耗时IO要尽量避免否则连接池很快被打满。这个方法里三次数据库操作都是快查询事务时间都在几十毫秒级适合这样的设计。如果以后要接第三方支付回调回调逻辑要放到事务提交后再处理可以注入TransactionTemplate或者用事务同步器afterCommit。5. 避坑与排查这套系统交付前我踩过的5个坑这个系统看起来简单但真到部署交付时坑往往不在业务逻辑里而在环境、编码和打包这些边角上。下面按现象、原因、解决的顺序写5条条条都是我实际遇到并处理过的。5.1 中文乱码页面和数据库全是问号现象前端页面商品名称显示“????”数据库里中文也是问号或者保存后乱码。原因三处字符集不一致。数据库建库时没指定utf8mb4连接串没带characterEncodingutf8或者Tomcat接收请求时默认不是UTF-8。解决数据库层面执行ALTER DATABASE supermarket CHARACTER SET utf8mb4表也改一遍连接串补上useUnicodetruecharacterEncodingutf8Spring Boot在配置文件里加一个server.servlet.encoding.forcetrue强制请求和响应都用UTF-8。这三处改完重新启动中文就没问题了。5.2 启动报The server time zone value is unrecognized现象Spring Boot启动时数据库连接报错堆栈里出现server time zone提示无法识别时区。原因MySQL 8.x的时区信息没有初始化默认值不合法驱动要求显式指定serverTimezone。解决连接串里加serverTimezoneAsia/Shanghai。如果服务器安装在海外就换成自己的本地时区。这个坑看似小实则是MySQL 8的标配问题项目里所有数据源配置都要带上这个参数。5.3 收银并发时库存变成负数现象用两个请求同时买同一个商品最后一个商品被买了两次数据库库存显示-1。原因先查库存再扣减两个请求同时读到stock1都通过了判断然后各自执行update扣减。解决把判断与扣减合并成一条条件更新SQLWHERE里加stock #{count}如4.3节所示。数据库行锁会保证同一行只有一个update能命中另一个返回0然后抛异常回滚。建议写完后用线程池同时发100个请求做一次验证。5.4 商品ID在浏览器端被截断修改商品指向错误数据现象前端表格显示的商品ID尾数变成000比如19223372036854775807变成19223372036854778000点击编辑时出现“记录不存在”。原因BIGINT主键超过JavaScript的Number.MAX_SAFE_INTEGERJSON序列化后前端丢失精度。这在订单号、商品ID这种长期增长的主键上很容易触发。解决在实体的id字段上加JsonSerialize(using ToStringSerializer.class)把Long序列化成字符串。前端拿到的就是字符串不再丢失精度。这个方法对MyBatis-Plus的雪花ID、自增ID都适用。5.5 打好的jar包运行没反应java -jar后秒退现象mvn package打包成功java -jar运行时报“no main manifest attribute”或者报找不到类。原因项目没有使用Spring Boot的打包插件打出来的是普通jar而不是可执行fat jar缺少启动入口。解决pom.xml里加spring-boot-maven-plugin并确保在build标签内build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /build加完重新打包target目录下会出现两个jar选名字不带original的那个运行。这算是最常见的“翻车”了第一次做Spring Boot项目的人几乎都会遇到。6. 打好包再验证冒烟测试脚本与两个可以顺手加上的扩展6.1 一条命令跑完登录、查商品、下单三个核心接口打包部署后我习惯先跑一遍冒烟测试再收工。用一条bash脚本把最核心的三件事过一遍登录拿session、查商品列表、提交一个收银订单。不依赖Postman服务器上直接能跑。# 登录把cookie存到/tmp/cookie.txt curl -s -c /tmp/cookie.txt \ -X POST http://localhost:8080/api/login \ -H Content-Type: application/json \ -d {username:admin,password:123456} # 带上cookie查商品列表确认分页正常 curl -s -b /tmp/cookie.txt \ http://localhost:8080/api/product/page?page1size10 # 模拟买一瓶矿泉水确认库存扣减 curl -s -b /tmp/cookie.txt \ -X POST http://localhost:8080/api/order/create \ -H Content-Type: application/json \ -d {items:[{productId:1,count:1,price:2.00}]}看输出里是否有订单号再查一次数据库t_product的stock字段从100变成99就说明链路通了。这套冒烟脚本建议放进交付文档里答辩现场演示时不用打开页面点半天直接跑命令更干净。6.2 两个值得加的小扩展Redis缓存和Spring Boot Admin监控如果时间有余给这个系统加一个Redis缓存是性价比很高的扩展。商品列表是读多写少的接口把热门分类下的商品缓存起来key用categoryIdvalue用JSON修改商品时del掉对应缓存能显著降低数据库压力。代码量不大但在论文里可以写“引入Redis降低热点数据查询延迟”比纯增删改查的选题有亮点。另一个我推荐的是Spring Boot Admin它能把应用的健康状态、内存、接口调用情况可视化。在pom里加spring-boot-admin-starter-client配置文件里指定admin服务端地址运行起来后在控制台就能看到当前库存接口的调用耗时和错误率。毕设演示时展示一下这个监控界面比口头解释“系统很稳定”有说服力得多。交付这套系统的最后一件事我会再查一遍库存有没有负数再把application.yml里的数据库密码换成环境变量引用。这算是我自己养成的习惯毕竟教别人跑通项目要比自己跑通多花一倍精力。希望这篇笔记能帮你少踩几个坑把更多时间花在真正有亮点的业务设计上。本文还有配套的精品资源点击获取
返回列表