|
|
@ -1,9 +1,11 @@ |
|
|
package cc.hiver.mall.serviceimpl.mybatis; |
|
|
package cc.hiver.mall.serviceimpl.mybatis; |
|
|
|
|
|
|
|
|
|
|
|
import cc.hiver.core.common.redis.RedisTemplateHelper; |
|
|
import cc.hiver.core.common.utils.SnowFlakeUtil; |
|
|
import cc.hiver.core.common.utils.SnowFlakeUtil; |
|
|
import cc.hiver.mall.dao.mapper.*; |
|
|
import cc.hiver.mall.dao.mapper.*; |
|
|
import cc.hiver.mall.entity.*; |
|
|
import cc.hiver.mall.entity.*; |
|
|
import cc.hiver.mall.pojo.dto.CreateOrderDTO; |
|
|
import cc.hiver.mall.pojo.dto.CreateOrderDTO; |
|
|
|
|
|
import cc.hiver.mall.pojo.dto.ShopCacheDTO; |
|
|
import cc.hiver.mall.pojo.query.MallOrderPageQuery; |
|
|
import cc.hiver.mall.pojo.query.MallOrderPageQuery; |
|
|
import cc.hiver.mall.pojo.vo.MallOrderVO; |
|
|
import cc.hiver.mall.pojo.vo.MallOrderVO; |
|
|
import cc.hiver.mall.service.mybatis.MallOrderGroupService; |
|
|
import cc.hiver.mall.service.mybatis.MallOrderGroupService; |
|
|
@ -15,6 +17,7 @@ import cn.hutool.json.JSONObject; |
|
|
import cn.hutool.json.JSONUtil; |
|
|
import cn.hutool.json.JSONUtil; |
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
|
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
|
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
|
|
import com.baomidou.mybatisplus.core.metadata.IPage; |
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
|
|
@ -79,6 +82,9 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder |
|
|
@Autowired |
|
|
@Autowired |
|
|
private MallDeliveryOrderMapper mallDeliveryOrderMapper; |
|
|
private MallDeliveryOrderMapper mallDeliveryOrderMapper; |
|
|
|
|
|
|
|
|
|
|
|
@Autowired |
|
|
|
|
|
private MallUserCouponMapper mallUserCouponMapper; |
|
|
|
|
|
|
|
|
@Autowired |
|
|
@Autowired |
|
|
private MallRefundRecordMapper mallRefundRecordMapper; |
|
|
private MallRefundRecordMapper mallRefundRecordMapper; |
|
|
|
|
|
|
|
|
@ -106,6 +112,9 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder |
|
|
@Autowired |
|
|
@Autowired |
|
|
private cc.hiver.mall.service.mybatis.MallUserCouponService mallUserCouponService; |
|
|
private cc.hiver.mall.service.mybatis.MallUserCouponService mallUserCouponService; |
|
|
|
|
|
|
|
|
|
|
|
@Autowired |
|
|
|
|
|
private RedisTemplateHelper redisTemplateHelper; |
|
|
|
|
|
|
|
|
// ================================================================
|
|
|
// ================================================================
|
|
|
// 核心下单逻辑
|
|
|
// 核心下单逻辑
|
|
|
// ================================================================
|
|
|
// ================================================================
|
|
|
@ -127,12 +136,25 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder |
|
|
BigDecimal goodsAmount = BigDecimal.ZERO; |
|
|
BigDecimal goodsAmount = BigDecimal.ZERO; |
|
|
List<MallOrderGoods> goodsSnapshots = new ArrayList<>(); |
|
|
List<MallOrderGoods> goodsSnapshots = new ArrayList<>(); |
|
|
|
|
|
|
|
|
for (CreateOrderDTO.OrderItemDTO item : dto.getItems()) { |
|
|
// 一次性查出所有商品,避免循环查询
|
|
|
Product product = productMapper.selectById(item.getProductId()); |
|
|
List<String> productIds = dto.getItems().stream() |
|
|
if (product == null) { |
|
|
.map(CreateOrderDTO.OrderItemDTO::getProductId) |
|
|
throw new RuntimeException("商品不存在: " + item.getProductId()); |
|
|
.collect(Collectors.toList()); |
|
|
|
|
|
List<Product> productList = productMapper.selectBatchIds(productIds); |
|
|
|
|
|
Map<String, Product> productMap = productList.stream() |
|
|
|
|
|
.collect(Collectors.toMap(Product::getId, p -> p)); |
|
|
|
|
|
|
|
|
|
|
|
// 校验是否所有商品都存在
|
|
|
|
|
|
for (String pid : productIds) { |
|
|
|
|
|
if (!productMap.containsKey(pid)) { |
|
|
|
|
|
throw new RuntimeException("商品不存在: " + pid); |
|
|
} |
|
|
} |
|
|
// 扣减库存(更新 attributeListPrice 中的 specNum)
|
|
|
} |
|
|
|
|
|
Integer shopSaleCount = 0; |
|
|
|
|
|
for (CreateOrderDTO.OrderItemDTO item : dto.getItems()) { |
|
|
|
|
|
Product product = productMap.get(item.getProductId()); |
|
|
|
|
|
shopSaleCount += item.getQuantity(); |
|
|
|
|
|
// 扣减库存(更新 attributeListPrice 中的 specNum)以及销量
|
|
|
deductStock(product, item.getQuantity()); |
|
|
deductStock(product, item.getQuantity()); |
|
|
|
|
|
|
|
|
// 累计商品金额
|
|
|
// 累计商品金额
|
|
|
@ -149,6 +171,17 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder |
|
|
snapshot.setQuantity(item.getQuantity()); |
|
|
snapshot.setQuantity(item.getQuantity()); |
|
|
goodsSnapshots.add(snapshot); |
|
|
goodsSnapshots.add(snapshot); |
|
|
} |
|
|
} |
|
|
|
|
|
//更新店铺销量
|
|
|
|
|
|
if(shopSaleCount != 0){ |
|
|
|
|
|
shopMapper.update(null,new UpdateWrapper<Shop>().eq("id",dto.getShopId()).set("sale_count",shopSaleCount)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 增量更新Redis缓存(店铺销量 + 商品销量),避免走refreshShopCache全量查询
|
|
|
|
|
|
try { |
|
|
|
|
|
updateSaleCacheIncremental(dto.getShopId(), dto.getRegionId(), dto.getItems(), productMap, shopSaleCount); |
|
|
|
|
|
} catch (Exception e) { |
|
|
|
|
|
log.warn("增量更新Redis销量缓存失败,不影响下单: {}", e.getMessage()); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
BigDecimal packageFee = dto.getPackageFee() != null ? dto.getPackageFee() : BigDecimal.ZERO; |
|
|
BigDecimal packageFee = dto.getPackageFee() != null ? dto.getPackageFee() : BigDecimal.ZERO; |
|
|
|
|
|
|
|
|
@ -860,6 +893,8 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder |
|
|
if (order.getStatus() != STATUS_WAIT_PICKUP && order.getStatus() != STATUS_DELIVERING) { |
|
|
if (order.getStatus() != STATUS_WAIT_PICKUP && order.getStatus() != STATUS_DELIVERING) { |
|
|
throw new RuntimeException("当前状态不可完成"); |
|
|
throw new RuntimeException("当前状态不可完成"); |
|
|
} |
|
|
} |
|
|
|
|
|
//优惠券变成已使用
|
|
|
|
|
|
mallUserCouponService.useCoupon(orderId); |
|
|
updateOrderStatus(orderId, STATUS_DONE); |
|
|
updateOrderStatus(orderId, STATUS_DONE); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -931,6 +966,12 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder |
|
|
MallOrderGroup group = mallOrderGroupMapper.selectOne(gq2); |
|
|
MallOrderGroup group = mallOrderGroupMapper.selectOne(gq2); |
|
|
vo.setGroupInfo(group); |
|
|
vo.setGroupInfo(group); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//优惠券
|
|
|
|
|
|
LambdaQueryWrapper<MallUserCoupon> uq = new LambdaQueryWrapper<>(); |
|
|
|
|
|
uq.eq(MallUserCoupon::getOrderId, orderId); |
|
|
|
|
|
vo.setUserCoupon(mallUserCouponMapper.selectList(uq)); |
|
|
|
|
|
|
|
|
// 售后、退款信息
|
|
|
// 售后、退款信息
|
|
|
LambdaQueryWrapper<MallRefundRecord> rq = new LambdaQueryWrapper<>(); |
|
|
LambdaQueryWrapper<MallRefundRecord> rq = new LambdaQueryWrapper<>(); |
|
|
rq.eq(MallRefundRecord::getOrderId, orderId); |
|
|
rq.eq(MallRefundRecord::getOrderId, orderId); |
|
|
@ -967,10 +1008,10 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder |
|
|
int current = spec.getInt("specNum"); |
|
|
int current = spec.getInt("specNum"); |
|
|
if (current < quantity) throw new RuntimeException("商品库存不足: " + product.getProductName()); |
|
|
if (current < quantity) throw new RuntimeException("商品库存不足: " + product.getProductName()); |
|
|
spec.set("specNum", current - quantity); |
|
|
spec.set("specNum", current - quantity); |
|
|
|
|
|
//同时更新销量
|
|
|
LambdaUpdateWrapper<Product> uw = new LambdaUpdateWrapper<>(); |
|
|
LambdaUpdateWrapper<Product> uw = new LambdaUpdateWrapper<>(); |
|
|
uw.eq(Product::getId, product.getId()) |
|
|
uw.eq(Product::getId, product.getId()) |
|
|
.set(Product::getAttributeListPrice, arr.toString()); |
|
|
.set(Product::getAttributeListPrice, arr.toString()).set(Product::getTailWarn, product.getTailWarn() + quantity); |
|
|
productMapper.update(null, uw); |
|
|
productMapper.update(null, uw); |
|
|
} catch (RuntimeException e) { |
|
|
} catch (RuntimeException e) { |
|
|
throw e; |
|
|
throw e; |
|
|
@ -979,6 +1020,56 @@ public class MallOrderServiceImpl extends ServiceImpl<MallOrderMapper, MallOrder |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 增量更新Redis缓存中的销量字段,避免走refreshShopCache全量重查 |
|
|
|
|
|
* 1. SHOP_CACHE:{regionId} → hash field shopId → ShopCacheDTO.shop.saleCount 累加 |
|
|
|
|
|
* 2. SHOP_PRODUCTS:{shopId} → JSON数组 → 对应商品的 tailWarn 累加 |
|
|
|
|
|
*/ |
|
|
|
|
|
private void updateSaleCacheIncremental(String shopId, String regionId, |
|
|
|
|
|
List<CreateOrderDTO.OrderItemDTO> items, |
|
|
|
|
|
Map<String, Product> productMap, |
|
|
|
|
|
int shopSaleCount) { |
|
|
|
|
|
// 1. 更新店铺缓存中的 saleCount
|
|
|
|
|
|
if (StringUtils.isNotBlank(regionId) && shopSaleCount > 0) { |
|
|
|
|
|
String shopCacheKey = "SHOP_CACHE:" + regionId; |
|
|
|
|
|
String shopJson = (String) redisTemplateHelper.hGet(shopCacheKey, shopId); |
|
|
|
|
|
if (StringUtils.isNotBlank(shopJson)) { |
|
|
|
|
|
ShopCacheDTO cacheDTO = JSONUtil.toBean(shopJson, ShopCacheDTO.class); |
|
|
|
|
|
if (cacheDTO.getShop() != null) { |
|
|
|
|
|
Integer oldSaleCount = cacheDTO.getShop().getSaleCount(); |
|
|
|
|
|
cacheDTO.getShop().setSaleCount((oldSaleCount != null ? oldSaleCount : 0) + shopSaleCount); |
|
|
|
|
|
redisTemplateHelper.hPut(shopCacheKey, shopId, JSONUtil.toJsonStr(cacheDTO)); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 2. 更新商品缓存中的 tailWarn(销量)
|
|
|
|
|
|
String productsKey = "SHOP_PRODUCTS:" + shopId; |
|
|
|
|
|
String productsJson = redisTemplateHelper.get(productsKey); |
|
|
|
|
|
if (StringUtils.isNotBlank(productsJson)) { |
|
|
|
|
|
// 构建本次每个商品的销量增量 map: productId -> quantity
|
|
|
|
|
|
Map<String, Integer> quantityMap = new HashMap<>(); |
|
|
|
|
|
for (CreateOrderDTO.OrderItemDTO item : items) { |
|
|
|
|
|
quantityMap.merge(item.getProductId(), item.getQuantity(), Integer::sum); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
JSONArray productArr = JSONUtil.parseArray(productsJson); |
|
|
|
|
|
boolean changed = false; |
|
|
|
|
|
for (int i = 0; i < productArr.size(); i++) { |
|
|
|
|
|
JSONObject productObj = productArr.getJSONObject(i); |
|
|
|
|
|
String pid = productObj.getStr("id"); |
|
|
|
|
|
if (pid != null && quantityMap.containsKey(pid)) { |
|
|
|
|
|
int oldTailWarn = productObj.getInt("tailWarn", 0); |
|
|
|
|
|
productObj.set("tailWarn", oldTailWarn + quantityMap.get(pid)); |
|
|
|
|
|
changed = true; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if (changed) { |
|
|
|
|
|
redisTemplateHelper.set(productsKey, productArr.toString()); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* 构建基础订单对象 |
|
|
* 构建基础订单对象 |
|
|
*/ |
|
|
*/ |
|
|
|