欢迪迈手机商城是一个基于SpringBoot+Vue+MySQL技术栈的电子商务平台,专为中小型手机零售商设计。作为一名有多年全栈开发经验的工程师,我认为这个项目最大的价值在于它完整呈现了一个现代化电商系统的核心架构和实现细节。
在当前的移动互联网环境下,手机购物已经成为主流消费方式。根据我的项目经验,一个合格的电商平台需要同时满足三个核心需求:稳定的交易流程、良好的用户体验和可扩展的技术架构。这个项目通过前后端分离的设计,很好地平衡了这三方面的要求。
从技术选型来看,SpringBoot提供了企业级的后端支持,Vue.js构建了响应式的前端界面,MySQL则确保了数据存储的可靠性。这种组合在中小型电商项目中非常典型,我在多个商业项目中都验证过其可行性。
选择SpringBoot作为后端框架主要基于以下几个考虑:
Vue.js作为前端框架的优势在于:
MySQL数据库的选择则考虑了:
系统采用典型的前后端分离架构,这种设计带来了几个显著优势:
在实际部署时,我建议使用Nginx作为前端静态资源服务器,同时配置反向代理到后端SpringBoot应用。这种部署方式在我参与的商业项目中表现非常稳定。
sql复制CREATE TABLE `user_info` (
`user_id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password_hash` varchar(100) NOT NULL,
`phone_number` varchar(20) DEFAULT NULL,
`email` varchar(50) DEFAULT NULL,
`register_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_login` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`user_id`),
UNIQUE KEY `idx_username` (`username`),
UNIQUE KEY `idx_phone` (`phone_number`),
UNIQUE KEY `idx_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
sql复制CREATE TABLE `product_info` (
`product_id` bigint NOT NULL AUTO_INCREMENT,
`product_name` varchar(100) NOT NULL,
`category` varchar(50) NOT NULL,
`price` decimal(10,2) NOT NULL,
`stock` int NOT NULL DEFAULT '0',
`description` text,
`publish_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`status` tinyint NOT NULL DEFAULT '1',
PRIMARY KEY (`product_id`),
KEY `idx_category` (`category`),
KEY `idx_price` (`price`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
sql复制CREATE TABLE `order_info` (
`order_id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL,
`product_id` bigint NOT NULL,
`quantity` int NOT NULL,
`total_price` decimal(10,2) NOT NULL,
`payment_status` varchar(20) NOT NULL DEFAULT 'unpaid',
`order_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`payment_time` timestamp NULL DEFAULT NULL,
`shipping_address` varchar(200) NOT NULL,
PRIMARY KEY (`order_id`),
KEY `idx_user` (`user_id`),
KEY `idx_product` (`product_id`),
KEY `idx_status` (`payment_status`),
KEY `idx_time` (`order_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
根据我的项目经验,电商系统的数据库设计有几个关键点需要注意:
索引策略:除了主键索引外,查询频繁的字段如用户ID、商品分类、价格区间等都需要建立合适的索引。但要注意索引不是越多越好,过多的索引会影响写入性能。
字段类型选择:
分表考虑:当订单数据量很大时(比如超过500万条),需要考虑按时间范围分表存储。可以使用Spring的AbstractRoutingDataSource实现动态数据源切换。
缓存层设计:热门商品信息可以使用Redis缓存,减轻数据库压力。我通常采用"先读缓存,缓存不存在再查DB"的策略。
用户认证采用经典的JWT(JSON Web Token)方案,后端实现主要包含以下几个部分:
java复制// 使用BCrypt加密密码
public String encodePassword(String rawPassword) {
return new BCryptPasswordEncoder().encode(rawPassword);
}
// 验证密码
public boolean matches(String rawPassword, String encodedPassword) {
return new BCryptPasswordEncoder().matches(rawPassword, encodedPassword);
}
java复制// 生成Token
public String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS512, SECRET_KEY)
.compact();
}
// 验证Token
public Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/products/**").permitAll()
.antMatchers("/api/cart/**").authenticated()
.antMatchers("/api/order/**").authenticated()
.and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
商品模块实现了以下几个关键功能:
java复制@GetMapping("/products")
public Page<Product> getProducts(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String category) {
Pageable pageable = PageRequest.of(page, size, Sort.by("publishTime").descending());
if (category != null && !category.isEmpty()) {
return productRepository.findByCategory(category, pageable);
}
return productRepository.findAll(pageable);
}
java复制@GetMapping("/products/search")
public List<Product> searchProducts(@RequestParam String keyword) {
return productRepository.findByProductNameContainingOrDescriptionContaining(keyword, keyword);
}
java复制@Cacheable(value = "productDetail", key = "#productId")
public Product getProductDetail(Long productId) {
return productRepository.findById(productId)
.orElseThrow(() -> new ResourceNotFoundException("Product not found"));
}
购物车功能需要考虑以下几个技术点:
java复制@Entity
@Table(name = "shopping_cart")
public class ShoppingCart {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
private User user;
@ManyToOne
@JoinColumn(name = "product_id", nullable = false)
private Product product;
private Integer quantity;
@Column(name = "created_at", updatable = false)
private LocalDateTime createdAt = LocalDateTime.now();
@Column(name = "updated_at")
private LocalDateTime updatedAt = LocalDateTime.now();
// getters and setters
}
java复制@Transactional
public Order createOrder(Long userId, List<CartItemDTO> cartItems, String shippingAddress) {
// 1. 验证库存
checkStock(cartItems);
// 2. 创建订单
Order order = new Order();
order.setUser(userRepository.findById(userId).orElseThrow());
order.setShippingAddress(shippingAddress);
order.setStatus(OrderStatus.CREATED);
// 3. 添加订单项
List<OrderItem> orderItems = new ArrayList<>();
for (CartItemDTO item : cartItems) {
OrderItem orderItem = new OrderItem();
orderItem.setProduct(productRepository.findById(item.getProductId()).orElseThrow());
orderItem.setQuantity(item.getQuantity());
orderItem.setPrice(item.getPrice());
orderItems.add(orderItem);
}
order.setOrderItems(orderItems);
// 4. 计算总价
BigDecimal totalPrice = orderItems.stream()
.map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
order.setTotalPrice(totalPrice);
// 5. 扣减库存
deductStock(cartItems);
// 6. 保存订单
return orderRepository.save(order);
}
java复制public PaymentResponse processPayment(Order order, PaymentRequest paymentRequest) {
// 调用第三方支付API
PaymentGatewayResponse gatewayResponse = paymentGatewayClient.createPayment(
order.getId().toString(),
order.getTotalPrice(),
paymentRequest.getPaymentMethod(),
paymentRequest.getCardInfo());
// 更新订单状态
if (gatewayResponse.isSuccess()) {
order.setStatus(OrderStatus.PAID);
order.setPaymentTime(LocalDateTime.now());
orderRepository.save(order);
// 发送支付成功通知
notificationService.sendPaymentSuccessNotification(order.getUser(), order);
}
return new PaymentResponse(gatewayResponse.isSuccess(), gatewayResponse.getMessage());
}
bash复制mvn clean package -DskipTests
上传jar包到服务器
启动应用:
bash复制nohup java -jar mobile-shop-backend.jar --spring.profiles.active=prod > app.log 2>&1 &
bash复制npm run build
nginx复制server {
listen 80;
server_name yourdomain.com;
location / {
root /path/to/dist;
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
ANALYZE TABLE和OPTIMIZE TABLE问题1:MySQL连接失败
解决方案:
问题2:前端跨域访问
解决方案:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*");
}
}
问题1:高并发下的性能瓶颈
解决方案:
问题2:支付接口超时
解决方案:
问题1:慢查询
解决方案:
问题2:数据一致性
解决方案:
通过这个项目的开发,我深刻体会到电商系统的几个关键设计要点:
数据一致性:在订单、库存等核心业务上必须保证数据准确,采用合适的事务隔离级别和锁机制。
性能考量:从数据库设计到缓存策略,每个环节都需要考虑性能影响。特别是在促销活动期间,系统需要能够应对流量高峰。
可扩展性:良好的架构设计应该能够方便地扩展新功能,比如添加新的支付方式或物流渠道。
安全性:用户数据、支付信息都需要严格保护,实现完善的认证授权机制。
对于初学者,我建议先重点理解核心业务流程,如用户下单到支付的完整链路。然后再逐步深入各个模块的实现细节。在开发过程中,多写单元测试和集成测试,确保每个功能模块的可靠性。