ARTICLE DETAIL

资讯详情

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

Spring Boot萌宠商城实战:源码+数据库+部署全解析

Spring Boot萌宠商城实战:源码+数据库+部署全解析 我敢说任何一个学过Spring Boot的人都动过“自己动手做个商城网站”的念头。正巧这段时间我整理了一套以萌宠为主题的商城项目从需求梳理、数据库建模到页面渲染、订单闭环再到最后部署上线踩了不少坑也攒了不少经验。这套Spring Boot萌宠商城网站源码包含完整的用户端和后台管理、MySQL数据库脚本、调试部署说明还配了一份可读性很强的课程设计论文。这篇文章算是一次完整的项目复盘把标题里提到的“程序源码数据库调试部署开发环境”具体拆开讲清楚给准备做课设、毕业设计或者想通过实战项目巩固Spring Boot基础的同学一个参考。很多初学者学完Spring Boot的CRUD之后不知道下一步该做什么做用户管理系统觉得太简单做电商平台又觉得无从下手。萌宠商城其实是特别合适的一个中间难度项目它既有用户注册、商品展示这类基础模块又有购物车、订单、库存扣减这类带业务逻辑的核心功能后台还涉及到文件上传和权限控制。把这些功能走通你对Spring Boot的理解绝对不只是“会写几个接口”而是真正知道一个Web项目从开发到部署的完整链路是怎么运转的。1. 为什么说萌宠商城是Spring Boot练手项目的黄金选题1.1 这个项目到底解决了什么问题简单来说这个萌宠商城网站解决的问题非常实在用户可以浏览宠物商品、按分类筛选、搜索自己喜欢的猫狗或宠物用品加入购物车提交订单模拟支付管理员则可以在后台维护宠物信息、处理订单、管理用户和分类。整个业务是一个完整的电商闭环不是那种“只做了增删改查”的练习项目。我当时的定位是做一个可以直接拿去答辩、且不用依赖第三方支付真实接口的商城系统。支付环节用模拟支付页面代替这样既避免了申请支付商户号的麻烦又不影响订单状态流转的演示效果。整个系统基于Spring Boot 2.x Thymeleaf MyBatis MySQL构建前端页面采用服务端渲染没有拆前后端降低部署复杂度更适合学生项目。这个项目比较适合三类人。第一类是计算机专业的学生做课程设计或毕业设计第二类是自学Spring Boot想找项目练手的人通过它把MVC分层、Session鉴权、事务控制、文件上传这些知识点串起来第三类是准备面试的人商城项目在简历上是常见亮点但前提是你真的理解里面每一行代码为什么这么写。1.2 技术选型从单体到Spring Boot的务实考量技术选型不需要追求新奇关键是每一项选择都要解释得通。我在这个项目里没有引入Redis、MQ、Elasticsearch这些重型组件不是因为它们不好而是对一个教学性质的单体商城来说Spring Boot自带的Session、MySQL的关系型存储、服务端渲染已经完全够用引入过多技术反而会把主线淹没。Spring Boot它的价值在于自动配置和内嵌Tomcat。以前用SSH或SSM搭框架要写一大堆XML配置现在只需要一个启动类加几个注解。我用的是Spring Boot 2.6.x版本稳定社区资料多遇到问题基本都能搜到解决方案。Thymeleaf作为服务端模板引擎可以直接在HTML里写th:each、th:if配合Controller传过来的Model数据渲染页面。相比前后端分离减少了Ajax调试成本和跨域问题的处理非常适合一个人开发的小型项目。MyBatis半自动ORM框架SQL由自己控制尤其是多表关联查询、动态SQL时很灵活。相比JPA它对SQL性能的掌控更直观面试时也更好聊。MySQL免费、普及率高而且本题涉及“数据库”关键词用MySQL建库建表、写存储过程、做事务演示都方便。花点时间先确定好技术栈后面的开发会顺很多。如果一开始就纠结“要不要用VueSpring Boot前后端分离”大概率会在环境搭建阶段耗掉大量精力。2. 数据库建模是整站的地基宠物、用户、订单三张核心表2.1 宠物商品表与分类表的设计思路商城网站的核心是商品但这个项目里商品不是普通的数码产品而是活体宠物和宠物用品。因此在设计表结构时我单独把宠物信息里的“性格特点”“疫苗情况”“是否驱虫”这类字段抽出来不是为了炫技而是因为客户浏览萌宠时这些信息直接影响购买决策。数据库里一共有8张表用户表、宠物分类表、宠物商品表、购物车表、订单表、订单明细表、广告图表和管理员表。这里重点说说宠物分类和宠物商品两张表。CREATE TABLE pet_category ( id int NOT NULL AUTO_INCREMENT, name varchar(50) NOT NULL COMMENT 分类名称, sort int DEFAULT 0 COMMENT 排序权重, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; CREATE TABLE pet ( id int NOT NULL AUTO_INCREMENT, category_id int NOT NULL COMMENT 所属分类, name varchar(100) NOT NULL, breed varchar(50) DEFAULT NULL COMMENT 品种, age varchar(20) DEFAULT NULL COMMENT 月龄/年龄, gender tinyint DEFAULT NULL COMMENT 0母 1公, price decimal(10,2) NOT NULL, original_price decimal(10,2) DEFAULT NULL, cover_image varchar(255) DEFAULT NULL COMMENT 封面图, images text COMMENT 轮播图逗号分隔, personality varchar(255) DEFAULT NULL COMMENT 性格特点, vaccine_status varchar(50) DEFAULT NULL COMMENT 疫苗情况, stock int DEFAULT 0, sales_count int DEFAULT 0, status tinyint DEFAULT 1 COMMENT 1上架 0下架, description text, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_category_id (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;这里有一个容易被忽略的细节category_id外键我没有加物理约束。在实际开发中我通常只建立逻辑关联关系用索引去加速查询而不是设置真正的FOREIGN KEY。原因很简单物理外键在插入、更新、删除时会有额外的约束检查影响性能而且在后期做数据归档、分库分表时物理外键会变成麻烦。面试时如果被问到“为什么不用外键”这是一个很好的话术点。2.2 用户与购物车、订单的状态流转用户表字段不算复杂核心是用户名唯一索引和密码加密存储。密码我用了BCrypt加密而不是MD5。MD5加盐虽然比明文强但BCrypt内部自带盐值每次加密结果都不同暴力破解难度高得多。下面是用户表的关键结构CREATE TABLE user ( id int NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL, password varchar(100) NOT NULL, nickname varchar(50) DEFAULT NULL, phone varchar(20) DEFAULT NULL, avatar varchar(255) DEFAULT NULL, create_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;购物车表最初我想过存内存或者Cookie后来还是决定落库。Cookie购物车有大小限制而且用户换设备就丢失内存购物车重启就没了。落库虽然每次请求多一次数据库查询但胜在稳定。购物车表只需要用户ID、宠物ID、数量三个核心字段加一个唯一索引保证同一用户对同一宠物只有一条购物车记录。订单状态是整个项目的关键。订单表我设计了几个状态字段订单状态待支付、已支付、已发货、已完成、已取消、支付状态未支付、已支付、发货状态未发货、已发货。用一个字段还是多个字段我纠结过一会儿。最后选择了拆分因为业务中会出现“已支付但是未发货”这种组合分开字段查询和管理更直观。状态流转通过Service层控制不允许前端直接传状态值。2.3 一个容易被忽视的数据库索引与事务细节电商项目绕不开“超卖”问题也就是库存只剩下1件时两个用户同时下单都扣减成功。解决这个问题我在代码和数据库两个层面都做了处理。数据库层面宠物表里stock字段在更新时加上条件判断UPDATE pet SET stock stock - 1 WHERE id #{petId} AND stock 0;这样即使并发很高数据库行锁也会让后到的更新等在前面事务提交之后而此时stock已经变成0条件不满足更新行数为0代码里就能判断库存不足。代码层面使用Transactional控制事务。下单时先插入订单主表和订单明细再扣减库存任何一个环节失败则整体回滚。这里配合Spring的声明式事务比手动管理连接要可靠得多。索引设计上也踩过一个小坑。订单表经常按用户ID和时间查询一开始没建索引用户下单后查“我的订单”分页查询速度很慢数据量从几十条涨到上千条后感觉更明显。后来给order表的user_id和create_time建了联合索引查询速度直线上升。做课设时数据库数据量不大性能问题不容易暴露但答辩时如果能主动说出“我加了索引并测试过执行计划”会是一个明显的加分项。3. 核心功能拆解从首页宠物墙到模拟支付的完整闭环3.1 基于Thymeleaf的服务端渲染页面结构项目的页面结构我尽量保持轻量没有用任何前端框架。静态资源放在src/main/resources/static下包含CSS、JS、图片模板页面放在src/main/resources/templates下按照功能分包index、pet、cart、order、user、admin。首页展示的是“宠物墙”从数据库里取出上架状态的宠物按销量或上新时间排序。这里我用了一个简单的分页查询没有引入PageHelper插件而是手写LIMIT语句避免给初学者增加额外负担。Controller返回ModelAndView时除了宠物列表还会把分类列表、轮播图数据一起塞进Model。Controller public class IndexController { Autowired private PetService petService; Autowired private CategoryService categoryService; GetMapping(/) public String index(RequestParam(defaultValue 1) Integer page, RequestParam(defaultValue 1) Integer categoryId, Model model) { PageResultPet pageResult petService.queryOnSalePets(page, 12, categoryId); model.addAttribute(pets, pageResult.getList()); model.addAttribute(page, pageResult.getCurrentPage()); model.addAttribute(totalPages, pageResult.getTotalPages()); model.addAttribute(categories, categoryService.listAll()); return index; } }Thymeleaf页面里使用th:each遍历宠物列表用th:if控制“没有更多宠物”的空状态提示图片地址用th:src动态拼接。整个页面渲染下来效果是一个标准的电商首页顶部导航、分类菜单、宠物卡片网格。萌宠主题的优势就在这时体现出来了选几张好看的金毛、柯基、布偶猫图片页面视觉分直接拉高。3.2 登录鉴权与购物车操作的实现要点用户登录我用的是Session方案没有引入Spring Security。原因有两个一是Spring Security的学习曲线对课设项目来说偏陡二是Session拦截器已经足以覆盖“未登录不能下单”这种需求。具体实现是写了一个LoginInterceptor实现HandlerInterceptor接口在preHandle方法里判断Session中是否有loginUser如果没有且请求路径不是白名单就重定向到登录页。白名单包括首页、宠物列表、宠物详情、登录注册接口、静态资源路径。然后在WebMvcConfig里注册拦截器指定拦截/**排除白名单。购物车操作有两个功能点需要注意。一个是加入购物车时如果当前用户购物车中已经有了同一只宠物要做数量累加而不是再插入一条新记录。另一个是购物车列表页面的总价计算建议在Service层做不要在模板里用${pet.price * cartItem.quantity}这种一长串表达式不易维护也不方便循环嵌套中取值。我在Service层封装了CartVO对象里面包含商品详情、数量、小计页面只需要直接读取。3.3 订单生成与库存扣减的并发思考订单模块是整个项目中业务逻辑最集中的地方。从用户提交订单到下单成功我的处理流程是从购物车表查出选中的商品列表前端传购物车记录ID数组。校验商品状态确保都是上架状态且库存不小于购买数量。生成订单主表订单编号使用了时间戳加随机数不使用自增ID直接展示给用户。插入订单明细表同时把商品名称、封面图、单价冗余到明细里。扣减库存使用前面提到的带条件的UPDATE语句。清空已购买的购物车记录。跳转到模拟支付页面。这些步骤必须放在同一个事务方法里。如果第5步失败前面插入的订单数据也要回滚否则会出现“订单里有商品但库存没扣”的脏数据。模拟支付页面的设计比较简单显示订单编号和应付金额提供一个“确认支付”按钮。点击后更新订单状态为已支付然后跳转到支付成功页面。如果用户中途取消订单保持待支付状态可以在“我的订单”里继续操作。当然真实电商的支付流程远不止这些但作为课程设计这个闭环已经足够解释清楚。Transactional(rollbackFor Exception.class) public Long createOrder(Integer userId, ListInteger cartItemIds) { // 校验用户、购物车记录 // 计算总金额 // 插入 orders 记录 // 插入 order_item 记录 // 循环执行库存扣减并判断受影响行数 // 删除购物车记录 return orderId; }3.4 后台管理的权限控制与文件上传后台管理模块单独放在/admin路径下。管理员使用独立的管理员表和前台用户表分开。后台系统的主要功能包括宠物管理增删改查、上下架、分类管理、订单管理、用户管理、广告图管理。后台权限控制同样是拦截器实现但拦截的路径和校验的内容不同。管理员登录后Session里存的是adminUser如果访问后台页面且未登录直接重定向到管理员登录页。这样前台用户登录态不会影响到后台。文件上传是后台管理的重点。宠物图片我使用MultipartFile接收上传文件保存到服务器本地目录而不是数据库。数据库里只存图片的相对路径。上传路径单独配置在application.yml里避免硬编码。这里有一个非常重要的坑不要把图片直接保存到Spring Boot的静态目录里。因为项目打包成JAR后静态目录在JAR包内部无法动态写入文件而且重启后临时文件会被清空。正确做法是保存到服务器的一个独立目录比如/usr/local/pet-images/然后通过自定义静态资源映射暴露给前端访问。4. 开发环境搭建与调试部署实录4.1 开发环境清单JDK、Maven、MySQL、IDEA的版本搭配环境问题虽然听起来基础但我在帮同学看项目时发现至少一半的问题出在版本搭配上。这个项目我建议用下面的环境组合完美兼容且不容易出幺蛾子组件推荐版本备注JDK1.8 或 11Spring Boot 2.x对JDK8支持最好11也没问题Maven3.6.3 及以上用来管理依赖IDEA自带也行MySQL5.7 或 8.05.7更稳8.0注意驱动和时区问题IDEA2022.1 及以上社区版够用旗舰版更好Spring Boot2.6.x选这个版本的稳定版即可有一点提醒不要一上来就装Spring Boot 3.x。虽然3.x是趋势但它基于JDK17并且把javax命名空间换成了jakarta很多老教程的代码都不能直接复用。对于课程设计项目稳定成熟的版本永远是第一选择。4.2 从零跑通项目的五个步骤把源码导入本地并跑通整体流程可以说相当固定。按下面五步走基本不会卡住创建数据库并导入SQL脚本。项目源码中带了一个pet_shop.sql文件用Navicat或命令行执行即可。重点确认数据库字符集是utf8mb4否则中文容易乱码。修改数据库连接配置。打开application.yml把数据库名、用户名、密码改成自己本地的配置。这一步最常见的问题是密码里包含特殊字符比如符号YAML解析时容易报错建议加引号或使用URL编码。使用IDEA导入Maven项目。选择项目的pom.xml文件点击Import as Maven Project让IDEA自动下载依赖。首次导入会下载很多JAR包如果网络不稳记得配置阿里云Maven镜像否则会等很久。启动Spring Boot应用。找到Application启动类右键运行。看到Started Application日志表示启动成功。默认端口是8080浏览器访问http://localhost:8080即可。验证前后台功能。先注册一个前台用户走一遍“浏览宠物-加入购物车-下单-模拟支付”流程再用管理员账号登录后台测试宠物上架、图片上传、订单修改。application.yml的核心配置大致如下server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/pet_shop?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver thymeleaf: cache: false servlet: multipart: max-file-size: 10MB max-request-size: 20MB mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.petshop.entity提示不同MySQL版本对应的driver-class-name不一样。MySQL 5.7用com.mysql.jdbc.DriverMySQL 8.0用com.mysql.cj.jdbc.Driver。而且8.0必须在URL里指定serverTimezone否则会有时区报错。4.3 部署到云服务器时遇到的三个典型问题本地跑通只是第一步真正把项目部署到服务器上才是调试部署中最刺激的部分。我当时用的是一台2核4G的CentOS服务器部署方式是打包成JAR后通过java -jar运行。第一个典型问题是MySQL 8.0的时区报错。在服务器上新装的MySQL 8.0项目启动后访问数据库报错The server time zone value CST is unrecognized。排查时发现是数据库连接URL里没有带时区参数加上serverTimezoneAsia/Shanghai就解决了。第二个问题是端口被占用。我部署时发现8080端口已经被另一个Java进程占用nohup java -jar pet-shop.jar 启动后一直访问失败。后来用netstat -nlpt | grep 8080查到了占用进程果断改用了8090端口。这个问题虽然简单但第一次遇到时会懵其实先看日志和端口占用就能快速定位。第三个问题比较隐蔽后台管理上传宠物图片后页面无法显示图片。我在本地开发时把图片路径写成了D:/upload/到了Linux服务器上这个路径不存在而且Tomcat映射也不对。最后把图片路径统一改为相对路径/upload/并在配置类中新增一个WebMvcConfigurer将/upload/**映射到服务器的实际目录问题才彻底解决。5. 源码组织与论文文档课程设计也能做出专业感5.1 源码包结构controller/service/mapper分层如何分配很多人写项目时喜欢把所有代码塞进几个Controller里这在国内课程设计里很常见但到了代码量稍微上来以后维护会变得很痛苦。我维护这个萌宠商城时坚持了经典的三层架构com.petshop ├── controller # 接收请求返回页面或结果 ├── service # 业务逻辑事务控制 │ └── impl ├── mapper # MyBatis Mapper接口 ├── entity # 数据库实体类 ├── vo # 视图对象 ├── interceptor # 拦截器 ├── config # 配置类 └── common # 通用常量、统一返回结果Controller层只负责参数接收和页面跳转Service层负责业务逻辑Mapper层负责数据库访问。这样分层的直接好处是订单的创建逻辑可以在Service里独立复用Controller不会越来越臃肿出了问题也能通过分层快速定位是接口传参问题还是SQL问题。代码量不大时看不出优势但答辩时如果老师让你讲讲项目架构这套分层就是最好的回答素材。关于entity和vo我特别强调一下。实体类不宜直接暴露给页面。像宠物实体中如果有状态字段status页面不一定需要它在JSON接口里出现。我通常会创建一个PetVO把页面需要的字段聚合起来。如果不做前后端分离实体类直接传给Model其实问题不大但养成VO思维对后面接真实项目很有益。5.2 一万字论文的结构安排与写作技巧标题里提到“带论文文档1万字以上”确实当时为了交课程设计我写了一份完整的论文。这里分享一个我的论文结构以它为骨架填充内容1万字并不难论文章节内容要点建议字数摘要系统背景、主要功能、技术方案、设计成果300字第一章 绪论项目背景、研究意义、国内外电商现状1200字第二章 需求分析可行性分析、功能需求、用例图、非功能需求1800字第三章 系统设计系统架构图、功能模块设计、数据库设计2600字第四章 系统实现环境介绍、前台各模块实现、后台各模块实现、核心代码2800字第五章 系统测试测试环境、测试用例、功能测试结果、性能分析1000字总结与展望项目亮点、不足、后续优化方向500字写论文最容易犯的毛病是把整个数据库所有表结构全部复制上去占篇幅却没有解读。比较好的写法是在数据库设计部分挑出三张核心表做详细字段解释其它表用表格汇总。在实现部分不要大段贴Controller代码而是搭配页面截图解释“这个页面实现了什么功能、数据从哪来、关键的逻辑是什么”。页面截图建议用自己运行起来的真实截图清晰且高端这比任何华丽的辞藻都有说服力。5.3 答辩演示的准备思路论文写完了源码跑通了最后一步是答辩演示。我的经验是提前准备一条“演示主线”注册账号、浏览宠物、搜索、加入购物车、结算下单、模拟支付、查看订单。这条主线走完必须流畅所有按钮在哪都要烂熟于心。老师经常会问的几个问题也提前准备一下为什么选择Spring Boot它的核心优势是什么购物车为什么要存数据库而不是用Session订单编号是怎么生成的为什么不用自增ID库存扣减时如何避免超卖前后台权限是怎么控制的这些问题的答案前面已经全部覆盖了。如果你是自己动手写完的回答起来会很轻松如果是临时借来的项目至少要把上面这几个问题彻底理解否则演示环节很容易露馅。6. 做完这个项目后我总结的几条实战心得6.1 关于分页查询和图片资源放哪的几个经验第一个经验是分页查询不要只用LIMIT offset, size因为当页码靠后时offset会很大查询性能急剧下降。课程设计阶段可能没什么感觉但如果你在简历上写“商城项目”面试官可能会追问深分页优化。最简单的回答是使用子查询或游标分页比如WHERE id 上一页最大ID ORDER BY id DESC LIMIT 12。我在这个项目里用还是普通LIMIT因为数据量太小但要清楚它的局限。第二个经验是图片资源不要存进数据库。有些初学者觉得把图片转成Base64字符串存到数据库里很省事实际上这会让数据库体积暴涨查询速度变慢前端渲染也吃力。正确的做法是图片存文件服务器数据库只存URL路径。如果是本地运行把图片放在独立的上传目录再通过Spring Boot的静态资源映射暴露出去。第三个经验是项目里的用户密码一律加密存储。我用了BCrypt在注册和登录时分别调用BCryptPasswordEncoder的encode和matches方法。你可能会觉得课设不需要这么讲究但数据安全是计算机行业的基本素养这一点做到位了整个项目的档次会明显不一样。6.2 后续可以扩展的方向这个项目做完以后我明显感觉还有很多可以改进的地方。如果时间充裕我会优先做下面几件事引入Redis缓存热点宠物信息减轻数据库压力购物车从数据库表改为Redis存储用Hash结构保存用户ID和商品ID列表把模拟支付替换成真实的支付宝/微信支付沙箱环境理解回调机制将文件上传改造成对接阿里云OSS或MinIO让图片管理更规范用Docker编写部署脚本把Maven打包、镜像构建、容器启动一条命令完成。不过这些扩展有一个前提先把当前这条主链路彻底吃透。我见过很多同学项目功能越加越多结果核心下单流程都跑不顺最后答辩时连最基本的功能都演示失败了。那才是得不偿失。最后再分享一点我的个人体会。这套萌宠商城项目让我收获最大的不是“代码量多少”而是我第一次体会到了从需求分析到上线部署的完整闭环。以前看教程总觉得什么都懂了真正动手时才发现一个表结构设计、一个事务注解位置、一个图片路径问题都可能卡住半天。建议大家做这类项目时不要只满足于把Demo跑起来更要试着改一改需求、删掉一个表重建、或者换一种实现方案试试。只有真正踩过坑这些东西才会变成你自己的经验。这篇复盘里的每个问题都是我当时一步步试出来的如果你也打算做类似的商城项目希望能帮你避开我走过的弯路。
返回列表