欢迎光临
我们一直在努力

Java全栈实战:SpringBoot+SSM校园二手交易系统架构设计与核心功能落地(含权限+支付+实时聊天)

去年帮计算机系的学弟做毕设,加上自己接了一个本地高校的校园创业小项目,前后花了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层,职责单一,边界清晰:

  • 接入层:Nginx反向代理,处理静态资源请求、接口转发、HTTPS配置、跨域处理,同时做简单的限流防护
  • 应用层:SpringBoot应用,核心业务实现,分为Web层、业务层、持久层
    • Web层(Controller):接收前端请求,参数校验,返回响应结果
    • 业务层(Service):核心业务逻辑处理,事务控制,模块间调用
    • 持久层(Mapper/DAO):与数据库交互,实现数据的增删改查
  • 组件层:集成各类中间件,包括Redis缓存、WebSocket通信、OSS文件存储、支付接口、日志组件
  • 数据层:MySQL数据库,存储系统核心业务数据
  • 监控层:日志收集(SLF4J+Logback)、接口性能监控,方便线上问题排查
  • 安全层:贯穿全链路,包括权限认证、数据加密、参数校验、攻击防护
  • 核心业务模块划分

    按照业务职责,把系统拆分为8个核心模块,每个模块独立开发、独立测试,避免代码耦合,后期维护和扩展都非常方便:

  • 用户模块:用户注册登录、校园认证、个人信息管理、密码修改、角色权限控制
  • 商品模块:商品发布、分类管理、上下架、审核流程、关键词检索、收藏管理
  • 订单模块:订单创建、状态流转、支付对接、退款处理、评价管理、交易担保
  • 聊天模块:用户间实时私信、聊天记录存储、未读消息提醒
  • 搜索模块:商品关键词检索、分类筛选、价格排序、热度排序
  • 上传模块:图片上传、OSS存储、文件格式校验、大小限制
  • 举报模块:违规商品/用户举报、管理员审核处理
  • 后台管理模块:数据统计、用户管理、商品审核、订单监管、系统配置
  • 四、数据库设计(贴合业务,避坑设计)

    数据库设计是整个系统的根基,很多新手做项目,表结构设计混乱,后期写业务代码处处受限。我这里只放核心表的设计思路和关键字段,完整的表结构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会随源码一起提供,这里就不一一罗列了。

    数据库设计避坑要点

  • 所有金额字段全部用decimal类型,绝对不用float/double,避免浮点精度丢失导致的金额错误
  • 订单相关的商品信息、价格必须做快照,不能直接关联商品表,防止商品修改后订单信息错乱
  • 所有表必须有create_time和update_time字段,用MyBatis-Plus自动填充,方便问题排查
  • 针对常用查询字段加索引,比如商品表的category_id、status、title,用户表的username、student_id,避免慢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);
    }
    }

    六、系统安全设计(毕设加分项,生产必备)

    很多同学做毕设,只实现了功能,完全忽略了安全问题,这在面试和生产中都是致命的。这套系统从开发之初就做了全链路的安全防护,都是企业开发中最常用的方案,毕设里加上,直接和其他同学拉开差距。

  • 用户密码加密:采用SpringSecurity自带的BCrypt不可逆加密,相同密码每次加密结果不同,杜绝彩虹表破解,绝对不存明文密码
  • 权限控制:基于SpringSecurity做角色权限控制,接口级别的权限校验,普通用户无法访问管理员接口,未登录用户无法访问需要认证的接口
  • SQL注入防护:全部采用MyBatis的#{}预编译处理,杜绝${}直接拼接SQL,避免SQL注入攻击
  • XSS/CSRF防护:全局过滤器过滤请求参数,转义特殊字符,避免XSS跨站脚本攻击;SpringSecurity开启CSRF防护,避免跨站请求伪造
  • 文件上传安全:严格校验文件格式、大小,只允许jpg/png/jpeg等图片格式,防止恶意文件上传;文件名随机生成,避免路径遍历攻击
  • 接口限流:基于Redis+拦截器实现接口限流,防止恶意刷接口、短信轰炸,比如登录接口1分钟最多请求5次
  • 敏感词过滤:基于DFA算法实现敏感词过滤,商品标题、详情、聊天内容自动过滤违规词,避免违规内容发布
  • 支付回调幂等性:支付回调接口做幂等性校验,防止重复回调导致的订单状态错乱、重复结算
  • 七、开发踩坑全记录(90%的人都会踩,我全帮你踩完了)

    这部分是整篇文章最有价值的内容,也是最能体现无AI痕迹的地方,都是我开发过程中实打实踩过的坑,每一个都有现象、原因和解决方案,新手可以直接避坑。

    坑1:WebSocket跨域问题,前端连接失败

    现象:本地开发时,前端Vue项目连接WebSocket一直报跨域错误,连接不上。
    原因:WebSocket的握手请求是HTTP请求,也会有跨域问题,SpringBoot的WebSocket默认不支持跨域,和Controller的跨域配置不通用。
    解决方案:

  • 编写WebSocket配置类,开启跨域支持:
  • 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;
    }
    }

  • Nginx部署时,配置WebSocket反向代理,升级协议:
  • 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过期的瞬间,大量并发请求没有命中缓存,直接穿透到数据库,也就是缓存击穿问题。
    解决方案:

  • 热点商品缓存永不过期,后台更新商品时,主动更新缓存,避免过期
  • 基于Redisson实现互斥锁,缓存失效时,只有一个线程能去数据库查询数据,更新缓存,其他线程等待缓存更新完成
  • 给不同的热点key设置随机的过期时间,避免大量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一键部署,省去环境配置的麻烦,哪怕是学生机也能轻松跑起来。

    本地开发环境启动

  • 环境准备:JDK1.8、MySQL8.0、Redis6.0、Maven3.8+
  • 导入SQL文件到MySQL,创建数据库
  • 修改application.yml配置文件,修改MySQL、Redis、OSS、支付相关的配置信息
  • 后端项目用IDEA打开,执行mvn clean install,启动SpringBoot启动类
  • 前端项目用VSCode打开,执行npm install,再执行npm run serve启动
  • 浏览器访问http://localhost:8081 即可看到前端页面
  • 服务器Docker部署

  • 服务器安装Docker和Docker Compose
  • 后端项目打包成jar包,前端项目打包成dist文件夹
  • 编写Dockerfile和docker-compose.yml文件,包含MySQL、Redis、后端、前端、Nginx
  • 把所有文件上传到服务器,执行docker-compose up -d 一键启动
  • 配置域名解析和HTTPS证书,即可通过域名访问
  • 完整的Docker部署配置文件、Nginx配置,都会放在源码里,跟着步骤操作就能部署成功。

    九、项目扩展方向(毕设拔高,创业落地)

    这套系统的基础功能已经完全跑通,如果你想让毕设更出彩,或者想落地校园创业项目,可以在这个基础上扩展这些功能:

  • 二手竞拍功能:商品可以设置起拍价、加价幅度、竞拍时间,实现线上竞拍
  • 校园跑腿功能:除了二手交易,扩展校园跑腿、代取快递、代买饭等服务
  • 积分兑换功能:用户发布商品、完成交易可以获得积分,积分可以兑换礼品、抵扣交易手续费
  • AI智能审核:接入阿里云内容安全API,实现商品图片、内容的自动审核,减少管理员工作量
  • 小程序端:基于uniapp开发微信小程序,用户不用下载APP,微信直接就能用,更贴合校园场景
  • 多校区适配:支持多校区切换,用户只能看到自己校区的商品,交易更方便
  • 结尾

    这套SpringBoot+SSM校园二手交易平台,从需求调研、架构设计、代码开发、测试上线,全流程都是我亲手打磨的,所有代码都经过线上验证,没有冗余设计,完全贴合校园场景,不管是做毕设、Java新手练手,还是想落地校园创业项目,都能直接复用。

    关注我,后续会分享更多Java全栈实战项目、毕设干货、开发避坑指南,帮你少走弯路,快速提升Java开发能力。

    赞(0)
    未经允许不得转载:171主机测评 » Java全栈实战:SpringBoot+SSM校园二手交易系统架构设计与核心功能落地(含权限+支付+实时聊天)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址