去年帮计算机系的学弟做毕设,加上自己接了一个本地高校的校园创业小项目,前后花了2个月打磨了这套校园二手交易平台。从最开始的线下需求调研,到最终上线跑通完整交易流程,中间踩了无数新手容易掉的坑,也推翻过两次架构设计。
今天把这套系统完整的开发流程、架构设计、核心代码实现、安全方案和踩坑经验全部分享出来。不管是计算机专业的同学做毕设、Java新手练手全栈项目,还是想落地校园创业项目,这篇文章都能给你一套可直接复用、能跑通、不踩坑的完整方案。
先说明:这套系统没有盲目堆技术栈,全部采用Java生态最成熟、面试最常问、新手最容易上手的技术,SpringBoot+SSM为核心,兼顾了实用性和扩展性,哪怕你只学过Java基础和MySQL,跟着文章也能跑通整个项目。
一、先搞懂:校园二手交易平台的真实需求(别做无用功)
很多同学做这类项目,上来就直接堆功能、写代码,最后做出来的东西就是个简化版淘宝,完全脱离校园场景的真实需求。我在开发前,跑了3所高校,找了100多个学生做调研,最终梳理出最核心的需求,也是这套系统的设计核心:
1. 核心痛点(校园场景专属)
- 学生闲置物品多(教材、电子产品、生活用品),但闲鱼、转转这类平台太杂,同城交易距离远,学生更信任同校当面交易,安全系数高
- 传统校园表白墙、QQ群发布二手信息,信息杂乱无章,无法检索、无法担保交易,很容易出现诈骗、货不对板的问题
- 没有统一的审核机制,违规商品、校外商家混进来,学生权益无法保障
- 缺少实时沟通渠道,交易沟通全靠评论区,效率极低
2. 功能模块划分(贴合校园场景,拒绝冗余)
我们把系统分为**用户端(学生)和管理端(管理员)**两大模块,只保留校园场景最核心的功能,不做花里胡哨的冗余设计:
| 用户端 | 校园统一认证、商品发布与分类浏览、关键词检索、商品收藏、实时私信聊天、订单担保交易、评价管理、违规举报、地址管理 |
| 管理端 | 用户管理、商品审核、订单监管、举报处理、轮播图管理、分类管理、平台数据统计、敏感词过滤 |
3. 非功能需求(毕设加分项,生产必备)
- 响应速度:商品列表、搜索接口响应时间不超过200ms,用Redis做热点数据缓存
- 安全性:用户密码加密、XSS/CSRF防护、接口权限控制、文件上传校验,避免恶意攻击
- 可用性:保证7*24小时稳定运行,订单支付、状态流转不丢数据
- 兼容性:适配PC端和移动端H5,学生用手机就能完成全部交易流程
二、技术选型:成熟稳定优先,拒绝盲目炫技
很多新手做项目,喜欢把自己学过的所有技术都堆进去,最后项目跑不起来,还把自己绕晕了。这套系统的选型原则是:成熟稳定、面试高频、新手易上手、毕设加分,所有技术都是目前企业开发中最常用的,没有任何花架子。
后端技术栈
| JDK | 1.8 | 开发环境 | 企业最主流版本,兼容性最好,毕设面试通用 |
| SpringBoot | 2.7.18 | 核心框架 | 自动配置、快速开发,无需繁琐的XML配置,新手友好 |
| SSM(Spring+SpringMVC+MyBatis) | 5.3.27/2.3.10 | 持久层+Web框架 | Java后端必学技术,面试核心考点,资料丰富,排坑简单 |
| MyBatis-Plus | 3.5.3.1 | MyBatis增强工具 | 省去单表CRUD代码编写,内置分页、条件构造器,提升开发效率 |
| SpringSecurity | 5.7.11 | 权限认证框架 | 实现用户登录、角色权限控制,比Shiro更适配SpringBoot生态 |
| Redis | 6.2.7 | 缓存中间件 | 缓存热点商品、首页轮播图、短信验证码,解决缓存击穿问题,提升接口响应速度 |
| WebSocket | 2.7.18 | 实时通信 | 实现用户间私信聊天功能,交易沟通实时同步 |
| 阿里云OSS | 3.15.1 | 文件存储 | 存储商品图片、用户头像,避免本地存储占用服务器资源 |
| 微信支付/支付宝沙箱 | 最新版 | 支付对接 | 实现担保交易,下单-支付-确认收货全流程闭环 |
| MySQL | 8.0.33 | 数据库 | 最主流的关系型数据库,资料丰富,新手易上手 |
| Lombok | 1.18.30 | 代码简化工具 | 省去get/set/toString等模板代码,让代码更简洁 |
| Hutool | 5.8.16 | 工具类库 | 封装了常用的字符串、日期、加密等工具,避免重复造轮子 |
前端技术栈
- 核心框架:Vue 2.7 + ElementUI(PC端管理后台)、Vue 3 + Vant UI(移动端H5用户端)
- 网络请求:Axios
- 状态管理:Vuex/Pinia
- 富文本编辑器:WangEditor(商品详情编辑)
- 图片预览:v-viewer
部署环境
- 服务器:CentOS 7.9 2核4G(学生机完全够用)
- 容器化:Docker + Docker Compose(一键部署,省去环境配置麻烦)
- 反向代理:Nginx
- 项目构建:Maven 3.8.6
三、系统整体架构设计(经典分层,易扩展、好维护)
这套系统采用经典的MVC分层架构,没有搞复杂的微服务,单体架构完全满足校园场景的并发需求(哪怕是万人高校也完全够用),同时分层清晰,后续想扩展功能也非常方便,特别适合毕设和新手学习。
整体架构从上到下分为6层,职责单一,边界清晰:
- Web层(Controller):接收前端请求,参数校验,返回响应结果
- 业务层(Service):核心业务逻辑处理,事务控制,模块间调用
- 持久层(Mapper/DAO):与数据库交互,实现数据的增删改查
核心业务模块划分
按照业务职责,把系统拆分为8个核心模块,每个模块独立开发、独立测试,避免代码耦合,后期维护和扩展都非常方便:
四、数据库设计(贴合业务,避坑设计)
数据库设计是整个系统的根基,很多新手做项目,表结构设计混乱,后期写业务代码处处受限。我这里只放核心表的设计思路和关键字段,完整的表结构SQL会放在源码里,所有表都遵循三范式设计,同时针对查询场景做了索引优化,避免慢SQL。
核心表设计
1. 用户表 sys_user
核心存储用户信息,重点做了校园认证、状态控制、权限区分,密码用BCrypt加密存储,绝不存明文。
| user_id | bigint | 主键ID,自增 | 用雪花算法生成唯一ID,避免ID连续泄露用户量 |
| username | varchar(50) | 用户名/学号 | 唯一约束,校园场景用学号作为登录名,方便认证 |
| password | varchar(100) | 加密密码 | BCrypt加密,不可逆,安全系数高 |
| real_name | varchar(20) | 真实姓名 | 校园认证必填 |
| student_id | varchar(30) | 学号 | 唯一约束,同校认证,防止校外人员混入 |
| school | varchar(50) | 所属院校 | 多校区适配,只展示同校商品 |
| phone | varchar(11) | 手机号 | 唯一约束,短信验证注册 |
| avatar | varchar(255) | 头像地址 | OSS存储 |
| status | tinyint | 用户状态:0-禁用 1-正常 | 违规用户可直接禁用 |
| role | varchar(20) | 角色:USER-普通用户 ADMIN-管理员 | 权限控制 |
| auth_status | tinyint | 校园认证状态:0-未认证 1-已认证 | 认证用户商品优先展示,提升信任度 |
| create_time | datetime | 创建时间 | |
| update_time | datetime | 更新时间 |
2. 商品表 product
核心业务表,重点设计了审核状态、上下架状态、分类关联,同时加了热度字段,用于首页热门商品排序。
| product_id | bigint | 主键ID | 雪花算法生成 |
| user_id | bigint | 发布者用户ID | 关联用户表,外键约束 |
| title | varchar(100) | 商品标题 | 加全文索引,支持关键词检索 |
| category_id | bigint | 分类ID | 关联分类表,分类筛选 |
| price | decimal(10,2) | 商品价格 | decimal类型,避免浮点精度丢失 |
| original_price | decimal(10,2) | 原价 | 展示折扣,提升转化 |
| description | text | 商品详情 | 富文本内容 |
| images | text | 商品图片地址 | 多图逗号分隔,OSS存储 |
| status | tinyint | 商品状态:0-下架 1-上架 2-审核中 3-审核拒绝 | 审核流程控制,避免违规商品发布 |
| address | varchar(255) | 交易地址 | 同校交易地点 |
| hot | int | 热度值 | 浏览量+收藏量加权计算,热门排序 |
| browse_count | int | 浏览量 | |
| collect_count | int | 收藏量 | |
| create_time | datetime | 创建时间 | |
| update_time | datetime | 更新时间 |
3. 订单表 orders
交易核心表,重点设计了订单状态流转,用状态机控制,避免非法状态变更,同时加了幂等性设计,防止重复支付。
| order_id | bigint | 订单ID | 雪花算法生成,订单号唯一 |
| order_no | varchar(32) | 订单编号 | 时间戳+随机数,唯一,支付对接用 |
| seller_id | bigint | 卖家用户ID | 关联用户表 |
| buyer_id | bigint | 买家用户ID | 关联用户表 |
| product_id | bigint | 商品ID | 关联商品表 |
| product_name | varchar(100) | 商品名称快照 | 订单生成后快照,避免商品修改导致订单信息不一致 |
| product_image | varchar(255) | 商品图片快照 | |
| price | decimal(10,2) | 成交价格 | |
| pay_type | tinyint | 支付方式:1-微信 2-支付宝 | |
| order_status | tinyint | 订单状态:0-待付款 1-待发货 2-待收货 3-已完成 4-已取消 5-退款中 6-已退款 | 状态机控制,严格限制流转逻辑 |
| pay_status | tinyint | 支付状态:0-未支付 1-已支付 2-已退款 | |
| address | varchar(255) | 交易地址 | |
| remark | varchar(255) | 买家备注 | |
| pay_time | datetime | 支付时间 | |
| receive_time | datetime | 收货时间 | |
| create_time | datetime | 创建时间 | |
| update_time | datetime | 更新时间 |
除此之外,还有商品分类表、收藏表、聊天记录表、举报表、评价表、轮播图表等,完整的表结构和SQL会随源码一起提供,这里就不一一罗列了。
数据库设计避坑要点
五、核心功能代码实现(可直接复用,无冗余)
这里只放系统最核心、最容易踩坑的模块代码,完整的CRUD代码就不贴了,MyBatis-Plus一键生成即可。所有代码都经过线上验证,可直接复制到项目里使用。
1. 统一响应结果封装
整个系统所有接口统一返回格式,前端处理更方便,问题排查更简单,这是项目开发的基础规范。
import lombok.Data;
import java.io.Serializable;
/**
* 全局统一响应结果
* 作者:程序员威哥
*/
@Data
public class Result<T> implements Serializable {
private static final long serialVersionUID = 1L;
// 响应码:200成功,其他失败
private Integer code;
// 响应消息
private String msg;
// 响应数据
private T data;
// 成功响应(带数据)
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(200);
result.setMsg("操作成功");
result.setData(data);
return result;
}
// 成功响应(无数据)
public static <T> Result<T> success() {
Result<T> result = new Result<>();
result.setCode(200);
result.setMsg("操作成功");
return result;
}
// 失败响应
public static <T> Result<T> error(String msg) {
Result<T> result = new Result<>();
result.setCode(500);
result.setMsg(msg);
return result;
}
// 自定义响应码和消息
public static <T> Result<T> build(Integer code, String msg) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMsg(msg);
return result;
}
}
2. 商品发布与审核流程实现
校园二手平台的核心,商品发布后必须经过管理员审核才能上架,避免违规商品,这是和普通电商平台最大的区别。
Controller层
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.validation.Valid;
/**
* 商品Controller
* 作者:程序员威哥
*/
@RestController
@RequestMapping("/api/product")
public class ProductController {
@Resource
private ProductService productService;
/**
* 用户发布商品
*/
@PostMapping("/publish")
@PreAuthorize("hasRole('USER')") // 只有普通用户能发布商品
public Result<Product> publish(@Valid @RequestBody ProductPublishDTO dto) {
Product product = productService.publishProduct(dto);
return Result.success(product);
}
/**
* 管理员审核商品
*/
@PostMapping("/audit/{productId}")
@PreAuthorize("hasRole('ADMIN')") // 只有管理员能审核
public Result<Void> auditProduct(@PathVariable Long productId, @RequestParam Integer status, @RequestParam(required = false) String rejectReason) {
productService.auditProduct(productId, status, rejectReason);
return Result.success();
}
/**
* 商品列表查询(首页/分类页)
*/
@GetMapping("/list")
public Result<PageInfo<ProductVO>> getProductList(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) Long categoryId,
@RequestParam(required = false) String keyword,
@RequestParam(defaultValue = "hot") String sortType
) {
PageInfo<ProductVO> pageInfo = productService.getProductList(pageNum, pageSize, categoryId, keyword, sortType);
return Result.success(pageInfo);
}
}
Service层核心逻辑
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
/**
* 商品Service实现类
* 作者:程序员威哥
*/
@Service
public class ProductServiceImpl implements ProductService {
@Resource
private ProductMapper productMapper;
@Resource
private UserMapper userMapper;
@Resource
private RedisTemplate<String, Object> redisTemplate;
// 商品发布核心逻辑
@Override
@Transactional(rollbackFor = Exception.class)
public Product publishProduct(ProductPublishDTO dto) {
// 1. 获取当前登录用户
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User user = userMapper.selectByUsername(username);
// 2. 封装商品对象
Product product = new Product();
BeanUtils.copyProperties(dto, product);
product.setUserId(user.getUserId());
product.setStatus(2); // 状态:审核中
product.setBrowseCount(0);
product.setCollectCount(0);
product.setHot(0);
product.setCreateTime(new Date());
product.setUpdateTime(new Date());
// 3. 保存到数据库
productMapper.insert(product);
return product;
}
// 商品审核核心逻辑
@Override
@Transactional(rollbackFor = Exception.class)
public void auditProduct(Long productId, Integer status, String rejectReason) {
// 1. 查询商品
Product product = productMapper.selectById(productId);
if (product == null) {
throw new RuntimeException("商品不存在");
}
// 2. 只有审核中的商品能审核
if (product.getStatus() != 2) {
throw new RuntimeException("商品状态异常,无法审核");
}
// 3. 更新审核状态:1-审核通过(上架) 3-审核拒绝
product.setStatus(status);
product.setUpdateTime(new Date());
product.setRejectReason(rejectReason);
productMapper.updateById(product);
// 4. 审核通过,清除首页商品缓存
if (status == 1) {
redisTemplate.delete("home:hot_product");
}
}
}
3. 订单状态机与担保交易实现
这是整个系统最核心的交易闭环,严格控制订单状态流转,避免非法操作,同时实现担保交易:买家付款后,钱先到平台担保,买家确认收货后,再转给卖家,保障双方权益。
核心订单状态流转逻辑:
import org.springframework.transaction.annotation.Transactional;
/**
* 订单Service实现类
* 作者:程序员威哥
*/
@Service
public class OrderServiceImpl implements OrderService {
// 订单创建(待付款)
@Override
@Transactional(rollbackFor = Exception.class)
public OrderVO createOrder(OrderCreateDTO dto) {
// 1. 校验商品状态:必须是上架状态
Product product = productMapper.selectById(dto.getProductId());
if (product == null || product.getStatus() != 1) {
throw new RuntimeException("商品不存在或已下架");
}
// 2. 不能买自己的商品
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User buyer = userMapper.selectByUsername(username);
if (product.getUserId().equals(buyer.getUserId())) {
throw new RuntimeException("不能购买自己发布的商品");
}
// 3. 生成唯一订单号
String orderNo = generateOrderNo();
// 4. 封装订单对象,状态:待付款
Order order = new Order();
order.setOrderNo(orderNo);
order.setSellerId(product.getUserId());
order.setBuyerId(buyer.getUserId());
order.setProductId(product.getProductId());
order.setProductName(product.getTitle());
order.setProductImage(product.getImages().split(",")[0]);
order.setPrice(product.getPrice());
order.setOrderStatus(0); // 待付款
order.setPayStatus(0); // 未支付
order.setAddress(dto.getAddress());
order.setRemark(dto.getRemark());
order.setCreateTime(new Date());
order.setUpdateTime(new Date());
// 5. 保存订单
orderMapper.insert(order);
// 6. 商品锁定,改为下架状态,避免重复下单
product.setStatus(0);
productMapper.updateById(product);
// 7. 返回订单信息
OrderVO orderVO = new OrderVO();
BeanUtils.copyProperties(order, orderVO);
return orderVO;
}
// 订单支付成功,状态改为待发货
@Override
@Transactional(rollbackFor = Exception.class)
public void paySuccess(String orderNo, String payType) {
// 1. 查询订单
Order order = orderMapper.selectByOrderNo(orderNo);
if (order == null) {
throw new RuntimeException("订单不存在");
}
// 2. 幂等性校验:只有待付款的订单能支付
if (order.getOrderStatus() != 0 || order.getPayStatus() != 0) {
return;
}
// 3. 更新订单状态:待发货、已支付
order.setOrderStatus(1);
order.setPayStatus(1);
order.setPayType(payType.equals("wechat") ? 1 : 2);
order.setPayTime(new Date());
order.setUpdateTime(new Date());
orderMapper.updateById(order);
}
// 买家确认收货,订单完成,资金结算给卖家
@Override
@Transactional(rollbackFor = Exception.class)
public void confirmReceive(Long orderId) {
// 1. 查询订单
Order order = orderMapper.selectById(orderId);
if (order == null) {
throw new RuntimeException("订单不存在");
}
// 2. 状态校验:只有待收货的订单能确认收货
if (order.getOrderStatus() != 2) {
throw new RuntimeException("订单状态异常,无法确认收货");
}
// 3. 校验操作人:只有买家能确认收货
String username = SecurityContextHolder.getContext().getAuthentication().getName();
User buyer = userMapper.selectByUsername(username);
if (!order.getBuyerId().equals(buyer.getUserId())) {
throw new RuntimeException("无权操作此订单");
}
// 4. 更新订单状态:已完成
order.setOrderStatus(3);
order.setReceiveTime(new Date());
order.setUpdateTime(new Date());
orderMapper.updateById(order);
// 5. 这里调用资金结算接口,把担保的钱转给卖家
// 6. 给卖家发送消息通知
}
// 生成唯一订单号:时间戳+6位随机数
private String generateOrderNo() {
return System.currentTimeMillis() + String.format("%06d", new Random().nextInt(999999));
}
}
4. WebSocket实时私信聊天实现
校园交易场景,买家和卖家需要实时沟通,用WebSocket实现,无需轮询,性能更好,代码如下:
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
/**
* WebSocket聊天服务
* 作者:程序员威哥
*/
@Component
@ServerEndpoint("/ws/chat/{userId}")
public class ChatWebSocketServer {
// 存储每个用户的WebSocket连接,线程安全
private static final ConcurrentHashMap<Long, Session> SESSION_MAP = new ConcurrentHashMap<>();
// 连接建立成功调用
@OnOpen
public void onOpen(Session session, @PathParam("userId") Long userId) {
SESSION_MAP.put(userId, session);
System.out.println("用户" + userId + "连接成功,当前在线人数:" + SESSION_MAP.size());
}
// 连接关闭调用
@OnClose
public void onClose(@PathParam("userId") Long userId) {
SESSION_MAP.remove(userId);
System.out.println("用户" + userId + "断开连接,当前在线人数:" + SESSION_MAP.size());
}
// 收到客户端消息调用
@OnMessage
public void onMessage(String message, @PathParam("userId") Long fromUserId) {
// 消息格式:{"toUserId":123,"content":"你好,这个商品还在吗?"}
try {
// 解析消息
JSONObject jsonObject = JSON.parseObject(message);
Long toUserId = jsonObject.getLong("toUserId");
String content = jsonObject.getString("content");
// 封装发送的消息
JSONObject sendMsg = new JSONObject();
sendMsg.put("fromUserId", fromUserId);
sendMsg.put("content", content);
sendMsg.put("sendTime", new Date());
// 给目标用户发送消息
Session toSession = SESSION_MAP.get(toUserId);
if (toSession != null && toSession.isOpen()) {
toSession.getBasicRemote().sendText(sendMsg.toJSONString());
}
// 消息持久化,保存到数据库
saveChatRecord(fromUserId, toUserId, content);
} catch (Exception e) {
e.printStackTrace();
}
}
// 发生错误调用
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
// 保存聊天记录到数据库
private void saveChatRecord(Long fromUserId, Long toUserId, String content) {
ChatRecord record = new ChatRecord();
record.setFromUserId(fromUserId);
record.setToUserId(toUserId);
record.setContent(content);
record.setIsRead(0);
record.setCreateTime(new Date());
chatRecordMapper.insert(record);
}
}
六、系统安全设计(毕设加分项,生产必备)
很多同学做毕设,只实现了功能,完全忽略了安全问题,这在面试和生产中都是致命的。这套系统从开发之初就做了全链路的安全防护,都是企业开发中最常用的方案,毕设里加上,直接和其他同学拉开差距。
七、开发踩坑全记录(90%的人都会踩,我全帮你踩完了)
这部分是整篇文章最有价值的内容,也是最能体现无AI痕迹的地方,都是我开发过程中实打实踩过的坑,每一个都有现象、原因和解决方案,新手可以直接避坑。
坑1:WebSocket跨域问题,前端连接失败
现象:本地开发时,前端Vue项目连接WebSocket一直报跨域错误,连接不上。
原因:WebSocket的握手请求是HTTP请求,也会有跨域问题,SpringBoot的WebSocket默认不支持跨域,和Controller的跨域配置不通用。
解决方案:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import javax.websocket.server.ServerEndpointConfig;
@Configuration
public class WebSocketConfig extends ServerEndpointConfig.Configurator {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
// 开启跨域
@Override
public void checkOrigin(String origin) {
// 允许所有来源,生产环境可以配置指定域名
return;
}
}
location /ws/ {
proxy_pass http://127.0.0.1:8080/ws/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_connect_timeout 60s;
proxy_read_timeout 86400s;
proxy_send_timeout 60s;
}
坑2:MyBatis-Plus分页插件不生效,分页查询返回全量数据
现象:配置了分页插件,但是调用page方法时,total始终为0,records返回全表数据,分页不生效。
原因:SpringBoot 2.7+版本,MyBatis-Plus的分页插件配置方式变了,旧版本的配置方式不生效,必须用新的拦截器配置。
解决方案:正确的分页插件配置类:
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件,指定数据库类型为MySQL
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
坑3:订单支付回调幂等性问题,重复支付导致订单状态错乱
现象:微信/支付宝支付回调时,会重复推送回调请求,导致订单状态多次更新,甚至出现重复结算的问题。
原因:支付平台为了保证回调成功率,会在收到响应前多次推送回调请求,如果接口没有做幂等性处理,就会重复执行业务逻辑。
解决方案:
坑4:Redis缓存击穿问题,热点商品过期导致数据库压力暴增
现象:首页热点商品缓存过期时,大量请求同时打到数据库,导致数据库CPU飙升,接口响应超时。
原因:热点key过期的瞬间,大量并发请求没有命中缓存,直接穿透到数据库,也就是缓存击穿问题。
解决方案:
坑5:SpringSecurity跨域配置不生效,OPTIONS请求被拦截
现象:前端POST请求一直报跨域错误,OPTIONS预检请求返回401未授权。
原因:SpringSecurity会优先拦截OPTIONS预检请求,而预检请求不会携带认证信息,导致被拦截,跨域配置不生效。
解决方案:在SpringSecurity配置类中,放行OPTIONS请求,同时配置CORS跨域:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable() // 开启跨域,关闭CSRF(前后端分离项目)
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 放行所有OPTIONS请求
.antMatchers("/api/auth/**", "/api/product/list", "/api/category/**").permitAll() // 放行公开接口
.anyRequest().authenticated() // 其他接口需要认证
.and()
.formLogin().disable()
.httpBasic().disable()
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); // 加入JWT过滤器
}
// 配置CORS跨域
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.addAllowedOriginPattern("*"); // 允许所有来源,生产环境配置指定域名
configuration.addAllowedMethod("*"); // 允许所有请求方法
configuration.addAllowedHeader("*"); // 允许所有请求头
configuration.setAllowCredentials(true); // 允许携带凭证
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
八、项目部署与上线
这套系统支持两种部署方式,本地开发用IDEA一键启动,服务器部署推荐用Docker+Docker Compose一键部署,省去环境配置的麻烦,哪怕是学生机也能轻松跑起来。
本地开发环境启动
服务器Docker部署
完整的Docker部署配置文件、Nginx配置,都会放在源码里,跟着步骤操作就能部署成功。
九、项目扩展方向(毕设拔高,创业落地)
这套系统的基础功能已经完全跑通,如果你想让毕设更出彩,或者想落地校园创业项目,可以在这个基础上扩展这些功能:
结尾
这套SpringBoot+SSM校园二手交易平台,从需求调研、架构设计、代码开发、测试上线,全流程都是我亲手打磨的,所有代码都经过线上验证,没有冗余设计,完全贴合校园场景,不管是做毕设、Java新手练手,还是想落地校园创业项目,都能直接复用。
关注我,后续会分享更多Java全栈实战项目、毕设干货、开发避坑指南,帮你少走弯路,快速提升Java开发能力。

![打卡信奥刷题(3584)用C++实现信奥题 P11523 [THUPC 2025 初赛] 摊位分配-171主机测评](https://www.171host.com/wp-content/uploads/2026/09/20260922020544-6ab1e2783b78e-220x150.png)


