1. 项目概述
这个基于SpringBoot3+Vue3的二手商城系统是我最近完成的一个实战项目,它采用了前后端分离架构,并创新性地引入了协同过滤算法来实现个性化商品推荐。作为一个完整的电商平台,系统包含了用户端和管理端两大模块,共设计了18张数据表来支撑复杂的业务逻辑。
在实际开发过程中,我遇到了不少挑战,比如如何设计高效的推荐算法、如何处理高并发的订单请求、如何优化前后端数据交互等。最终实现的系统不仅功能完善,还具备以下特色:
- 采用最新的SpringBoot3和Vue3技术栈
- 使用Echarts实现丰富的数据可视化
- 实现了基于用户行为的协同过滤推荐
- 设计了完善的权限管理和日志系统
2. 技术选型与架构设计
2.1 技术栈解析
后端技术栈:
- SpringBoot3:作为基础框架,提供了自动配置、依赖管理等特性,大幅简化了项目配置
- MyBatis:持久层框架,配合动态SQL和二级缓存,有效提升了数据库访问效率
- MySQL8:关系型数据库,使用InnoDB引擎,支持事务和行级锁
- Redis:缓存用户会话和热门商品数据,减轻数据库压力
前端技术栈:
- Vue3:采用Composition API编写,代码更清晰、更易维护
- Element Plus:UI组件库,快速构建美观的界面
- Axios:处理HTTP请求,配合拦截器实现权限验证
- ECharts:数据可视化,展示用户行为和商品统计
2.2 系统架构设计
系统采用典型的前后端分离架构:
code复制客户端浏览器 ←HTTP→ Nginx ←API→ SpringBoot应用 ←JDBC→ MySQL
↑
Redis
这种架构的优势在于:
- 前后端可以并行开发,通过API文档约定接口
- 前端资源由Nginx直接服务,减轻应用服务器压力
- 后端服务无状态,便于水平扩展
- 通过Redis缓存热点数据,提升系统响应速度
提示:在实际部署时,建议使用Docker容器化部署,便于环境管理和扩展。
3. 核心功能实现
3.1 用户模块实现
用户模块采用了RBAC(基于角色的访问控制)模型,定义了三种角色:
- 游客:只能浏览公开信息
- 普通用户:可以买卖商品、发布帖子等
- 管理员:拥有后台管理权限
关键代码示例 - 用户认证:
java复制@PostMapping("/login")
public Result<User> login(@RequestBody LoginDTO dto) {
// 1. 验证验证码
String cacheCode = redisTemplate.opsForValue().get(dto.getUuid());
if (!dto.getCode().equalsIgnoreCase(cacheCode)) {
return Result.error("验证码错误");
}
// 2. 查询用户
User user = userService.lambdaQuery()
.eq(User::getUsername, dto.getUsername())
.one();
// 3. 密码校验
if (user == null || !passwordEncoder.matches(dto.getPassword(), user.getPassword())) {
return Result.error("用户名或密码错误");
}
// 4. 生成Token
String token = JwtUtil.createToken(user.getId(), user.getRole());
user.setToken(token);
return Result.success(user);
}
3.2 商品模块实现
商品模块支持多级分类、搜索、收藏等功能。特别设计了商品状态机:
code复制待审核 → 已上架 → 已下架
↓
已售出
商品搜索优化:
- 使用Elasticsearch构建商品索引
- 对标题和描述字段进行分词处理
- 实现综合排序算法(销量×0.6 + 收藏×0.3 + 点赞×0.1)
3.3 订单模块实现
订单系统采用了状态模式设计:
java复制public interface OrderState {
void pay(Order order);
void cancel(Order order);
void complete(Order order);
}
@Component
@Scope("prototype")
public class UnpaidState implements OrderState {
@Override
public void pay(Order order) {
order.setState(OrderStatus.PAID);
// 扣减库存等操作
}
@Override
public void cancel(Order order) {
order.setState(OrderStatus.CANCELLED);
}
}
4. 协同过滤推荐系统
4.1 算法原理
系统采用基于用户的协同过滤算法,核心步骤:
- 构建用户-商品评分矩阵
- 计算用户相似度(余弦相似度)
- 找出K个最相似用户
- 推荐相似用户喜欢而目标用户未购买的商品
余弦相似度计算公式:
code复制sim(u,v) = (∑(r_ui × r_vi)) / (√∑r_ui² × √∑r_vi²)
4.2 具体实现
用户行为权重设计:
- 购买:5分
- 收藏:3分
- 点赞:1分
- 浏览:0.5分
代码实现:
java复制public List<Long> recommendItems(Long userId, int k) {
// 1. 获取所有用户行为数据
List<UserBehavior> allBehaviors = behaviorMapper.selectList(null);
// 2. 构建用户-商品评分矩阵
Map<Long, Map<Long, Double>> userItemMatrix = buildUserItemMatrix(allBehaviors);
// 3. 计算目标用户与其他用户的相似度
Map<Long, Double> similarities = new HashMap<>();
Map<Long, Double> targetUser = userItemMatrix.getOrDefault(userId, new HashMap<>());
userItemMatrix.forEach((otherUserId, items) -> {
if (!otherUserId.equals(userId)) {
double similarity = calculateCosineSimilarity(targetUser, items);
similarities.put(otherUserId, similarity);
}
});
// 4. 获取最相似的K个用户
List<Long> similarUsers = similarities.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.limit(k)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
// 5. 推荐商品
Set<Long> targetItems = targetUser.keySet();
Map<Long, Double> itemScores = new HashMap<>();
similarUsers.forEach(similarUser -> {
double similarity = similarities.get(similarUser);
userItemMatrix.get(similarUser).forEach((itemId, score) -> {
if (!targetItems.contains(itemId)) {
itemScores.put(itemId, itemScores.getOrDefault(itemId, 0.0) + score * similarity);
}
});
});
return itemScores.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
.map(Map.Entry::getKey)
.limit(10)
.collect(Collectors.toList());
}
5. 数据可视化实现
使用ECharts实现了多种统计图表:
5.1 用户注册趋势图
javascript复制option = {
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: { type: 'value' },
series: [{
data: [120, 200, 150, 80, 70, 110, 130],
type: 'line',
smooth: true
}]
};
5.2 商品分类占比图
javascript复制option = {
tooltip: { trigger: 'item' },
series: [{
name: '商品分类',
type: 'pie',
radius: '50%',
data: [
{ value: 1048, name: '电子产品' },
{ value: 735, name: '图书' },
{ value: 580, name: '服装' },
{ value: 484, name: '家居' },
{ value: 300, name: '其他' }
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}]
};
6. 性能优化实践
6.1 数据库优化
- 为常用查询字段添加索引
- 使用EXPLAIN分析慢查询
- 对大表进行分库分表(用户表按ID取模分片)
6.2 缓存策略
java复制@Cacheable(value = "goods", key = "#id")
public Goods getGoodsById(Long id) {
return goodsMapper.selectById(id);
}
@CacheEvict(value = "goods", key = "#goods.id")
public void updateGoods(Goods goods) {
goodsMapper.updateById(goods);
}
6.3 前端优化
- 使用Vue的keep-alive缓存组件
- 路由懒加载
- 图片懒加载
- 使用Webpack进行代码分割
7. 部署与运维
7.1 环境要求
- JDK 21
- MySQL 8.0+
- Redis 6.2+
- Node.js 18+
7.2 部署步骤
- 数据库初始化:
sql复制CREATE DATABASE second_hand DEFAULT CHARACTER SET utf8mb4;
USE second_hand;
SOURCE init.sql;
- 后端启动:
bash复制mvn clean package
java -jar target/secondhand-1.0.0.jar
- 前端启动:
bash复制npm install
npm run dev
7.3 监控方案
- 使用Spring Boot Actuator暴露健康检查端点
- 使用Prometheus收集指标
- 使用Grafana展示监控数据
8. 常见问题与解决方案
8.1 跨域问题
问题现象:前端请求报CORS错误
解决方案:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.maxAge(3600);
}
}
8.2 并发下单问题
问题现象:商品超卖
解决方案:
- 数据库乐观锁:
sql复制UPDATE goods SET stock = stock - 1 WHERE id = ? AND stock > 0
- Redis分布式锁:
java复制public boolean tryLock(String key, long expire) {
return redisTemplate.opsForValue()
.setIfAbsent(key, "1", expire, TimeUnit.SECONDS);
}
8.3 推荐效果不佳
问题现象:新商品得不到推荐
解决方案:
- 引入基于内容的推荐作为冷启动方案
- 对新产品设置初始权重
- 混合使用多种推荐策略
9. 项目扩展方向
- 移动端适配:开发微信小程序版本
- 即时通讯:集成WebSocket实现买卖家实时沟通
- 支付对接:接入真实的支付宝/微信支付接口
- 物流跟踪:对接快递100等物流查询API
- 智能客服:集成NLP技术实现自动问答
在实际开发过程中,我发现协同过滤算法对用户行为数据的质量非常敏感。建议在初期可以适当增加一些人工规则来补充推荐结果,等用户行为数据积累到一定量后再完全依赖算法推荐。另外,商品图片的处理也很关键,可以使用阿里云OSS等云存储服务来管理用户上传的图片资源。