Spring Boot汉服商城管理系统实战:从数据库设计到订单状态机

发布时间:2026/9/17 17:42:40
Spring Boot汉服商城管理系统实战:从数据库设计到订单状态机 1. 这个项目能解决什么问题从“毕业设计交差”到“扛得住面试追问”先说点实在的。每年这个时间点大批Java学习者开始迷茫学校课程讲完了语法和框架基础但真正要动手做一个“能写进简历、能在面试时讲清楚”的项目时大部分人第一个想到的还是图书管理、学生选课这种被写烂了的系统。不是说这些项目不行而是面试官一天能收到几十份几乎一模一样的“图书管理系统”你一开口他就知道下一句要讲什么这样的项目很难拿出亮点。汉服商城管理系统是个不错的切入点。它属于电商领域但又比普通的“凑合版商城”多了一层文化属性和特色业务逻辑。汉服这个品类有非常鲜明的特点同款分码数、颜色、形制比如齐胸襦裙、明制、宋制、预售和现货并存、限时团购、还有“尾款补款”这种特殊订单状态——这些都不是普通的增删改查能糊弄过去的。把这些业务规则落地到代码里项目的含金量一下就不一样了。这篇博文里的项目总结下来就是用Java做后端、Spring Boot做基础框架、MySQL存数据、前端用Thymeleaf加Bootstrap同时保留了完整的商家端和用户端覆盖商品管理、购物车、订单流转、库存扣减、会员折扣这一整条电商核心链路。整个项目的代码结构、数据库设计、接口划分都按照企业级项目的习惯来组织而不是学校实训课那种“所有代码堆在一个类里”的写法。无论是用来作为毕业设计还是作为Java后端求职简历里的主导项目这个选题都站得住。我写这篇文章不打算只给你贴一堆代码截图我会把每个关键模块的设计思路、当时踩过的坑、遇到性能或并发问题时的排查过程也一并讲清楚。这才是一个真实项目最有价值的部分——面试官追问的往往就是这些代码之外的决策过程。2. 技术选型不是越新越好这套组合为什么适合练手也适合落地2.1 核心框架组合与版本选择这个项目我选用的是Spring Boot 2.7.x不是最新的Spring Boot 3.x。原因很实在很多学校的教学资源、网上现成的资料、公司的旧项目都还停留在2.x生态。如果选3.xJava版本最低要求17而不少人的电脑上装的是JDK 8环境问题就能卡掉一拨人。技术选型要考虑团队和受众的实际情况不能只看新。配套技术栈如下层次技术选型用途说明后端基础Spring Boot 2.7.14项目骨架、依赖管理、自动配置ORM框架MyBatis-Plus 3.5.3数据访问层单表CRUD几乎不用写SQL数据库MySQL 5.7 / 8.0业务数据存储权限认证Spring Security JWT登录认证与接口鉴权模板引擎Thymeleaf服务端渲染页面前端样式Bootstrap 5 jQuery页面布局和交互构建工具Maven依赖管理和打包接口文档Knife4j调试接口、生成API文档这套组合最大的好处是“中间没有冷门环节”。每一个组件都有海量的中文资料出了任何问题几乎都能搜到解决方案。同时又不至于太老旧——JWT鉴权、MyBatis-Plus这种用法在企业里非常普遍用人单位看到了会觉得你的技术栈跟实际工作是匹配的。2.2 为什么不用前后端分离这两年前后端分离很流行Vue Spring Boot的组合成了培训班标配。但在这个项目里我刻意选了服务端渲染。不是说Vue不好而是要看你做这个项目的目的是什么。如果你是想去面纯前端岗位那Vue项目自然更有说服力。但Java后端岗位面试核心考察的是Java基础、Spring原理、数据库设计和业务逻辑能力。用Thymeleaf做服务端渲染所有页面跳转、参数传递、权限控制都发生在后端你反而能把登录拦截、会话管理、数据渲染这套后端逻辑理解得更透彻。而且省去了跨域处理、前端构建、反向代理这些环境复杂度一个项目跑起来的成本大大降低。对于学习阶段来说先掌握服务端渲染把后端基本功打牢之后再学前后端分离时你理解Vue请求后端接口的过程会比直接从分离项目入手的人清晰得多。3. 数据库设计汉服业务的特殊字段和状态机是整张表的灵魂3.1 核心表结构与字段设计思路数据库我拆了九张表分别是用户表、分类表、商品表、商品SKU表、购物车表、订单表、订单明细表、收货地址表和轮播图表。其中商品和订单两张表是设计的重点尤其要把汉服的特殊性体现进去。商品表的核心字段如下CREATE TABLE product ( id bigint(20) NOT NULL AUTO_INCREMENT, category_id bigint(20) DEFAULT NULL COMMENT 分类id关联分类表, name varchar(120) NOT NULL COMMENT 商品名称, subtitle varchar(200) DEFAULT NULL COMMENT 副标题如【现货】或【预售】, main_image varchar(500) DEFAULT NULL COMMENT 主图, detail text COMMENT 商品详情HTML富文本, stock int(11) NOT NULL DEFAULT 0 COMMENT 总库存, status tinyint(4) NOT NULL DEFAULT 1 COMMENT 状态1上架 0下架, is_presale tinyint(4) NOT NULL DEFAULT 0 COMMENT 是否预售1是 0否, presale_deposit decimal(10,2) DEFAULT NULL COMMENT 预售定金, presale_final decimal(10,2) DEFAULT NULL COMMENT 预售尾款, create_time datetime NOT NULL, update_time datetime NOT NULL, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里面最关键的是is_presale、presale_deposit和presale_final三个字段。汉服圈非常流行“预约-尾款”的购买模式商家会先上架一个预售链接顾客支付定金锁定名额等工厂出货后再补尾款发货。这个模式如果只用一个price字段根本表达不了所以在设计阶段就为这种业务场景预留了独立的定金尾款字段。SKU表设计也跟普通服饰不同。汉服的码数体系不是S/M/L这么简单而是按“胸围/裙长/袖长”的组合来算的不同形制的尺码表差异很大。我用SKU表存具体规格组合CREATE TABLE product_sku ( id bigint(20) NOT NULL AUTO_INCREMENT, product_id bigint(20) NOT NULL COMMENT 所属商品id, size_name varchar(50) NOT NULL COMMENT 尺码名如S/M/L或定制, color_name varchar(50) NOT NULL COMMENT 颜色如豆绿/绯红/月白, stock int(11) NOT NULL DEFAULT 0 COMMENT 当前SKU库存, price decimal(10,2) NOT NULL COMMENT 当前SKU售价, sku_code varchar(50) DEFAULT NULL COMMENT SKU编码用于后端追库存, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这样设计之后用户在商品详情页选择“绯红色 S码 齐胸襦裙”时前端通过SKU id来查询具体价格和库存而不是直接从商品表读取一个笼统的价格。真正的电商系统都是这么处理的不是只做一张商品表就完事。3.2 订单状态机设计从“待付款”到“已完成”的流转规则订单表是另一个值得展开设计的地方。汉服商城的订单流转比普通商品多出几个状态完整的订单状态待付款 - 待发货 - 待收货 - 已完成 待付款 - 已取消用户主动取消或超时取消 待发货 - 售后中 - 退款完成但对预售商品来说下单流程变成了两段式用户先下一笔“定金订单”锁定库存等商家设置“尾款开始时间”之后用户再补缴尾款此时才真正进入待发货流程。这对数据库设计的影响是订单表需要一个order_type字段区分定金订单和普通订单同时需要一个deposit_status字段来跟踪定金支付状态。我在订单表里加了这样几个关键字段order_type tinyint(4) NOT NULL DEFAULT 1 COMMENT 订单类型1普通订单 2预售定金订单 3尾款订单, parent_order_no varchar(64) DEFAULT NULL COMMENT 尾款订单的关联定金单号, deposit_status tinyint(4) NOT NULL DEFAULT 0 COMMENT 定金/尾款支付状态0未付 1已付, pay_time datetime DEFAULT NULL, delivery_time datetime DEFAULT NULL, finish_time datetime DEFAULT NULL这套状态流程的落地是我觉得这个项目跟那些“只有一个状态字段然后代码里硬写if else”的脚本式系统最大的差异。像“定金订单在未支付尾款期间库存是什么状态”“取消定金订单退款后库存怎么回补”这类业务问题在设计数据库时就必须想清楚代码实现才会顺理成章。4. 后端核心模块实现权限控制、购物车与订单事务处理4.1 基于JWT的登录认证与拦截器实现系统分两个身份角色普通用户和管理员。用户操作商城前端页面管理员进后台管理商品和订单。我用JWT生成令牌配合Spring Security做接口鉴权没有用传统的Session-Cookie方案。原因是JWT无状态后端不需要存会话记录无论以后扩展成前后端分离还是接小程序端现有的认证逻辑都可以复用。核心配置类里通过SecurityFilterChain定义规则Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeRequests() .antMatchers(/user/login, /user/register, /product/list, /product/detail/**).permitAll() .antMatchers(/admin/**).hasRole(ADMIN) .antMatchers(/cart/**, /order/**).authenticated() .and() .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class); return http.build(); }JWT过滤器每来一个请求就从Header里取token、解析用户信息、把用户身份塞进SecurityContext。这个过程中最容易踩的坑是“token过期后前端拿到的错误信息不友好”我在全局异常处理器里单独捕获了ExpiredJwtException返回401加明确的提示文案而不是让Spring Security返回一长串默认错误页面。4.2 购物车的临时态与库存预占购物车模块看起来简单但有一个细节容易被忽略购物车里的商品状态是会变化的。用户把一件汉服加进购物车可能过了一周才结算这期间商品可能已经下架、价格变动、库存不足。所以购物车表里存的不只是商品id和数量我还会冗余一份商品当前的快照价格price_snapshot decimal(10,2) NOT NULL COMMENT 加入购物车时的价格快照,结算下单时后端会重新校验商品状态、最新价格和SKU库存如果价格有变动会提示用户以最新价格为准。库存预占的逻辑是进入下单页面时执行一次库存检查点“提交订单”时再次检查并预扣库存订单取消或超时未支付后释放库存。我用MySQL的乐观锁实现库存扣减Update(UPDATE product_sku SET stock stock - #{quantity}, version version 1 WHERE id #{skuId} AND stock #{quantity}) int deductStock(Param(skuId) Long skuId, Param(quantity) Integer quantity);这种写法的好处是原子性由数据库保证并发情况下不会出现超卖。返回0说明库存不足事务直接回滚。4.3 下单事务一主多从的表结构如何保证数据一致性一笔订单牵扯到四张表订单主表、订单明细表、SKU库存表、购物车表。我用了Transactional来保证原子操作一切校验通过后生成订单号插入主表和明细扣减SKU库存清空对应用户的购物车记录。任何一个步骤抛异常全部回滚。订单号我采用的是“时间戳用户id后四位随机数”的方式。单纯用数据库自增id做订单号有两个问题一是容易被猜到业务量二是多张表关联时不够直观。自定义生成规则可以做到全局唯一也方便按订单号快速定位问题。下单接口的校验顺序很重要这也是一个真实经验先验用户、再验地址、再验商品状态、再验库存、最后算价格。校验顺序一旦反了可能出现用户填了无效地址却先扣了库存这种严重业务事故。我在第一版就踩过这个坑后来把校验逻辑拆成了一个独立的方法链每一步都返回明确的错误码才彻底解决。5. 前端实现细节Thymeleaf页面与商品的多规格联动5.1 商品详情页的多规格选择逻辑前端层面我用了Thymeleaf模板加少量的jQuery来实现交互。整个页面开发难度最大的是商品详情页的多规格选择。用户进来看到的是一个商品要选“颜色”和“尺码”选完之后价格和库存要联动变化。思路是这样的后端返回当前商品下的所有SKU列表前端把SKU数据序列化后存在一个JavaScript对象里var skuMap { 绯红|S: {id: 1, price: 399, stock: 12}, 绯红|M: {id: 2, price: 399, stock: 0}, 豆绿|S: {id: 3, price: 429, stock: 8} };用户选择颜色或尺码后通过颜色 | 尺码拼接key去查SKU对象动态更新价格和库存展示。如果某个组合已经无货对应的尺码按钮直接置灰不可选。这套逻辑虽然简单但业务上很实用。汉服店铺最怕的就是用户在详情页里选中了一个颜色之后发现想要的尺码没货需要来回切换确认。联动的体验极大降低了这种信息不透明带来的操作成本。5.2 后台管理页面的表格化设计与操作流后台管理端包含商品管理、分类管理、订单管理、用户管理和轮播图管理五个模块。每个模块的页面风格是统一的顶部是查询条件中间是数据表格表格行尾是操作按钮。商品管理里我实现了“上下架”和“编辑”两个核心操作。下架操作通过修改status字段实现同时会在SKU层面判断是否有未完成的订单如果有未发货订单系统会给出警告提示而不是直接硬删商品。这里也体现了一个重要的业务思路电商系统里商品几乎都是逻辑删除做物理删除会破坏历史订单的数据完整性。后台接口我统一以/admin/开头配合Spring Security的角色权限控制管理员才能访问。表格数据的分页用MyBatis-Plus的Page插件实现前端传页码和每页条数后端返回封装好的分页对象。整个后台开发效率非常高MyBatis-Plus确实是这类管理系统的开发利器。6. 项目启动与部署遇到过的问题和跑通的完整过程6.1 从Git拉下代码到本地跑起来的完整步骤我把项目结构按标准Maven工程组织好拿到的同学不需要做复杂的前端构建直接就能启动。启动步骤很简单大致过程如下创建数据库hanfu_mall导入项目里附带的sql/hanfu_mall.sql脚本初始化基础数据。修改application.yml里的数据库账号密码以及JWT的密钥配置。Maven里clean package打成jar包或者直接在IDE里运行HanfuMallApplication.java的main方法。启动成功后访问localhost:8080默认管理员账号在初始化脚本里已经预置。过程中最容易出问题的是数据库版本和时区配置。MySQL 8.x的驱动连接串最好显式加上serverTimezoneAsia/Shanghai不然插入数据时可能会报时间相关的错误。还有一个容易被忽略的点是JDK版本项目是基于Java 8写的如果你本机装的是Java 17部分老版本依赖会反射报错需要把编译器级别调回Java 8。6.2 我把项目放到云服务器上之后遇到的真实问题本地跑通之后我把它部署到了一台轻量云服务器上使用Java 8环境加MySQL 8.0。部署时遇到两个值得记录的问题。第一个是内存问题。小内存服务器上同时跑MySQL和Spring Boot应用经常会出现内存不足导致Java进程被系统杀掉的情况。后来我改用java -jar -Xmx256m -Xms128m hanfu-mall.jar这种方式手动限制JVM堆内存并且在服务器上配置了swap空间系统才稳定下来。开发阶段用默认内存没问题生产环境一定要评估内存用量。第二个是端口安全。直接对外开放8080端口不太安全我加了防火墙规则只允许通过Nginx反向代理访问同时把8080端口禁止了外网直接访问。实际访问路径是浏览器 - Nginx 80端口 - Java应用8080端口。同时针对这个项目配置了/api/路径的动静分离静态资源由Nginx直接返回动态请求转发到Spring Boot访问响应速度提升明显。7. 测试与性能优化接口压测和并发场景下的真实表现7.1 核心接口的压测数据与瓶颈定位我使用JMeter对商品列表、商品详情、下单三个接口做了一轮简单的并发压测。测试环境是2核4G的云服务器模拟100个线程同时循环请求观察到的数据是商品列表接口的QPS大约在380左右详情接口QPS在220左右下单接口由于涉及多表写入QPS较低大约为80。从压测结果里能明显看到下单接口是瓶颈。通过打印SQL日志发现主要耗时在订单主表和明细表的插入操作以及库存扣减语句上。为了减轻数据库压力我给order表的主要查询字段添加了复合索引user_idcreate_time给明细表的order_no加了唯一索引同时把商品列表接口里原本查数据库的分类名称改成了Redis缓存。在项目里引入spring-boot-starter-data-redis商品列表接口先从Redis读缓存未命中再去查数据库并回填缓存。加上一层缓存后同样的压测条件下商品列表的QPS提升到了接近820效果非常明显。如果只做一个优化点我建议优先做这个改动量不大但收益立竿见影。7.2 SQL日志分析与慢查询排查MyBatis-Plus在配置里开启了mybatis-plus.configuration.log-implorg.apache.ibatis.logging.stdout.StdOutImpl控制台能直接打印出完整SQL。排查慢查询时用上了MySQL自带的慢查询日志功能定位到一条订单列表查询语句在订单量大了之后执行很慢。原因是最初的列表查询没有分页时就把所有字段查出来再在内存里排序。后来改成只查需要展示的字段并且配合Page插件做真正的数据库分页慢查询问题就消失了。这条经验可以提炼成一句通用原则SQL不要select *列表页永远走数据库分页而不是全量查出来内存截取。这个错误在初学者项目里极其常见面试时主动把这个优化过程讲出来会比单纯说“我用了分页”有说服力得多。8. 这个项目能扩展成什么简历上的一句话和真实业务逻辑的差距8.1 给准备拿这个项目去面试的同学三个建议第一个建议是项目描述不要只写“实现了商品管理、订单管理”要突出业务复杂度和技术深度。比如你可以这样写基于Spring Boot MyBatis-Plus实现汉服商城支持商品多SKU规格联动、预售定金/尾款切换、库存超卖防护乐观锁、JWT无状态认证订单模块采用多表事务处理设计了包含普通订单/定金订单/尾款订单的完整状态机通过Redis缓存优化商品列表接口QPS从380提升至820第二是针对“为什么这么设计”的追问要提前准备。面试官大概率会问为什么用JWT不用Session为什么库存扣减用乐观锁不用悲观锁定金订单取消后库存怎么返还这些设计决策在文中都有对应的解释你要能用自己的话讲出来而不是背答案。第三是提前想一想自己目录里的代码结构是否能讲清楚。我在项目里按controller / service / mapper / entity / common分包每个包职责单一没有出现一个类几千行的现象。面试官如果让你打开IDE讲项目按这个结构讲下来思路会非常清晰。8.2 更进一步的扩展方向如果时间充裕可以往这几个方向延伸接入支付宝沙箱支付完成真实的支付流程引入RabbitMQ做订单超时未支付自动关闭的延迟队列做数据统计报表分析用户画像和热门商品将项目改造成前后端分离用Vue3重写前端。这些扩展方向每一个都能单独写成项目亮点在简历上会非常有竞争力。我在部署和扩展这个项目的过程中最深的感受是一个项目的价值不在于用了多么炫酷的技术而在于你能否把一条完整的业务链路设计清楚、实现清楚、讲清楚。汉服商城这个选题正好把电商的通用能力包装到了一个具体且有趣的场景里做一遍之后你对Spring Boot、数据库设计、接口设计、部署上线的理解都会扎实很多。如果你正在为项目选题发愁不妨就直接拿这套方案动手试试从克隆代码跑起来开始然后尝试自己改需求、加功能这个从“运行别人代码”到“能自己改代码”的阶段才是收获最大的过程。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询