add
yj
2024-12-05 b9900893177c78fc559223521fe839aa21000017
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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<SpuDO> 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<SkuDO> skuDOList = skuMapper.selectList(new QueryWrapper<SkuDO>().eq("spu_id", spuDTO.getId()));
            detailSpuInfo.setSkuList(skuDOList);
            // 2. 图片列表
            List<String> imgs = imgMapper.getImgs(BizType.GOODS.getCode(), spuId);
            detailSpuInfo.setImgList(imgs);
            // 3. 设置detail
            SpuDO spuLoadDetail = spuMapper.selectOne(new QueryWrapper<SpuDO>().select("detail").eq("id", spuId));
            detailSpuInfo.setDetail(spuLoadDetail.getDetail());
            // 4. 设置商品属性
            List<SpuAttributeDO> attributeList = spuAttributeMapper.selectList(new QueryWrapper<SpuAttributeDO>().eq("spu_id", spuId));
            detailSpuInfo.setAttributeList(attributeList);
            // 5. 设置商品规格
            List<SpuSpecificationDO> specificationList = spuSpecificationMapper.selectList(new QueryWrapper<SpuSpecificationDO>().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;
    }
}