每年到这个时间点,总会有一批同学盯着“计算机毕业设计选题”发愁。今年问我最多的一个题目是“基于SpringBoot的闲置物品循环利用管理系统”,有的学校叫“旧物回收商城”,也有叫“二手物品交易与捐赠平台”。名字好几个,内核其实就一个:让用户把手里闲置的东西挂出来,给别人买走,或者直接捐出去,平台负责撮合、记录和审核。
这个题目属于典型的SpringBoot全栈项目,技术栈不偏门、业务链路清晰、可发挥的点多,不管是毕业设计、课程设计还是自己练手,都值得认真做一遍。这篇文章我从选题拆解、功能规划、数据库设计、核心流程实现,到开发中容易踩的坑、答辩常见提问,一次性讲透。
1. 项目定位与核心需求拆解
1.1 这个系统到底要解决什么问题
先想清楚一点:你这个题目不是要做成电商平台,也不是要做成闲鱼式的大型C2C社区。它的核心定位是“校园级/社区级的闲置物品循环利用平台”,用户量不大,业务链路要完整,功能不必冗余到支付分账、物流跟踪、信用体系那一层。
被这个题目卡住的同学,大多卡在同一个地方——不知道“二手交易”和“捐赠”两条线怎么在同一个系统里组织。这里先把业务模型说清楚:
- 普通商品交易链路:用户发布闲置商品 → 审核上架 → 买家浏览下单 → 模拟支付 → 卖家发货 → 买家确认收货 → 订单完成。
- 捐赠物资链路:用户发布捐赠物品 → 审核公示 → 有需要的人提出申请 → 发布者确认 → 线下自提或平台虚拟交付 → 生成捐赠记录。
两条链路共用一套用户体系、商品发布体系、后台审核体系,但是订单状态和物资流转方式完全不同。这个“两线并行”的设计如果一开始想明白了,后面数据库和接口设计都会顺畅很多。
1.2 功能模块的边界划分
建议按角色拆,别按“交易”和“捐赠”拆,不然代码结构会乱。角色就三个:普通用户、管理员、游客(未登录用户)。游客只能浏览,注册登录后才能操作。
| 端侧 | 功能点 | 说明 |
|---|---|---|
| 用户端 | 注册登录、个人中心 | 用户名密码,不要搞复杂登录 |
| 用户端 | 商品发布、编辑、下架 | 支持图片上传、分类选择、成色描述 |
| 用户端 | 商品搜索、分类筛选 | 按关键词、分类、价格区间筛选 |
| 用户端 | 商品详情、收藏 | 方便二次浏览 |
| 用户端 | 下单购买 | 地址选择、模拟支付 |
| 用户端 | 发布捐赠、申请领取 | 申请需填写用途说明 |
| 用户端 | 订单管理、捐赠记录 | 查看自己买到的、卖出的、捐出的 |
| 管理端 | 用户管理 | 禁用/启用账号 |
| 管理端 | 商品审核 | 审核上架、强制下架 |
| 管理端 | 捐赠审核 | 审核捐赠申请、确认完成 |
| 管理端 | 数据统计 | 商品数量、订单量、捐赠数量、用户增长 |
功能不在多,在链路完整。你能把交易和捐赠两条链路各跑通一遍,再把后台审核串起来,这个系统就已经超过大多数选了类似题目的同学了。
1.3 哪些功能建议做成亮点
毕设想拿高分,不能只做CRUD四个字母。建议在常规功能里突出几个点:
第一,商品状态机设计。一个商品从发布到完成交易,状态会经历“待审核 → 上架中 → 已被锁定 → 已售出”,或者“上架中 → 已下架”。很多同学用字符串存状态,状态一多就乱。我建议用int枚举值,配合一个状态流转工具类,代码干净,答辨时也讲得清楚。
第二,捐赠公示功能。捐赠完成后,在首页或者独立页面展示“xxx于x月x日捐赠了xx物品,由xx签收”。这一步看似简单,却能体现你的业务思考——公益行为需要公开透明,这在系统设计里是有真实价值的。
第三,绿色积分或环保值。每完成一笔闲置交易或捐赠,给用户累加积分,个人中心显示累计减少碳排放或节约资源的数据。不一定精确,但作为激励用户使用的机制,答辩时是亮点。
2. 技术选型:为什么是SpringBoot而不是SSH/SSM
2.1 后端选型分析
现在毕设环境里,SpringBoot已经是事实标准,为什么?最核心的原因就是“开箱即用”。以前用SSM,光是Spring、SpringMVC、MyBatis三个框架的xml配置文件就要写一大堆,版本兼容问题能把人磨掉一层皮。SpringBoot用自动配置把这些问题全部压下去,你只需要专注写业务代码。
如果你是在校学生,对这点的理解可以再进一步:SpringBoot默认集成内嵌Tomcat,项目打包成jar就能跑。演示的时候在答辩教室的电脑上,只要装了JDK,一行命令就能启动,不需要额外配置外部Tomcat,这对毕设演示是一个巨大的优势。换成SSH框架,老项目依赖一堆XML配置,换个环境还得调JDK版本、Tomcat版本,出问题以后现场很难收场。
2.2 前端与前后端分离
前端我推荐Vue 3 + Element Plus + Vite。Element Plus本身是现成的后台管理组件库,一套页面搭起来非常快。你要是对Vue不熟,就用Vue 2也行,但既然是新项目,建议直接用Vue 3,版本新一点,简历上写出去也不显得陈旧。
前后端分离以后,开发模式是:后端只提供JSON接口,前端通过axios调用。这里有一个常见误区,很多同学一开始喜欢用模板引擎写服务端渲染页面,比如直接用Thymeleaf把页面和后端耦合在一起。不是说不能用,而是如果做毕设,前后端分离会让你的项目结构更清晰,方便你分模块写论文。答辩老师看见你有独立的前端工程目录、有接口统一封装,会觉得你具备工程化意识。
接口路径建议统一以/api开头,例如/api/product/list、/api/order/create。这样在nginx部署或后端统一拦截权限时,一个前缀就能覆盖所有需要鉴权的接口。
2.3 存储与中间件的取舍
数据库用MySQL,这个不用多考虑。ORM层建议用MyBatis-Plus,不是纯粹的原生MyBatis。原因很简单:MyBatis-Plus有现成的BaseMapper、LambdaQueryWrapper、分页插件,单表查询基本不用写SQL,可以省下大量时间。除非你的查询特别复杂,否则尽量别写一堆XML里的动态SQL。
文件存储这里要慎重。如果你选云存储,比如阿里云OSS,需要注册账号、创建Bucket、配置密钥,对于很多同学来说,卡在开通流程上就能耗掉一整天。我建议本地存储起步:后端配置一个上传目录,把文件流写到磁盘,再通过一个/upload/文件名的映射路径对外访问。后期如果项目要部署上线,再替换成云存储也不难。
Redis不是必选项。如果你已经会用Redis,可以在首页轮播图、商品分类、热点商品上加缓存;如果不会,也不用强行加。不要为了显得高级,把Session存到Redis里,却连过期策略都说不清楚,答辩时反而减分。
2.4 版本锁定小技巧
版本问题在毕设项目里最容易爆炸。SpringBoot 2.7.x和SpringBoot 3.x之间,很多东西不兼容,比如JDK版本要求、javax与jakarta命名空间、Spring Security的配置方式。
我的建议很明确:如果你JDK是8或11,就选SpringBoot 2.7.x,配MyBatis-Plus 3.5.x、MySQL 8.x;如果你JDK是17及以上,可以选SpringBoot 3.x,但要确认MyBatis-Plus版本支持SpringBoot3。不要混合用,不要随手复制网上的依赖版本,最好用Spring Initializr直接生成基础工程,再往里面加依赖。毕业设计不是为了演示“最新版本”,而是要稳定跑通。
| 组件 | 推荐版本 | 备注 |
|---|---|---|
| Spring Boot | 2.7.18 | 稳定,支持JDK8 |
| MyBatis-Plus | 3.5.3+ | 对应SpringBoot2 |
| MySQL | 8.0+ | 8.0以上默认utf8mb4 |
| Vue | 3.x | 配Element Plus |
| JDK | 1.8或11 | 2.x版本最高支持到17 |
3. 数据库设计与业务表结构
3.1 表结构怎么梳理
数据库设计是整个项目的根基,后面接口写得好不好、逻辑顺不顺,全看这一步。以这个系统为例,建议先按“实体-关系”的思路思考:
有哪些核心实体?用户、地址、商品、商品图片、商品分类、订单、订单明细、收藏、捐赠、捐赠申请、系统公告、审核记录。
这些实体之间的典型关系是:
- 用户与商品:一对多,一个用户可以发布多个商品
- 商品与分类:多对一,一个分类下有多个商品
- 商品与图片:一对多,一个商品多张图
- 用户与订单:一对多,一个用户可买可卖
- 商品与订单:通过订单明细建立关联
- 用户与收藏:多对多,通过收藏表体现
建议至少建下面这些表:
sys_user 用户表 sys_address 地址表(下单时用) category 商品分类表 product 商品表 product_picture 商品图片表 orders 订单主表 order_item 订单明细表 favorite 收藏表 donation 捐赠物品表 donation_apply 捐赠申请表 message 站内信/留言表3.2 关键字段设计经验
这里挑几个容易设计出错的关键字段重点说。
第一个是金额字段。交易涉及的钱一律用DECIMAL(10,2),不要用double。二进制浮点数在做金额计算时会出精度问题,虽然平时测试可能看不出来,但一旦遇到校验,给用户的金额显示就会出现0.30000000000000004这种恶心结果。用decimal,后端Java实体对应BigDecimal,前端展示时再格式化两位小数。
第二个是状态字段。状态类型统一用TINYINT或INT,不要用VARCHAR存“已上架”“已下架”这种中文。用int的好处是状态流转时可以直接比较数字,而且以后扩展状态不涉及改表结构。比如商品状态:
0-待审核 1-上架中 2-已被锁定 3-已售出 4-已下架 5-审核拒绝订单状态同理:
0-待支付 1-已支付待发货 2-已发货 3-已完成 4-已取消 5-退款中第三是时间字段。创建时间、更新时间这种,用create_time、update_time统一命名,数据库类型datetime,插入和更新时在后端代码里统一填充,或者用MyBatis-Plus的自动填充注解@TableField(fill = FieldFill.INSERT)。千万别在数据库里建一堆time1、time2这种无意义字段名。
第四是逻辑删除。不要物理删除用户发布过的商品,用deleted字段标记,0表示正常,1表示已删除。这是因为订单、收藏、统计报表里都可能引用商品信息,物理删除会把关联数据搞成孤儿。
3.3 一段核心建表SQL示例
商品表product的设计如下:
CREATE TABLE `product` ( `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', `user_id` BIGINT NOT NULL COMMENT '发布者ID', `category_id` BIGINT DEFAULT NULL COMMENT '分类ID', `title` VARCHAR(100) NOT NULL COMMENT '标题', `description` TEXT COMMENT '物品描述', `price` DECIMAL(10,2) DEFAULT NULL COMMENT '售价,捐赠物品为NULL', `is_donation` TINYINT DEFAULT 0 COMMENT '0-普通出售 1-捐赠', `status` TINYINT DEFAULT 0 COMMENT '状态:0待审核 1上架 2锁定 3售出 4下架 5拒绝', `cover_image` VARCHAR(255) DEFAULT NULL COMMENT '封面图', `view_count` INT DEFAULT 0 COMMENT '浏览量', `version` INT DEFAULT 0 COMMENT '乐观锁版本号', `deleted` TINYINT DEFAULT 0 COMMENT '逻辑删除', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_user_id` (`user_id`), KEY `idx_category_id` (`category_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='闲置物品表';这个表里比较大的亮点是is_donation字段与version字段。前者把“普通交易”和“捐赠”两种模式并到一张表,用字段区分而不是拆两张表,逻辑集中、代码好写。后者是给乐观锁预留的,防止两个用户同时在秒杀场景下买到同一件仅剩一件的宝贝。
3.4 订单与商品的关系别搞复杂
很多同学喜欢把所有信息都塞进订单表,比如订单里存商品标题、商品图片、卖家ID、买家ID、地址、金额、备注。这样做很爽,因为查询订单时不用关联其他表,但代价是数据冗余严重,商品信息改了以后旧订单里的商品标题还是老的,显示不一致。
正确做法是订单主表只存业务级信息:订单号、买家ID、卖家ID、总金额、状态、创建时间、支付时间。具体买了什么东西拆到订单明细表里,明细表存商品ID、商品快照标题、商品快照图片、单价、数量。为什么强调“快照”?因为商品下架或改价后,订单里的历史信息不应该跟着变,明细表里冗余一份当时的标题和图片反而是一种合理的做法。
4. 核心流程实现:二手交易与捐赠双链路
4.1 商品发布到上架
商品发布接口建议设计成两个阶段:前端先上传图片拿到图片路径,再把表单数据和图片路径一并提交到后端,最后数据库里保存商品记录和图片记录。
用户在前端点“发布闲置”后,要填标题、分类、成色、价格、描述、上传图片。这里最基本的校验包括:标题不能为空、长度不能超过100;价格如果是出售物品,必须大于0;图片必须至少一张。
后端Controller接收ProductDTO之后,Service层的步骤是:
- 把DTO里的字段赋值到Product实体
- 设置
userId为当前登录用户ID - 设置
status=0表示待审核 - 插入商品主表
- 遍历图片列表,逐一插入
product_picture表 - 返回商品ID
写这个流程的时候注意一件事:不要在前端把状态直接写成“上架”,状态必须由后端管理员审核后才置为1。有些同学偷懒,发布后直接上架,审核机制整个没了,那商品审核模块就白做了。
4.2 买家下单与支付状态设计
用户点击“立即购买”时,前端跳转创建订单页,选择地址,确认金额后提交。后端创建订单的核心逻辑如下:
- 根据
productId查商品,校验商品状态是否为1上架中 - 不是捐赠物品,校验价格并计算总金额
- 使用乐观锁或状态条件更新:
UPDATE product SET status = 2, version = version + 1 WHERE id = ? AND status = 1 - 如果更新影响行数为0,说明商品被抢先下单或已下架,直接返回“商品已售出或已锁定”
- 创建订单主表记录,状态为0(待支付)
- 创建订单明细表
这里必须强调一个关键设计:商品状态从1变为2,叫做“锁单”。如果你不做锁定,支付过程中商品再次被别人下单,就会一物多卖。把状态更新放到一条SQL里完成,天然能防止并发超卖,比在应用层if(product.getStatus()==1)判断安全得多。
模拟支付怎么做?不要真的去接微信支付或支付宝,申请商户号对于毕设来说不现实,而且涉及营业执照。建议在支付接口里做一个“模拟支付成功”的路程:前端点击支付按钮,调用/api/order/pay,后端验证订单属于当前用户且状态为0,然后把订单状态改为1,记录支付时间。也可以在页面上做一个小弹窗“确认支付?本系统为模拟支付”,看起来更真实。
订单超时未支付怎么办?如果没做定时任务,一般就让它挂着,演示时手动取消。但我建议做一个简单的延时释放:用Spring自带的@Scheduled定时任务,每分钟扫描一次待支付订单,超过30分钟未支付就把订单置为取消,同时把对应商品状态从2改回1。加了这个功能,商品释放的逻辑就完整了。
4.3 捐赠流程怎么闭环
捐赠是区分这个项目和普通商城的关键模块。业务上怎么设计呢?
第一步,用户发布捐赠物品时,前端多一个“选择捐赠”的开关。选中后价格字段隐藏,描述里建议填物品使用情况、适合人群。数据库里is_donation=1,price=NULL,状态同样先走待审核。
第二步,审核通过后,商品在列表中展示,但详情页不显示“购买”按钮,而是显示“申请领取”按钮。申请领取时,需要填申请理由或用途说明——这一点千万要保留。真实场景里,公益物资要让给真正需要的人,而不是谁手快谁拿走。
第三步,管理员在后台能看到捐赠申请列表,结合申请理由,可以选择“批准申请”或“拒绝申请”。同时发布者也能在自己的捐赠记录里看到谁申请了,双方都可以确认。比较简单的做法是:管理员批准后,订单状态直接进入“待领取”或“已确认”,然后由发布者线下交付物品,再在系统里确认“已完成”。
第四步,捐赠完成后,生成一条公开的捐赠记录,展示捐赠物品名称、捐赠时间、领取人昵称(脱敏处理,不要展示真实姓名和手机号)。这条记录放在首页“捐赠公示”模块,让整个链路形成闭环,业务上说得通,答辩时也比较有料。
捐赠流程的核心代码不会比普通交易复杂,但很容易被做成“只有发布和删除”的半成品。如果你能把捐赠的“申请-审核-公示”三段式流程完整实现,这个项目的完成度立刻就和别人拉开差距。
4.4 商品详情接口要同时返回哪些内容
商品详情页是核心聚合页面,后端接口建议一次返回完,减少前端多次请求。返回值可以包含:
- 商品基本信息(标题、价格、状态、是否捐赠、描述、成色)
- 图片列表
- 发布者信息(昵称、信用积分、发布总数)
- 当前登录用户是否已收藏该商品
- 如果是捐赠物品,是否有待审核的申请记录
Service层组合数据时,用Map或专门写一个ProductDetailVO,别把Entity直接返回给前端。因为Entity里可能有userId这种敏感字段,你直接返回,前端即使不显示,抓包也能看到,看着就不专业。
5. 权限、安全与后台管理
5.1 角色权限设计
后台管理的账号也放进用户表,加一个role字段,用0-普通用户 1-管理员区分,不用额外建一张角色表。如果你后续想扩展多角色(比如运营、审核员),再加sys_role、sys_user_role表也不迟。
校验方式可以非常简单:前端在路由守卫里根据当前用户的role判断能否进入管理端页面;后端在每个管理接口上校验当前登录用户的role是否为1。不要只用前端隐藏按钮来防越权,安全设计的底线是后端校验。
还有一类越权特别容易被忽略:普通用户把请求地址里的订单ID改掉,去查看别人的订单详情。后端查询订单时,必须判断订单的buyerId或sellerId是不是当前登录用户,不是就返回无权限。这个逻辑写在Service里,别只靠前端权限。
5.2 登录状态与Token方案
很多老教程还是Session+Redis的登录方案,但实际开发中用JWT更常见。JWT本质上是一段自包含的加密令牌,用户登录成功后,后端返回一个token,前端存到localStorage里,之后每次请求放在请求头Authorization: Bearer <token>中。
后端写一个拦截器,对所有以/api/开头、但不是/api/user/login、/api/user/register等公开接口的请求进行token校验。校验通过后,从token中解析出用户ID,放到ThreadLocal或者请求属性里,方便Service取用。
JWT方案也有坑:token过期后,前端所有的接口都会报401,用户体验不好。建议token有效期设为一个较长的时间,比如7天,或者做refresh_token机制。毕设没必要做得太重,设7天并提示“重新登录”即可。
5.3 密码存储不能明文
所有系统都绕不开密码安全,这个点几乎是答辩必问。直接存明文密码是最低级的做法。用BCrypt哈希处理,Spring Security的BCryptPasswordEncoder可以直接用。BCrypt的特点是同一密码每次加密结果不同,因为带随机盐,数据库被拖走后也无法反向解析。
注册接口的流程是:前端把用户名密码传到后端,后端使用BCryptPasswordEncoder.encode()加密后存库。登录时取出数据库里的哈希值,调用.matches(rawPassword, encodedPassword)校验。
因为JWT方案不需要依赖Spring Security的完整过滤器链,你可以只引入spring-security-crypto包用它的加密工具,不需要让整套Spring Security接管你的接口,降低配置复杂度。
5.4 管理端审核链路怎么设计
后台管理端的商品审核页要支持列表和详情预览。列表建议分页展示所有status=0的商品。点击“审核通过”后,将status改为1;点击“拒绝”后,填写拒绝原因,状态改为5。
这里我建议加一个audit_record表,记录谁在什么时候审核了哪个商品、结果是什么、备注是什么。这样做的好处有两个:一是答辩时有一张能讲清楚的“链路表”,体现你的系统考虑到了可追溯性;二是商品被举报或纠纷时,后台有据可查。
审核记录表字段可以简单一点:target_type表示对象类型(商品/捐赠),target_id表示对象ID,user_id审核人,result结果,remark备注,create_time时间。
6. 实际编码过程中的避坑点
6.1 表名与字段名别踩保留字
这个坑几乎每年都有人踩:用户表想命名为user,订单表想命名为order。但在MySQL里,USER是系统表相关关键字,ORDER也是SQL关键字,直接使用会报错,或者需要不停地加反引号,非常痛苦。
解决方法是表名统一加业务前缀,比如sys_user、ord_orders,或者直接用t_user、t_order这种命名风格。字段同理,像desc、level、condition这些词,能避开就避开,改用description、user_level,避免和SQL关键字冲突。
6.2 MyBatis-Plus的更新策略坑
用MyBatis-Plus时,实体字段默认更新策略是对非空字段进行更新。也就是说,前端传一个Product对象,如果某个字段没填,它就认为你不想更新这个字段,不会把原来的值覆盖成NULL。
这个机制在大多数时候是好事,但如果你希望把某个字段设置为空或0,就会更新不上去。比如要清空备注信息,setRemarks(null)后调用updateById,数据库里的值没变。
解决的方法是,在这些字段上加上注解@TableField(updateStrategy = FieldStrategy.NOT_NULL),默认策略保持非空更新,但特殊的字段可以单独覆盖。或者也可以直接用UpdateWrapper手动设置字段:
LambdaUpdateWrapper<Product> wrapper = new LambdaUpdateWrapper<>(); wrapper.eq(Product::getId, productId) .set(Product::getStatus, 4) .set(Product::getUpdateTime, new Date()); productMapper.update(null, wrapper);6.3 日期格式返回给前端变成一串数字
后端实体里的LocalDateTime,如果不配置序列化格式,前端拿到的JSON可能是一串数字,比如1681361234567,或者是2024-01-08T12:00:00这种带T的格式。
在SpringBoot里加一个全局格式配置:
@Configuration public class JacksonConfig { @Bean public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() { return builder -> { builder.simpleDateFormat("yyyy-MM-dd HH:mm:ss"); builder.serializers(new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); builder.deserializers(new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); }; } }这样前后端时间格式统一,省去一联调就发现时间对不上的麻烦。
6.4 本地文件上传的路径问题
本地存储图片时,最容易被坑的是路径,尤其是Windows和Linux路径分隔符不同。建议配置文件中定义上传根目录,代码里用Paths.get(uploadPath).toAbsolutePath().normalize()处理,别直接写死D:\\upload这种路径。
上传文件时,文件名一定要重新生成,用UUID或System.currentTimeMillis()加随机数,再加原文件扩展名。不要直接使用用户上传的原始文件名,否则重名文件会互相覆盖,还有路径穿越攻击的风险。
访问图片时,后端要写一个目录映射:
@Configuration public class WebConfig implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/upload/**") .addResourceLocations("file:" + uploadPath + "/"); } }6.5 前后端联调时的跨域问题
前后端分离以后,前端是8080端口,后端是8081或9090端口,不同端口之间默认有跨域限制。浏览器请求后端会被CORS拦截。
解决方案有两个:一是在后端写CORS配置类,允许指定的前端地址跨域访问;二是在前端配置Vite的开发代理。我更推荐第二种,生产部署时也可以把前端的静态文件放到nginx,再由nginx把/api下的请求反向代理到后端,这样整个项目就没有跨域问题。
Vite代理配置大概是这样:
export default defineConfig({ server: { port: 3000, proxy: { '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })6.6 分页查询的多个条件同时生效
列表接口一般都有分页和筛选。拿商品列表举例,可以传入keyword、categoryId、minPrice、maxPrice、sortType。用MyBatis-Plus写分页查询时,最容易出现的问题是拼SQL时条件互相覆盖,或者全是空条件。
规范写法是:先把都非空的查询条件拼到LambdaQueryWrapper里,校验前先判断字符串是否为空、数值是否为null,然后再调用分页查询。分页插件配置时,一定要确认PaginationInnerInterceptor已经注册。不注册的话,分页查询返回的是全量数据,前端翻页永远只有第一页有数据。
6.7 商品状态与订单状态的一致性
交易流程中,商品状态和订单状态必须联动更新,这一块前文已经提到。但有一个边界案例要注意:商品下单锁定后,管理员能不能强制下架?如果管理员强制下架了商品,买家那笔待支付的订单怎么办?
我的建议是,如果商品存在未完成的订单,管理员只能“禁止继续销售”,不能直接改商品状态为下架。或者强制下架时,把待支付订单一起取消,并把商品释放。这个一致性逻辑写清楚,代码看起来会非常完整,答辩老师很可能追问这一层。
7. 项目扩展与评分提升思路
7.1 数据统计面板让项目质感上台阶
只做增删改查,系统看着很“平”。加一个数据统计面板,视觉上立刻有质感。用ECharts展示:每周新增商品趋势、商品分类占比饼图、订单金额按月统计、捐赠物资数量排行。
后端需要提供统计接口,比如按月份分组统计订单数:
SELECT DATE_FORMAT(create_time, '%Y-%m') AS month, COUNT(*) AS order_count, SUM(total_amount) AS total_amount FROM orders WHERE status = 3 GROUP BY DATE_FORMAT(create_time, '%Y-%m') ORDER BY month前端在管理端首页加载这些数据并绘制图表。不要小看这几张图表,它在演示环节能直观证明系统每时每刻产生的数据是有价值的。
7.2 定时任务扩展
SpringBoot里加定时任务非常简单,在启动类上加上@EnableScheduling,然后写一个类,用@Scheduled(cron = "0 0/5 * * * ?")标记方法即可。
可以做一个订单超时自动取消功能:每5分钟扫描一次,把所有待支付超过30分钟的订单取消,同时把关联商品状态从2重置回1。这个定时任务既解决业务问题,又体现你对系统健壮性的考虑。
7.3 Redis缓存提速
如果项目里已经集成了Redis,可以做两件事:一是缓存商品分类列表,因为分类大概率长时间不变;二是缓存首页热门的商品ID列表,每隔10分钟刷新一次。注意缓存更新的策略,商品上下架后要把旧缓存清掉,或者设置比较短的过期时间。
没有Redis也不要紧,用内存版ConcurrentHashMap写一个本地抽象缓存也能达到演示效果,只是压测时内存会偏高。
7.4 写毕业论文时怎么提炼价值
到论文阶段,很多同学不知道怎么把这个项目写得有厚度。我提供一个结构思路:
摘要里强调“构建闲置物品循环利用的信息化管理平台,形成二手物品交易和公益捐赠双闭环,促进资源节约与再利用”。关键词写“SpringBoot;闲置物品;循环利用;二手交易;捐赠管理”。
正文第一章绪论,背景写资源浪费问题和绿色低碳理念,不要扯大政策,就写校园里毕业生离校前大量物品被丢弃的现实场景;第二章需求分析,画用例图和功能图;第三章总体设计,画系统架构图、数据库ER图;第四章详细设计与实现,分模块贴类图和核心代码;第五章测试,写功能测试用例表,再补充几个接口测试结果。
最容易被忽略的是数据库设计说明,务必画出ER图并解释表关系。答辩老师看到ER图会很安心,因为你至少证明了自己不是只抄代码。
写在最后的实操体会
这个题目从选题到完成,我反复强调了“链路完整”四个字。很多同学做完一个项目,代码量很大,但仔细一看全是CRUD页面堆叠,用户、商品、订单互相之间的状态流转根本没打通。真正让人眼前一亮的项目,不在于用了多少新技术,而在于业务逻辑是不是严谨。你把商品从发布到交易、再从交易到评价的全链路走通,把捐赠从申请到公示的全链路走通,这个系统就已经超过绝大多数同选题项目了。
另外一个容易被忽略的是项目运行环境的统一。开发环境、演示环境建议保持一致,数据库脚本要可以用一套SQL一次性建好,配置文件里不要写死本机IP。尤其是数据库连接,如果最后要部署到服务器演示,密码、端口这些提前确认清楚,别到答辩前一天才去改配置。
最后想说,多花一点时间在边界条件的处理上。比如用户余额不足时下单会不会报错、商品已下架时还能不能加入购物车、捐赠物品被人申请后还能不能取消。这些细节才是区分“能跑”和“好项目”的分水岭。如果你现在正卡在这个项目上,按上面7个章节循序渐进推进,先做数据库设计,再写后端核心流程,最后补前端页面和统计图表,整个系统的完成度会非常可观。