1. 项目概述
作为一名从事电商系统开发多年的Java工程师,我最近完成了一个基于Spring Boot的美妆销售系统开发项目。这个系统从零开始构建,前后历时3个月,目前已经稳定运行在客户的生产环境中。今天我想分享一下这个项目的完整开发过程和其中的技术细节。
美妆电商行业近年来发展迅猛,但很多中小型商家依然在使用传统的销售管理系统,这些系统往往存在性能瓶颈、功能单一、扩展性差等问题。我们开发的美妆销售系统正是为了解决这些痛点,为商家提供一个高性能、易扩展、功能完善的电商解决方案。
2. 技术选型与架构设计
2.1 技术栈选择
在项目初期,我们经过多次技术评估会议,最终确定了以下技术栈:
后端技术:
- Spring Boot 2.7.3:作为基础框架,提供快速开发能力
- Spring Security:负责系统安全和权限控制
- MyBatis-Plus:简化数据库操作
- Redis:缓存热点数据,提高系统响应速度
- RabbitMQ:处理异步任务,如订单状态更新、消息通知等
前端技术:
- Vue.js 3:构建用户友好的前端界面
- Element Plus:提供丰富的UI组件
- Axios:处理HTTP请求
数据库:
- MySQL 8.0:主数据库,存储业务数据
- MongoDB:存储非结构化数据,如用户行为日志
基础设施:
- Docker:容器化部署
- Nginx:负载均衡和反向代理
- Jenkins:持续集成和部署
2.2 系统架构设计
我们采用了经典的分层架构设计:
code复制表现层(Web) → 业务逻辑层(Service) → 数据访问层(DAO) → 数据库
同时引入了以下设计模式:
- 工厂模式:创建复杂对象
- 策略模式:处理不同的支付方式
- 观察者模式:实现事件通知机制
系统架构图如下:

提示:在实际开发中,我们特别注意了各层之间的解耦,确保系统具有良好的扩展性和维护性。
3. 核心功能实现
3.1 用户模块
用户模块采用了JWT(JSON Web Token)进行身份认证,主要包含以下功能点:
- 注册流程:
java复制@PostMapping("/register")
public Result register(@Valid @RequestBody UserRegisterDTO dto) {
if(userService.existsByUsername(dto.getUsername())) {
throw new BusinessException("用户名已存在");
}
User user = new User();
BeanUtils.copyProperties(dto, user);
user.setPassword(passwordEncoder.encode(dto.getPassword()));
userService.save(user);
// 发送激活邮件
mailService.sendActivationEmail(user);
return Result.success();
}
- 登录流程:
java复制@PostMapping("/login")
public Result login(@RequestBody LoginDTO dto) {
Authentication authentication = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(dto.getUsername(), dto.getPassword())
);
SecurityContextHolder.getContext().setAuthentication(authentication);
UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
String jwt = jwtUtils.generateJwtToken(userDetails);
return Result.success(new JwtResponse(jwt));
}
3.2 商品模块
商品模块采用了Elasticsearch实现高效的搜索功能:
- 商品搜索实现:
java复制public Page<Product> searchProducts(String keyword, Integer page, Integer size) {
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
if(StringUtils.isNotBlank(keyword)) {
queryBuilder.withQuery(QueryBuilders.multiMatchQuery(keyword, "name", "description", "brand"));
}
queryBuilder.withPageable(PageRequest.of(page, size));
queryBuilder.withSort(SortBuilders.fieldSort("sales").order(SortOrder.DESC));
SearchHits<Product> searchHits = elasticsearchRestTemplate.search(
queryBuilder.build(), Product.class);
List<Product> products = searchHits.stream()
.map(SearchHit::getContent)
.collect(Collectors.toList());
return new PageImpl<>(products, PageRequest.of(page, size), searchHits.getTotalHits());
}
- 商品详情缓存策略:
java复制@Cacheable(value = "product", key = "#id")
public Product getProductById(Long id) {
return productMapper.selectById(id);
}
@CachePut(value = "product", key = "#product.id")
public Product updateProduct(Product product) {
productMapper.updateById(product);
return product;
}
@CacheEvict(value = "product", key = "#id")
public void deleteProduct(Long id) {
productMapper.deleteById(id);
}
3.3 订单模块
订单模块采用了分布式事务保证数据一致性:
- 创建订单流程:
java复制@Transactional
public Order createOrder(OrderCreateDTO dto, Long userId) {
// 1. 验证库存
List<OrderItem> items = validateStock(dto.getItems());
// 2. 扣减库存
reduceStock(items);
// 3. 生成订单
Order order = buildOrder(dto, userId, items);
orderMapper.insert(order);
// 4. 发送订单创建事件
rabbitTemplate.convertAndSend("order.event.exchange",
"order.create",
new OrderEvent(order.getId(), userId));
return order;
}
- 支付回调处理:
java复制@Transactional
public void handlePaymentCallback(PaymentCallbackDTO callback) {
Order order = orderMapper.selectById(callback.getOrderId());
if(order.getStatus() != OrderStatus.UNPAID) {
throw new BusinessException("订单状态异常");
}
// 更新订单状态
order.setStatus(OrderStatus.PAID);
order.setPayTime(LocalDateTime.now());
orderMapper.updateById(order);
// 发送支付成功通知
notificationService.sendPaymentSuccessNotification(order.getUserId(), order.getId());
}
4. 性能优化实践
4.1 数据库优化
- 索引优化:
sql复制-- 为常用查询字段添加索引
CREATE INDEX idx_product_category ON product(category_id);
CREATE INDEX idx_product_status ON product(status);
CREATE INDEX idx_order_user ON `order`(user_id);
- 分表策略:
java复制// 使用Sharding-JDBC实现订单表按月分表
spring.shardingsphere.sharding.tables.order.actual-data-nodes=ds0.order_$->{2023..2030}0$->{1..9},ds0.order_$->{2023..2030}1$->{0..2}
spring.shardingsphere.sharding.tables.order.table-strategy.standard.sharding-column=create_time
spring.shardingsphere.sharding.tables.order.table-strategy.standard.precise-algorithm-class-name=com.example.sharding.TimeShardingAlgorithm
4.2 缓存策略
我们采用了多级缓存架构:
- 本地缓存(Caffeine):缓存热点数据,如商品分类
- 分布式缓存(Redis):缓存用户会话、商品详情等
- CDN缓存:静态资源缓存
缓存更新策略:
java复制@CacheEvict(value = "product", key = "#productId")
public void updateProductCache(Long productId) {
// 清除缓存后,下次查询会自动从数据库加载最新数据
}
@Scheduled(fixedRate = 30 * 60 * 1000) // 每30分钟刷新一次
public void refreshHotProducts() {
List<Product> hotProducts = productMapper.selectHotProducts();
redisTemplate.opsForValue().set("hot:products", hotProducts, 1, TimeUnit.HOURS);
}
4.3 高并发处理
- 秒杀场景解决方案:
java复制public boolean seckill(Long productId, Long userId) {
// 1. 验证库存(Redis原子操作)
Long remain = redisTemplate.opsForValue().decrement("seckill:stock:" + productId);
if(remain < 0) {
redisTemplate.opsForValue().increment("seckill:stock:" + productId);
return false;
}
// 2. 生成预订单(异步处理)
rabbitTemplate.convertAndSend("seckill.order.queue",
new SeckillOrder(userId, productId));
return true;
}
- 限流策略:
java复制@RateLimiter(value = 100, key = "'product:' + #productId")
public ProductDetailDTO getProductDetail(Long productId) {
return productService.getDetail(productId);
}
5. 安全防护措施
5.1 常见安全防护
- XSS防护:
java复制@Bean
public FilterRegistrationBean<XssFilter> xssFilter() {
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new XssFilter());
registration.addUrlPatterns("/*");
registration.setName("xssFilter");
return registration;
}
- CSRF防护:
java复制@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse());
}
- SQL注入防护:
- 使用MyBatis预编译语句
- 禁止拼接SQL
- 使用MyBatis-Plus提供的Wrapper构建查询条件
5.2 数据安全
- 敏感数据加密:
java复制public class SensitiveDataEncryptor implements AttributeConverter<String, String> {
@Override
public String convertToDatabaseColumn(String attribute) {
return AESUtil.encrypt(attribute);
}
@Override
public String convertToEntityAttribute(String dbData) {
return AESUtil.decrypt(dbData);
}
}
@Entity
public class User {
@Convert(converter = SensitiveDataEncryptor.class)
private String phone;
@Convert(converter = SensitiveDataEncryptor.class)
private String email;
}
- 日志脱敏:
java复制@Around("execution(* com.example..*.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] args = joinPoint.getArgs();
// 对参数进行脱敏处理
desensitizeArgs(args);
Object result = joinPoint.proceed(args);
// 对返回结果进行脱敏处理
return desensitizeResult(result);
}
6. 部署与监控
6.1 容器化部署
我们使用Docker Compose编排服务:
yaml复制version: '3.8'
services:
app:
image: beauty-sale-app:${VERSION}
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=prod
depends_on:
- redis
- mysql
- rabbitmq
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
MYSQL_DATABASE: beauty_sale
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:6.2
ports:
- "6379:6379"
volumes:
- redis_data:/data
rabbitmq:
image: rabbitmq:3.9-management
ports:
- "5672:5672"
- "15672:15672"
volumes:
- rabbitmq_data:/var/lib/rabbitmq
volumes:
mysql_data:
redis_data:
rabbitmq_data:
6.2 监控系统
我们集成了以下监控组件:
- Prometheus:收集指标数据
- Grafana:可视化监控数据
- ELK:日志收集和分析
- SkyWalking:分布式追踪
Spring Boot Actuator配置:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
endpoint:
health:
show-details: always
metrics:
export:
prometheus:
enabled: true
7. 项目总结与经验分享
7.1 项目成果
经过3个月的开发和1个月的试运行,系统取得了以下成果:
- 日均订单量:5000+
- 峰值QPS:3000+
- 平均响应时间:<200ms
- 系统可用性:99.99%
7.2 经验教训
- 数据库设计经验:
- 不要过度设计,满足当前需求即可
- 预留扩展字段(varchar类型的extra_info)
- 合理使用枚举类型代替字符串
- 缓存使用经验:
- 缓存穿透:使用布隆过滤器
- 缓存雪崩:设置不同的过期时间
- 缓存击穿:使用互斥锁
- 性能调优经验:
java复制// 不好的写法:N+1查询问题
List<Order> orders = orderMapper.selectList(queryWrapper);
for(Order order : orders) {
User user = userMapper.selectById(order.getUserId());
order.setUser(user);
}
// 好的写法:批量查询
List<Order> orders = orderMapper.selectList(queryWrapper);
List<Long> userIds = orders.stream().map(Order::getUserId).distinct().collect(Collectors.toList());
Map<Long, User> userMap = userMapper.selectBatchIds(userIds).stream()
.collect(Collectors.toMap(User::getId, Function.identity()));
orders.forEach(order -> order.setUser(userMap.get(order.getUserId())));
- 团队协作经验:
- 使用Git Flow工作流
- 代码审查必须严格执行
- 自动化测试覆盖率要达到80%以上
7.3 未来优化方向
- 技术债务清理:
- 重构部分历史代码
- 统一异常处理机制
- 完善API文档
- 功能扩展:
- 增加直播带货功能
- 实现智能推荐系统
- 开发小程序端
- 性能提升:
- 引入分布式文件系统
- 尝试Service Mesh架构
- 优化JVM参数
这个项目让我深刻体会到,一个成功的电商系统不仅需要完善的功能设计,还需要考虑性能、安全、可扩展性等多方面因素。希望我的分享能对正在开发类似系统的同行有所帮助。