1.环境准备
-
配置本地域名解析hosts文件
192.168.101.1 item.mall.com -
nginx配置文件中配置的是
*.mall.com,故不用再修改了,可以实现监听。 -
修改网关配置,跳转到商品页
- id: mall_host_route uri: lb://mall-product predicates: - Host=www.mall.com,item.mall.com -
将前端静态页面文件放在product服务下,并修改为
item.html -
将静态资源防止nginx对应item【自行创建】目录下
-
将search服务中详细商品服务跳转链接配置上
-
将item.html页面中获取静态资源路径进行修改
2.编写商城业务
1)Controller编写
- 需要获取页面跳转后的skuId,显示对应skuId的sku详细信息
@Controller
public class ItemController {
@Autowired
SkuInfoService skuInfoService;
/**
* 展示当前sku的详情
* @return
*/
@GetMapping("/{skuId}.html")
public String skuItem(@PathVariable("skuId") Long skuId, Model model){
SkuItemVo item = skuInfoService.item(skuId);
model.addAttribute("item", item);
return "item";
}
}
2)具体实现业务
- 将需要获取的数据封装成一个VO,方便封装
- 需要五部分信息
- 指定skuId的sku基本信息【SkuInfoEntity】
- 指定skuId的sku多个图片信息【SkuImagesEntity】
- 指定skuId对应的spu下所有sku的属性信息【SkuItemSaleAttrVo】
- 指定skuId对应的spu详细介绍信息【SpuInfoDescEntity】
- 指定skuId对应的spu规格参数信息【SpuItemAttrGroupVo】
@Data
public class SkuItemVo {
//1、sku基本信息的获取 pms_sku_info
private SkuInfoEntity info;
private boolean hasStock = true;
//2、sku的图片信息 pms_sku_images
private List<SkuImagesEntity> images;
//3、获取spu的销售属性组合
private List<SkuItemSaleAttrVo> saleAttr;
//4、获取spu的介绍
private SpuInfoDescEntity desc;
//5、获取spu的规格参数信息
private List<SpuItemAttrGroupVo> groupAttrs;
}
@Data
@ToString
public class SkuItemSaleAttrVo {
private Long attrId;
private String attrName;
private List<AttrValueWithSkuIdVo> attrValues;
}
@Data
@ToString
public class SpuItemAttrGroupVo {
private String groupName;
private List<Attr> attrs;
}
详细业务代码编写
public SkuItemVo item(Long skuId) {
SkuItemVo skuItemVo = new SkuItemVo();
// 1.sku基本信息获取
SkuInfoEntity info = getById(skuId);
Long spuId = info.getSpuId();
skuItemVo.setInfo(info);
// 2.sku的图片信息
List<SkuImagesEntity> images = skuImagesService.getImagesBySkuId(skuId);
skuItemVo.setImages(images);
// 3.获取spu的销售属性组合
List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(spuId);
skuItemVo.setSaleAttr(saleAttrVos);
// 4.获取spu的介绍
SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(spuId);
skuItemVo.setDesc(spuInfoDescEntity);
// 5.获取spu的规格参数信息
Long catalogId = info.getCatalogId();
List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrBySpuId(spuId, catalogId);
skuItemVo.setGroupAttrs(attrGroupVos);
return skuItemVo;
}
获取图片信息
@Override
public List<SkuImagesEntity> getImagesBySkuId(Long skuId) {
List<SkuImagesEntity> images = this.baseMapper.selectList(new QueryWrapper<SkuImagesEntity>().eq("sku_id", skuId));
return images;
}
获取销售属性信息
@Override
public List<SkuItemSaleAttrVo> getSaleAttrsBySpuId(Long spuId) {
List<SkuItemSaleAttrVo> vos = this.baseMapper.getSaleAttrsBySpuId(spuId);
return vos;
}
- 对应SkuSaleAttrValueDao.xml
<select id="getSaleAttrsBySpuId" resultType="com.white.mall.product.vo.SkuItemSaleAttrVo">
select
ssav.attr_id attr_id,
ssav.attr_name attr_name,
GROUP_CONCAT(DISTINCT ssav.attr_value) attr_values
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
</select>
获取规格参数信息
@Override
public List<SpuItemAttrGroupVo> getAttrGroupWithAttrBySpuId(Long spuId, Long catalogId) {
List<SpuItemAttrGroupVo> vos = this.baseMapper.getAttrGroupWithAttrBySpuId(spuId, catalogId);
return vos;
}
- 对应AttrGroupDao.xml
<resultMap id="spuItemAttrGroupVo" type="com.white.mall.product.vo.SpuItemAttrGroupVo">
<result property="groupName" column="attr_group_name"></result>
<collection property="attrs" ofType="com.white.mall.product.vo.Attr">
<result property="attrName" column="attr_name"></result>
<result property="attrValue" column="attr_value"></result>
</collection>
</resultMap>
<select id="getAttrGroupWithAttrBySpuId"
resultMap="spuItemAttrGroupVo">
SELECT value.spu_id,
group.attr_group_name,
group.attr_group_id,
attr.attr_name,
value.attr_value
FROM pms_attr_group `group`
LEFT JOIN pms_attr_attrgroup_relation `rela` ON group.attr_group_id = rela.attr_group_id
LEFT JOIN pms_attr `attr` ON rela.attr_id = attr.attr_id
LEFT JOIN pms_product_attr_value `value` ON value.attr_id = attr.attr_id
WHERE group.catelog_id = #{catalogId} AND value.spu_id = #{spuId}
</select>
3)点击规格实现sku跳转
后端逻辑
-
编写sql获取到所涉及到的规格对应的skuIds
-
跳转页面时,前端获取到所有规格的交集skuId,完成页面跳转
<resultMap id="skuItemSaleAttrVo" type="com.white.mall.product.vo.SkuItemSaleAttrVo"> <result column="attr_id" property="attrId"></result> <result column="attr_name" property="attrName"></result> <collection property="attrValues" ofType="com.white.mall.product.vo.AttrValueWithSkuIdVo"> <result column="sku_ids" property="skuIds"></result> <result column="attr_values" property="attrValue"></result> </collection> </resultMap> <select id="getSaleAttrsBySpuId" resultMap="skuItemSaleAttrVo"> SELECT attr.attr_id attr_id, attr.attr_name attr_name, attr.attr_value attr_values, GROUP_CONCAT(DISTINCT info.sku_id) sku_ids FROM pms_sku_info `info` LEFT JOIN pms_sku_sale_attr_value `attr` ON info.sku_id = attr.sku_id WHERE info.spu_id = #{spuId} GROUP BY attr.attr_id, attr.attr_name,attr.attr_value </select>
前端逻辑
- 根据skuIds集合, 修改对应sku的属性样式并将其边框标红
- 找到多个属性集合中公共的那个skuId【foreach遍历】
- 实现页面跳转
4)异步编排
配置线程池
@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()
);
}
}
@Component
@ConfigurationProperties(prefix = "mall.thread")
@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>
配置编写
mall.thread.core-size=20
mall.thread.max-size=200
mall.thread.keep-alive-time=10
代码编写
- 除了图片信息,其它信息需要拿到sku基本信息才能继续执行
- 图片信息与sku基本信息无关,因此可以单独开启一个异步任务
@Override
public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {
SkuItemVo skuItemVo = new SkuItemVo();
CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
// 1.sku基本信息获取
SkuInfoEntity info = getById(skuId);
skuItemVo.setInfo(info);
return info;
}, executor);
CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {
// 2.获取spu的销售属性组合
List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());
skuItemVo.setSaleAttr(saleAttrVos);
}, executor);
CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync((res) -> {
// 3.获取spu的介绍
SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());
skuItemVo.setDesc(spuInfoDescEntity);
}, executor);
CompletableFuture<Void> baseAttrFuture = infoFuture.thenAcceptAsync((res) -> {
// 4.获取spu的规格参数信息
List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrBySpuId(res.getSpuId(), res.getCatalogId());
skuItemVo.setGroupAttrs(attrGroupVos);
}, executor);
// 5.sku的图片信息,与infoFuture无关,因此可以单独开一个异步任务
CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
List<SkuImagesEntity> images = skuImagesService.getImagesBySkuId(skuId);
skuItemVo.setImages(images);
}, executor);
//等到所有任务都完成
CompletableFuture.allOf(saleAttrFuture,descFuture,baseAttrFuture,imageFuture).get();
return skuItemVo;
}@Override
public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {
SkuItemVo skuItemVo = new SkuItemVo();
CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
// 1.sku基本信息获取
SkuInfoEntity info = getById(skuId);
skuItemVo.setInfo(info);
return info;
}, executor);
CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {
// 2.获取spu的销售属性组合
List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrsBySpuId(res.getSpuId());
skuItemVo.setSaleAttr(saleAttrVos);
}, executor);
CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync((res) -> {
// 3.获取spu的介绍
SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());
skuItemVo.setDesc(spuInfoDescEntity);
}, executor);
CompletableFuture<Void> baseAttrFuture = infoFuture.thenAcceptAsync((res) -> {
// 4.获取spu的规格参数信息
List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrBySpuId(res.getSpuId(), res.getCatalogId());
skuItemVo.setGroupAttrs(attrGroupVos);
}, executor);
// 5.sku的图片信息,与infoFuture无关,因此可以单独开一个异步任务
CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
List<SkuImagesEntity> images = skuImagesService.getImagesBySkuId(skuId);
skuItemVo.setImages(images);
}, executor);
//等到所有任务都完成
CompletableFuture.allOf(saleAttrFuture,descFuture,baseAttrFuture,imageFuture).get();
return skuItemVo;
}