ARTICLE DETAIL

资讯详情

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

Spring Boot漫画商城系统:商品建模、Redis购物车与订单状态机实战

Spring Boot漫画商城系统:商品建模、Redis购物车与订单状态机实战 1. 漫画商城系统先搞清楚它到底要卖什么Spring Boot做的网上漫画商城系统源码编号03056前后台功能都齐全这份工程我前前后后迭代了十几轮今天把业务梳理、技术选型、表结构设计和最容易踩的坑一次性讲透。无论你是拿它做毕业设计、课程设计还是想快速搭一个内容类电商的原型这篇文章都能帮你把源码真正吃透。搞电商类系统的朋友应该都有体会图书、漫画、数字藏品这类非标内容品的商城做起来比普通服饰百货更磨人。漫画商品不是简单的型号颜色尺码就能描述的它有作者、出版社、册数、试读章节、编辑推荐这一堆内容属性用户也不是冲着参数对比来的而是冲着逛和追更来的。这些差异决定了数据模型、页面结构、推荐逻辑都要跟着调整。1.1 业务梳理漫画商品与普通电商的三个差异第一商品的规格维度不同。一件T恤的规格是颜色加尺码一套漫画的规格可能是单行本、套装、电子版、典藏版每册还有独立的ISBN、出版社、出版日期。直接在传统SKU表里硬凑会出现大量空字段维护起来很痛苦。所以这套系统的商品表采用的是单表冗余思路把漫画的公共属性作者、出版社、ISBN、简介直接放主表把扩展内容目录、试读链接、编辑推荐放JSON字段里而不是强行拆一堆关联表。第二用户的浏览路径不同。买手机的用户会直接搜索品牌加型号买漫画的用户更多是逛出来的从首页热销榜点进一部作品在详情页看到同分类推荐再顺手把第二卷加进购物车。这个行为模式决定了前台要把榜单、上新、分类聚合放在核心位置而不是把搜索框当成唯一入口。第三内容属性强。漫画详情页需要展示封面图、作者介绍、试读章节、用户评价这些字段数量多、形态各异。数据库设计上必须预留足够的扩展空间我在product表里留了一个extra_info的TEXT字段存JSON后续想加是否连载完结与否获奖信息都不用改表结构。1.2 功能清单前台与后台的边界这套源码整体分为两个端功能划分如下。前台用户侧用户注册、登录、个人中心、收货地址管理首页轮播图、热销榜、新品榜分类浏览两级分类、关键词搜索、多维排序商品详情页封面、作者、简介、试读、同分类推荐购物车加购、改数量、删除、批量结算订单提交订单、模拟支付、取消订单、确认收货收藏与浏览历史后台管理员侧管理员登录商品管理新增、编辑、上架下架、库存调整分类管理与轮播图配置订单管理查询、发货、备注、退款处理用户管理禁用、启用数据看板用户数、商品数、订单数、销售额、近7天趋势这个功能集合是典型的课程设计加真实业务的平衡点。它没有做到多商户、秒杀、优惠券这种重型电商功能但把一条完整的电商主链路——从用户登录到商品浏览、加购、下单、支付、发货、确认收货——全部走通了。做二次开发的时候在这个骨架上加促销模块、积分模块都不难。2. 技术选型为什么是Spring Boot这一套组合很多教程喜欢堆技术栈仿佛框架越新越炫就越厉害。我自己做项目反而比较保守原则是能让接手的人用最低成本跑起来的技术就是好技术。这个项目最终选型是Spring Boot 2.7 MyBatis-Plus MySQL 8 Redis Thymeleaf Layui下面讲清楚每一项选择的理由。2.1 后端框架Spring Boot 2.7与MyBatis-Plus后端我用的Spring Boot 2.7.x。很多朋友一上来就问为什么不用Spring Boot 3我的回答一直是看场景。Spring Boot 3强制JDK 17而大部分学校机房、老服务器、公司内部测试环境还在用JDK 8。更重要的是一些老版本的MyBatis、分页插件、连接池在Spring Boot 3下会出现兼容性问题。对于一套要给很多人跑起来的源码稳定和低门槛比炫技重要得多。持久层选了MyBatis-Plus而不是原生MyBatis。理由很简单电商系统70%的SQL是条件查询加分页MyBatis-Plus的BaseMapper封装了通用的insert、updateById、selectPage这些方法日常开发里我只需要写业务逻辑完全不用为每个实体维护一套XML。遇到复杂的多表联查再单独写Mapper接口加注解SQL或者XML灵活性也保住了。工程结构上我按常见的分层来组织controller接收请求、参数校验、返回统一结果service业务逻辑事务在这里控制mapper数据库访问entity实体类config全局配置包括跨域、静态资源映射、拦截器common通用返回体、常量、异常处理这种结构老套但实用接手的人看两眼就知道代码在哪。2.2 数据层MySQL 8与Redis的分工数据库用MySQL 8.0InnoDB引擎字符集强制utf8mb4。这一点必须强调漫画简介和推荐语里经常出现特殊符号和生僻字用utf8会丢字符utf8mb4才能完整存储。Redis在这个项目里承担两类职责一是存购物车二是缓存高频读取的数据比如首页的轮播图、热销榜、商品详情。为什么要拆这么细因为两类数据的失效策略不一样购物车要跟用户会话走缓存要跟商品变更走。混在一起管理反而容易出错。2.3 前端方案Thymeleaf与Layui的现实考量前台页面我用Thymeleaf服务端渲染加Bootstrap后台管理用Layui。有人可能不理解为什么不用Vue全家桶我讲两个实际原因。第一个原因是项目复杂度。漫画商城的核心价值在后端的事务一致性、库存扣减、状态流转。如果前端硬上Vue加Axios加Router就得引入跨域配置、Token刷新、打包部署的整套流程对想快速跑通源码的人来说是个负担。Thymeleaf直接在HTML里渲染数据改完就能看到效果调试成本极低。第二个原因是SEO。漫画属于内容型站点用户会在搜索引擎搜某部漫画全集。服务端渲染的页面搜索引擎直接抓HTML就能拿到关键内容而纯SPA需要额外做SSR或预渲染。用Thymeleaf省掉了这一层麻烦。后端管理端用Layui是因为它的表格组件、弹窗组件、表单布局对管理类页面非常友好。商品列表页的筛选加分页加操作列我用Layui table组件大概几十行代码就搭完了不用自己纠结表格交互的细节。提示如果你跑起来后发现页面的CSS、JS、图片全部加载不出来优先检查Spring Boot的静态资源映射再看模板里资源路径的前缀有没有写对。这两个原因占了静态资源问题的九成。3. 数据库设计从商品模型到订单状态机数据库设计决定了一个项目能走多远。这套系统里最值得学习的不是单表怎么写而是商品模型怎么为后续扩展留余地、订单状态怎么保证不混乱。下面把核心表结构和几个关键设计决策拆开讲。3.1 核心表结构一览完整的建表SQL在源码doc目录里我在这里把最重要的六张表拉出来讲。表名用途关键字段comic_product漫画商品表id、title、author、publisher、isbn、category_id、cover_url、price、original_price、stock、locked_stock、sales_count、status、extra_infocategory分类表id、name、parent_id、sortuser用户表id、username、password、nickname、avatar、phone、statusorder_info订单主表id、order_no、user_id、total_amount、status、consignee、phone、address、create_time、pay_time、deliver_timeorder_item订单明细表id、order_id、product_id、product_title、cover_url、price、quantitybanner轮播图表id、image_url、link_url、sort、status你可能注意到我没有把购物车表列进来因为购物车放在Redis里不需要落库后文会细说。3.2 商品表的设计细节商品表里最值得讲的是四个字段的设计思路。price和original_price分开存。original_price用于详情页展示划线价price是实际售价。这样后续做限时折扣、满减活动时不用改表结构改这两个字段的取值逻辑就行。stock和locked_stock分离。下单时先扣locked_stock支付成功后再扣总库存stock超时未支付则回滚locked_stock。这个预占库存的模型是电商系统避免超卖的基础也是这套源码里比较值得学习的地方。sales_count作为冗余字段单独维护。真实大厂会通过订单流水实时汇总销量但在这种体量下在商品表里直接维护一个累计销量字段查热销榜时一条SQL就出来了性价比高得多。代价是每次支付成功更新销量时多一条UPDATE这个成本完全可以接受。extra_info用TEXT存JSON。这是给漫画内容属性多且不确定准备的。想加连载状态、评分、获奖信息直接改JSON内容不需要改表。3.3 订单状态机的约束订单系统的核心不是表结构而是状态流转。我在代码里用常量类定义了六个状态0待支付1待发货已支付2待收货已发货3已完成4已取消5已退款状态流转必须单向而且每次变更前都要校验。我在OrderService里写了一个checkStatus方法先从数据库查出当前状态再判断目标状态是否合法。比如待发货只能由待支付变过来用户不能从待收货直接取消订单。这个校验看着笨但能防住前端并发请求、连续点击按钮导致的状态错乱。很多新手写订单接口时直接在SQL里UPDATE订单状态不做前置校验结果就是用户在一秒内点了两次取消第二次把已经发货的订单也取消了。这套源码里的校验逻辑建议你们拿到后仔细读一遍。4. 前台核心链路从浏览、加购到下单支付前台是用户直接接触的部分浏览体验、购物车响应速度、下单成功率每一个环节都会影响用户是否愿意完成购买。这一节讲实现思路也讲我为什么这样设计。4.1 分类检索与列表排序的实现商品列表页的前端逻辑不算复杂但要做好三件事。第一两级分类的过滤。一级分类下面挂二级分类用户如果选了一级分类后端需要先查出它所有子分类的ID再用IN条件过滤。如果直接按一级分类ID查子分类的商品就全漏了。第二关键词搜索。我对title、author、publisher三个字段做模糊匹配。这种LIKE查询性能一般但数据量在几万条内完全够用。真要上生产再考虑接ES。第三排序白名单。列表页提供综合、销量、价格从低到高、价格从高到低、新品几个维度。后端写一个白名单Map把前端传的sortKey映射成真实字段名再拼进QueryWrapper的orderBy。这里有个安全细节永远不要让前端直接传字段名否则等于把你的数据库字段暴露给攻击者做注入测试。4.2 购物车的Redis设计购物车方案我对比过三种存数据库、存Cookie、存Redis。最终选了Redis原因如下。存数据库的问题在于加购、改数量、勾选都是高频操作每次都写库压力大而且用户未登录时往购物车塞东西这个场景数据库方案很难处理。存Cookie的问题在于容量小而且手机和电脑之间不同步。Redis读写快天然支持过期时间以userId为key正好对应登录用户的会话场景。数据结构上我用Hashkey为cart:{userId}field是productIdvalue是数量。加购用HINCRBY命令删除用HDEL查询用HGETALL。这里有一个特别重要的经验购物车里只存商品ID和数量不要把商品价格、封面图也存进去。否则后台改了商品价格用户购物车里还是旧数据到结算页才发现金额对不上体验会很差。正确做法是查购物车时拿ID批量去库里查最新价格。4.3 下单流程与库存扣减下单接口是整个项目里我花精力最多的部分因为它是并发环境下最容易翻车的一环。完整流程如下前端提交购物车中勾选的商品ID列表后端批量查询商品校验状态必须为上架校验可用库存够不够生成订单号规则是时间戳加用户ID加随机数保证全局唯一扣减locked_stock插入order_info和order_item删除购物车中已下单的商品返回订单号前端跳转模拟支付页第4和第5步必须放在同一个事务里。我在Service方法上加了Transactional注解任何一步抛异常整体回滚。很多人犯的错误是分开两次提交订单创建失败但库存已经扣了数据就永久对不上了。模拟支付的逻辑也很直接页面展示订单金额点模拟支付按钮后后端把订单状态从待支付更新为待发货同时扣减总库存、增加销量。如果要接真实微信或支付宝支付只需在PayController里把模拟逻辑替换成SDK调用回调验签后执行同样的状态更新即可。5. 后台管理商品、订单与数据看板后台是运营人员每天要用的工具页面可以朴素但流程必须顺畅。这一节重点讲商品管理、订单处理里容易被忽略的细节以及一个轻量级的统计看板怎么做。5.1 商品管理与图片上传的坑后台商品管理的核心操作是新增、编辑、上下架和库存调整。新增表单的字段包含标题、作者、出版社、分类下拉、封面图上传、价格、库存、简介等。整个后台最容易出问题的不是SQL而是文件上传。Spring Boot默认的上传文件大小上限是1MB漫画封面图动辄几百KB到2MB不配置的话上传直接报错。所以application.yml里必须加spring: servlet: multipart: max-file-size: 10MB max-request-size: 20MB图片保存路径我设置成服务器磁盘的/data/upload再通过自定义WebMvcConfigurer把该目录映射成URL地址对外访问。为什么不在项目目录里存图片因为项目每次重新部署都会覆盖文件图片就丢了。放到外部目录后部署和图片完全分离升级系统不影响历史图片。5.2 订单处理的状态校验后台订单页的核心操作是发货管理员填物流公司、物流单号确认后订单从待发货变成待收货同时写入deliver_time。这个操作我刻意做了双重校验页面端按订单状态控制按钮的disabled后端Service里再校验一次当前状态。双重校验不是代码洁癖而是因为前端控制只是体验优化后端校验才是数据安全的底线。如果只在前端控制别人直接构造请求就能跳过按钮限制。订单列表页我还接了Layui的步骤条组件把订单从待支付、待发货、待收货、已完成渲染成一条时间线用户在前台订单详情里能直观看到进度。这种细节对课程设计的答辩演示很有加分效果。5.3 简易数据看板后台首页放了四个指标卡——总用户数、总商品数、总订单数、总销售额外加近7天订单趋势折线图。数据来源都是聚合SQL比如总销售额SELECT SUM(total_amount) FROM order_info WHERE status IN (1, 2, 3);近7天趋势就是把订单按天分组统计。这种统计方式在大数据量下不够看但在当前场景里简单、直观、无维护成本。做项目要遵循一个原则永远用最简方案满足当前需求等数据量大了再换ClickHouse、再上报表平台都来得及千万别一开始就给自己加复杂度。6. 源码导入、配置与部署的实战笔记拿到源码之后怎么跑起来是很多读者最先卡住的环节。我把导入流程、常见报错、部署建议都整理在下面照着操作基本能一次性跑通。6.1 导入工程与本地启动拿到源码后建议按下面的顺序操作可以少踩很多坑创建数据库执行doc目录下的SQL脚本初始化表和演示数据修改application.yml里的数据库账号密码、Redis连接信息用IDEA打开Maven工程等待依赖下载完成本地启动Redis最简单的方式是Docker跑一个docker run -d -p 6379:6379 redis运行Application主类浏览器访问前台地址和后台管理地址如果你是IDEA 2022以上的版本建议先给Maven配阿里云镜像仓库否则从中央仓库拉取Spring Boot全家桶加MyBatis-Plus的依赖可能要等到怀疑人生。6.2 常见启动报错与排查我把跑这个项目时大家报得最多的几类问题整理成了一张表直接对照排查。现象原因解决办法数据库连接失败密码不一致或时区问题MySQL 8连接串加serverTimezoneAsia/ShanghaiRedis连接失败Redis没启动或设了密码启动Redis有密码则在yml里配置8080端口被占用本地其他服务占用改server.port或结束占用进程前端页面样式丢失静态资源路径不对检查模板里的资源前缀与静态拦截配置图片上传报错上传大小超过默认1MB按上文配置multipart参数在这几个问题里时区问题最容易迷惑人。MySQL 8对时区要求严格连接串不加serverTimezone启动时直接给你抛一个server time zone的异常新手看到英文报错就懵了。实际上就是一行配置的事。6.3 部署到服务器的一些建议如果要把系统部署到云服务器推荐用宝塔面板对新手很友好。流程是本地打成jar包上传到服务器配置MySQL和Redis再用Systemd或宝塔进程守护工具托管Java进程。打包命令mvn clean package -DskipTests有个反直觉的经验加上-DskipTests跳过测试打包时间依然可能很长因为Spring Boot的repackage会把全部依赖打进一个可执行jar。所以建议在本地打好包再上传不要在低配服务器上跑Maven构建。部署完成后我会额外开启两个生产配置。一个是Jackson的日期格式和时区避免接口返回时间带了一串数字时间戳另一个是Gzip压缩spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT8 server: compression: enabled: true开启压缩后页面的JS和CSS体积能缩小七成左右对漫画商城这种图片多、富文本多的页面提升非常明显。我做完这个项目最大的体会是电商系统看似简单但把商品-购物车-订单-库存这条链路真正跑通并在每一个环节考虑好边界情况需要的细节远比你想象的要多。文章里讲的这些设计和踩坑都是我在实际开发中验证过的。如果只是照着源码跑一遍你收获有限但如果你能动手改数据库表、调整订单状态机、把Redis换成数据库缓存你对Spring Boot的理解会上一个台阶。后面我还会继续分享这套系统的二次开发方向比如接入真实支付、对接物流查询API、加上简单的推荐算法到时候再聊。
返回列表