资讯动态

Spring Boot图书销售平台设计与实现:从数据库设计到部署监控全解析

发布时间:2026/10/5 11:27:14 来源:尧图企业网站定制
1. 从书店到系统先给项目划清边界很多人拿到在线图书销售平台这类题目第一反应是赶紧建工程、写接口。但做过几个电商项目之后我越来越觉得这类题目的真正难点不在代码而在把业务边界想清楚。图书销售平台看起来简单实际拆开之后用户端、管理端、商品、订单、库存、支付、售后全部搅在一起如果一开始没有明确的范围写到最后往往是一团乱麻。这个题之所以适合作为毕业设计恰恰是因为它既不像简单增删改查那样没有区分度又不会像大型电商平台那样复杂到无法驾驭。我在动手之前先把整个系统切成了两大块面向普通读者的前台书城以及面向运营和管理员的后台管理系统。这两块的关注点完全不同前台讲究检索效率、浏览体验、下单链路顺畅后台讲究数据准确性、订单处理能力和可操作的管理界面。如果把二者混在一套Controller里后续维护和答辩追问都会很难看。1.1 用户端到底需要哪些功能用户端我最终圈定了七个核心模块注册登录、图书分类浏览、多条件检索、图书详情、购物车、订单结算、个人中心。注册登录是入口分类浏览和检索负责把人引流到商品详情购物车承载挑选过程订单结算触达核心交易闭环个人中心存放地址、订单记录和基本信息维护。图书检索这里我多说一句很多参考代码只做了按书名模糊查这在答辩时很容易被问到如果用户想按作者查、按出版社查怎么办。我实际上做了组合条件查询支持书名、作者、出版社、ISBN四项任意组合再叠加分类和价格区间过滤。这个功能从技术上只是多几个查询参数但业务上是完全不同的体验写进论文里也是一个很实在的亮点。支付模块我不建议在毕业设计里直接对接真实第三方支付原因很现实个人开发者申请商户号需要营业执照而且真实支付涉及回调签名、对账、退款等一系列坑很容易把精力全部消耗掉。稳妥的做法是做一个模拟支付的中间层在测试环境直接完成支付回调把支付渠道抽象成接口订单状态流转的逻辑和真实支付完全一致这样既安全又能体现设计思路。1.2 后台管理端的功能切分后台管理系统是很多毕业设计容易忽略的部分但图书销售平台真正的业务重心恰恰在后台。运营人员要维护图书分类和商品信息需要一套清晰的管理界面订单售后需要查看和操作订单状态库存管理需要实时掌握每本书的剩余数量销售统计则需要从订单数据中产出有价值的业务报表。我的后台按角色功能拆成了五个模块图书管理、分类管理、订单管理、用户管理、数据统计。图书管理包含上下架和库存调整订单管理包含订单查看和发货操作数据统计负责绘制销售额趋势图和热销图书排行。权限方面没有做复杂的RBAC而是用了最简单的角色区分管理员和普通用户各一套入口后台接口统一做角色校验。这个取舍我认为对毕业设计是合理的RBAC确实是好技术但这个项目里真正的管理角色只有一种强行引入反而显得过度设计。1.3 非功能需求决定系统能不能真正跑起来功能清单只是做什么非功能需求决定系统做得怎么样。这个项目我重点考虑了三个方面数据安全、响应速度和异常兜底。数据安全主要靠密码加密存储和前端的越权防护用户登录后携带的凭证必须能标识身份并且无法伪造响应速度靠Redis缓存热点图书数据和分类信息避免频繁请求数据库异常兜底则是设计统一的返回结构和全局异常处理器让任何一条报错信息都能被规范捕获。提示功能列表写出来之后一定要先区分核心流程和次要流程。下单、支付、库存扣减属于核心流程评论、收藏属于次要流程项目时间紧张的时候优先保证前者干干净净后者哪怕砍掉也不影响整体骨架。2. 技术选型不能无脑跟风Spring Boot版本与核心依赖的取舍技术选型是项目落地前最重要的决策也是答辩时最容易出现两极分化评价的环节。有的同学恨不得把微服务全家桶都塞进去结果分布式事务都讲不清楚也有同学全程只用JDBC裸写SQL代码量巨大却体现不出设计能力。我的原则很简单技术栈要能解决当前项目的真实问题同时每一层选型都能说清楚为什么不用另一个方案。2.1 Spring Boot版本怎么选2.7还是3.x现在网上教程铺天盖地都是Spring Boot 3.x确实新但我不建议所有人毕业设计一上来就选3.x。Spring Boot 3.x强制要求JDK 17这意味着你的开发环境、服务器环境、CI配置全部要跟着升级。如果你的机器和教程环境都停留在JDK 8或者实验室提供的服务器还是老版本的JDK那么Spring Boot 2.7.x配合JDK 8反而更稳。我自己的选择是Spring Boot 2.7.x核心原因有三点一是它在Spring Boot 2.x的最后一个维护版本社区资料和第三方中间件兼容性都非常成熟二是JDK 8依然是国内大量企业环境的客观现实用这套组合写出来的经验贴近实际三是MyBatis-Plus、Spring Boot Admin等配套组件在2.7这一代有大量现成配置可以直接用踩坑成本低。如果你的环境已经是JDK 17那选3.x没有任何问题只要注意把版本兼容表提前查清楚。2.2 持久层选择为什么是MyBatis-Plus而不是JPA或原生MyBatis持久层我用的是MyBatis-Plus理由一句话就能说明它让我把80%的CRUD从手写SQL里解放出来同时把剩下20%的复杂查询牢牢握在自己手里。Spring Data JPA的抽象级别更高但多表关联和动态SQL的场景反而束手束脚学习曲线也偏陡原生MyBatis自由度最大但一个简单的分页查询都要自己处理Total和Pages毕业设计周期内很容易被重复工作拖住。MyBatis-Plus最实用的几个能力是内置分页插件、逻辑删除、条件构造器和代码生成器。分页插件一行配置即可返回IPage对象逻辑删除让我不用每次写where is_deleted 0条件构造器让组合查询的Controller层代码非常清爽代码生成器可以一次性帮我把实体、Mapper、Service、Controller全部生成出来省下的时间可以用在核心业务逻辑上。2.3 其他核心依赖的搭配除了Spring Boot和MyBatis-Plus我整理了整个项目用到的组件清单组件用途选择理由MySQL 8.x数据存储成熟稳定本地环境部署方便Redis缓存与Token存储提升热点数据访问速度支持登录凭证的集中管理JWT无状态登录凭证前后端分离场景下标准做法便于拦截器校验Lombok消除样板代码让实体和DTO保持干净减少答辩时的代码噪音Hutool工具类集合生成订单号、加密、Bean拷贝等常用能力开箱即用Knife4j接口文档自动生成API文档方便自测和演示Spring Boot Admin运行监控可视化查看应用健康状况、线程和请求指标这个组合的特点是每个组件都有明确任务没有重复造轮子也没有为了技术而技术的冗余。下面给出pom.xml里最核心的依赖配置方便直接参考dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-validation/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.5/version /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-extension/artifactId version3.5.5/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-api/artifactId version0.11.5/version /dependency dependency groupIdio.jsonwebtoken/groupId artifactIdjjwt-impl/artifactId version0.11.5/version scoperuntime/scope /dependency dependency groupIdcn.hutool/groupId artifactIdhutool-all/artifactId version5.8.25/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency /dependencies3. 数据库是书城的骨架核心表设计与索引规划电商类项目最见功力的部分在我看来不是Controller里写了多少个接口而是数据库表设计是否经得起推敲。图书销售平台的表并不算多但每张表之间的关联关系、字段类型的选择、索引的铺设每一个决定都会在后面写SQL的时候被无限放大。我这版数据库一共设计了六张核心表用户表、收货地址表、分类表、图书表、购物车表、订单表加上订单明细表一共七张。3.1 用户与地址身份信息的关键字段用户表不只要存账号密码还要为后面前台后台两套身份做区分。我的用户表核心字段如下字段类型说明idbigint主键雪花IDusernamevarchar(50)登录名唯一索引passwordvarchar(100)BCrypt加密后的密码nicknamevarchar(50)昵称phonevarchar(20)手机号roletinyint1-普通用户 2-管理员statustinyint1-正常 0-禁用is_deletedtinyint逻辑删除标记create_timedatetime注册时间密码加密这一点特别重要千万不要用MD5直接存。MD5查表破解成本太低了Spring Security自带的BCryptPasswordEncoder或者Hutool里的BCrypt工具都行哪怕只用到它的加密能力也足够应对答辩追问。逻辑删除字段我统一加了is_deleted这样用户删除操作只是更新标记历史订单里的用户信息仍然可以关联到。地址表就简单一些核心字段是user_id、收货人姓名、电话、省市区三级、详细地址和is_default默认标记。一个用户可以有多个地址但只有一个默认地址下单时自动带出默认地址用户在结算页也可以切换其他地址。3.2 图书与分类商品信息的核心模型图书表和分类表是整个商品体系的两个支柱。分类表我用parent_id实现两级分类一级分类比如计算机、文学、经济管理二级分类比如计算机下面再分Java、Python、数据库。两级分类对图书电商来说足够清晰超过两级不仅前端展示复杂管理端维护成本也会成倍增加。图书表的字段设计直接决定详情页能展示什么、搜索能过滤什么。我的字段清单里有book_name、author、publisher、isbn、category_id、price、original_price、stock、sales、cover、description、status上架状态还有album_images用来存详情页的多图轮播。price和original_price必须用Decimal(10,2)避免float/double带来的精度误差凡是涉及金额的字段都要用定点数这是电商项目的基本素养。ISBN这个字段我特意建了普通索引将来按ISBN精确查询或者校验重复商品都能走索引。book_name、author、publisher三个字段我用的是单列索引加模糊查询的方案没有上全文索引因为图书数据的量级在毕业设计场景下根本不需要全文索引组合条件查询用LIKE加索引前缀已经足够。3.3 购物车和订单交易核心的数据闭环购物车表是最简单也最容易反复改的一张表因为它涉及一个勾选结算的问题。我的cart表字段是id、user_id、book_id、quantity、checked、is_deleted。checked字段表示这条购物车记录是否被勾选用于结算下单接口只处理checked为true的记录。这个设计看起来微不足道但如果你没有这个字段结算逻辑就会被迫处理整车的书和实际购物场景严重不符。订单表是整套数据库设计的重心。表名需要特别注意我一开始很自然地命名为order结果执行建表语句时发现MySQL把order当成保留字报错之后才改成了t_order。这个坑很小但特别典型所有新手都值得留意凡是表名撞了保留字要么加前缀t_要么建表时给表名加反引号。订单表的字段包括order_no、user_id、total_amount、pay_amount、freight、receiver_name、receiver_phone、address_detail、status订单状态、pay_time、delivery_time、finish_time、cancel_time、is_deleted。order_no我用的方案是时间戳加随机数再拼接用户ID后四位保证全局唯一没有依赖数据库自增这样订单号不会泄露当天订单量也更接近真实电商系统的做法。订单明细表t_order_item则记录下单那一刻的快照信息book_id、book_name、book_cover、price、quantity、subtotal要注意的是明细里的书名和价格必须从数据库查出来写入不能直接信任前端提交的数据。4. 主链路实现细节从登录注册到订单落库的完整代码路径表结构设计好之后后面就是顺着业务链路把每个环节的实现打通。我把主链路分成了四个阶段认证授权、商品检索、购物车结算、订单生成。这一节的内容是整套项目里最核心的代码路径我按自己实际实现时的顺序展开讲。4.1 注册登录与JWT鉴权登录模块我用的是JWT加Redis的组合方案。用户登录成功后后端把用户ID和角色信息生成一个JWT令牌返回前端同时把令牌存入Redis并设置过期时间。这个做法的好处是后端随时可以主动让某个令牌失效比如管理员禁用某个用户时直接从Redis里删掉对应令牌而不像纯JWT方案那样只能等令牌自然过期。生成JWT的代码大概是这个思路使用Hutool的JWT工具可以大幅简化操作String token JWTUtil.createToken( Map.of(userId, user.getId(), role, user.getRole()), secretKey.getBytes() );对应的拦截器里解析请求头中的Authorization字段取出token校验签名再根据token里的userId去Redis查一下是否存在。如果Redis里能查到且token一致就放行并将用户信息放入ThreadLocal供后续业务使用。这里有一个关键点登录接口本身要放行图书列表和详情可以匿名访问但购物车、下单、个人中心都必须登录后台管理接口还要额外校验角色。每一类接口的鉴权要求不同不要图省事全线拦截再逐个放行那样很容易漏掉某些匿名接口。4.2 图书检索与MyBatis-Plus分页配置图书列表和搜索接口是我花时间最多的一个Controller。MyBatis-Plus的LambdaQueryWrapper让条件构造非常直观分页也只需要调用Page对象。前提是你必须正确配置分页拦截器很多人的分页不生效就是因为缺了这个配置Configuration public class MybatisPlusConfig { Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }分页查询的Service方法大概长这样书名、作者、出版社和ISBN这四个条件用StringUtils.hasText判断是否拼接价格区间用between分类用eq全部条件组合完成后再排序public PageResultBookVO searchBooks(BookQuery query) { PageBook page new Page(query.getPageNum(), query.getPageSize()); LambdaQueryWrapperBook wrapper new LambdaQueryWrapper(); wrapper.eq(Book::getCategoryId, query.getCategoryId()) .like(StringUtils.hasText(query.getBookName()), Book::getBookName, query.getBookName()) .like(StringUtils.hasText(query.getAuthor()), Book::getAuthor, query.getAuthor()) .like(StringUtils.hasText(query.getPublisher()), Book::getPublisher, query.getPublisher()) .eq(StringUtils.hasText(query.getIsbn()), Book::getIsbn, query.getIsbn()) .between(query.getMinPrice() ! null query.getMaxPrice() ! null, Book::getPrice, query.getMinPrice(), query.getMaxPrice()) .eq(Book::getStatus, 1) .orderByDesc(Book::getSales); PageBook result bookMapper.selectPage(page, wrapper); return PageResult.of(result); }热点数据缓存方面首页的分类列表和销量前八的热门图书我做了Redis缓存缓存key带版本号后台改上下架或调整库存时主动删除对应缓存。这个方案简单高效足够应付这个小规模项目的性能演示。4.3 购物车链路和订单生成的事务边界购物车接口比较直白加入购物车时先查该用户购物车里是否已有同一本书有则累加数量没有则插入新记录修改数量时要校验库存上限防止用户把数量加到超过库存还下单成功。购物车列表返回的时候要联查图书表把书名、封面、单价、库存状态带出来让前端能直接展示和禁用超库存的条目。订单生成是整套系统里事务边界最长的一个方法涉及的操作包括读取勾选的购物车记录、校验库存、扣减库存、计算总金额、生成订单主记录、生成订单明细、清空勾选的购物车记录。任何一步失败都不应该留下脏数据所以必须用Transactional包裹整个方法。我第一次实现时天真的以为只要加了事务注解就万事大吉后来才发现同一个类内部方法自调用时注解的事务是不生效的必须把这段逻辑拆分到另一个Service类里或者通过注入自身代理对象解决。项目里我把OrderServiceImpl和CartServiceImpl分开下单方法里注入CartService完成清空操作事务边界自然就正确了。4.4 库存扣减与防止超卖库存超卖是图书电商必须正面回答的问题。两个用户同时买同一本书如果先查库存再更新库存那在并发场景下完全可能两个人都看到库存还剩1本然后都下单成功最后把库存扣成负数。虽然毕业设计演示时并发量不大但这个问题是答辩老师高频率追问的点必须用并发安全的写法堵住这一层。我的扣减库存SQL用的是乐观锁方案在图书表加一个version字段执行更新时同时匹配库存充足且版本号一致Update(UPDATE t_book SET stock stock - #{quantity}, version version 1, sales sales #{quantity} WHERE id #{bookId} AND stock #{quantity} AND version #{version}) int deductStock(Long bookId, Integer quantity, Integer version);这里为什么选乐观锁而不是悲观锁的SELECT ... FOR UPDATE因为悲观锁会在事务期间一直持有行锁虽然更严格但并发性能差且需要锁的代码块覆盖整个下单方法稍不注意锁范围就会扩散到不必要的表。乐观锁则把冲突检测放在最后一刻失败时让前端提示库存不足或商品信息已变化并要求刷新重试对图书这种低并发商品场景完全够用。如果后续项目真的要做到秒杀级并发再换分布式锁也不迟但毕业设计的形态里用乐观锁讲清楚并发控制思想已经足够出彩。5. 订单状态管理和后台模块边界情况比主流程更考验人订单在系统中不是一条静止的记录而是一个有生命周期的状态机。很多毕业设计把订单做成生成之后就万事大吉这在答辩时一定会被问到用户不付款怎么办发货之后怎么确认收货取消订单库存怎么处理。订单状态管理是本项目里最考验细节的部分我会把完整的流转逻辑展开讲。5.1 订单状态流转的业务规则我的订单状态定义为六种状态值含义触发操作0待付款用户提交订单成功1已付款/待发货模拟支付回调成功2已发货后台管理员点击发货3已收货/已完成用户确认收货或系统自动确认4已取消用户取消或超时未支付自动取消5退款已付款订单取消这里每一条状态迁移都要附加对应的业务动作。待付款变成已付款时要更新pay_time已付款变成已发货时要记录delivery_time和物流单号已收货要更新finish_time已取消不仅要改状态还必须把订单里每本书的库存回补回去否则用户取消订单后库存就凭空少了。库存回补的SQL和扣减是对称的但要注意只有已付款这个状态下的订单才能回补如果订单从未成功扣减过库存取消时就不能再回补一次这个判断必须在代码里写清楚。5.2 超时未支付与自动关单的实现用户提交订单后不付款的场景太常见了不能靠管理员手动取消。我的方案是Spring自带的Scheduled定时任务每30秒扫描一次待付款订单中创建时间超过30分钟的记录先把订单状态改成已取消再把库存回补。这个方案在一个单机应用里完全够用如果将来拆成多实例部署需要借助Redis分布式锁保证定时任务只有一个节点在执行或者在方案里加上任务幂等设计。实现定时任务的代码需要注意一点开启调度需要在启动类加EnableScheduling注解。扫描逻辑要带上当前状态是待付款这个条件同时把本次要处理的主键列表一次性查出来逐条在事务里更新状态并回补库存避免在循环里反复查询导致数据不一致。5.3 后台图书管理与销售统计后台图书管理的核心是上下架操作和库存调整。下架并不需要删除记录只要把status字段置为0前台所有查询都会自动过滤掉这本书已经加入购物车的条目也会在结算校验时被拦截上架同理一键恢复即可。库存调整要区分手工修改库存和销售扣减手工调整直接设置新的库存值并记录操作日志销售扣减走4.4节的并发安全SQL后台上架新书时初始库存也要走手工调整通道。销售统计是后台的一个加分项。我实现了一个简单的Dashboard接口统计今日订单数、今日销售额、总订单数、总销售额四个核心指标另外再查一份近七天的销售额折线数据和热销图书TOP10榜单。这些数据全部从订单和订单明细表里实时聚合图书量级小时性能没有问题也就没必要再引入单独的报表组件。前端配合ECharts画折线图和柱状图答辩演示时的视觉效果非常直观。6. 部署、日志与监控构建完整项目必做的三件收尾事一个项目的完成度不光看功能是否齐全还要看能不能在真实环境里稳定运行、出了问题能不能快速定位。很多毕业生把项目跑在IDEA里就算完事但部署、日志和监控这三个环节做不做直接决定评阅老师对项目的整体印象。这三件事本身技术含量不算高却是从玩具项目走向完整作品的必经之路。6.1 多环境配置与打包部署我从一开始就按dev和prod两套环境来管理配置。application.yml里只保留公共配置比如应用名称、JWT密钥、MyBatis-Plus的映射配置application-dev.yml指向本地MySQL和Redis日志级别是DEBUGapplication-prod.yml指向服务器上的数据库关闭Swagger文档的访问日志级别调整为INFO。启动时通过--spring.profiles.activeprod指定环境不需要改任何代码。打包方式我用的是Maven的mvn clean package打出jar包然后在服务器上通过nohup java -jar bookshop.jar --spring.profiles.activeprod启动。这个方案对单机部署来说最简单直接不需要额外安装TomcatSpring Boot内置的Web容器已经处理好了对外端口监听。如果希望开机自启配一个systemd服务单元文件即可注意JVM内存参数要根据服务器实际情况设置比如-Xms512m -Xmx512m防止默认堆内存太大导致云服务器OOM。6.2 统一返回结构、全局异常与日志切面前后端分离项目最忌讳每个接口返回的数据结构都不一样。我定义了一个ResultT类里面包含code、message和data三个字段所有Controller接口统一返回这个结构。配合RestControllerAdvice做全局异常处理业务异常抛出自定义的BizException未知异常统一包装成500返回这样前端只用处理一种响应格式排查问题时也能一眼看出是业务错误还是系统错误。日志这块我用Logback自带的配置按天滚动输出到logs目录单个文件上限100MB保留30天。另外我用AOP写了一个请求日志切面统一记录每个接口的请求路径、参数、耗时和返回状态。这个切面写起来很简单但价值非常大出问题时可以直接从日志里看到完整的调用链不需要去翻阅Tomcat的零散输出。这里要注意的是日志不能打印敏感字段比如用户密码、银行卡号这一类信息必须脱敏。6.3 引入Spring Boot Admin做运行监控Spring Boot Admin是Spring官方生态里非常成熟的监控方案分成服务端和客户端两部分。我在项目里单独建了一个简单的Admin Server模块引入spring-boot-admin-starter-server并开启EnableAdminServer被监控的业务项目引入spring-boot-admin-starter-client配置好Admin Server地址和暴露的端点。这样就能在一个管理页面上看到应用的CPU、内存、线程池、HTTP请求指标、日志级别动态调整等信息。对于这个项目来说Spring Boot Admin并不承担特别重的运维职责但它能让答辩现场显得更专业也为你将来接触Spring Cloud Alibaba体系里的监控组件做了很好的铺垫。如果不想单独建一个Server工程也可以直接把Admin的依赖加进主项目跑起来后访问/admin路径即可看到监控页面不过单独建工程的方式更规范也更容易写进论文。6.4 对外第三方接口应该放在哪里这个项目原本只服务前端和后台但既然被问到给第三方提供接口的问题我就顺带讲一下实际工程里的做法。给第三方使用的接口最好不要和内部前端接口混在一起核心原因有三层鉴权方式不同内部接口走登录态JWT第三方接口通常走AppId加签名接口规范不同第三方需要详细的文档、版本号、错误码安全要求不同第三方接口要做频率限制避免单个调用方拖垮整个系统。对于图书销售平台这个体量我认为最优做法是在同一个工程里单独建一个open包Controller路径统一以/open/api/v1开头。进入这个包的请求走独立的拦截器不校验用户登录态而是校验请求头里的AppId和Sign签名签名算法用MD5同时包含AppId、业务参数和时间戳服务端按相同算法重算再比对有效时间设置为5分钟。若将来第三方增长迅速再把open包拆成独立微服务也不迟。把接口按访问方逻辑隔离比一个字面意义上的单独服务更适合这个项目的阶段。7. 我实测后留档的坑以及给后来者的扩展建议项目写到这个程度主链路已经能跑通但我实测过程中踩掉的坑值得单独复盘一遍。这里的每一条问题我都实际遇到过不是从文档里抄来的理论写在这里希望能够帮你把同样的弯路直接跳过去。7.1 六个我实际遇到且值得留档的问题第一个坑是order表名撞了MySQL保留字前面已经提过这里不再赘述只提醒建表前先确认表名是否在保留字列表里。第二个坑是Long类型主键传到前端后精度丢失JavaScript的Number类型无法精确表示超过2^53的整数而雪花ID的值恰好可能很大。解决办法是在返回JSON时把主键字段序列化为字符串可以给字段单独加JsonSerialize(using ToStringSerializer.class)也可以做全局配置。第三个坑是LocalDateTime默认序列化格式带T比如2025-01-01T12:00:00这在浏览器里看着非常别扭。我通过Jackson配置统一设置了日期格式为yyyy-MM-dd HH:mm:ss同时规范了Java实体里的日期字段全部使用LocalDateTime类型。第四个坑是MyBatis-Plus的逻辑删除配置好之后分页查询的total依然可能不准确这通常是因为自定义SQL里自己写了count语句而没有自动拼接逻辑删除条件排查时优先检查XML里的SQL是否完整。第五个坑是事务自调用失效前面已经解释过属于Spring AOP的经典问题。第六个坑是开发阶段前后端联调时的跨域问题通过CorsFilter统一配置允许的域名、方法和请求头注意不要直接放行所有来源限定为你前端的实际地址即可。7.2 项目还能怎么扩展如果时间和精力允许我建议在这个基础框架上再加两个扩展点。第一个是引入消息队列做订单超时消息的延迟处理把定时扫描改成基于延迟消息的精准触发这能直接体现你对分布式系统中异步削峰的理解第二个是增加促销模块比如满减优惠券和秒杀活动优惠券可以训练你设计更复杂的金额计算模型秒杀活动可以引出Redis分布式锁和预扣库存的进阶思考。更重要的是这两个扩展点都能在你的毕业论文里形成独立章节让创新点不再是空话。答辩时如果能讲清楚为什么我的方案在这个场景下够用、在什么条件下需要换更强的方案就已经比大多数停留在CRUD层面的作品高出一个层次。就我个人的体会来说Spring Boot项目的价值不在于把多少新技术堆在一起而在于把一个经典场景做完整、做扎实。图书销售平台这个题目看似普通但从数据库设计、并发控制、事务边界到监控部署每个环节都能挖出足够的深度。把这套链路亲手走通之后你会发现后面做任何基于Spring Boot的应用骨架都已经在心中了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑