1. 项目概述
这个基于SpringBoot+Vue3的二手交易平台项目,是我去年带队完成的一个校园创业项目。当时我们发现校园内二手物品交易需求旺盛,但信息分散在各类社交平台,交易过程缺乏保障。于是我们决定开发一个专为校园场景优化的二手交易系统,从技术选型到部署上线共耗时3个月,目前已在5所高校稳定运行。
平台采用前后端分离架构,后端基于SpringBoot 2.7 + MyBatis-Plus + Redis技术栈,前端使用Vue3 + TypeScript + Element Plus。特别值得一提的是我们实现的协同过滤推荐算法,使商品点击率提升了35%。下面我会从架构设计到核心实现,完整分享这个项目的技术细节。
2. 技术架构解析
2.1 后端技术栈选型
选择SpringBoot作为后端框架主要基于以下考量:
- 快速开发:SpringBoot的约定优于配置原则,让我们用3天就搭好了基础框架
- 微服务友好:为后续扩展留出空间(虽然当前是单体架构)
- 生态丰富:整合MyBatis-Plus后,数据库操作代码量减少60%
数据库选用MySQL 8.0,主要因为:
- 事务完整性要求高(支付、订单状态变更等)
- 校园场景下数据量在百万级,MySQL完全够用
- 配合Redis缓存热点数据,QPS实测可达1200+
2.2 前端技术决策
Vue3相比Vue2带来的优势:
- 性能提升:组合式API使代码复用更高效
- TypeScript支持:类型检查让团队协作更顺畅
- 更小的包体积:gzip后首屏资源仅380KB
特别说明选择Element Plus而非Ant Design Vue的原因:
- 表单组件更符合二手商品发布场景
- 主题定制更方便(我们做了校园风格主题)
- 社区问题响应更快
3. 核心模块实现
3.1 用户认证方案
采用JWT + 双Token机制实现认证:
java复制// Token生成示例
public String generateToken(User user) {
return Jwts.builder()
.setHeaderParam("typ", "JWT")
.setSubject(user.getId().toString())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + ACCESS_TOKEN_EXPIRE))
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.compact();
}
关键设计点:
- AccessToken过期时间设为2小时
- RefreshToken过期时间7天,用Redis存储
- 每次请求在拦截器中自动续期AccessToken
踩坑提醒:早期没做IP绑定导致Token被盗用,后来增加了设备指纹校验
3.2 商品推荐系统
3.2.1 协同过滤实现
用户相似度计算采用改进的余弦相似度算法:
java复制public double calculateSimilarity(String user1, String user2) {
// 获取共同评价过的商品
Set<String> commonItems = getCommonItems(user1, user2);
if (commonItems.isEmpty()) return 0.0;
// 计算相似度
double sum1 = 0, sum2 = 0, sum3 = 0;
for (String item : commonItems) {
double r1 = getRating(user1, item);
double r2 = getRating(user2, item);
sum1 += r1 * r2;
sum2 += r1 * r1;
sum3 += r2 * r2;
}
return sum1 / (Math.sqrt(sum2) * Math.sqrt(sum3));
}
优化点:
- 引入时间衰减因子:3个月前的行为权重降为0.7
- 冷启动处理:新用户展示热门商品
- 实时更新:用户行为触发异步计算
3.2.2 推荐结果缓存
用Redis有序集合存储推荐结果:
code复制ZADD rec:user:1 0.85 "item:1001"
ZADD rec:user:1 0.72 "item:1002"
...
缓存更新策略:
- 定时任务每天全量更新一次
- 用户重大行为(购买、收藏)触发实时更新
- 缓存失效时间设为6小时
4. 关键业务实现
4.1 商品发布流程
前端采用多步骤表单:
vue复制<el-steps :active="activeStep">
<el-step title="基本信息"/>
<el-step title="商品详情"/>
<el-step title="价格设置"/>
</el-steps>
后端处理逻辑:
- 图片上传到OSS(限制10张以内)
- 敏感词过滤(接入第三方API)
- 自动分类(基于标题NLP分析)
- 审核队列(校园场景需要人工审核)
4.2 交易系统设计
状态机设计:
java复制public enum OrderStatus {
UNPAID(1),
PAID(2),
SHIPPED(3),
COMPLETED(4),
CANCELLED(5);
// 状态转换校验逻辑
public boolean canTransferTo(OrderStatus status) {
switch(this) {
case UNPAID: return status == PAID || status == CANCELLED;
case PAID: return status == SHIPPED;
// ...其他状态转换规则
}
}
}
支付对接要点:
- 微信支付使用V3接口
- 支付宝使用RSA2签名
- 支付结果异步通知要做幂等处理
5. 性能优化实践
5.1 缓存策略
采用多级缓存架构:
- 本地缓存:Caffeine缓存商品详情(TTL=5分钟)
- 分布式缓存:Redis缓存热门列表
- 持久层缓存:MyBatis二级缓存
缓存击穿解决方案:
java复制public Product getProduct(Long id) {
// 1. 先查缓存
Product product = cache.get(id);
if (product != null) return product;
// 2. 获取分布式锁
Lock lock = redisson.getLock("product:" + id);
try {
lock.lock();
// 3. 二次检查
product = cache.get(id);
if (product != null) return product;
// 4. 查数据库
product = dao.selectById(id);
if (product != null) {
cache.put(id, product);
}
} finally {
lock.unlock();
}
return product;
}
5.2 数据库优化
索引设计示例:
sql复制ALTER TABLE `product`
ADD INDEX `idx_category_status` (`category_id`, `status`),
ADD INDEX `idx_user_created` (`user_id`, `created_at`);
SQL优化技巧:
- 避免SELECT *,只查询必要字段
- 批量操作使用rewriteBatchedStatements=true
- 大表分页用id偏移替代LIMIT
6. 部署与监控
6.1 容器化部署
Docker Compose配置示例:
yaml复制version: '3'
services:
backend:
image: registry.cn-hangzhou.aliyuncs.com/yourrepo/backend:${TAG}
ports:
- "8080:8080"
depends_on:
- redis
- mysql
frontend:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./dist:/usr/share/nginx/html
6.2 监控方案
- Prometheus:采集JVM/MySQL指标
- Grafana:展示监控仪表盘
- ELK:日志收集分析
- 健康检查端点:/actuator/health
7. 踩坑经验总结
- 跨域问题:开发环境要配置allowedOrigins,生产环境用Nginx反向代理
- 文件上传:前端要限制文件类型,后端要校验Content-Type
- 支付超时:设置合理的HTTP超时(微信建议5-10秒)
- 短信防刷:用Redis记录发送次数+IP限制
- 事务失效:@Transactional要注意自调用问题
这个项目让我深刻体会到,校园场景的技术方案要特别考虑:
- 网络环境不稳定时的降级处理
- 学生用户的操作容错性
- 学期初/末的流量高峰应对
后续我们计划加入即时通讯的离线消息功能,并尝试用Flutter开发跨平台App。如果你也在做类似项目,欢迎交流遇到的问题。