
1. 项目拆解农业生产设备销售平台到底在做什么这个选题我最初拿到手的第一反应是——又是一套标准的“Spring Boot 电商”模板题。但真正把农业生产设备这个领域拆开看之后才发现里面的门道比想象中多。农机和普通商品不一样几十万的大型拖拉机没法走三通一达播种机需要根据地块大小匹配动力农药喷雾器涉及安全使用培训订单也不像买衣服那样线上下单就完事后面还得跟物流调度、安装培训、售后保养绑定在一起。这些年帮人评审过不少这类平台类项目也带着团队完整落地过两个比较相似的农资电商系统。我干脆把整个设计思路、技术选型、核心实现和踩坑记录一次性写透希望能给正在做这个选题的同学少走点弯路。1.1 这个平台的核心业务边界农业生产设备销售服务平台本质上是把传统的农机销售门店搬到线上。但和通用电商平台相比它的业务边界要窄得多也特殊得多。先说说这个平台到底要管哪些东西。从业务链条上看主要包含这样几条线商品中心管理农机的品类、品牌、型号、参数配置、图片视频、价格库存。这里有个特殊点农机不是标准品同一型号可能因为挂接方式、动力输出、作业宽幅不同价格差异很大所以商品的SKU设计不能照抄服装电商那套。订单交易从用户下单、支付、到发货物流、确认收货这个流程和普通电商一致但订单的状态节点要多考虑几个——农机可能有定金预售、尾款支付、发货前验机这些环节。履约服务这是和普通电商差异最大的地方。大件农机设备涉及物流配送、安装调试、操作培训、售后维修平台需要支持服务工单的流转和记录。用户体系包括个人农户、合作社、经销商三类角色不同角色的价格策略和权限都不一样。从技术实现的角度看这个平台并不需要做到淘宝那种体量但它要求业务模型设计得足够清楚尤其是商品规格、订单状态、库存扣减这几块稍有疏忽就会埋下大坑。1.2 目标用户画像与核心场景分析做项目之前先把用户画像想清楚比急着写代码重要得多。我接触过的农机销售平台用户基本可以分成三类个人农户这类用户对价格敏感操作习惯偏传统很多人是第一次在网上买农机。他们最关心的是“这个机器适不适合我的地”“坏了谁给我修”。所以平台在商品详情页要突出参数解读、适用场景说明最好配实拍视频而不是一堆看不懂的配置表。农业合作社批量采购为主一次可能买十几台需要询价、议价、批量下单、统一开票这些功能。这类用户更看重的是能不能对接专属客服拿到团购价。经销商/代理商他们既是平台的供应商也是平台的服务方。设备售出后的安装、培训、维修都是由当地经销商完成所以平台要有一个简单的经销商后台用来接单和处理售后工单。核心使用场景我梳理下来有三个一是选型对比。农户买农机之前一定会反复比较不同品牌、型号的参数和价格。平台需要提供参数对比功能这比普通电商的“加入购物车”更刚需。二是定金尾款的大额交易。一台收割机二三十万用户不太可能直接在线付全款常见的做法是线上付定金线下看机验机后付尾款。订单状态机需要支持这种特殊流程。三是售后报修。农机作业季节性强农忙时设备坏了非常耽误事。平台要支持用户一键报修系统根据农机所在位置自动匹配最近的经销商服务点。把这三个场景想明白功能模块基本上就能画出来了。后面所有的技术设计都是围绕这些场景展开的。2. 技术选型为什么用Spring Boot以及周边生态怎么搭技术选型这部分很多同学的思路是“什么火用什么”结果项目做完发现一半依赖根本没用上。我个人的习惯是先定主框架再从业务需求倒推需要哪些组件避免过度设计。2.1 Spring Boot版本选择与核心优势主框架选Spring Boot几乎是这个题目的必然选择原因有三点。第一Spring Boot的自动配置机制极大降低了项目启动成本。以前用Spring写一个Web项目光XML配置文件就要写好几页现在一个spring-boot-starter-web依赖加一个SpringBootApplication注解就能跑起一个内嵌Tomcat的Web服务。对于课程设计或毕业设计这种时间有限的场景这个优势是决定性的。第二Spring Boot生态太成熟了。做电商平台要用的东西它都有对应的Starter——MyBatis-Plus负责数据库操作Spring Security负责认证授权Redis做缓存MinIO做文件存储包括后面对接微信支付、阿里云短信全都有现成的SDK和示例。第三Spring Boot的社区活跃度高遇到问题搜索一下基本都能找到答案。这对独立开发的学生项目来说很重要你不太可能所有问题都靠自己从零排查。版本选择上建议直接用Spring Boot 2.7.x不要追新用3.x。原因后面细说简单讲就是2.7.x相关的中文资料最多遇到问题最容易找到解决方案做项目求稳不追新。如果非要用Spring Boot 3.x那就必须配套JDK 17及以上还要注意很多老版本的第三方库不兼容比如MyBatis-Plus和Spring Security的配置方式都有变化。2.2 周边组件选型MyBatis-Plus、Redis、MinIO、Spring Security主框架定了之后周边组件我按项目的实际需要逐个过一遍。持久层框架选MyBatis-Plus而不是JPA。做电商这种偏业务型的系统SQL的灵活性和可控性很重要尤其涉及多表联查、复杂的条件统计时MyBatis-Plus的QueryWrapper和自定义XML SQL结合使用非常顺手。它内置的BaseMapper直接提供了单表的CRUD方法代码量能省一大半。相比之下JPA虽然也很强大但学习曲线陡调试起来不如MyBatis直观。缓存选Redis。项目里的缓存场景很多首页轮播图、商品分类、热门商品列表这些数据访问频繁但更新频率低非常适合做缓存。另外后面要做的购物车也可以用Redis来实现比用数据库表做购物车要灵活得多。文件存储选MinIO。农机商品需要大量的参数图片、作业视频这些文件不能直接存到数据库里需要单独的文件存储服务。MinIO最大的优势是开源自建不花钱S3协议兼容以后想换云存储也很方便。认证授权选Spring Security JWT。说实话Spring Security的学习成本不低配置也容易把人绕晕但它是Java Web安全的事实标准项目里用了它能加分不少。配合JWT做无状态认证前后端分离的架构下体验很好。技术栈汇总就是下面这张表后面所有模块的实现都围绕这套组合展开组件选型用途说明核心框架Spring Boot 2.7.xWeb服务、IOC容器、自动配置持久层MyBatis-Plus 3.5.x数据库CRUD操作、条件构造器数据库MySQL 5.7 / 8.0业务数据持久化存储缓存Redis购物车、验证码、热点数据缓存安全框架Spring Security JWT登录认证、接口权限控制文件存储MinIO商品图片、视频、附件存储接口文档Knife4j (Swagger)自动生成API调试文档构建工具Maven依赖管理和项目打包JDK版本JDK 8 或 JDK 11配合Spring Boot 2.7.x使用这套组合的好处是每一层都有明确的替代方案。如果学校服务器装不了MinIO可以直接换成阿里云OSS如果不想用Spring Security也可以用拦截器实现简单的Token验证。技术选型要有弹性不要被某个组件卡死。2.3 前后端分离架构单体应用的分层设计思路这个项目的架构属于典型的前后端分离单体应用对于这个体量的系统是最务实的选择。相比微服务架构单体应用的开发效率高、部署简单、调试方便不用担心分布式事务、服务发现、配置中心这些复杂度。说实话一个课程设计或毕业设计级别的电商平台强行上微服务反而是给自己挖坑。后端按经典的四层结构组织Controller层接收HTTP请求参数校验返回统一响应结果Service层业务逻辑处理事务管理模块间的调用Mapper层数据库访问MyBatis-Plus的BaseMapper接口扩展Entity/DTO/VO层数据模型定义实体类、数据传输对象、视图对象分离重点说一下DTO和VO为什么要分开。很多同学偷懒一个Entity类从头用到尾数据库字段直接暴露给前端这样做后患无穷。比如用户表有密码字段如果用User实体直接返回密码就泄露了商品表有内部成本价也不能暴露给用户端。正确的做法是Controller返回VO对象Service内部用DTO传输数据Entity只负责和数据库打交道。前端框架如果不是必须自己写建议用Vue 3 Element Plus如果不想折腾前端也可以直接用后端的Thymeleaf模板引擎。我做这个项目的时候用的是前后端分离方案开发阶段用Vite代理转发请求解决跨域生产环境直接把前端打包后的静态文件放到Spring Boot的static目录下一个Jar包搞定部署。3. 数据库设计一张订单表如何撑起农机的交易闭环数据库设计是这类系统的重头戏。很多项目做到后面发现改来改去根因就是表结构没设计好。我习惯在写第一行代码前先把所有表结构和字段关系确定下来后面开发基本不会走回头路。3.1 核心表结构概览整个平台的数据库我设计了12张核心表分成四个模块用户模块用户表含个人用户和合作社、角色表、用户角色关联表、收货地址表。商品模块商品分类表、商品信息表、商品规格SKU表、商品图片表。交易模块购物车表、订单表、订单明细表、支付流水表。服务模块物流信息表、售后工单表。挑几张核心表具体说。用户表的核心字段除了常规的用户名、密码、手机号、昵称、头像之外建议加上两个字段user_type用户类型个人/合作社/经销商和audit_status审核状态。为什么因为合作社和经销商不是注册就能用的需要平台管理员审核资质这两个字段能让你用最简单的方式实现角色差异化权限。商品信息表字段比较多关键的有product_name、category_id、brand、model、price、original_price、stock、sales_count、status、detail_html。还有一个容易被忽略的是unit单位农机类目的计量单位可能是台、套、组别写死在代码里。商品SKU表的设计是这个项目的一个亮点也可以说是难点。以拖拉机为例同一个型号可能有两驱、四驱、不同马力段之分价格差异很大。所以我把规格参数拆到独立的product_spec表用JSON字符串存储规格项比如{power: 120马力, drive_mode: 四驱, cabin: 封闭式}这样既能灵活扩展规格维度又不需要为每种规格建单独的字段。订单表是整个系统的核心字段包括order_no订单编号、user_id、total_amount、pay_amount、freight_amount、status、pay_type、pay_time、delivery_time、finish_time、receiver_name、receiver_phone、receiver_address。重点说一下status字段农机订单的状态流转比普通电商多两个环节待支付 - 待付尾款定金订单 - 待发货 - 待收货 - 已完成 ↓ 已取消 / 已退款这个状态机在订单服务中用枚举类管理每一步的合法流转都做了校验避免脏数据。比如已取消的订单不能直接跳到已完成必须要走完整的流程。3.2 购物车表临时数据与持久化的取舍购物车是电商系统的标配功能但实现方式有两种选择用数据库表持久化或者用Redis临时存储。我推荐用Redis来实现购物车。原因有几点第一购物车是高频读写、低频长期保存的数据。用户加购、改数量、勾选、删除这些操作对响应速度要求高用Redis的Hash结构操作是微秒级的比查数据库快得多。第二购物车数据允许丢失。用户几天不登录购物车里的东西丢了虽然有影响但不是灾难性的很多用户根本不记得自己加过什么。用Redis设置7天过期时间既保证体验又能释放存储空间。第三降低数据库压力。如果用数据库表存购物车用户每次加购都是一次INSERT操作高频场景下会给数据库增加不必要的负担。购物车Redis Key的设计是cart:userIdHash结构里field对应SKU IDvalue存的是数量加勾选状态比如{skuId: 123, count: 2, checked: true}。不过要注意最终生成订单的时候一定要从Redis里取数据算好金额再落到数据库不能盲目相信前端传过来的数据否则可能出现改价格漏洞。3.3 订单号生成策略与流水表设计订单号看着不起眼设计不好会出很多麻烦。我之前见过一个项目直接用数据库自增ID当订单号结果用户下单后凭订单号查不到订单排查了半天发现是ID对不上——因为有多张表都有自增ID搞混了。正确做法是订单号独立生成保证全局唯一且带业务含义。我用的是时间戳随机数的方式yyyyMMddHHmmss 6位随机数比如20250615143055123456。这样做的优势是从订单号能看出下单时间方便排查问题6位随机数保证同一秒内不会重复概率极低排序基本按时间有序。支付流水表是很多同学容易遗漏的表。它的作用不是记录“用户付了多少钱”而是解决一个大问题支付回调的幂等性。微信或支付宝支付成功后服务器会异步通知我们的后端接口这个通知可能因为网络原因发送多次。如果后端不加判断每次回调都更新订单状态就会出问题——比如用户付了一次款订单被更新了两次状态后面的操作全乱套了。支付流水表的字段包括payment_no支付流水号、order_no、user_id、pay_type、amount、status、transaction_id第三方支付流水号、callback_time回调时间。每次收到支付回调先根据order_no查流水表如果status已经是“支付成功”直接忽略本次回调。4. 核心功能实现从登录验证到订单流转的完整闭环理论设计说完了接下来是实实在在的编码实现。这一部分不会贴完整代码而是把关键逻辑讲透把我在实际开发中觉得容易写错的地方重点标出来。4.1 基于JWT的用户认证与权限控制用户认证这块用Spring Security JWT是前后端分离项目的主流方案。整体流程是用户提交用户名密码登录成功后后端生成一个JWT Token返回给前端前端之后的每次请求都在Header里带上Authorization: Bearer token后端通过过滤器解析Token识别当前用户身份和权限。代码层面有几个关键点JWT工具类。用于生成Token和解析Token。Token里只放必要信息用户ID、用户名、角色编码。不要把手机号、密码这种敏感信息放进去JWT是Base64编码的任何人都能解码看内容。Token有效期建议设2小时如果是课程设计为了方便演示也可以设24小时。Spring Security配置类。这一步是最容易出错的。Spring Security 5.7之后的版本推荐使用SecurityFilterChain这种基于Lambda的配置方式而不是继承WebSecurityConfigurerAdapter这个类在Spring Boot 2.7中已经标记为过时。配置的核心内容有三个放行哪些接口登录、注册、商品列表、商品详情这些不需要登录就能访问、哪些接口需要认证下单、购物车、个人中心、自定义JWT过滤器放在Spring Security过滤器链的什么位置。还有个细节是配置PasswordEncoder用BCryptPasswordEncoder用户注册时密码加密存储千万不能明文存数据库。自定义JWT过滤器。这个过滤器继承OncePerRequestFilter在Spring Security的UsernamePasswordAuthenticationFilter之前执行。逻辑是从请求Header中取Token解析成功后构造UsernamePasswordAuthenticationToken设置到SecurityContextHolder中。解析失败或Token过期直接放行让Spring Security的认证入口决定是否返回401。我见过很多同学在这一步卡住原因通常是Spring Security的配置顺序不对或者过滤器中的异常处理没有做好。一个实用的建议是在JWT过滤器中不要自己处理所有异常让异常抛出去在Spring Security的ExceptionTranslationFilter层统一处理这样代码更清晰。4.2 商品模块分类、检索、详情展示商品模块是平台的门面也是用户接触最多的功能。实现上分三个部分。商品分类采用两级分类就够了不需要太深。一级分类比如“种植机械”“收获机械”“农用运输”二级分类比如“拖拉机”“收割机”“播种机”。分类表用parent_id关联父子关系查询时先查一级分类再查子分类或者用一次查询全部列表后在内存中组装树形结构。数据量不大这种方式性能完全够。商品检索这个功能要注意农机商品的专业参数多用户可能会搜“120马力四驱拖拉机”这种长尾关键词。最简单的实现是SQL的LIKE查询字段包括商品名称、品牌、型号、关键词。如果数据量大了超过几万条再引入Elasticsearch但对这个项目来说LIKE完全够用没必要为了技术亮点给自己加复杂度。这里有个性能优化的小技巧商品列表页的分页查询配合MyBatis-Plus的Page对象设置合理的每页条数建议10到20条再加上Redis缓存第一页的热门商品数据响应速度能有质的提升。商品详情页是这个项目的重头戏。农机的详情页不能只放几张图片和一段简介用户需要看到的是完整的参数规格表、作业场景实拍视频、同品牌其他型号对比、附近的经销商服务网点。这些内容组合到一起才是农机电商和普通电商的本质区别。后端返回详情页的数据时用ProductDetailVO聚合多个来源的数据基本信息查商品表规格参数查规格表图片查图片表视频查文件存储。一次接口调用把详情页需要的所有数据返回给前端避免前端多次请求增加延迟。4.3 订单流程从购物车到确认收货的状态机设计订单模块的核心不是写一个下单接口而是把整个状态流转设计清楚。我把流程拆成四个步骤每一步都有明确的校验逻辑和异常处理。第一步创建订单。用户从购物车勾选商品点击结算后端接收一个orderCreateRequest包含地址ID、商品SKU列表、备注信息。后端要做的事校验用户登录状态、校验商品是否存在且上架、校验库存是否充足、计算订单金额SKU单价乘以数量累加加上运费、生成订单号和订单明细、扣减库存。这些操作必须在同一个事务中执行任何一个环节失败都回滚。库存扣减这里有个经典的坑。如果在Java代码里先查出库存判断够不够再执行UPDATE扣减高并发下会出现超卖。正确做法是用一条带条件的SQL一次完成“判断扣减”UPDATE product_sku SET stock stock - #{count} WHERE id #{skuId} AND stock #{count}如果受影响行数为0说明库存不足下单失败。这个写法利用数据库的行级锁天然解决了并发问题不用引入分布式锁那么重的方案。第二步支付处理。创建订单后用户发起支付。流程是后端生成支付参数金额、订单号、商品描述、回调地址调用微信/支付宝的统一下单接口返回支付二维码给前端。用户扫码支付完成后第三方服务器异步回调后端接口后端验证签名、校验金额、更新订单状态为“已支付”、写入支付流水表。这一步开发时如果不想真的对接第三方支付可以用一个模拟支付的开关。在测试环境中提供一个接口模拟支付成功并触发回调方便联调和演示。但代码结构上要保证接入真实支付时改动最小——把支付逻辑封装成一个PayService接口真实的微信支付和模拟支付分别实现这个接口通过配置项切换。第三步发货与物流。这个环节农机电商有自己的特殊性——发货方可能是平台仓或者当地经销商。订单的delivery_type字段支持两种平台直发和经销商配送。后台管理员创建物流单填写物流公司和运单号订单状态变更为“待收货”。用户端可以查看物流进度物流信息可以对接快递100的免费接口也可以先用模拟数据。第四步确认收货与售后。用户确认收货后订单完成。然后进入售后服务闭环用户提交售后工单选择订单、填写问题描述、上传照片系统自动匹配该农机品牌最近的经销商经销商在后台接单、上门检修、填写维修结果用户确认服务完成并评价。这个功能是农业设备平台区别于普通电商的特色亮点答辩时可以重点展示。4.4 文件存储MinIO在商品图片视频场景的接入农机商品的图片和视频文件普遍偏大一台收割机的实拍视频动辄几百MB用Nginx直接托管静态文件既占磁盘又不安全。我选择用MinIO做对象存储原因前面说过开源、免费、兼容S3协议。接入MinIO的核心配置很简单minio: endpoint: http://localhost:9000 access-key: minioadmin secret-key: minioadmin bucket: agri-device上传文件的代码逻辑是前端把文件POST到后端接口后端接收MultipartFile生成唯一的文件名称UUID 原始文件名后缀防止文件名冲突和路径穿越调用MinIO的putObject方法上传返回访问URL。下载和预览直接通过MinIO生成的预签名URL完成。存文件时有个注意点文件名一定不能直接用用户上传的原始文件名防止路径穿越攻击和重复文件覆盖。我习惯用UUID 扩展名的格式做存储名原始文件名存到数据库的original_name字段展示时前端用这个字段显示。图片处理方面如果图片太大可以考虑在MinIO旁边加一层Thumbnailator做缩略图列表页显示小图详情页加载原图。不过做课程设计的话不是必须可以根据时间取舍。5. 项目落地过程中的高频Bug与排查技巧这一节是根据我和团队实际踩过的坑总结出来的网上教程基本不会告诉你这些。每一条都是我亲眼见过或亲手解决过的问题希望能帮你省掉几个通宵排查的时间。5.1 数据库连接池和时区问题问题一连接池耗尽导致接口假死。项目跑一段时间后接口突然全部超时Tomcat线程池等待数据库连接数据库连接池报HikariPool-1 - Connection is not available, request timed out after 30000ms。排查过程是这样的先在Navicat里执行SHOW PROCESSLIST看数据库连接状态发现大量连接处于sleep状态。再看代码发现有一个查询商品的Service方法里先查询了商品信息又在一个循环里查询每个SKU的库存导致执行特别慢。解决办法是改成一次性联表查询用一个IN查询把所有SKU库存查出来避免循环查询数据库。同时也把Hikari连接池的最大连接数从默认的10提高到20。问题二数据库时区导致时间字段差8小时。插入的时间戳比实际时间晚了8个小时看起来像是“时光倒流”。这是因为MySQL连接串里没有配置时区参数。解决办法是在数据库连接URL里加上serverTimezoneAsia/Shanghai同时在MySQL配置里设置default-time-zone 8:00。5.2 MyBatis-Plus的常见使用陷阱问题一使用selectById查不到数据。排查发现主键策略是IdType.AUTO但数据库表的主键字段名是order_id而实体类的字段名是idMyBatis-Plus默认把id映射到主键字段导致查询时SQL生成的是WHERE order_id ...而不是WHERE id ...。解决方法是给实体类的主键字段加TableId(value order_id, type IdType.AUTO)。问题二updateById更新不生效。这是MyBatis-Plus的经典陷阱——默认策略是FieldStrategy.NOT_NULL也就是实体类里为null的字段不会参与更新。如果想把某个字段更新为null比如清空备注直接传null是无效的。解决办法是在实体类字段上加TableField(updateStrategy FieldStrategy.IGNORED)或者使用UpdateWrapper显式指定set字段。问题三逻辑删除和唯一索引冲突。我给用户表加了逻辑删除字段deleted同时给手机号字段建了唯一索引。用户注销后再次用同一个手机号注册会报唯一索引冲突因为逻辑删除的记录还在表里。解决方案有两个一是把唯一索引改成联合唯一索引(phone, deleted)但这样同一个人只能注销一次二是不用逻辑删除改用物理删除或者迁移到历史表。做课程设计的话推荐直接物理删除或定期清理逻辑删除的数据省心。5.3 Lombok和JSON序列化的问题用Lombok的Data注解时很多人会忽略一个细节如果实体类里自定义了getter方法Lombok不会覆盖它。比如我写了一个getStatusDesc()方法返回订单状态的中文描述但Lombok已经生成了getStatus()方法两者不影响。然而如果一个字段叫isHotLombok生成的getter是getIsHot()但JSON序列化时会有兼容性问题。更常见的一个问题是循环引用。商品实体类里引用了分类实体分类实体又引用了商品列表JSON序列化时StackOverflowError。解决办法是在实体的ManyToOne或OneToMany关系中加JsonIgnore注解或者在DTO/VO中手动组装避免实体类直接序列化。5.4 跨域问题前后端分离项目的必踩之坑前后端分离开发时前端跑在http://localhost:5173后端跑在http://localhost:8080端口不同必然触发跨域。浏览器控制台报Access-Control-Allow-Origin错误。我的处理办法是在后端定义一个全局配置类实现WebMvcConfigurer重写addCorsMappings方法Configuration public class CorsConfig implements WebMvcConfigurer { Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping(/**) .allowedOriginPatterns(*) .allowedMethods(GET, POST, PUT, DELETE, OPTIONS) .allowedHeaders(*) .allowCredentials(true) .maxAge(3600); } }这里有个容易踩的坑如果用了Spring Security跨域配置不仅要配WebMvcConfigurer还要在Spring Security的配置里放行OPTIONS请求否则预检请求根本到不了Controller层。另外一个实用建议是生产环境部署时用Nginx反向代理把/api/前缀的请求转发到后端这样前端和后端就是同源的跨域问题彻底消失。开发环境中跨域只是暂时的痛点不用无脑加一堆CrossOrigin注解。5.5 支付回调幂等与重复通知处理支付回调的幂等性前面在流水表设计时提过这里说具体实现。收到支付回调后第一步不是直接更新订单状态而是先做两件事验签和查流水。验签的目的是确认这个回调确实是微信/支付宝服务器发来的不是伪造的。用第三方SDK提供的验签工具类传入回调参数和签名返回布尔值。验签失败直接返回错误第三方会重新通知。验签通过后查支付流水表根据out_trade_no商户订单号查流水。如果流水存在且状态是“支付成功”说明已经处理过这个回调了直接返回成功响应不重复处理。如果流水不存在或者状态是“待支付”才执行后续的订单状态更新和流水记录插入。这个逻辑用伪代码表示就是if 验证签名失败: return 错误 if 根据out_trade_no查到流水记录且状态为成功: return 成功 // 幂等判断避免重复处理 更新订单状态为已支付 插入支付流水记录 更新商品销量 return 成功注意更新订单状态和插入流水记录要在同一个事务里避免订单已更新但流水没记录或者反过来流水有了订单状态没变的情况。我当时在这个问题上就吃过亏订单状态已更新但流水没写成功导致用户重复支付时系统判断不了。6. 部署上线与性能调优的实战经验代码写完了项目怎么跑起来、怎么给别人演示这是一个经常被忽略但实际非常重要的问题。很多同学在开发环境跑得好好的一到部署就各种问题根源在于开发和生产环境的差异。6.1 项目打包与部署Spring Boot项目最终交付就是一个可直接运行的Jar包这是它作为单体应用最大的便利。部署步骤很简单第一步在application-prod.yml中配置生产环境的数据库、Redis、MinIO地址确保不是连接本地的localhost。第二步执行mvn clean package -DskipTests在target目录下生成Jar包。这一步注意设置好JVM参数和打包时的finalName方便后续运维识别。第三步在服务器上运行nohup java -jar agri-device-platform.jar --spring.profiles.activeprod app.log 21 Spring Boot内嵌的Tomcat自动启动无需额外安装容器。这里有个实际经验如果项目里用了前端页面把前端打包后的dist目录内容复制到src/main/resources/static下再重新打包这样前端后端就是一个Jar包部署时只传一个文件即可避免单独部署前端服务器。6.2 性能瓶颈排查与优化点课程设计或毕业设计阶段的系统性能瓶颈通常不在代码逻辑而在数据库设计和缓存使用上。我从实际项目中总结几个值得优化的点热点数据缓存。首页的商品分类、轮播图、热销商品Top10这些数据每次用户刷新首页都会被查询非常浪费数据库性能。在Redis中缓存一份设置10分钟过期时间能有效降低数据库负载。实现上用Spring Cache的Cacheable注解配合Redis做二级缓存比手动写Redis操作代码简洁得多。列表查询的分页优化。商品列表页的分页查询要避免SELECT *只查需要的字段避免在LIKE查询时使用%关键词%这种前置模糊匹配无法走索引数据量大时全表扫描。如果确实需要全文检索可以用ngram全文索引替代。慢SQL日志监控。在MyBatis-Plus中开启慢SQL输出mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl开发阶段打开可以看到每条SQL的执行参数便于排查问题。生产环境建议关闭避免日志量过大。6.3 项目演示与答辩准备的几个加分项项目做完之后怎么在答辩或汇报中把亮点讲清楚。技术类项目答辩老师最常问的问题无非是“这个系统的核心难点是什么”“你是怎么解决的”“有没有考虑过并发场景”“数据库为什么这样设计”。提前把这些问题想好答辩就不慌了。我建议准备这三个加分项第一做一个包含核心流程的演示脚本。从用户注册登录开始到浏览商品、加购物车、下单支付、查看订单、申请售后一气呵成走通整个链路。每一步之间展示数据库的变化比如订单表多了一条记录、库存扣减、支付流水更新这个演示效果远胜于只展示页面。第二准备一个“如果并发量变高怎么办”的方案。哪怕实际没实现也要能说出思路。比如数据库层面加索引、Redis缓存热点数据、库存扣减用乐观锁、支付回调做幂等处理、将来可以升级为微服务分库分表。能把这些点说出来老师就知道你是真的理解系统而不是只会调用框架。第三项目目录结构要清晰。按照controller/service/mapper/entity/config/common分包类名规范注释到位。技术答辩老师会打开你的代码看一个好的工程结构会留下很好的第一印象。我见过不少项目功能实现了但整个项目所有类都堆在一个包下面看起来就掉价。写在最后这个题目做到最后我最大的体会是Spring Boot本身并不是难点真正的难点在于把业务逻辑搞清楚之后再动手写代码。一个农业设备销售平台如果你能讲清楚农机商品和普通商品的区别在哪里、订单流程为什么要多一个“付尾款”的环节、售后服务和经销商的协作关系怎么处理你的项目就已经超过大多数同类选题了。再分享一个小的实用技巧开发过程中多用Git做版本管理每完成一个功能模块就提交一次哪怕只有你自己看。项目到了最后几天这个习惯能救你很多次——改坏了代码可以回滚写错了逻辑可以找回之前的版本晚上熬夜写完的代码草稿也有地方存。技术选型上Spring Boot MyBatis-Plus Redis MinIO这套组合做这个体量的项目游刃有余该有的都有了不需要更多。如果时间充裕可以考虑给项目加一个简单的数据统计看板用于展示销售趋势和热门品类这会让项目的完整度提升一个档次。