一、详情数据
封装成 vo 如下:
SkuItemVo:
@Data
public class SkuItemVo {
//1、sku基本信息获取 pms_sku_info
SkuInfoEntity info;
boolean hasStock = true;
//2、sku的图片信息 pms_sku_images
List<SkuImagesEntity> images;
//3、获取spu的销售属性组合。
List<SkuItemSaleAttrVo> saleAttr;
//4、获取spu的介绍
SpuInfoDescEntity desp;
//5、获取spu的规格参数信息。
List<SpuItemAttrGroupVo> groupAttrs;
SeckillInfoVo seckillInfo;//当前商品的秒杀优惠信息
}
SkuItemSaleAttrVo:
package com.fancy.gulimall.product.vo;
import lombok.Data;
import lombok.ToString;
import java.util.List;
@ToString
@Data
public class SkuItemSaleAttrVo {
private Long attrId;
private String attrName;
private List<AttrValueWithSkuIdVo> attrValues;
}
SpuItemAttrGroupVo:
package com.fancy.gulimall.product.vo;
import lombok.Data;
import lombok.ToString;
import java.util.List;
@Data
@ToString
public class SpuItemAttrGroupVo {
private String groupName;
private List<Attr> attrs;
}
二、查询详情
优化方面采用异步编排,要使用异步的话,首先我们需要自定义线程池。。。
package com.fancy.gulimall.product.config;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@EnableConfigurationProperties(ThreadPoolConfigProperties.class)
@Configuration
public class MyThreadConfig {
@Bean
public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) {
return new ThreadPoolExecutor(
pool.getCoreSize(),
pool.getMaxSize(),
pool.getKeepAliveTime(),
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(100000),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
}
}
要想实现从 application.properties 中获取数据,我们需要创建一个配置文件
package com.fancy.gulimall.product.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "gulimall.thread")
// @Component
@Data
public class ThreadPoolConfigProperties {
private Integer coreSize;
private Integer maxSize;
private Integer keepAliveTime;
}
提示要导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
相应的值在 application.properties 中配置与取出
#配置线程池
gulimall.thread.coreSize=20
gulimall.thread.maxSize=200
gulimall.thread.keepAliveTime=10
商品详情的请求:
package com.fancy.gulimall.product.web;
import com.fancy.gulimall.product.service.SkuInfoService;
import com.fancy.gulimall.product.vo.SkuItemVo;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import javax.annotation.Resource;
import java.util.concurrent.ExecutionException;
@Controller
public class ItemController {
@Resource
private SkuInfoService skuInfoService;
/**
* 展示当前sku的详情
* @param skuId
* @return
*/
@GetMapping("/{skuId}.html")
public String skuItem(@PathVariable("skuId") Long skuId, Model model) throws ExecutionException, InterruptedException {
System.out.println("准备查询" + skuId + "详情");
SkuItemVo vos = skuInfoService.item(skuId);
model.addAttribute("item",vos);
return "item";
}
}
相应的处理方法:采用异步编排
@Override
public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {
SkuItemVo skuItemVo = new SkuItemVo();
CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() ->{
//1、sku 基本信息获取 pms_sku_info
SkuInfoEntity info = getById(skuId);
skuItemVo.setInfo(info);
return info;
}, executor);
CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {
//3、获取 spu 的销售属性组合。
List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());
skuItemVo.setSaleAttr(saleAttrVos);
}, executor);
CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync(res -> {
//4、获取 spu 的介绍 pms_spu_info_desc
SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());
skuItemVo.setDesp(spuInfoDescEntity);
}, executor);
CompletableFuture<Void> baseAttrFuture = infoFuture.thenAcceptAsync(res -> {
//5、获取 spu 的规格参数信息。
List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());
skuItemVo.setGroupAttrs(attrGroupVos);
}, executor);
//2、sku 的图片信息 pms_sku_images
CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
List<SkuImagesEntity> images = imagesService.getImagesBySkuId(skuId);
skuItemVo.setImages(images);
}, executor);
//等到所有任务都完成
CompletableFuture.allOf(saleAttrFuture,descFuture,baseAttrFuture,imageFuture).get();
return skuItemVo;
}
getSaleAttrsBySpuId 方法的实现:
@Override
public List<SkuItemSaleAttrVo> getSaleAttrBySpuId(Long spuId) {
SkuSaleAttrValueDao baseMapper = this.getBaseMapper();
List<SkuItemSaleAttrVo> saleAttrVos = baseMapper.getSaleAttrBySpuId(spuId);
return saleAttrVos;
}
baseMapper.getSaleAttrBySpuId 对应的 mapper 映射文件中方法如下:
<resultMap id="spuAttrGroup" type="com.fancy.gulimall.product.vo.SpuItemAttrGroupVo">
<result property="groupName" column="attr_group_name"/>
<collection property="attrs" ofType="com.xunqi.gulimall.product.vo.Attr">
<result property="attrId" column="attr_id"></result>
<result property="attrName" column="attr_name"></result>
<result property="attrValue" column="attr_value"></result>
</collection>
</resultMap>
<select id="getAttrGroupWithAttrsBySpuId" resultMap="spuAttrGroup">
SELECT
product.spu_id,
pag.attr_group_id,
pag.attr_group_name,
product.attr_id,
product.attr_name,
product.attr_value
FROM
pms_product_attr_value product
LEFT JOIN pms_attr_attrgroup_relation paar ON product.attr_id = paar.attr_id
LEFT JOIN pms_attr_group pag ON paar.attr_group_id = pag.attr_group_id
WHERE
product.spu_id = #{spuId}
AND pag.catelog_id = #{catalogId}
</select>
getAttrGroupWithAttrsBySpuId 方法的实现:
@Override
public List<SpuItemAttrGroupVo> getAttrGroupWithAttrsBySpuId(Long spuId, Long catalogId) {
//1、查出当前spu对应的所有属性的分组信息以及当前分组下的所有属性对应的值
AttrGroupDao baseMapper = this.getBaseMapper();
List<SpuItemAttrGroupVo> vos = baseMapper.getAttrGroupWithAttrsBySpuId(spuId,catalogId);
return vos;
}
getImagesBySkuId 方法的实现:
@Override
public List<SkuImagesEntity> getImagesBySkuId(Long skuId) {
List<SkuImagesEntity> imagesEntities = this.baseMapper.selectList(new QueryWrapper<SkuImagesEntity>().eq("sku_id", skuId));
return imagesEntities;
}
三、sku 组合切换
页面遍历
<div class="box-attr clear" th:each="attr:${item.saleAttr}">
<dl>
<dt>选择[[${attr.attrName}]]</dt>
<dd th:each="vals:${attr.attrValues}">
<a class="sku_attr_value" th:attr="skus=${vals.skuIds},class=${#lists.contains(#strings.listSplit(vals.skuIds,','), item.info.skuId.toString())?'sku_attr_value checked':'sku_attr_value'}" >
[[${vals.attrValue}]]
<!-- <img src="/static/item/img/59ddfcb1Nc3edb8f1.jpg" /> -->
</a>
</dd>
</dl>
</div>
动态切换
$(".sku_attr_value").click(function(){
//1、点击的元素先添加上自定义的属性。为了识别我们是刚才被点击的var skus = new Array();
$(this).addClass("clicked");
var curr = $(this).attr("skus").split(",");
//当前被点击的所有 sku 组合数组放进去
skus.push(curr);
//去掉同一行的所有的 checked
$(this).parent().parent().find(".sku_attr_value").removeClass("checked");
$("a[class='sku_attr_value checked']").each(function(){
skus.push($(this).attr("skus").split(","));
});
console.log(skus);
//2、取出他们的交集,得到 skuId
var filterEle = skus[0];
for(var i = 1;i<skus.length;i++){
filterEle = $(filterEle).filter(skus[i]);
}
console.log(filterEle[0]);
location.href = "http://item.gulimall.com/"+filterEle[0]+".html";
//4、跳转
});
四、关键 SQL
SELECT
ssav.`attr_id` attr_id,
ssav.`attr_name` attr_name,
ssav.`attr_value`,
GROUP_CONCAT(DISTINCT info.`sku_id`) sku_ids
FROM `pms_sku_info` info
LEFT JOIN `pms_sku_sale_attr_value` ssav
ON ssav.`sku_id`=info.`sku_id`
WHERE info.`spu_id`=#{spuId}
GROUP BY ssav.`attr_id`,ssav.`attr_name`,ssav.`attr_value`