ARTICLE DETAIL

资讯详情

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

Spring Boot电脑商城系统设计:从数据库到部署的工程实践

Spring Boot电脑商城系统设计:从数据库到部署的工程实践 毕设里十个有七个是商城这话虽然夸张但确实道出了现状。几乎每个做Java方向的同学都绕不过基于Spring Boot的XX商城系统这类题目这次要拆的电脑商城系统就是其中很典型的一个。很多同学拿到题目之后直接去GitHub找个开源项目改改就交了运气好能过运气不好答辩时被问到为什么这么设计库存怎么保证不超卖支付回调重复通知怎么办当场卡壳。我写这篇文章的目的不是让你再抄一遍代码而是站在设计和实现的角度把从题目到落地这一路的关键决策、核心实现、常见坑和部署优化整个链路讲清楚让项目里的每个模块都能说出个所以然来——这才是答辩和面试真正看重的东西。1. 项目定位先搞清楚电脑商城该有什么不该有什么1.1 从标题反推需求边界很多人拿到基于Spring Boot的电脑商城系统的设计与实现这个题目第一反应就是去抄一个淘宝或者京东。这里有个非常严重的误区这题目里的电脑商城和通用电商平台在需求边界上差着十万八千里。电脑商城有几个非常具体的业务特征品类聚焦属性标准化卖电脑和电脑配件商品的描述字段是CPU型号、内存容量、显卡型号、硬盘类型、屏幕尺寸、接口数量这类标准化参数不需要像服装那样搞颜色、尺码的多维SKU组合。客单价高决策链路长一台电脑几千上万用户在购买前通常需要详细对比参数、看评价、查库存下单环节对库存准确性、订单状态可追踪性要求极高。售后和物流关联性强电脑类商品涉及保修、退换货订单状态需要明确展示和普通日用品下单即完事的模型有区别。这意味着什么意味着数据库设计上你可以不用引入复杂的SKU规格模型而是用商品主表 参数表或商品表 JSON扩展字段的方式搞定意味着用户端的需求重点不是秒杀拼团这种高并发玩法而是清晰的信息展示、可靠的购物车、严谨的下单流程和订单追踪。所以第一步不是写代码而是把核心功能模块列清楚。一个标准的电脑商城系统至少应该包含用户端注册登录、商品分类浏览、商品搜索、商品详情、购物车、下单结算、订单列表与详情、个人中心、收货地址管理。管理端后台登录、商品管理增删改查、上下架、分类管理、库存管理、订单管理发货、取消、退款、用户管理。公共模块文件上传商品图片、统一异常处理、日志记录、权限拦截。这些模块一般够撑起一篇合格的毕设论文了。如果时间充裕可以再加支付对接、首页轮播图管理、评论功能但如果时间紧张先把主链路跑通比堆功能点重要得多。1.2 前台、后台与权限模型怎么划分商城系统的权限模型看起来简单但很多人做的时候会翻车。最常见的做法是把用户表设计成包含一个角色字段user和admin两种角色。这种做法在毕设阶段可以接受但你要是想在答辩时体现一点专业度我建议这样处理用户端接口普通用户登录后才能访问用JWT拦截器做统一鉴权用户只能操作自己的数据。管理端接口单独校验管理员身份。更稳妥的做法是用户表加一个type字段0表示普通用户1表示管理员在拦截器里对/admin/**路径单独做强校验。Spring Security还是一个拦截器够不够毕设阶段用一个自定义HandlerInterceptor加JWT解析完全够用。Spring Security当然更标准但它本身的学习成本不低配置不当反而容易出幺蛾子。如果是为了毕设我建议先用拦截器方案把核心业务做完再考虑Security的集成。实际开发中还有一个容易忽略的点前台用户和管理员最好不要同时使用同一个登录接口。我会把用户登录和管理员登录分开管理员走/admin/login用户走/user/login这样在拦截器层面就好区分权限不用在业务代码里到处判断你是不是管理员。2. 技术选型Spring Boot为什么是主角配角怎么选2.1 Spring Boot解决了什么问题题目已经指定了Spring Boot这是一件好事。Spring Boot这个框架最核心的价值就是把Spring框架里大量繁琐的配置自动化了——你只需要引入依赖写少量配置甚至零配置就能快速把项目跑起来。展开说两点可以写进论文的底层逻辑自动装配机制Spring Boot通过SpringBootApplication注解里的EnableAutoConfiguration配合各个starter包里的META-INF/spring.factories配置在启动时自动加载对应场景的配置类。比如你引入了spring-boot-starter-web它就会自动帮你配置好内嵌Tomcat和Spring MVC引入了spring-boot-starter-data-redis它就会自动帮你创建RedisTemplate的Bean。这就是约定优于配置思想的落地。内嵌容器不需要单独装TomcatMaven打包成可执行jar后直接java -jar就能跑这对部署和演示来说太友好了。我当时做这个商城项目的时候选择Spring Boot 2.x系列具体用的2.7.x配Java 8这个组合在毕设项目里非常稳网上资料也最多遇到问题基本都能搜到解决方案。如果你用的是Spring Boot 3.x那要配Java 17有些老教程里的代码可能跑不通需要留心版本兼容问题。2.2 ORM、缓存、前端框架的搭配方案Spring Boot是主角但配角的选择会直接决定开发效率和项目稳定性。我给的推荐搭配如下模块推荐方案备选方案理由ORM框架MyBatis-PlusMyBatis / JPAMyBatis-Plus的BaseMapper让你免写大量单表CRUD SQL分页插件开箱即用适合快速开发数据库MySQL 5.7 / 8.0-生态最成熟教程最多8.0的性能和JSON支持更好缓存Redis (Spring Data Redis)本地Map缓存Redis要做缓存和分布式Session都方便而且面试高频考点前端Vue 2/3 Element UI/PlusThymeleaf服务端渲染前后端分离是目前主流建议用Vue答辩时也能多聊一个技术栈权限方案自定义拦截器 JWTSpring Security JWT毕设阶段拦截器够用代码自己可控出问题好排查接口文档springdoc-openapi (Swagger)knife4jSwagger能让答辩老师一眼看到你的接口设计加分项这里有个我个人的偏好说明MyBatis-Plus在毕设项目里是非常大的生产力工具。你不需要自己写selectById、insert、updateById这些基础方法内置的QueryWrapper和LambdaQueryWrapper让条件查询也变得很简洁。比如按商品名模糊查询一行代码ListProduct list productMapper.selectList( new LambdaQueryWrapperProduct() .like(StringUtils.isNotBlank(keyword), Product::getName, keyword) .eq(Product::getStatus, 1));省掉了写XML的功夫时间可以花在核心业务上。当然如果你准备去面试大厂并且要体现自己的SQL功底把MyBatis的XML手写SQL练一练也没坏处。前后端分离的结构里还要特别注意前端项目的组织方式。我自己的习惯是前端用Vue CLI或者Vite创建独立项目后端Spring Boot单独一个项目开发时前端通过代理vue.config.js里的proxy配置把请求转发到后端的localhost:8080这样就不需要处理跨域问题只有部署时才让Nginx统一代理。3. 数据库设计把商品、购物车、订单的主链路理顺3.1 商品表与分类表的两种设计思路数据库设计是整个商城系统的地基很多项目后期改到吐都是因为表结构当初没设计好。商品和分类这一块我见过两种主流设计第一种经典的单表分类 外键关联。分类表categoryid、parent_id、name、sort_order商品表product通过category_id关联到分类。这种设计简单直观适合分类层级不深的场景比如电脑/笔记本/游戏本后台管理也方便。第二种商品表独立参数表 商品参数表。电脑硬件参数太多如果把CPU、内存、显卡、硬盘、屏幕全部做成商品表的字段表会变得很长而且不同商品主机、显示器、鼠标、键盘的参数差异很大。我推荐的做法是核心通用字段放商品表扩展参数放JSON字段或者单独的规格参数表。我的商品表核心结构大致是这样的CREATE TABLE product ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT 商品ID, category_id BIGINT DEFAULT NULL COMMENT 分类ID, name VARCHAR(200) NOT NULL COMMENT 商品名称, subtitle VARCHAR(500) DEFAULT NULL COMMENT 副标题/卖点, main_image VARCHAR(500) DEFAULT NULL COMMENT 主图URL, detail TEXT COMMENT 商品详情HTML, price DECIMAL(10,2) NOT NULL COMMENT 价格, stock INT NOT NULL DEFAULT 0 COMMENT 库存, status TINYINT NOT NULL DEFAULT 1 COMMENT 状态1上架 0下架, params_json JSON COMMENT 扩展参数存CPU/内存/显卡等, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_category (category_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;params_json字段用MySQL的JSON类型来存电脑的具体参数比如{cpu: i7-13700K, memory: 32GB DDR5, gpu: RTX 4070, disk: 1TB SSD}。前端商品详情页把JSON解析出来渲染成一个参数表格就行。这样做的好处是灵活不用为每个品类建不同的表坏处是没法针对单个参数做SQL查询比如查询所有内存为32GB的电脑。但商城场景里这种参数筛选一般用搜索引擎或者前端过滤不会直接SQL查所以完全够用。3.2 购物车是临时态订单才是核心购物车表的设计有两种流派。一种是用户维度——一行记录存一个用户的所有购物车项用JSON存商品列表另一种是条目维度——每行是一个购物车条目有user_id、product_id、quantity。我强烈推荐后者因为修改数量、删除单个商品、勾选结算这些操作都只需要操作单行记录逻辑清晰SQL简单。后面做库存校验和迁移到订单明细特别方便。购物车表大概长这样CREATE TABLE cart_item ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, product_id BIGINT NOT NULL, quantity INT NOT NULL DEFAULT 1, checked TINYINT NOT NULL DEFAULT 1 COMMENT 是否勾选1勾选 0取消, PRIMARY KEY (id), UNIQUE KEY uk_user_product (user_id, product_id), KEY idx_user (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;user_id和product_id加了唯一索引这样同一个商品在购物车里重复添加时会触发冲突你可以用ON DUPLICATE KEY UPDATE让数量累加也可以在代码里先查再更新。这个细节我在代码里是用先查一次存在就加数量的方式逻辑更直白虽然多一条SQL但对这个数量级的系统来说毫无压力。订单表是整个系统里最重要的表。我这里给出一个比较合理的拆分订单主表order_info存订单号、用户ID、订单总金额、订单状态、收货地址快照、创建时间、支付时间、发货时间。订单明细表order_item存订单ID、商品ID、商品名称快照、商品图片快照、购买价格、购买数量。order_item里存商品名称和图片快照是很多初学者会漏掉的关键点。因为商品的信息是可以被修改的如果下单之后商品改名或者下架了用户的订单历史里应该还是显示购买当时的商品信息不能跟着变。这就是业务数据快照的思想。订单状态我用一个status字段管理取值定义为0待付款、1已付款待发货、2已发货、3已完成、4已取消、5退款中/已退款。状态流转要画在论文里但代码层面我建议用状态机思路控制流转——不是任何状态都能跳到任何状态比如已完成的订单不能被取消待付款的订单不能直接变已发货。3.3 库存扣减下单时最核心的坑如果你只在论文里写下单时UPDATE product SET stock stock - 1 WHERE id ?那大概率会在答辩时被追问到并发场景下库存怎么保证不超卖。这个问题必须提前准备。什么是超卖假设库存只剩1件两个用户同时下单都先SELECT到库存为1都判断库存充足然后都执行库存减1结果库存变成-1卖出去了2件——这就是超卖。解决思路有两个层次第一个层次数据库层面的原子扣减。不要把查库存和扣库存分开操作而是直接用UPDATE语句带条件扣减UPDATE product SET stock stock - 1 WHERE id #{productId} AND stock 0;这条SQL是原子的stock 0条件确保不会把库存扣成负数。如果影响行数为0说明库存不足下单失败。这是最简单也最有效的防超卖方案。第二个层次就是靠数据库扣减 事务 唯一约束兜底。比如在订单明细表里对order_no product_id加唯一约束防止同一个订单里出现重复商品明细用事务包裹扣库存 创建订单保证一致性。对于这个项目来说第一层方案在单机MySQL下已经足够了不需要引入Redis分布式锁这种重型方案。但如果你的论文里想体现高并发思考可以嘴上提一下当并发量上升时可以考虑用Redis预扣库存或分布式锁这就够了。4. 核心业务链路的实现与关键代码4.1 登录鉴权JWT接力拦截器这套方案怎么落地商城系统的登录状态管理我推荐JWTJSON Web Token方案这是目前前后端分离项目的主流做法。核心思路是用户登录成功后服务端签发一个JWT字符串返回给前端。前端把JWT存在localStorage里每次请求在HTTP Header里携带Authorization: Bearer token。后端写一个拦截器拦截需要登录的请求解析token解析成功就放行并把用户信息塞进请求上下文。JWT的无状态特性非常适合前后端分离场景因为服务端不用存Session水平扩展时也不用考虑Session同步问题。代码层面用io.jsonwebtoken:jjwt这个库比较省事。生成token的代码大致如下public String generateToken(Integer userId, String username) { Date now new Date(); Date expiryDate new Date(now.getTime() 1000 * 60 * 60 * 24 * 7); // 7天有效期 return Jwts.builder() .setSubject(String.valueOf(userId)) .claim(username, username) .setIssuedAt(now) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS256, secretKey) .compact(); }拦截器里解析token的流程也简单从Header拿到token用相同的密钥解析如果校验通过就把userId放进request的attribute里后面的Controller用RequestAttribute(userId)取出来用。需要提醒的点有两个密钥不要硬编码在代码里放到application.yml的配置项里后续部署不用改代码就能切换密钥。token要设置过期时间过期后的请求拦截器直接返回401前端收到401跳转登录页这是很基础的用户体验问题。JWT不够安全的地方也要心里有数服务端无法主动让token失效除非引入黑名单机制。对于毕设项目来说这不是问题但面试被问到可以答如果把安全性要求提高可以配合Redis做token黑名单或者改用Session方案。4.2 下单链路从购物车到订单的完整流程下单是商城系统的核心业务代码组织得好不好一眼就能看出来。我会把下单流程设计成一个Service方法用Transactional事务包裹核心步骤拆开来看从前端接收本次要结算的商品列表传购物车item的id数组。根据userId查出这些购物车条目再查出对应的商品信息。校验商品状态是否上架、价格、计算总金额。扣减库存用前面说的原子UPDATE。生成订单号插入订单主表和订单明细表。删除购物车中已结算的条目。返回订单号和支付参数给前端。这里有个常见的代码坏味道提醒一下不要在Controller里写业务逻辑。Controller只负责接收参数和返回结果业务逻辑全部放在Service层Controller层保持薄薄的一层。这样做的原因是业务逻辑需要事务支持而事务注解放在Service层方法上才有效Controller层的方法加事务经常失效这是个隐形的坑后面我会专门讲。订单号生成我推荐用时间戳随机数用户ID的组合避免用数据库自增ID当订单号暴露到前端会泄露销量信息String orderNo String.format(%s%s%s, DateTimeFormatter.ofPattern(yyyyMMddHHmmss).format(LocalDateTime.now()), String.format(%04d, ThreadLocalRandom.current().nextInt(10000)), userId);下单Service方法的骨架示意Transactional(rollbackFor Exception.class) public OrderVO createOrder(Integer userId, ListLong cartItemIds) { // 1. 查询购物车条目 ListCartItem cartItems cartItemMapper.selectByIds(cartItemIds); // 2. 遍历校验商品并计算金额 BigDecimal totalAmount BigDecimal.ZERO; for (CartItem item : cartItems) { Product product productMapper.selectById(item.getProductId()); if (product null || product.getStatus() ! 1) { throw new BizException(商品不存在或已下架); } totalAmount totalAmount.add(product.getPrice().multiply(BigDecimal.valueOf(item.getQuantity()))); } // 3. 原子扣库存 for (CartItem item : cartItems) { int rows productMapper.deductStock(item.getProductId(), item.getQuantity()); if (rows 0) { throw new BizException(商品库存不足 item.getProductId()); } } // 4. 创建订单主表 明细表 // 5. 清空已结算的购物车条目 // 6. 返回订单信息 }这个流程虽然代码量不大但每一步都有留意点哪一步做错都可能导致线上问题。尤其注意先扣库存再创建订单这个顺序如果先创建订单再扣库存一旦扣库存失败就要回滚订单稍微麻烦一点。4.3 支付对接回调处理的设计原则支付这一块是很多毕设项目的空白。如果完全不接支付论文里支付模块只能写个假接口答辩时容易被问住。我建议至少接一个沙箱环境——支付宝沙箱或者微信支付沙箱都行主要目的不在于真能收到钱而在于打通发起支付-回调通知-更新订单状态这整条链路。支付对接最容易做错的地方就是回调接口的设计。回调是支付平台主动请求你的服务器通知这笔订单支付成功了这里有三个必须注意的点回调接口不能要求登录它不在JWT拦截器的拦截范围内。回调接口必须验签验证请求确实是支付平台发来的而不是有人伪造的接口调用。回调可能重复通知支付平台会说如果你的服务器没返回成功我会重复通知你多次所以回调处理逻辑必须幂等——同一个订单被通知两次不能把订单状态从已支付改成已退款再改成已支付。// 幂等处理如果订单已经是已支付状态直接返回成功 OrderInfo existingOrder orderMapper.selectByOrderNo(outTradeNo); if (existingOrder ! null existingOrder.getStatus() 1) { return SUCCESS; } // 更新订单状态为已支付 设置支付时间这行先查状态再决定是否更新的代码就是幂等性的最简实现。很多同学回调里直接这个状态改成已支付结果重复回调时状态被重置订单记录就乱了。我个人的建议是如果时间实在不够也可以不做真实支付对接但要在项目里把支付回调处理的逻辑写好哪怕模拟回调场景测试这比放着不做要强太多。5. 真实开发中踩过的坑与排查过程5.1 前后端分离项目最常见的跨域报错前后端分离项目第一次联调必报错九成是跨域问题。浏览器看到后端接口返回的Access-Control-Allow-Origin响应头没有包含前端域名就会把请求拦下来。你会在浏览器控制台看到经典的报错Access to XMLHttpRequest at http://localhost:8080/api/... from origin http://localhost:8081 has been blocked by CORS policy。解决方案有两个层次开发环境最省事的做法用Vue的proxy代理转发请求。改vue.config.jsmodule.exports { devServer: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }前端请求的url是/api/user/login代理把请求转发到http://localhost:8080/api/user/login浏览器看到的是同源请求自然不报跨域。生产环境最省事的做法Nginx反向代理。前端静态文件和API接口都由Nginx对外提供配置大概长这样location /api/ { proxy_pass http://127.0.0.1:8080/api/; }这样前端域名和接口域名完全一致不存在跨域问题。如果你两个方法都不做非要靠后端加CrossOrigin注解也行但生产环境一旦涉及Cookie或者多个前端域名就不太好用了。我当时第一次联调时直接在Controller上加了CrossOrigin能通但总觉得不干净。后来改成Nginx统一代理配置灵活多了后端代码里一个跨域相关注解都不用加。5.2 Transactional事务失效的几个隐蔽场景Transactional在Spring Boot项目里是个高频注解但也是翻车重灾区。我们项目里有一个下单接口测试时发现库存明明扣成功了订单居然没创建成功——数据不一致这意味着扣库存和创建订单没有在同一个事务里。排查过程是这样的第一步确认异常有没有被rollbackFor Exception.class捕获。Transactional默认只在运行时异常时回滚如果方法是抛出Exception但没指定rollbackFor事务是不会回滚的。这是最常见的翻车点。第二步确认调用入口。Transactional只有通过Spring代理对象调用时才生效。如果是在同一个类里一个方法调另一个方法比如this.createOrder()这样的内部调用事务注解是不生效的。第三步确认数据库表引擎是InnoDB而不是MyISAM。MyISAM不支持事务这个坑在旧项目里偶尔会遇到。我们当时的问题就出在第二步。同事在下单Controller里调同一个Service的另一个方法然后把Transactional放在被调用的私有方法上注解完全不生效库存扣了但订单表没写进去。改成在Controller调用的那个public方法上加注解并在调用链里用Autowired注入的代理对象来调另一个方法问题就解决了。5.3 Redis缓存穿透热点商品被疯狂刷库存商城系统接上Redis之后第一件想干的事就是把商品详情缓存起来减少数据库压力。但如果不考虑缓存穿透的问题好事会变成坏事。场景是这样的某些商品ID不存在比如前端恶意请求一个很大的ID999999代码逻辑是查Redis缓存没查到再去查数据库数据库里也不存在每次请求都会打到数据库。如果有个脚本疯狂请求这种不存在的ID数据库压力瞬间爆表这就是缓存穿透。我碰到过一次某天数据库CPU异常飙高排查发现是有人对不存在的商品ID做大量请求。当时做的处理分了两层缓存空值查询结果为空时在Redis里缓存一个空标记过期时间设短一点比如60秒。这样同样ID的请求直接返回空不会打到数据库。参数校验商品ID必须是合法的正整数直接拦截非法ID请求。还有一个相关的坑叫缓存击穿——某个热点商品缓存过期的一瞬间大量请求同时打到数据库。解决办法是对查询数据库并回填缓存这个动作加锁保证同一时刻只有一个请求去查数据库其他请求等待后直接走缓存。毕设项目里手动写一个RedisLock工具类也不复杂或者直接用Redisson的RLock这个扩展点如果写在论文的系统优化章节里是很好的加分项。5.4 从jar包反编译找回丢失的配置文件这个技能虽然不常用但真遇到时能救命。我有个同事接手一个老项目源码里的application.yml丢了只拿到一个可运行的jar包环境要重新搭配置全在一头雾水里。后来是老同事提了一句jar包里的配置可以直接反编译提取才解决。做法很简单# 解压jar包 jar xf my-app.jar # 配置类在BOOT-INF/classes下直接查看 cd BOOT-INF/classes cat application.ymlSpring Boot的jar包本质上就是一个zip用jar命令或者解压软件直接打开就能看到BOOT-INF/classes下的全部配置文件和类文件。.class文件可以直接用javap命令看字节码或者用IDEA自带的反编译工具打开。我之前调试一个老项目的定时任务逻辑时靠这个方式直接从线上jar包里翻出了application-prod.yml里的数据库连接池参数省了一下午抓瞎时间。5.5 接口幂等性的另外一重保障防重复下单用户在网页上点提交订单按钮因为网络延迟手抖点了两下产生了两个一模一样的订单——这个问题商城系统一定会遇到。解决思路在数据库设计阶段就应该考虑同一个用户短时间内对同一个购物车列表重复提交应该只生成一个订单。常见的做法有前端做提交按钮禁用防住一部分失误操作。后端在Redis里用userId 本次提交的cartItemIds的hash作为key做个短时间去重比如5秒内重复提交直接返回第一次的订单。数据库层用唯一约束兜底。第一种和第二种组合起来基本能覆盖绝大多数场景。这类防重复细节自己主动做了答辩时被问有没有考虑过重复请求问题就能对答如流。6. 上线部署与性能优化的实用手段6.1 Docker部署让演示环境一键起停毕设答辩演示的时候环境能不能稳定起跑比什么都重要。用Docker部署Spring Boot项目可以让你从环境变量地狱里解脱出来。最基础的做法是写一个DockerfileFROM openjdk:8-jdk-alpine COPY target/computer-mall.jar /app.jar ENTRYPOINT [java, -jar, /app.jar]构建镜像是docker build -t computer-mall . docker run -d -p 8080:8080 --name mall computer-mall如果还把MySQL和Redis也用Docker Compose编排起来整个项目一条命令全部起停演示的时候非常省心。docker-compose.yml大概长这样version: 3 services: mysql: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: computer_mall ports: - 3306:3306 redis: image: redis:6 ports: - 6379:6379 mall: build: . depends_on: - mysql - redis ports: - 8080:8080这里有一个经验之谈Spring Boot应用要等MySQL和Redis先启动完成如果应用启动比数据库早启动时会报连接异常直接退出。建议在启动类加一个小判断或者用depends_on加上简单的健康检查别让这个低级错误在答辩现场翻车。6.2 接口性能的几个日常优化点论文里写系统优化章节不能只写用Redis缓存一句话。我把这个项目里真正用上且效果明显的优化手段列出来商品列表接口的分页查询MyBatis-Plus的分页插件在配置类注册一下selectPage搞定注意不要用selectList查全表再内存分页数据量大了必然卡死。首页高频数据的Redis缓存比如分类列表、推荐商品、轮播图这些数据变化频率低但访问量高非常适合缓存。缓存key的设计要有规范比如mall:product:detail:{id}统一加mall:前缀后续清理缓存时也清晰。商品详情页静态化商品详情HTML本身可以做静态化或者至少用CDN承载图片等静态资源。这个优化对于电脑商城这种图片多的场景效果显著。数据库连接池的配置默认的HikariCP连接池参数对毕设项目够用但如果项目里做了报表查询、批处理类的功能要把maximum-pool-size从默认的10适当往上调比如调到20并给慢查询设个connectionTimeout避免请求排队。6.3 哪些扩展值得做哪些别做项目做到一定阶段总想加点亮点。我的建议是挑一个方向做深比功能堆一大堆更有说服力。以下几个扩展方向的性价比排序供你参考高性价比接入真实第三方登录比如用Gitee/GitHub OAuth登录代码量不大但能体现你对OAuth2协议的理解引入WebSocket实现客服聊天或订单动态通知这个在演示时效果很直观。中性价比用Elasticsearch做商品搜索能讲出倒排索引的原理但做起来要先部署ES、同步数据、写查询DSL工作量不小用MinIO做图床熟悉一下对象存储这思路。低性价比且别碰非要自己搞秒杀系统、分布式事务、分库分表。这些场景对商城这种体量的项目是伪需求做出来也是生搬硬套答辩老师一问你流量多少单表多少数据就露馅了。我见过一个同学在项目里引入了消息队列做订单异步通知用ActiveMQ发消息到队列又写了一个消费者自动更新库存——如果你能讲清楚为什么要异步、消息丢失怎么办、重复消费怎么办那确实加分但如果只是为了让技术栈看起来高大上而硬塞中间件答辩一问三不知那就丧失意义了。我最后再分享一点个人体会。做了一个完整的商城项目之后你会发现它跟真实电商平台的距离比想象中更大但核心链路——登录、商品、购物车、订单、支付回调、库存扣减、状态流转——这套东西背后的设计思想是通用的。把一个商城从零做一遍你会被迫把数据库事务、缓存策略、幂等设计、接口安全这些基本功都过一遍这比刷十道八股文都来得实在。如果这篇文章能帮你在设计阶段少走几个弯那我这一通折腾也算值了。
返回列表