# Spring Boot 3与Vue3全栈实践:构建企业级电商系统
在数字化转型的浪潮中,电商系统作为企业核心业务平台,其技术架构的现代化程度直接影响着业务发展。基于Spring Boot 3与Vue3的全栈技术栈,为企业级商城系统提供了高性能、可扩展的解决方案。
## 后端架构设计与核心模块
Spring Boot 3提供了现代化的Java开发体验,结合Spring Security 6和Spring Data JPA 3构建稳定的后端服务。
```java
// ProductController.java - 商品管理API
package com.mall.product.controller;
import com.mall.product.dto.*;
import com.mall.product.service.ProductService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/v1/products")
@RequiredArgsConstructor
@SecurityRequirement(name = "bearerAuth")
public class ProductController {
private final ProductService productService;
@Operation(summary = "分页查询商品列表")
@GetMapping
public ResponseEntity
@RequestParam(required = false) Long categoryId,
@RequestParam(required = false) String keyword,
@RequestParam(required = false) Integer minPrice,
@RequestParam(required = false) Integer maxPrice,
@RequestParam(required = false, defaultValue = "false") Boolean onSale,
Pageable pageable) {
ProductQueryDTO query = ProductQueryDTO.builder()
.categoryId(categoryId)
.keyword(keyword)
.minPrice(minPrice)
.maxPrice(maxPrice)
.onSale(onSale)
.build();
Page
return ResponseEntity.ok(products);
}
@Operation(summary = "获取商品详情")
@GetMapping("/{productId}")
public ResponseEntity
ProductDetailDTO product = productService.getProductDetail(productId);
return ResponseEntity.ok(product);
}
@Operation(summary = "创建商品")
@PostMapping
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity
ProductDTO product = productService.createProduct(createDTO);
return ResponseEntity.status(HttpStatus.CREATED).body(product);
}
@Operation(summary = "更新商品信息")
@PutMapping("/{productId}")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity
@PathVariable Long productId,
@Valid @RequestBody UpdateProductDTO updateDTO) {
ProductDTO product = productService.updateProduct(productId, updateDTO);
return ResponseEntity.ok(product);
}
@Operation(summary = "批量更新商品状态")
@PatchMapping("/batch-status")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity
@Valid @RequestBody BatchStatusUpdateDTO updateDTO) {
productService.batchUpdateStatus(updateDTO);
return ResponseEntity.ok().build();
}
@Operation(summary = "获取商品库存")
@GetMapping("/{productId}/inventory")
public ResponseEntity
InventoryDTO inventory = productService.getInventory(productId);
return ResponseEntity.ok(inventory);
}
@Operation(summary = "调整商品库存")
@PostMapping("/{productId}/inventory/adjust")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity
@PathVariable Long productId,
@Valid @RequestBody InventoryAdjustDTO adjustDTO) {
InventoryDTO inventory = productService.adjustInventory(productId, adjustDTO);
return ResponseEntity.ok(inventory);
}
}
// ProductService.java - 商品服务层
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ProductService {
private final ProductRepository productRepository;
private final CategoryRepository categoryRepository;
private final InventoryRepository inventoryRepository;
private final ProductMapper productMapper;
private final CacheManager cacheManager;
@Transactional
public ProductDTO createProduct(CreateProductDTO createDTO) {
// 验证分类是否存在
Category category = categoryRepository.findById(createDTO.getCategoryId())
.orElseThrow(() -> new ResourceNotFoundException("分类不存在"));
// 创建商品
Product product = Product.builder()
.name(createDTO.getName())
.description(createDTO.getDescription())
.price(createDTO.getPrice())
.originalPrice(createDTO.getOriginalPrice())
.category(category)
.images(createDTO.getImages())
.specifications(createDTO.getSpecifications())
.status(ProductStatus.DRAFT)
.build();
// 保存商品
Product savedProduct = productRepository.save(product);
// 初始化库存
Inventory inventory = Inventory.builder()
.product(savedProduct)
.sku(createDTO.getSku())
.stock(createDTO.getInitialStock())
.lockStock(0)
.warningStock(10)
.build();
inventoryRepository.save(inventory);
// 清除缓存
evictProductCache(savedProduct.getId());
return productMapper.toDTO(savedProduct);
}
public Page
String cacheKey = generateCacheKey(query, pageable);
// 尝试从缓存获取
Cache cache = cacheManager.getCache("products");
if (cache != null) {
Page
if (cached != null) {
return cached;
}
}
// 构建查询条件
Specification
if (query.getCategoryId() != null) {
spec = spec.and((root, cq, cb) ->
cb.equal(root.get("category").get("id"), query.getCategoryId()));
}
if (StringUtils.hasText(query.getKeyword())) {
spec = spec.and((root, cq, cb) ->
cb.or(
cb.like(root.get("name"), "%" + query.getKeyword() + "%"),
cb.like(root.get("description"), "%" + query.getKeyword() + "%")
));
}
if (query.getMinPrice() != null) {
spec = spec.and((root, cq, cb) ->
cb.greaterThanOrEqualTo(root.get("price"), query.getMinPrice()));
}
if (query.getMaxPrice() != null) {
spec = spec.and((root, cq, cb) ->
cb.lessThanOrEqualTo(root.get("price"), query.getMaxPrice()));
}
if (query.getOnSale() != null) {
spec = spec.and((root, cq, cb) ->
cb.equal(root.get("status"), ProductStatus.ON_SALE));
}
// 执行查询
Page
Page
// 缓存结果
if (cache != null) {
cache.put(cacheKey, result);
}
return result;
}
private void evictProductCache(Long productId) {
Cache cache = cacheManager.getCache("products");
if (cache != null) {
cache.evictIfPresent("product:" + productId);
cache.evictIfPresent("product_detail:" + productId);
}
}
}
```
## 前端Vue3架构设计
Vue3的Composition API和TypeScript支持为大型前端应用提供了更好的类型安全和代码组织。
```vue
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useProductStore } from '@/stores/product'
import { useCartStore } from '@/stores/cart'
import { Product, ProductQuery } from '@/types/product'
import ProductCard from '@/components/product/ProductCard.vue'
import Pagination from '@/components/common/Pagination.vue'
import FilterSidebar from '@/components/product/FilterSidebar.vue'
import { message } from 'ant-design-vue'
const router = useRouter()
const productStore = useProductStore()
const cartStore = useCartStore()
// 响应式数据
const loading = ref(false)
const filterVisible = ref(false)
// 查询参数
const query = ref
page: 1,
pageSize: 12,
keyword: '',
categoryId: undefined,
minPrice: undefined,
maxPrice: undefined,
sortBy: 'createdAt',
sortOrder: 'desc'
})
// 计算属性
const products = computed(() => productStore.products)
const pagination = computed(() => productStore.pagination)
const total = computed(() => pagination.value?.total || 0)
// 方法
const fetchProducts = async () => {
loading.value = true
try {
await productStore.fetchProducts(query.value)
} catch (error) {
message.error('加载商品列表失败')
console.error('Failed to fetch products:', error)
} finally {
loading.value = false
}
}
const handlePageChange = (page: number) => {
query.value.page = page
fetchProducts()
}
const handleFilterChange = (newQuery: Partial
query.value = { ...query.value, ...newQuery, page: 1 }
fetchProducts()
}
const handleAddToCart = async (product: Product) => {
try {
await cartStore.addToCart({
productId: product.id,
sku: product.sku,
quantity: 1,
price: product.price
})
message.success('已添加到购物车')
} catch (error) {
message.error('添加到购物车失败')
}
}
const handleQuickView = (product: Product) => {
router.push(`/product/${product.id}`)
}
// 生命周期
onMounted(() => {
fetchProducts()
})
v-model:value="query.keyword"
placeholder="搜索商品"
@search="handleFilterChange({ keyword: query.keyword })"
/>
筛选
v-model:visible="filterVisible"
:query="query"
@filter-change="handleFilterChange"
/>
v-for="product in products"
:key="product.id"
:product="product"
@add-to-cart="handleAddToCart"
@quick-view="handleQuickView"
/>
<"svx.j9k5.org.cn"><"1t.p5k3.org.cn"><"5m.p5k3.org.cn">
:current="query.page"
:page-size="query.pageSize"
:total="total"
@change="handlePageChange"
/>
.product-list-page {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
.search-bar {
display: flex;
gap: 16px;
margin-bottom: 24px;
}
.product-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 24px;
margin-bottom: 32px;
}
.empty-state {
grid-column: 1 / -1;
text-align: center;
padding: 80px 0;
}
.pagination-wrapper {
display: flex;
justify-content: center;
margin-top: 32px;
}
```
## 购物车与订单处理
```java
// OrderService.java - 订单服务
@Service
@RequiredArgsConstructor
@Transactional
public class OrderService {
private final OrderRepository orderRepository;
private final OrderItemRepository orderItemRepository;
private final ProductRepository productRepository;
private final InventoryService inventoryService;
private final PaymentService paymentService;
private final MessageQueueService messageQueueService;
private final OrderMapper orderMapper;
@Transactional
public OrderDTO createOrder(CreateOrderDTO createDTO, Long userId) {
// 验证购物车商品
List
validateCartItems(cartItems);
// 锁定库存
List
try {
// 计算订单总金额
BigDecimal totalAmount = calculateOrderTotal(cartItems);
BigDecimal discountAmount = calculateDiscount(createDTO.getCouponCode(), totalAmount);
BigDecimal finalAmount = totalAmount.subtract(discountAmount);
// 创建订单
Order order = Order.builder()
.orderNumber(generateOrderNumber())
.userId(userId)
.totalAmount(totalAmount)
.discountAmount(discountAmount)
.finalAmount(finalAmount)
.shippingAddress(createDTO.getShippingAddress())
.contactInfo(createDTO.getContactInfo())
.status(OrderStatus.PENDING_PAYMENT)
.paymentMethod(createDTO.getPaymentMethod())
.build();
Order savedOrder = orderRepository.save(order);
// 创建订单项
List
orderItemRepository.saveAll(orderItems);
// 生成支付订单
PaymentDTO paymentDTO = paymentService.createPayment(
PaymentRequestDTO.builder()
.orderId(savedOrder.getId())
.amount(finalAmount)
.paymentMethod(createDTO.getPaymentMethod())
.subject("商城订单支付")
.build()
);
// 更新订单支付信息
savedOrder.setPaymentId(paymentDTO.getPaymentId());
savedOrder.setPaymentStatus(paymentDTO.getStatus());
orderRepository.save(savedOrder);
// 发送订单创建事件
messageQueueService.sendOrderCreatedEvent(savedOrder.getId());
return orderMapper.toDTO(savedOrder);
} catch (Exception e) {
// 解锁库存
inventoryService.unlockInventory(lockResults);
throw new BusinessException("订单创建失败", e);
}
}
@Transactional
public void processOrderPayment(Long orderId, PaymentResultDTO paymentResult) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new ResourceNotFoundException("订单不存在"));
if (order.getStatus() != OrderStatus.PENDING_PAYMENT) {
throw new BusinessException("订单状态不正确");
}
if (paymentResult.isSuccess()) {
// 支付成功,更新订单状态
order.setStatus(OrderStatus.PAID);
order.setPaidAt(LocalDateTime.now());
order.setPaymentStatus(PaymentStatus.SUCCESS);
// 扣减库存
List
inventoryService.reduceInventory(items);
// 发送订单支付成功事件
messageQueueService.sendOrderPaidEvent(orderId);
} else {
// 支付失败,解锁库存
order.setStatus(OrderStatus.PAYMENT_FAILED);
order.setPaymentStatus(PaymentStatus.FAILED);
List
inventoryService.unlockInventoryByOrder(items);
}
orderRepository.save(order);
}
@Transactional
public void cancelOrder(Long orderId, Long userId) {
Order order = orderRepository.findByIdAndUserId(orderId, userId)
.orElseThrow(() -> new ResourceNotFoundException("订单不存在"));
if (!order.isCancellable()) {
throw new BusinessException("当前订单状态不可取消");
}
order.setStatus(OrderStatus.CANCELLED);
order.setCancelledAt(LocalDateTime.now());
// 解锁库存
List
inventoryService.unlockInventoryByOrder(items);
orderRepository.save(order);
// 发送订单取消事件
messageQueueService.sendOrderCancelledEvent(orderId);
}
private void validateCartItems(List
if (cartItems == null || cartItems.isEmpty()) {
throw new BusinessException("购物车为空");
}
for (CartItemDTO item : cartItems) {
Product product = productRepository.findById(item.getProductId())
.orElseThrow(() -> new ResourceNotFoundException("商品不存在"));
if (!product.isOnSale()) {
throw new BusinessException("商品" + product.getName() + "已下架");
}
if (product.getPrice().compareTo(item.getPrice()) != 0) {
throw new BusinessException("商品价格已发生变化");
}
}
}
private String generateOrderNumber() {
return "ORD" + System.currentTimeMillis() + RandomUtils.nextInt(1000, 9999);
}
}
```
## 用户认证与安全配置
```java
// SecurityConfig.java - 安全配置
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {
private final UserDetailsService userDetailsService;
private final JwtAuthenticationFilter jwtAuthenticationFilter;
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.csrf(AbstractHttpConfigurer::disable)
.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/api/v1/auth/**",
"/api/v1/products/**",
"/api/v1/categories/**",
"/swagger-ui/**",
"/v3/api-docs/**",
"/webjars/**"
).permitAll()
.requestMatchers("/api/v1/admin/**").hasRole("ADMIN")
.requestMatchers("/api/v1/user/**").hasAnyRole("USER", "ADMIN")
.anyRequest().authenticated()
)
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
.exceptionHandling(exception -> exception
.authenticationEntryPoint(new JwtAuthenticationEntryPoint())
.accessDeniedHandler(new JwtAccessDeniedHandler())
);
return http.build();
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManager(
AuthenticationConfiguration authConfig) throws Exception {
return authConfig.getAuthenticationManager();
}
}
<"9z.p5k3.org.cn"><"2h.p5k3.org.cn"><"x7.p5k3.org.cn">
// JwtService.java - JWT令牌服务
@Service
@RequiredArgsConstructor
public class JwtService {
@Value("${jwt.secret}")
private String secretKey;
@Value("${jwt.expiration}")
private Long expiration;
public String generateToken(UserDetails userDetails) {
Map
claims.put("username", userDetails.getUsername());
claims.put("authorities", 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))
.signWith(getSigningKey(), SignatureAlgorithm.HS256)
.compact();
}
public boolean validateToken(String token) {
try {
Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token);
return true;
} catch (JwtException | IllegalArgumentException e) {
return false;
}
}
public String extractUsername(String token) {
return extractAllClaims(token).getSubject();
}
public List
List
return authorities.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}
private Claims extractAllClaims(String token) {
return Jwts.parserBuilder()
.setSigningKey(getSigningKey())
.build()
.parseClaimsJws(token)
.getBody();
}
private Key getSigningKey() {
byte[] keyBytes = Decoders.BASE64.decode(secretKey);
return Keys.hmacShaKeyFor(keyBytes);
}
}
```
## 支付与第三方集成
```java
// PaymentService.java - 支付服务
@Service
@RequiredArgsConstructor
public class PaymentService {
private final AlipayClient alipayClient;
private final WechatPayClient wechatPayClient;
private final PaymentRepository paymentRepository;
private final OrderService orderService;
@Async
public PaymentDTO createPayment(PaymentRequestDTO request) {
Payment payment = Payment.builder()
.orderId(request.getOrderId())
.amount(request.getAmount())
.paymentMethod(request.getPaymentMethod())
.status(PaymentStatus.PENDING)
.subject(request.getSubject())
.build();
Payment savedPayment = paymentRepository.save(payment);
// 根据支付方式调用不同的支付渠道
PaymentResponse response;
switch (request.getPaymentMethod()) {
case ALIPAY:
response = createAlipayPayment(savedPayment);
break;
case WECHAT_PAY:
response = createWechatPayPayment(savedPayment);
break;
default:
throw new BusinessException("不支持的支付方式");
}
savedPayment.setPaymentUrl(response.getPaymentUrl());
savedPayment.setPaymentId(response.getPaymentId());
paymentRepository.save(savedPayment);
return PaymentDTO.builder()
.paymentId(savedPayment.getId())
.paymentUrl(response.getPaymentUrl())
.paymentId(response.getPaymentId())
.status(savedPayment.getStatus())
.build();
}
private PaymentResponse createAlipayPayment(Payment payment) {
try {
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", payment.getId());
bizContent.put("total_amount", payment.getAmount());
bizContent.put("subject", payment.getSubject());
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
request.setBizContent(bizContent.toJSONString());
request.setNotifyUrl(paymentCallbackProperties.getAlipayNotifyUrl());
request.setReturnUrl(paymentCallbackProperties.getAlipayReturnUrl());
AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
return PaymentResponse.builder()
.paymentUrl(response.getBody())
.paymentId(response.getTradeNo())
.build();
} catch (AlipayApiException e) {
throw new PaymentException("支付宝支付创建失败", e);
}
}
@EventListener
public void handlePaymentNotification(PaymentNotificationEvent event) {
// 验证支付通知的签名
if (!verifyPaymentNotification(event)) {
throw new SecurityException("支付通知签名验证失败");
}
PaymentResultDTO result = PaymentResultDTO.builder()
.orderId(event.getOrderId())
.paymentId(event.getPaymentId())
.amount(event.getAmount())
.success(event.isSuccess())
.paymentTime(event.getPaymentTime())
.build();
// 更新订单支付状态
orderService.processOrderPayment(result.getOrderId(), result);
}
}
```
基于Spring Boot 3与Vue3的全栈企业级商城系统,通过现代化的技术栈和架构设计,实现了高性能、可扩展的电商平台。后端采用微服务架构和领域驱动设计,前端采用组件化开发和状态管理,同时集成支付、物流等第三方服务。在实际部署中,还需要考虑缓存策略、数据库优化、监控告警等运维层面,确保系统的高可用性和稳定性。