package com.iotechn.unimall.app.api.product; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.dobbinsoft.fw.core.exception.ServiceException; import com.dobbinsoft.fw.support.component.CacheComponent; import com.dobbinsoft.fw.support.model.Page; import com.iotechn.unimall.biz.service.footprint.FootprintBizService; import com.iotechn.unimall.biz.service.freight.FreightTemplateBizService; import com.iotechn.unimall.biz.service.groupshop.GroupShopBizService; import com.iotechn.unimall.biz.service.product.ProductBizService; import com.iotechn.unimall.data.constant.CacheConst; import com.iotechn.unimall.data.domain.SkuDO; import com.iotechn.unimall.data.domain.SpuAttributeDO; import com.iotechn.unimall.data.domain.SpuDO; import com.iotechn.unimall.data.domain.SpuSpecificationDO; import com.iotechn.unimall.data.dto.product.GroupShopDTO; import com.iotechn.unimall.data.dto.product.SpuDTO; import com.iotechn.unimall.data.enums.BizType; import com.iotechn.unimall.data.enums.SpuActivityType; import com.iotechn.unimall.data.mapper.*; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import java.util.List; /** * Created by rize on 2019/7/2. */ @Service public class ProductServiceImpl implements ProductService { @Autowired private ProductBizService productBizService; @Autowired private CacheComponent cacheComponent; @Autowired private SpuMapper spuMapper; @Autowired private ImgMapper imgMapper; @Autowired private SkuMapper skuMapper; @Autowired private SpuAttributeMapper spuAttributeMapper; @Autowired private SpuSpecificationMapper spuSpecificationMapper; @Autowired private FootprintBizService footprintBizService; @Autowired private GroupShopBizService groupShopBizService; @Autowired private FreightTemplateBizService freightTemplateBizService; @Autowired private TransactionTemplate transactionTemplate; @Override public Page getProductPage(Integer pageNo, Integer pageSize, Long categoryId, String orderBy, Boolean isAsc, String title) throws ServiceException { if (categoryId != null && categoryId <= 0) { categoryId = null; } return productBizService.getProductPage(pageNo, pageSize, categoryId, orderBy, isAsc, title); } @Override public SpuDTO getProduct(Long spuId, Long userId) throws ServiceException { // 1. 获取基本信息 SpuDTO spuDTO = productBizService.getProductByIdFromCache(spuId); // 2. // 2.1. 封装其他信息,此类属性只能在管理员编辑界面统一编辑改变,不能由用户行为改变 SpuDTO detailSpuInfo = cacheComponent.getHashObj(CacheConst.PRT_SPU_DETAIL_HASH_BUCKET, "P" + spuId, SpuDTO.class); if (detailSpuInfo == null) { // 从DB中封装属性 detailSpuInfo = new SpuDTO(); // 1. SkuDOList List skuDOList = skuMapper.selectList(new QueryWrapper().eq("spu_id", spuDTO.getId())); detailSpuInfo.setSkuList(skuDOList); // 2. 图片列表 List imgs = imgMapper.getImgs(BizType.GOODS.getCode(), spuId); detailSpuInfo.setImgList(imgs); // 3. 设置detail SpuDO spuLoadDetail = spuMapper.selectOne(new QueryWrapper().select("detail").eq("id", spuId)); detailSpuInfo.setDetail(spuLoadDetail.getDetail()); // 4. 设置商品属性 List attributeList = spuAttributeMapper.selectList(new QueryWrapper().eq("spu_id", spuId)); detailSpuInfo.setAttributeList(attributeList); // 5. 设置商品规格 List specificationList = spuSpecificationMapper.selectList(new QueryWrapper().eq("spu_id", spuId)); detailSpuInfo.setSpecificationList(specificationList); cacheComponent.putHashObj(CacheConst.PRT_SPU_DETAIL_HASH_BUCKET, "P" + spuId, detailSpuInfo); } // 2.2. 记住忽略以上 N 个属性 BeanUtils.copyProperties(spuDTO, detailSpuInfo, "skuList", "imgList", "detail", "attributeList", "specificationList"); spuDTO = detailSpuInfo; // 3. 封装各个Sku的库存 spuDTO.getSkuList().forEach(item -> { String stockStr = cacheComponent.getHashRaw(CacheConst.PRT_SKU_STOCK_BUCKET, "K" + item.getId()); if (stockStr == null) { // 几乎不可能。这次判断是悲观考虑 transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus transactionStatus) { // 在事务中,获取最新的值 Integer stockForUpdate = skuMapper.getStockForUpdate(item.getId()); cacheComponent.putHashRaw(CacheConst.PRT_SKU_STOCK_BUCKET, "K" + item.getId(), stockForUpdate + ""); item.setStock(stockForUpdate); } }); } else { Integer stock = Integer.parseInt(stockStr); item.setStock(stock); } }); // 4. 运费模板 MicroFix 运费模板存在RPC。建议移动到detailSpuInfo中,但是编辑运费模板的时候需要删除detailSpuInfo的缓存 spuDTO.setFreightTemplate(freightTemplateBizService.getFreightTemplateById(spuDTO.getFreightTemplateId())); if (userId != null) { // 添加用户足迹 & 封装用户是否LIKE此商品 footprintBizService.newFootprint(spuId, userId); spuDTO.setFavorite(cacheComponent.getHashRaw(CacheConst.PRT_USER_FAVORITE_HASH_BUCKET + spuId, "U" + userId) != null); } // 5. 封装活动信息 if (spuDTO.getActivityType() != null && spuDTO.getActivityType() != SpuActivityType.NONE.getCode() && spuDTO.getGmtActivityEnd() != null && spuDTO.getGmtActivityEnd().getTime() > System.currentTimeMillis()) { // 若存在正在进行的活动,封装活动实例。这里活动的缓存应该由各个活动BizService内部决定是否使用缓存?如何设计缓存? if (spuDTO.getActivityType() == SpuActivityType.GROUP_SHOP.getCode()) { GroupShopDTO groupShopDTO = groupShopBizService.getGroupShopById(spuDTO.getActivityId()); spuDTO.setActivity(groupShopDTO); } } return spuDTO; } }