SSM框架实现二手交易平台:Spring Boot+Vue全栈开发指南
2026/9/17 8:12:58 网站建设 项目流程

1. 项目概述

这个基于SSM框架的二手物品交易网站系统是我最近完成的一个课程设计/毕业设计项目。作为一个有10年开发经验的程序员,我想分享一下这个项目的完整实现过程和技术细节。这个系统实现了商品发布、订单处理和分类检索等核心功能,采用了Spring Boot + MyBatis + Vue的前后端分离架构,数据库使用MySQL。

在实际开发过程中,我发现很多同学在做类似项目时都会遇到一些共性问题:比如SSM框架整合不顺畅、前后端对接困难、数据库设计不合理等。这个项目我特别注重了这些问题点的解决,现在把完整的开发思路和实现过程分享出来,希望能帮助到正在做课程设计或毕业设计的同学。

2. 系统架构设计

2.1 技术选型分析

选择合适的技术栈是项目成功的关键。经过仔细评估,我最终确定了以下技术组合:

后端技术栈

  • Spring Boot 2.7.x:简化配置,快速构建项目
  • MyBatis-Plus 3.5.x:增强的ORM框架,减少SQL编写
  • Shiro 1.10.x:负责认证和授权
  • Lombok:简化实体类开发
  • Hutool:Java工具包

前端技术栈

  • Vue 3.x:前端框架
  • Element Plus:UI组件库
  • Axios:HTTP客户端
  • Vue Router:路由管理
  • Pinia:状态管理

数据库

  • MySQL 8.0:关系型数据库
  • Redis 6.x:缓存和会话管理

选择这些技术主要基于以下考虑:

  1. Spring Boot的自动配置和起步依赖能大幅减少配置工作
  2. MyBatis-Plus提供了强大的CRUD操作和代码生成器
  3. Vue 3的组合式API更适合复杂前端逻辑
  4. MySQL 8.0在性能和功能上都有显著提升

2.2 系统架构设计

系统采用典型的三层架构:

表示层(Web层) │ ├── 前端:Vue + Element Plus │ 业务逻辑层(Service层) │ ├── Spring Boot + MyBatis-Plus │ 数据访问层(DAO层) │ ├── MySQL + Redis

这种分层架构的优势在于:

  1. 职责分离,各层专注自己的功能
  2. 便于团队协作开发
  3. 易于维护和扩展
  4. 可以针对不同层进行独立测试

2.3 数据库设计

数据库设计遵循第三范式,主要表结构如下:

  1. 用户表(user)

    • id: 主键
    • username: 用户名
    • password: 加密密码
    • phone: 手机号
    • email: 邮箱
    • avatar: 头像
    • status: 状态
  2. 商品表(goods)

    • id: 主键
    • user_id: 卖家ID
    • category_id: 分类ID
    • title: 商品标题
    • price: 价格
    • original_price: 原价
    • description: 描述
    • images: 图片JSON
    • status: 状态
  3. 订单表(order)

    • id: 主键
    • order_no: 订单编号
    • buyer_id: 买家ID
    • seller_id: 卖家ID
    • goods_id: 商品ID
    • amount: 金额
    • status: 状态
    • address: 收货地址
  4. 分类表(category)

    • id: 主键
    • name: 分类名称
    • parent_id: 父分类ID
    • level: 层级

数据库关系图如下:

用户 1:n 商品 用户 1:n 订单(作为买家) 用户 1:n 订单(作为卖家) 商品 1:n 订单 分类 1:n 商品

设计心得:在实际开发中,商品图片存储采用了JSON格式存储多个图片URL,而不是创建单独的图片表,这样简化了查询逻辑。但要注意JSON字段在MySQL中的索引问题。

3. 核心功能实现

3.1 用户认证模块

用户认证采用了Shiro框架,实现了以下功能:

  1. 基于用户名密码的登录
  2. 基于JWT的无状态认证
  3. 角色和权限控制

关键代码示例:

// Shiro配置类 @Configuration public class ShiroConfig { @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) { ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean(); factoryBean.setSecurityManager(securityManager); Map<String, String> filterMap = new LinkedHashMap<>(); filterMap.put("/api/user/login", "anon"); filterMap.put("/api/user/register", "anon"); filterMap.put("/**", "authc"); factoryBean.setFilterChainDefinitionMap(filterMap); return factoryBean; } } // JWT工具类 public class JwtUtil { private static final String SECRET = "your-secret-key"; private static final long EXPIRE = 7 * 24 * 60 * 60 * 1000L; // 7天 public static String generateToken(String username) { Date now = new Date(); Date expire = new Date(now.getTime() + EXPIRE); return Jwts.builder() .setHeaderParam("typ", "JWT") .setSubject(username) .setIssuedAt(now) .setExpiration(expire) .signWith(SignatureAlgorithm.HS512, SECRET) .compact(); } public static boolean verifyToken(String token) { try { Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token); return true; } catch (Exception e) { return false; } } }

注意事项:在实际部署时,SECRET密钥应该从配置文件中读取,而不是硬编码在代码中。同时要考虑定期更换密钥的安全策略。

3.2 商品管理模块

商品管理实现了以下功能:

  1. 商品发布
  2. 商品编辑
  3. 商品上下架
  4. 商品搜索和筛选

核心接口设计:

@RestController @RequestMapping("/api/goods") public class GoodsController { @Autowired private GoodsService goodsService; @PostMapping public Result publishGoods(@RequestBody GoodsDTO goodsDTO) { return goodsService.publishGoods(goodsDTO); } @PutMapping("/{id}") public Result updateGoods(@PathVariable Long id, @RequestBody GoodsDTO goodsDTO) { return goodsService.updateGoods(id, goodsDTO); } @GetMapping("/{id}") public Result getGoodsDetail(@PathVariable Long id) { return goodsService.getGoodsDetail(id); } @GetMapping public Result searchGoods(@RequestParam(required = false) String keyword, @RequestParam(required = false) Long categoryId, @RequestParam(defaultValue = "0") Integer page, @RequestParam(defaultValue = "10") Integer size) { return goodsService.searchGoods(keyword, categoryId, page, size); } }

前端商品表单关键代码:

<template> <el-form :model="goodsForm" :rules="rules" ref="formRef"> <el-form-item label="商品标题" prop="title"> <el-input v-model="goodsForm.title"></el-input> </el-form-item> <el-form-item label="商品分类" prop="categoryId"> <el-cascader v-model="goodsForm.categoryId" :options="categoryOptions" :props="{value: 'id', label: 'name'}" ></el-cascader> </el-form-item> <el-form-item label="商品图片" prop="images"> <el-upload action="/api/upload" list-type="picture-card" :file-list="fileList" :on-success="handleUploadSuccess" > <el-icon><Plus /></el-icon> </el-upload> </el-form-item> <el-form-item label="商品描述" prop="description"> <el-input type="textarea" v-model="goodsForm.description"></el-input> </el-form-item> </el-form> </template>

开发技巧:商品图片上传时,前端可以先压缩图片再上传,减少带宽占用。可以使用compressorjs等库实现图片压缩。

3.3 订单处理模块

订单处理流程包括:

  1. 创建订单
  2. 支付订单
  3. 发货
  4. 确认收货
  5. 评价

状态机设计:

待支付 → 已支付 → 已发货 → 已完成 ↘ 已取消

订单服务核心逻辑:

@Service public class OrderServiceImpl implements OrderService { @Autowired private OrderMapper orderMapper; @Autowired private GoodsMapper goodsMapper; @Transactional public Result createOrder(OrderDTO orderDTO) { // 1. 检查商品状态 Goods goods = goodsMapper.selectById(orderDTO.getGoodsId()); if (goods == null || goods.getStatus() != GoodsStatus.ON_SALE) { return Result.fail("商品不存在或已下架"); } // 2. 创建订单 Order order = new Order(); order.setOrderNo(generateOrderNo()); order.setBuyerId(orderDTO.getBuyerId()); order.setSellerId(goods.getUserId()); order.setGoodsId(goods.getId()); order.setAmount(goods.getPrice()); order.setStatus(OrderStatus.WAIT_PAYMENT); orderMapper.insert(order); // 3. 锁定商品 goods.setStatus(GoodsStatus.LOCKED); goodsMapper.updateById(goods); return Result.success(order); } private String generateOrderNo() { // 时间戳+随机数 return System.currentTimeMillis() + "" + (int)(Math.random() * 1000); } }

注意事项:订单创建时要加事务注解@Transactional,确保订单创建和商品状态更新要么都成功,要么都失败。同时要考虑并发情况下的商品超卖问题。

4. 系统优化与部署

4.1 性能优化

  1. 缓存策略

    • 使用Redis缓存热门商品
    • 实现二级缓存(MyBatis二级缓存+Redis)
    • 缓存分类数据
  2. 数据库优化

    • 添加合适的索引
    • 优化慢查询
    • 使用读写分离
  3. 前端优化

    • 图片懒加载
    • 路由懒加载
    • 组件按需引入

缓存配置示例:

@Configuration @EnableCaching public class CacheConfig { @Bean public RedisCacheManager cacheManager(RedisConnectionFactory factory) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofHours(1)) .disableCachingNullValues() .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); return RedisCacheManager.builder(factory) .cacheDefaults(config) .transactionAware() .build(); } }

4.2 安全防护

  1. XSS防护

    • 前端使用vue-sanitize过滤输入
    • 后端对输出进行转义
  2. CSRF防护

    • 使用SameSite Cookie
    • 添加CSRF Token
  3. SQL注入防护

    • 使用MyBatis预编译
    • 避免拼接SQL
  4. 敏感数据保护

    • 密码加盐哈希
    • 敏感字段加密存储

安全配置示例:

@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .headers() .xssProtection() .and() .contentSecurityPolicy("script-src 'self'") .and() .authorizeRequests() .antMatchers("/api/public/**").permitAll() .anyRequest().authenticated(); } }

4.3 部署方案

推荐使用Docker Compose进行容器化部署:

version: '3' services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: secondhand ports: - "3306:3306" volumes: - mysql_data:/var/lib/mysql redis: image: redis:6 ports: - "6379:6379" volumes: - redis_data:/data backend: build: ./backend ports: - "8080:8080" depends_on: - mysql - redis frontend: build: ./frontend ports: - "80:80" volumes: mysql_data: redis_data:

部署步骤:

  1. 安装Docker和Docker Compose
  2. 编写Dockerfile和docker-compose.yml
  3. 构建镜像:docker-compose build
  4. 启动服务:docker-compose up -d

部署建议:生产环境应该使用Nginx做反向代理和负载均衡,并配置HTTPS证书。同时要设置日志轮转和监控告警。

5. 常见问题与解决方案

5.1 开发环境问题

问题1:MySQL连接失败,时区错误
解决方案:在连接字符串中添加时区参数
jdbc:mysql://localhost:3306/secondhand?useSSL=false&serverTimezone=Asia/Shanghai

问题2:前端跨域问题
解决方案:后端添加CORS配置

@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("*") .allowedHeaders("*"); } }

5.2 业务逻辑问题

问题1:商品超卖
解决方案:使用乐观锁或分布式锁

// 乐观锁实现 @Transactional public Result createOrderWithOptimisticLock(OrderDTO orderDTO) { Goods goods = goodsMapper.selectById(orderDTO.getGoodsId()); if (goods.getStock() <= 0) { return Result.fail("库存不足"); } int updated = goodsMapper.reduceStockWithVersion(goods.getId(), goods.getVersion()); if (updated == 0) { throw new OptimisticLockingFailureException("并发修改冲突"); } // 创建订单... }

问题2:订单状态混乱
解决方案:使用状态模式设计订单状态机

public interface OrderState { void pay(Order order); void ship(Order order); void receive(Order order); void cancel(Order order); } public class Order { private OrderState state; public void pay() { state.pay(this); } // 其他方法... }

5.3 性能问题

问题1:商品列表查询慢
解决方案

  1. 添加合适的索引
  2. 使用分页查询
  3. 实现缓存

问题2:图片加载慢
解决方案

  1. 使用CDN加速
  2. 实现图片懒加载
  3. 使用WebP格式图片

6. 项目扩展方向

这个基础版本完成后,还可以考虑以下扩展方向:

  1. 即时通讯:集成WebSocket实现买卖家实时沟通
  2. 推荐系统:基于用户行为实现商品推荐
  3. 支付集成:对接支付宝、微信支付
  4. 物流跟踪:对接快递100等物流查询接口
  5. 移动端适配:开发微信小程序或React Native App

即时通讯实现示例:

@ServerEndpoint("/chat/{userId}") @Component public class ChatEndpoint { private static final Map<Long, Session> sessions = new ConcurrentHashMap<>(); @OnOpen public void onOpen(@PathParam("userId") Long userId, Session session) { sessions.put(userId, session); } @OnMessage public void onMessage(String message, Session session) { // 处理消息并转发 } @OnClose public void onClose(@PathParam("userId") Long userId) { sessions.remove(userId); } }

在实际开发这个项目的过程中,我最大的体会是:设计阶段多花时间,编码阶段就能少走弯路。特别是数据库设计和接口定义,一旦确定后再修改成本很高。建议同学们在开始编码前,先画好ER图和接口文档,和导师或组员充分讨论确认。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询