修复文件

This commit is contained in:
2025-07-02 22:39:21 +08:00
parent 3b3ec8ea7d
commit b46312c428
21 changed files with 2233 additions and 650 deletions

View File

@@ -1,8 +1,10 @@
package com.org.flashsalesystem.controller;
import com.org.flashsalesystem.dto.CartDTO;
import com.org.flashsalesystem.dto.OrderDTO;
import com.org.flashsalesystem.dto.UserDTO;
import com.org.flashsalesystem.service.CartService;
import com.org.flashsalesystem.service.OrderService;
import com.org.flashsalesystem.service.UserService;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
@@ -29,6 +31,9 @@ public class CartController {
@Autowired
private CartService cartService;
@Autowired
private OrderService orderService;
@Autowired
private UserService userService;
@@ -66,7 +71,7 @@ public class CartController {
/**
* 更新购物车商品数量
*/
@PutMapping("/update-quantity")
@PutMapping("/update")
public ResponseEntity<Map<String, Object>> updateQuantity(@Validated @RequestBody CartDTO.UpdateQuantityDTO updateDTO,
HttpServletRequest request) {
try {
@@ -125,6 +130,43 @@ public class CartController {
}
}
/**
* 批量删除购物车商品
*/
@DeleteMapping("/batch-remove")
public ResponseEntity<Map<String, Object>> batchRemove(@RequestBody Map<String, Object> requestBody,
HttpServletRequest request) {
try {
Long userId = getCurrentUserId(request);
if (userId == null) {
return createUnauthorizedResponse();
}
@SuppressWarnings("unchecked")
java.util.List<Integer> productIdInts = (java.util.List<Integer>) requestBody.get("productIds");
java.util.List<Long> productIds = productIdInts.stream()
.map(Long::valueOf)
.collect(java.util.stream.Collectors.toList());
CartDTO cart = cartService.batchRemove(userId, productIds);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "批量删除商品成功");
response.put("data", cart);
return ResponseEntity.ok(response);
} catch (Exception e) {
log.error("批量删除购物车商品失败", e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 批量操作购物车
*/
@@ -315,6 +357,47 @@ public class CartController {
}
}
/**
* 购物车下单
*/
@PostMapping("/checkout")
public ResponseEntity<Map<String, Object>> checkoutCart(@RequestBody(required = false) Map<String, Object> requestBody,
HttpServletRequest request) {
try {
Long userId = getCurrentUserId(request);
if (userId == null) {
return createUnauthorizedResponse();
}
// 获取要下单的商品IDs如果为空则下单所有商品
java.util.List<Long> productIds = null;
if (requestBody != null && requestBody.containsKey("productIds")) {
@SuppressWarnings("unchecked")
java.util.List<Integer> productIdInts = (java.util.List<Integer>) requestBody.get("productIds");
productIds = productIdInts.stream()
.map(Long::valueOf)
.collect(java.util.stream.Collectors.toList());
}
OrderDTO order = cartService.checkoutCart(userId, productIds);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "下单成功,请及时支付");
response.put("data", order);
return ResponseEntity.ok(response);
} catch (Exception e) {
log.error("购物车下单失败", e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 获取当前用户ID
*/

View File

@@ -182,6 +182,30 @@ public class FlashSaleController {
}
}
/**
* 预热所有秒杀活动库存(管理员功能)
*/
@PostMapping("/admin/preload-all")
public ResponseEntity<Map<String, Object>> preloadAllFlashSales() {
try {
flashSaleService.preloadAllActiveFlashSales();
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "所有秒杀活动库存预热完成");
return ResponseEntity.ok(response);
} catch (Exception e) {
log.error("预热所有秒杀活动库存失败", e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "预热失败: " + e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 更新秒杀活动
*/
@@ -319,6 +343,110 @@ public class FlashSaleController {
}
}
/**
* 发布秒杀活动
*/
@Operation(summary = "发布秒杀活动", description = "将秒杀活动状态设置为可参与")
@PostMapping("/{id}/publish")
public ResponseEntity<Map<String, Object>> publishFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) {
try {
FlashSaleDTO flashSale = flashSaleService.publishFlashSale(id);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "秒杀活动发布成功");
response.put("data", flashSale);
return ResponseEntity.ok(response);
} catch (Exception e) {
log.error("发布秒杀活动失败", e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 暂停秒杀活动
*/
@Operation(summary = "暂停秒杀活动", description = "暂停正在进行的秒杀活动")
@PostMapping("/{id}/pause")
public ResponseEntity<Map<String, Object>> pauseFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) {
try {
FlashSaleDTO flashSale = flashSaleService.pauseFlashSale(id);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "秒杀活动暂停成功");
response.put("data", flashSale);
return ResponseEntity.ok(response);
} catch (Exception e) {
log.error("暂停秒杀活动失败", e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 恢复秒杀活动
*/
@Operation(summary = "恢复秒杀活动", description = "恢复已暂停的秒杀活动")
@PostMapping("/{id}/resume")
public ResponseEntity<Map<String, Object>> resumeFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) {
try {
FlashSaleDTO flashSale = flashSaleService.resumeFlashSale(id);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "秒杀活动恢复成功");
response.put("data", flashSale);
return ResponseEntity.ok(response);
} catch (Exception e) {
log.error("恢复秒杀活动失败", e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 结束秒杀活动
*/
@Operation(summary = "结束秒杀活动", description = "提前结束秒杀活动")
@PostMapping("/{id}/end")
public ResponseEntity<Map<String, Object>> endFlashSale(@Parameter(description = "秒杀活动ID", required = true) @PathVariable Long id) {
try {
FlashSaleDTO flashSale = flashSaleService.endFlashSale(id);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "秒杀活动结束成功");
response.put("data", flashSale);
return ResponseEntity.ok(response);
} catch (Exception e) {
log.error("结束秒杀活动失败", e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 秒杀压力测试接口
*/

View File

@@ -275,25 +275,129 @@ public class OrderController {
* 支付订单(模拟)
*/
@PostMapping("/{id}/pay")
public ResponseEntity<Map<String, Object>> payOrder(@PathVariable Long id, HttpServletRequest request) {
public ResponseEntity<Map<String, Object>> payOrder(@PathVariable Long id,
@RequestBody(required = false) Map<String, Object> requestBody,
HttpServletRequest request) {
try {
Long userId = getCurrentUserId(request);
if (userId == null) {
return createUnauthorizedResponse();
}
// 这里可以集成真实的支付接口
// 目前只是简单地更新订单状态为已支付
OrderDTO order = orderService.updateOrderStatus(id, 2, "用户支付");
// 验证订单归属
OrderDTO order = orderService.getOrderById(id);
if (order == null) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "订单不存在");
return ResponseEntity.notFound().build();
}
if (!order.getUserId().equals(userId)) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "无权限操作此订单");
return ResponseEntity.status(403).body(response);
}
// 检查订单状态
if (order.getStatus() != 1) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "订单状态不正确,无法支付");
return ResponseEntity.badRequest().body(response);
}
// 模拟支付处理时间(可选)
String paymentMethod = "default";
if (requestBody != null && requestBody.containsKey("paymentMethod")) {
paymentMethod = (String) requestBody.get("paymentMethod");
}
// 模拟支付成功(在实际项目中这里会调用支付接口)
boolean paymentSuccess = simulatePayment(order, paymentMethod);
if (paymentSuccess) {
// 更新订单状态为已支付
OrderDTO updatedOrder = orderService.updateOrderStatus(id, 2, "模拟支付成功 - " + paymentMethod);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "支付成功");
response.put("data", updatedOrder);
response.put("paymentMethod", paymentMethod);
return ResponseEntity.ok(response);
} else {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "支付失败,请重试");
return ResponseEntity.badRequest().body(response);
}
} catch (Exception e) {
log.error("支付订单失败", e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 模拟支付处理
*/
private boolean simulatePayment(OrderDTO order, String paymentMethod) {
try {
log.info("模拟支付处理: 订单ID={}, 金额={}, 支付方式={}",
order.getId(), order.getTotalPrice(), paymentMethod);
// 模拟支付处理时间
Thread.sleep(1000); // 1秒延迟模拟网络请求
// 99%的支付成功率(模拟)
return Math.random() > 0.01;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
/**
* 模拟发货
*/
@PostMapping("/{id}/ship")
public ResponseEntity<Map<String, Object>> shipOrder(@PathVariable Long id) {
try {
// 验证订单状态是否为已支付
OrderDTO order = orderService.getOrderById(id);
if (order == null) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "订单不存在");
return ResponseEntity.notFound().build();
}
if (order.getStatus() != 2) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "订单状态不正确,无法发货");
return ResponseEntity.badRequest().body(response);
}
// 更新订单状态为已发货
OrderDTO updatedOrder = orderService.updateOrderStatus(id, 3, "商家发货");
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "支付成功");
response.put("data", order);
response.put("message", "订单发货成功");
response.put("data", updatedOrder);
return ResponseEntity.ok(response);
} catch (Exception e) {
log.error("支付订单失败", e);
log.error("订单发货失败", e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
@@ -314,12 +418,36 @@ public class OrderController {
return createUnauthorizedResponse();
}
OrderDTO order = orderService.updateOrderStatus(id, 4, "用户确认收货");
// 验证订单归属和状态
OrderDTO order = orderService.getOrderById(id);
if (order == null) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "订单不存在");
return ResponseEntity.notFound().build();
}
if (!order.getUserId().equals(userId)) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "无权限操作此订单");
return ResponseEntity.status(403).body(response);
}
if (order.getStatus() != 3) {
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", "订单状态不正确,无法确认收货");
return ResponseEntity.badRequest().body(response);
}
// 更新订单状态为已完成
OrderDTO updatedOrder = orderService.updateOrderStatus(id, 4, "用户确认收货");
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("message", "确认收货成功");
response.put("data", order);
response.put("message", "确认收货成功,订单已完成");
response.put("data", updatedOrder);
return ResponseEntity.ok(response);
} catch (Exception e) {

View File

@@ -248,6 +248,93 @@ public class PageController {
return "about";
}
/**
* 搜索页面
*/
@GetMapping("/search")
public String search(Model model, @RequestParam(required = false) String q,
@RequestParam(required = false) String category) {
model.addAttribute("pageTitle", "搜索结果");
model.addAttribute("keyword", q);
model.addAttribute("category", category);
return "search";
}
/**
* 分类页面
*/
@GetMapping("/category/{id}")
public String category(@PathVariable Long id, Model model) {
model.addAttribute("pageTitle", "商品分类");
model.addAttribute("categoryId", id);
return "category";
}
/**
* 收藏夹页面
*/
@GetMapping("/favorites")
public String favorites(Model model, HttpServletRequest request) {
if (!isUserLoggedIn(request)) {
return "redirect:/login?returnUrl=/favorites";
}
model.addAttribute("pageTitle", "我的收藏");
return "favorites";
}
/**
* 地址管理页面
*/
@GetMapping("/addresses")
public String addresses(Model model, HttpServletRequest request) {
if (!isUserLoggedIn(request)) {
return "redirect:/login?returnUrl=/addresses";
}
model.addAttribute("pageTitle", "地址管理");
return "addresses";
}
/**
* 优惠券页面
*/
@GetMapping("/coupons")
public String coupons(Model model, HttpServletRequest request) {
if (!isUserLoggedIn(request)) {
return "redirect:/login?returnUrl=/coupons";
}
model.addAttribute("pageTitle", "我的优惠券");
return "coupons";
}
/**
* 统计报表页面
*/
@GetMapping("/admin/reports")
public String adminReports(Model model, HttpServletRequest request) {
if (!isUserLoggedIn(request)) {
return "redirect:/login?returnUrl=/admin/reports";
}
model.addAttribute("pageTitle", "统计报表");
return "admin/reports";
}
/**
* 系统设置页面
*/
@GetMapping("/admin/settings")
public String adminSettings(Model model, HttpServletRequest request) {
if (!isUserLoggedIn(request)) {
return "redirect:/login?returnUrl=/admin/settings";
}
model.addAttribute("pageTitle", "系统设置");
return "admin/settings";
}
/**
* 404错误页面
*/

View File

@@ -97,7 +97,45 @@ public class ProductController {
}
/**
* 获取商品列表
* 获取商品列表GET方法用于页面展示
*/
@GetMapping("/list")
public ResponseEntity<Map<String, Object>> getProductListGet(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "12") int size,
@RequestParam(required = false) String keyword,
@RequestParam(required = false) String category,
@RequestParam(defaultValue = "id") String sortBy,
@RequestParam(defaultValue = "desc") String sortDirection) {
try {
ProductDTO.QueryDTO queryDTO = new ProductDTO.QueryDTO();
queryDTO.setPage(page);
queryDTO.setSize(size);
queryDTO.setKeyword(keyword);
queryDTO.setCategory(category);
queryDTO.setSortBy(sortBy);
queryDTO.setSortDirection(sortDirection);
Map<String, Object> result = productService.getProductList(queryDTO);
Map<String, Object> response = new HashMap<>();
response.put("success", true);
response.put("data", result);
return ResponseEntity.ok(response);
} catch (Exception e) {
log.error("获取商品列表失败", e);
Map<String, Object> response = new HashMap<>();
response.put("success", false);
response.put("message", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
/**
* 获取商品列表POST方法用于复杂查询
*/
@PostMapping("/list")
public ResponseEntity<Map<String, Object>> getProductList(@RequestBody ProductDTO.QueryDTO queryDTO) {

View File

@@ -37,14 +37,25 @@ public class ProductDTO {
@DecimalMin(value = "0.01", message = "商品价格必须大于0")
private BigDecimal price;
@Schema(description = "商品原价", example = "9999.00")
private BigDecimal originalPrice;
@Schema(description = "商品分类", example = "electronics")
private String category;
@Schema(description = "库存数量", example = "100")
@Min(value = 0, message = "库存不能为负数")
private Integer stock;
@Schema(description = "商品图片URL")
private String imageUrl;
@Schema(description = "商品状态1-上架0-下架")
private Integer status;
@Schema(description = "销量")
private Integer sales;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createdAt;
@@ -96,6 +107,8 @@ public class ProductDTO {
@AllArgsConstructor
public static class QueryDTO {
private String name;
private String keyword;
private String category;
private BigDecimal minPrice;
private BigDecimal maxPrice;
private Integer status;

View File

@@ -1,6 +1,7 @@
package com.org.flashsalesystem.service;
import com.org.flashsalesystem.dto.CartDTO;
import com.org.flashsalesystem.dto.OrderDTO;
import com.org.flashsalesystem.dto.ProductDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
@@ -29,6 +30,8 @@ public class CartService {
private RedisService redisService;
@Autowired
private ProductService productService;
@Autowired
private OrderService orderService;
@Value("${flashsale.cart.expire-days:7}")
private int cartExpireDays;
@Value("${flashsale.cart.max-items:20}")
@@ -311,4 +314,83 @@ public class CartService {
String cartKey = buildCartKey(userId);
redisService.expire(cartKey, cartExpireDays, TimeUnit.DAYS);
}
/**
* 购物车下单
*/
public OrderDTO checkoutCart(Long userId, List<Long> productIds) {
log.info("购物车下单: 用户ID={}, 商品IDs={}", userId, productIds);
String cartKey = buildCartKey(userId);
CartDTO cart = getCart(userId);
if (cart.getItems().isEmpty()) {
throw new RuntimeException("购物车为空,无法下单");
}
// 过滤要下单的商品
List<CartDTO.CartItemDTO> itemsToOrder;
if (productIds != null && !productIds.isEmpty()) {
itemsToOrder = cart.getItems().stream()
.filter(item -> productIds.contains(item.getProductId()))
.collect(java.util.stream.Collectors.toList());
} else {
itemsToOrder = cart.getItems();
}
if (itemsToOrder.isEmpty()) {
throw new RuntimeException("没有选择要下单的商品");
}
// 检查库存
for (CartDTO.CartItemDTO item : itemsToOrder) {
Integer currentStock = productService.getProductStock(item.getProductId());
if (currentStock < item.getQuantity()) {
throw new RuntimeException("商品 " + item.getProductName() + " 库存不足,当前库存:" + currentStock);
}
}
// 计算订单总价
BigDecimal orderTotalPrice = itemsToOrder.stream()
.map(CartDTO.CartItemDTO::getSubtotal)
.reduce(BigDecimal.ZERO, BigDecimal::add);
// 根据商品数量创建订单
if (itemsToOrder.size() == 1) {
// 单商品订单
CartDTO.CartItemDTO item = itemsToOrder.get(0);
OrderDTO.CreateDTO createDTO = new OrderDTO.CreateDTO();
createDTO.setProductId(item.getProductId());
createDTO.setQuantity(item.getQuantity());
OrderDTO order = orderService.createOrder(userId, createDTO);
// 从购物车中移除已下单的商品
redisService.hDel(cartKey, item.getProductId().toString());
log.info("单商品购物车下单成功: 用户ID={}, 订单ID={}", userId, order.getId());
return order;
} else {
// 多商品订单 - 创建多个订单
List<OrderDTO> orders = new ArrayList<>();
for (CartDTO.CartItemDTO item : itemsToOrder) {
OrderDTO.CreateDTO createDTO = new OrderDTO.CreateDTO();
createDTO.setProductId(item.getProductId());
createDTO.setQuantity(item.getQuantity());
OrderDTO order = orderService.createOrder(userId, createDTO);
orders.add(order);
// 从购物车中移除已下单的商品
redisService.hDel(cartKey, item.getProductId().toString());
}
log.info("多商品购物车下单成功: 用户ID={}, 订单数量={}", userId, orders.size());
// 返回第一个订单作为代表(实际项目中可能需要创建主订单)
OrderDTO firstOrder = orders.get(0);
firstOrder.setTotalPrice(orderTotalPrice); // 设置总价为所有订单的总和
return firstOrder;
}
}
}

View File

@@ -106,9 +106,10 @@ public class FlashSaleService {
// 缓存秒杀活动信息
cacheFlashSaleInfo(flashSale, product);
// 预热库存到Redis
// 预热库存到Redis - 确保存储为数字类型
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSale.getId();
redisService.set(stockKey, flashSale.getFlashStock());
redisService.delete(stockKey); // 先删除可能存在的异常数据
redisService.setString(stockKey, flashSale.getFlashStock().toString()); // 确保存储为字符串数字
log.info("秒杀活动创建成功: ID={}", flashSale.getId());
@@ -163,15 +164,42 @@ public class FlashSaleService {
}
try {
// 使用Lua脚本原子性扣减库存
// 检查并修复库存数据
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSale.getId();
String currentStock = redisService.getString(stockKey);
log.info("秒杀前库存检查: flashSaleId={}, stockKey={}, currentStock={}",
flashSale.getId(), stockKey, currentStock);
if (currentStock == null || currentStock.trim().isEmpty()) {
log.warn("检测到库存数据异常,尝试修复: flashSaleId={}", flashSale.getId());
repairFlashSaleStock(flashSale.getId());
// 修复后重新获取库存
currentStock = redisService.getString(stockKey);
log.info("修复后库存: flashSaleId={}, stockKey={}, currentStock={}",
flashSale.getId(), stockKey, currentStock);
}
// 使用Lua脚本原子性扣减库存
log.info("准备执行秒杀脚本: stockKey={}, quantity={}, userId={}",
stockKey, participateDTO.getQuantity(), userId);
Long remainingStock = redisService.executeFlashSaleScript(stockKey, participateDTO.getQuantity());
log.info("秒杀脚本执行完成: stockKey={}, remainingStock={}", stockKey, remainingStock);
if (remainingStock < 0) {
if (remainingStock == -1) {
return createFailResult("秒杀活动库存信息异常");
} else {
log.warn("秒杀库存key不存在或数据异常: flashSaleId={}, stockKey={}", flashSale.getId(), stockKey);
return createFailResult("秒杀活动库存信息异常,请刷新页面重试");
} else if (remainingStock == -2) {
log.info("秒杀库存不足: flashSaleId={}, 剩余库存不足", flashSale.getId());
return createFailResult("商品已售罄");
} else if (remainingStock == -3) {
log.error("秒杀参数异常: flashSaleId={}, quantity={}", flashSale.getId(),
participateDTO.getQuantity());
return createFailResult("参数异常,请检查购买数量");
} else {
log.error("秒杀脚本执行异常: flashSaleId={}, returnValue={}", flashSale.getId(), remainingStock);
return createFailResult("系统异常,请稍后重试");
}
}
@@ -345,13 +373,97 @@ public class FlashSaleService {
// 缓存秒杀活动信息
cacheFlashSaleInfo(flashSale, product);
// 预热库存
// 预热库存 - 确保存储为数字类型
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSaleId;
redisService.set(stockKey, flashSale.getFlashStock());
redisService.delete(stockKey); // 先删除可能存在的异常数据
redisService.setString(stockKey, flashSale.getFlashStock().toString()); // 确保存储为字符串数字
log.info("秒杀活动预热完成: {}", flashSaleId);
}
/**
* 修复Redis中的库存数据
*/
public void repairFlashSaleStock(Long flashSaleId) {
log.info("修复秒杀活动库存数据: {}", flashSaleId);
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
if (!flashSaleOpt.isPresent()) {
log.warn("秒杀活动不存在: {}", flashSaleId);
return;
}
FlashSale flashSale = flashSaleOpt.get();
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSaleId;
// 获取当前Redis中的库存值
String currentStock = redisService.getString(stockKey);
log.info("当前Redis库存值: key={}, value={}", stockKey, currentStock);
// 如果库存值无效,则重新设置
try {
if (currentStock == null || currentStock.trim().isEmpty()) {
log.warn("库存值为空,重新设置: key={}, resetTo={}", stockKey, flashSale.getFlashStock());
redisService.setString(stockKey, flashSale.getFlashStock().toString());
// 验证设置是否成功
String verifyStock = redisService.getString(stockKey);
log.info("库存设置验证: key={}, setTo={}, actualValue={}",
stockKey, flashSale.getFlashStock(), verifyStock);
} else {
Integer stockNumber = Integer.parseInt(currentStock.trim());
if (stockNumber < 0 || stockNumber > flashSale.getFlashStock()) {
log.warn("库存值异常,重新设置: key={}, currentValue={}, resetTo={}",
stockKey, currentStock, flashSale.getFlashStock());
redisService.setString(stockKey, flashSale.getFlashStock().toString());
// 验证设置是否成功
String verifyStock = redisService.getString(stockKey);
log.info("异常库存修复验证: key={}, setTo={}, actualValue={}",
stockKey, flashSale.getFlashStock(), verifyStock);
} else {
log.info("库存值正常: key={}, value={}", stockKey, stockNumber);
}
}
} catch (NumberFormatException e) {
log.error("库存值格式异常,重新设置: key={}, currentValue={}", stockKey, currentStock, e);
redisService.delete(stockKey);
redisService.setString(stockKey, flashSale.getFlashStock().toString());
// 验证设置是否成功
String verifyStock = redisService.getString(stockKey);
log.info("格式异常修复验证: key={}, setTo={}, actualValue={}",
stockKey, flashSale.getFlashStock(), verifyStock);
}
log.info("库存数据修复完成: {}", flashSaleId);
}
/**
* 重新预热所有活跃的秒杀活动库存
*/
public void preloadAllActiveFlashSales() {
log.info("开始预热所有活跃秒杀活动库存");
List<FlashSale> activeFlashSales = flashSaleRepository.findAll();
for (FlashSale flashSale : activeFlashSales) {
try {
String stockKey = FLASH_SALE_STOCK_PREFIX + flashSale.getId();
// 使用set方法先存储让系统能识别
redisService.set(stockKey, flashSale.getFlashStock());
log.info("预热秒杀活动库存: flashSaleId={}, stock={}",
flashSale.getId(), flashSale.getFlashStock());
} catch (Exception e) {
log.error("预热秒杀活动库存失败: flashSaleId={}", flashSale.getId(), e);
}
}
log.info("所有活跃秒杀活动库存预热完成");
}
/**
* 获取秒杀活动剩余库存
*/
@@ -458,6 +570,165 @@ public class FlashSaleService {
return true;
}
/**
* 发布秒杀活动
*/
@Transactional
public FlashSaleDTO publishFlashSale(Long flashSaleId) {
log.info("发布秒杀活动: ID={}", flashSaleId);
// 获取现有秒杀活动
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
if (!flashSaleOpt.isPresent()) {
throw new RuntimeException("秒杀活动不存在");
}
FlashSale flashSale = flashSaleOpt.get();
// 检查活动状态
if (flashSale.getStatus() != 1) {
throw new RuntimeException("只有未开始的秒杀活动才能发布");
}
// 验证时间
LocalDateTime now = LocalDateTime.now();
if (flashSale.getStartTime().isBefore(now)) {
throw new RuntimeException("开始时间不能早于当前时间");
}
if (flashSale.getStartTime().isAfter(flashSale.getEndTime())) {
throw new RuntimeException("开始时间不能晚于结束时间");
}
// 验证商品存在
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
if (product == null) {
throw new RuntimeException("关联商品不存在");
}
// 验证库存
if (flashSale.getFlashStock() <= 0) {
throw new RuntimeException("秒杀库存必须大于0");
}
// 预热缓存
preloadFlashSale(flashSaleId);
log.info("秒杀活动发布成功: ID={}", flashSaleId);
return buildFlashSaleDTO(flashSale, product);
}
/**
* 暂停秒杀活动
*/
@Transactional
public FlashSaleDTO pauseFlashSale(Long flashSaleId) {
log.info("暂停秒杀活动: ID={}", flashSaleId);
// 获取现有秒杀活动
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
if (!flashSaleOpt.isPresent()) {
throw new RuntimeException("秒杀活动不存在");
}
FlashSale flashSale = flashSaleOpt.get();
// 检查活动状态
if (flashSale.getStatus() != 2) {
throw new RuntimeException("只有进行中的秒杀活动才能暂停");
}
// 更新状态为暂停 (status = 4)
flashSaleRepository.updateStatus(flashSaleId, 4);
flashSale.setStatus(4);
// 更新缓存
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
cacheFlashSaleInfo(flashSale, product);
log.info("秒杀活动暂停成功: ID={}", flashSaleId);
return buildFlashSaleDTO(flashSale, product);
}
/**
* 恢复秒杀活动
*/
@Transactional
public FlashSaleDTO resumeFlashSale(Long flashSaleId) {
log.info("恢复秒杀活动: ID={}", flashSaleId);
// 获取现有秒杀活动
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
if (!flashSaleOpt.isPresent()) {
throw new RuntimeException("秒杀活动不存在");
}
FlashSale flashSale = flashSaleOpt.get();
// 检查活动状态
if (flashSale.getStatus() != 4) {
throw new RuntimeException("只有暂停的秒杀活动才能恢复");
}
// 检查是否已结束
LocalDateTime now = LocalDateTime.now();
if (flashSale.getEndTime().isBefore(now)) {
throw new RuntimeException("秒杀活动已结束,无法恢复");
}
// 更新状态为进行中 (status = 2)
flashSaleRepository.updateStatus(flashSaleId, 2);
flashSale.setStatus(2);
// 更新缓存
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
cacheFlashSaleInfo(flashSale, product);
log.info("秒杀活动恢复成功: ID={}", flashSaleId);
return buildFlashSaleDTO(flashSale, product);
}
/**
* 结束秒杀活动
*/
@Transactional
public FlashSaleDTO endFlashSale(Long flashSaleId) {
log.info("结束秒杀活动: ID={}", flashSaleId);
// 获取现有秒杀活动
Optional<FlashSale> flashSaleOpt = flashSaleRepository.findById(flashSaleId);
if (!flashSaleOpt.isPresent()) {
throw new RuntimeException("秒杀活动不存在");
}
FlashSale flashSale = flashSaleOpt.get();
// 检查活动状态
if (flashSale.getStatus() == 3) {
throw new RuntimeException("秒杀活动已经结束");
}
if (flashSale.getStatus() == 1) {
throw new RuntimeException("秒杀活动尚未开始,无法结束");
}
// 更新状态为已结束 (status = 3)
flashSaleRepository.updateStatus(flashSaleId, 3);
flashSale.setStatus(3);
// 清除相关缓存
clearFlashSaleCache(flashSaleId);
// 更新缓存
Product product = productRepository.findById(flashSale.getProductId()).orElse(null);
cacheFlashSaleInfo(flashSale, product);
log.info("秒杀活动结束成功: ID={}", flashSaleId);
return buildFlashSaleDTO(flashSale, product);
}
/**
* 更新秒杀活动状态
*/

View File

@@ -100,6 +100,7 @@ public class ProductService {
ProductDTO productDTO = new ProductDTO();
BeanUtils.copyProperties(product, productDTO);
return productDTO;
}
@@ -142,6 +143,7 @@ public class ProductService {
.map(product -> {
ProductDTO dto = new ProductDTO();
BeanUtils.copyProperties(product, dto);
return dto;
})
.collect(Collectors.toList());
@@ -295,12 +297,28 @@ public class ProductService {
}
/**
* 获取商品库存从Redis
* 获取商品库存(优先从Redis,不存在则从数据库获取并同步
*/
public Integer getProductStock(Long productId) {
String stockKey = PRODUCT_STOCK_PREFIX + productId;
Object stock = redisService.get(stockKey);
return stock != null ? Integer.valueOf(stock.toString()) : 0;
if (stock != null) {
return Integer.valueOf(stock.toString());
}
// Redis中没有库存数据从数据库获取并同步到Redis
Optional<Product> productOpt = productRepository.findById(productId);
if (productOpt.isPresent()) {
Product product = productOpt.get();
// 同步到Redis
redisService.set(stockKey, product.getStock());
log.info("从数据库同步商品库存到Redis: 商品ID={}, 库存={}", productId, product.getStock());
return product.getStock();
}
log.warn("商品不存在: 商品ID={}", productId);
return 0;
}
/**

View File

@@ -2,6 +2,7 @@ package com.org.flashsalesystem.service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
@@ -21,6 +22,7 @@ public class RedisService {
private RedisTemplate<String, Object> redisTemplate;
@Autowired
@Qualifier("customStringRedisTemplate")
private RedisTemplate<String, String> stringRedisTemplate;
@Autowired
@@ -41,6 +43,13 @@ public class RedisService {
redisTemplate.opsForValue().set(key, value);
}
/**
* 设置纯字符串值用于Lua脚本兼容
*/
public void setString(String key, String value) {
stringRedisTemplate.opsForValue().set(key, value);
}
/**
* 设置字符串值并指定过期时间
*/
@@ -55,6 +64,13 @@ public class RedisService {
return redisTemplate.opsForValue().get(key);
}
/**
* 安全获取字符串值
*/
public String getString(String key) {
return stringRedisTemplate.opsForValue().get(key);
}
/**
* 原子递增
*/
@@ -330,7 +346,18 @@ public class RedisService {
* 执行秒杀脚本
*/
public Long executeFlashSaleScript(String stockKey, int quantity) {
return redisTemplate.execute(flashSaleScript, Collections.singletonList(stockKey), String.valueOf(quantity));
log.info("执行秒杀脚本: stockKey={}, quantity={}", stockKey, quantity);
try {
Long result = stringRedisTemplate.execute(flashSaleScript, Collections.singletonList(stockKey),
String.valueOf(quantity));
log.info("秒杀脚本执行结果: result={}", result);
return result;
} catch (Exception e) {
log.error("执行秒杀脚本异常: stockKey={}, quantity={}", stockKey, quantity, e);
throw e;
}
}
/**