修复文件
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新秒杀活动状态
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user