1. 项目概述
这个二手交易平台是我去年带队开发的一个实战项目,采用目前主流的前后端分离架构。系统上线后稳定运行了8个月,日均UV达到5000+,峰值时承载了2万+的商品信息。作为技术负责人,我想分享一下这个项目的完整开发历程和技术细节。
二手交易平台本质上是一个C2C的电子商务系统,但相比传统电商有以下特点:
- 商品SKU非标准化
- 交易流程简单化
- 用户身份双重性(既是买家也是卖家)
- 需要更强的社区属性和信任机制
2. 技术架构设计
2.1 为什么选择Spring Boot + Vue
后端技术选型考量:
- Spring Boot的自动配置特性让我们在3天内就搭建好了基础框架
- 内嵌Tomcat简化了部署流程,特别适合学生团队
- Actuator端点提供了完善的健康监控
- 与MyBatis的完美整合简化了数据库操作
前端技术选型原因:
- Vue的渐进式特性允许我们按需引入功能
- 组件化开发让多个成员可以并行工作
- Vuex的状态管理解决了跨组件通信问题
- CLI工具链大幅提升了开发效率
2.2 系统架构详解
我们的架构分为四个主要层次:
code复制┌───────────────────────────────────────┐
│ 客户端层 │
│ (Web、Android、iOS、小程序) │
└───────────────────────────────────────┘
↑ HTTPS ↓
┌───────────────────────────────────────┐
│ API网关层 │
│ (路由转发、负载均衡、限流熔断) │
└───────────────────────────────────────┘
↑ HTTP ↓
┌───────────────────────────────────────┐
│ 业务微服务层 │
│ (用户服务、商品服务、订单服务等) │
└───────────────────────────────────────┘
↑ JDBC ↓
┌───────────────────────────────────────┐
│ 数据持久层 │
│ (MySQL主从集群、Redis缓存、ES搜索) │
└───────────────────────────────────────┘
3. 核心功能实现
3.1 商品模块设计
商品表的核心字段设计:
java复制@Entity
@Table(name = "commodity")
public class Commodity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
@Column(columnDefinition = "TEXT")
private String description;
@Column(nullable = false)
private BigDecimal price;
@Enumerated(EnumType.STRING)
private CommodityStatus status;
@ManyToOne
@JoinColumn(name = "user_id")
private User seller;
@OneToMany(mappedBy = "commodity")
private List<CommodityImage> images = new ArrayList<>();
// 省略getter/setter
}
关键实现细节:
- 使用JPA实现对象-关系映射
- 价格使用BigDecimal避免精度问题
- 状态机模式管理商品生命周期
- 图片单独建表实现一对多关联
3.2 交易流程实现
交易状态机设计:
mermaid复制stateDiagram
[*] --> 待交易
待交易 --> 交易中: 买家发起交易
交易中 --> 已完成: 双方确认
交易中 --> 已取消: 任意方取消
已完成 --> [*]
已取消 --> [*]
核心交易接口:
java复制@PostMapping("/transactions")
public ResponseEntity<TransactionDTO> createTransaction(
@RequestBody @Valid TransactionCreateVO vo) {
// 1. 验证商品状态
Commodity commodity = commodityService.getById(vo.getCommodityId());
if (commodity.getStatus() != CommodityStatus.AVAILABLE) {
throw new BusinessException("商品不可交易");
}
// 2. 创建交易记录
Transaction transaction = new Transaction();
BeanUtils.copyProperties(vo, transaction);
transaction.setStatus(TransactionStatus.PENDING);
transactionService.save(transaction);
// 3. 更新商品状态
commodity.setStatus(CommodityStatus.TRADING);
commodityService.updateById(commodity);
return ResponseEntity.ok(transactionConverter.toDTO(transaction));
}
4. 性能优化实践
4.1 数据库优化
索引策略:
- 为商品表建立复合索引:(category_id, status, create_time)
- 交易表建立买家ID和卖家ID的单独索引
- 使用覆盖索引优化热门查询
分表方案:
- 按用户ID哈希分片用户表
- 按时间范围分片交易记录表
- 使用Sharding-JDBC实现透明访问
4.2 缓存设计
三级缓存架构:
-
本地缓存:Caffeine缓存热点数据
java复制@Bean public CaffeineCacheManager cacheManager() { return new CaffeineCacheManager( "commodityCache", "userCache" ); } -
分布式缓存:Redis集群缓存
- 商品详情:TTL 30分钟
- 用户信息:TTL 2小时
- 交易状态:TTL 15分钟
-
浏览器缓存:ETag协商缓存静态资源
5. 安全防护措施
5.1 认证授权方案
JWT令牌实现:
java复制public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
claims.put("roles", userDetails.getAuthorities()
.stream()
.map(GrantedAuthority::getAuthority)
.collect(Collectors.toList()));
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.compact();
}
5.2 防攻击策略
-
XSS防护:
- 前端使用vue-sanitize过滤输入
- 后端配置Spring Security的XSS防护头
-
CSRF防护:
- 启用Spring Security的CSRF保护
- 敏感操作要求二次验证
-
SQL注入防护:
- 全程使用预编译语句
- MyBatis使用#{}参数绑定
6. 部署方案
6.1 容器化部署
Docker Compose编排文件示例:
yaml复制version: '3.8'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:6.2
ports:
- "6379:6379"
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
6.2 监控方案
- Spring Boot Actuator:暴露健康检查端点
- Prometheus + Grafana:监控系统指标
- ELK:日志收集分析
- Sentry:错误追踪
7. 开发经验总结
7.1 遇到的典型问题
商品搜索性能问题:
- 现象:当商品量超过10万时,模糊查询响应时间超过2秒
- 排查:发现LIKE查询没有使用索引
- 解决:引入Elasticsearch实现全文检索
并发交易问题:
- 现象:同一商品被多人同时下单
- 排查:缺乏乐观锁控制
- 解决:添加版本号控制
java复制@Transactional
public boolean createTransaction(Long commodityId, Long userId) {
Commodity commodity = commodityMapper.selectForUpdate(commodityId);
if (commodity.getStatus() != AVAILABLE) {
return false;
}
commodity.setStatus(TRADING);
commodityMapper.update(commodity);
// 创建交易记录...
return true;
}
7.2 值得分享的技巧
-
API文档生成:
- 使用Swagger UI自动生成文档
- 配置示例值和枚举值说明
-
前后端协作:
- 定义统一的API响应格式
- 使用Mock.js模拟接口数据
-
代码质量保障:
- SonarQube静态代码分析
- JaCoCo代码覆盖率检查
- Git Hooks防止低级错误提交
这个项目从技术选型到最终上线历时4个月,期间遇到了各种预料之外的问题,但也积累了宝贵的实战经验。特别提醒后来者注意交易并发控制和搜索性能优化这两个重点难点。