|
|
# 商品详情页
|
|
|
|
|
|
# 一、服务搭建
|
|
|
|
|
|

|
|
|
|
|
|
## 1.配置host文件
|
|
|
|
|
|
  我们的商品详情服务是一个独立的服务,我们需要在客户端的host中来配置映射
|
|
|
|
|
|

|
|
|
|
|
|
## 2.在Nginx中配置
|
|
|
|
|
|
  我们需要在Nginx中配置商品详情服务的反向代理和静态资源的管理,首先看反向代理的配置
|
|
|
|
|
|

|
|
|
|
|
|
  然后就是静态资源的管理,我们在es和商城首页的资源的同级目录下创建一个item目录,其中保存的就是商品详情页中的资源
|
|
|
|
|
|

|
|
|
|
|
|
## 3.网关服务路由
|
|
|
|
|
|
  当Nginx方向代理到了网关服务后,网关服务得跟进host路由到商品服务,我们得修改对应的配置信息
|
|
|
|
|
|

|
|
|
|
|
|
## 4.商品服务处理
|
|
|
|
|
|
  到请求到了商品服务中我们需要接收请求,并且跳转到商品详情页中。
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
* 商品详情的控制器
|
|
|
*/
|
|
|
@Controller
|
|
|
public class ItemController {
|
|
|
|
|
|
@GetMapping("/{skuId}.html")
|
|
|
public String item(@PathVariable Long skuId){
|
|
|
System.out.println(skuId);
|
|
|
return "item";
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|

|
|
|
|
|
|
item.html页面中的内容在给大家的资源中有,拷贝进去即可,注意要修改相关的资源的路径
|
|
|
|
|
|

|
|
|
|
|
|
## 5.检索服务跳转
|
|
|
|
|
|
  商品详情服务搭建好了之后,我们需要通过商品的检索服务调整过来,那么我们需要修改跳转的地址信息
|
|
|
|
|
|
```html
|
|
|
<p class="da">
|
|
|
<a th:href="${'http://msb.item.com/'+product.skuId+'.html'}" >
|
|
|
<img th:src="${product.getSkuImg()}" class="dim">
|
|
|
</a>
|
|
|
</p>
|
|
|
```
|
|
|
|
|
|
最终的商品详情页的展示效果
|
|
|
|
|
|

|
|
|
|
|
|
# 二、商品详情数据
|
|
|
|
|
|
## 1.响应VO封装
|
|
|
|
|
|
  根据SKUID查询对应的商品详情信息,我们需要把对应的信息封装到对应的VO中,我们需要自己来封装该对象。
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
* 商品详情页的数据对象
|
|
|
*/
|
|
|
@Data
|
|
|
public class SpuItemVO {
|
|
|
// 1.sku的基本信息 pms_sku_info
|
|
|
SkuInfoEntity info;
|
|
|
// 2.sku的图片信息pms_sku_images
|
|
|
List<SkuImagesEntity> images;
|
|
|
// 3.获取spu中的销售属性的组合
|
|
|
List<SkuItemSaleAttrVo> saleAttrs;
|
|
|
// 4.获取SPU的介绍
|
|
|
SpuInfoDescEntity desc;
|
|
|
|
|
|
// 5.获取SPU的规格参数
|
|
|
List<SpuItemGroupAttrVo> baseAttrs;
|
|
|
|
|
|
}
|
|
|
```
|
|
|
|
|
|
销售属性的VO
|
|
|
|
|
|
```java
|
|
|
@ToString
|
|
|
@Data
|
|
|
public class SkuItemSaleAttrVo {
|
|
|
private Long attrId;
|
|
|
private String attrName;
|
|
|
private String attrValue;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
属性组相关的VO
|
|
|
|
|
|
```java
|
|
|
@ToString
|
|
|
@Data
|
|
|
public class SpuItemGroupAttrVo {
|
|
|
private String groupName;
|
|
|
private List<Attr> baseAttrs;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
其中的Attr是之前就创建的VO对象
|
|
|
|
|
|
## 2.商品详细的数据查询
|
|
|
|
|
|
### 2.1 sku的基本信息
|
|
|
|
|
|
我们可以直接查询获取
|
|
|
|
|
|
```java
|
|
|
// 1.sku的基本信息 pms_sku_info
|
|
|
SkuInfoEntity skuInfoEntity = getById(skuId);
|
|
|
```
|
|
|
|
|
|
### 2.2 sku的图片信息
|
|
|
|
|
|
图片信息也是直接获取
|
|
|
|
|
|
```java
|
|
|
List<SkuImagesEntity> images = skuImagesService.getImagesBySkuId(skuId);
|
|
|
```
|
|
|
|
|
|
### 2.3 销售属性
|
|
|
|
|
|
  销售属性的信息是根据SPU找到所有的SKU下的对应的属性信息,首先写出对应的SQL
|
|
|
|
|
|
```sql
|
|
|
## 根据SPU编号找到所有的SKU编号 然后进而找到所有的销售属性信息
|
|
|
SELECT
|
|
|
# psi.sku_id,
|
|
|
pssav.attr_id,
|
|
|
pssav.attr_name,
|
|
|
GROUP_CONCAT( DISTINCT pssav.attr_value)
|
|
|
FROM `pms_sku_info` psi
|
|
|
LEFT JOIN `pms_sku_sale_attr_value` pssav
|
|
|
ON psi.sku_id = pssav.sku_id
|
|
|
WHERE psi.spu_id = 6
|
|
|
GROUP BY pssav.attr_id,pssav.attr_name
|
|
|
```
|
|
|
|
|
|
SQL对应的查询结果
|
|
|
|
|
|

|
|
|
|
|
|
对应的Mapper映射
|
|
|
|
|
|
```xml
|
|
|
<select id="getSkuSaleAttrValueBySpuId" resultType="com.msb.mall.product.vo.SkuItemSaleAttrVo">
|
|
|
SELECT
|
|
|
pssav.attr_id attr_id,
|
|
|
pssav.attr_name attr_name,
|
|
|
GROUP_CONCAT( DISTINCT pssav.attr_value) attr_value
|
|
|
FROM `pms_sku_info` psi
|
|
|
LEFT JOIN `pms_sku_sale_attr_value` pssav
|
|
|
ON psi.sku_id = pssav.sku_id
|
|
|
WHERE psi.spu_id = #{spuId}
|
|
|
GROUP BY pssav.attr_id,pssav.attr_name
|
|
|
</select>
|
|
|
```
|
|
|
|
|
|
然后对应的Dao接口
|
|
|
|
|
|
```java
|
|
|
@Mapper
|
|
|
public interface SkuSaleAttrValueDao extends BaseMapper<SkuSaleAttrValueEntity> {
|
|
|
|
|
|
List<SkuItemSaleAttrVo> getSkuSaleAttrValueBySpuId(@Param("spuId") Long spuId);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
销售属性中的Service接口及实现
|
|
|
|
|
|
```java
|
|
|
public interface SkuSaleAttrValueService extends IService<SkuSaleAttrValueEntity> {
|
|
|
|
|
|
PageUtils queryPage(Map<String, Object> params);
|
|
|
|
|
|
List<SkuItemSaleAttrVo> getSkuSaleAttrValueBySpuId(Long spuId);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
```java
|
|
|
@Override
|
|
|
public List<SkuItemSaleAttrVo> getSkuSaleAttrValueBySpuId(Long spuId) {
|
|
|
List<SkuItemSaleAttrVo> attrsVo = skuSaleAttrValueDao.getSkuSaleAttrValueBySpuId(spuId);
|
|
|
return attrsVo;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
然后就是对应的SkuService中的串联
|
|
|
|
|
|
```java
|
|
|
// 3.获取spu中的销售属性的组合
|
|
|
List<SkuItemSaleAttrVo> saleAttrs = skuSaleAttrValueService.getSkuSaleAttrValueBySpuId(spuId);
|
|
|
```
|
|
|
|
|
|
### 2.4 spu的详情
|
|
|
|
|
|
也是直接获取的
|
|
|
|
|
|
```java
|
|
|
// 4.获取SPU的介绍
|
|
|
SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(spuId);
|
|
|
```
|
|
|
|
|
|
### 2.5 Spu的规格参数
|
|
|
|
|
|
  Spu的规格参数,我们需要根据sku对应的商品分类和spu编号来查询,完整的SQL语句
|
|
|
|
|
|
```sql
|
|
|
# `pms_attr_group`
|
|
|
# 根据 SPU编号和CatalogId类别编号 要查询出所有的属性组及其属性信息
|
|
|
|
|
|
SELECT t1.attr_group_id
|
|
|
,t1.attr_group_name
|
|
|
,t2.attr_id
|
|
|
,t3.attr_name
|
|
|
,t4.attr_value
|
|
|
FROM `pms_attr_group` t1
|
|
|
LEFT JOIN `pms_attr_attrgroup_relation` t2 ON t1.attr_group_id = t2.attr_group_id
|
|
|
LEFT JOIN `pms_attr` t3 ON t2.attr_id = t3.attr_id
|
|
|
LEFT JOIN `pms_product_attr_value` t4 ON t4.attr_id = t2.attr_id
|
|
|
WHERE t1.catelog_id = '225' AND t4.spu_id = 6
|
|
|
```
|
|
|
|
|
|
然后对应的查询效果
|
|
|
|
|
|

|
|
|
|
|
|
具体的实现,Dao层接口和映射文件
|
|
|
|
|
|
```java
|
|
|
@Mapper
|
|
|
public interface AttrGroupDao extends BaseMapper<AttrGroupEntity> {
|
|
|
|
|
|
List<SpuItemGroupAttrVo> getAttrgroupWithSpuId(@Param("spuId") Long spuId, @Param("catalogId") Long catalogId);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
```xml
|
|
|
<resultMap id="SpuItemGroupAttrVo" type="com.msb.mall.product.vo.SpuItemGroupAttrVo">
|
|
|
<result column="attr_group_name" property="groupName"/>
|
|
|
<collection property="baseAttrs" ofType="com.msb.mall.product.vo.Attr">
|
|
|
<id column="attr_id" property="attrId" />
|
|
|
<result column="attr_name" property="attrName"/>
|
|
|
<result column="attr_value" property="attrValue" />
|
|
|
</collection>
|
|
|
</resultMap>
|
|
|
|
|
|
<select id="getAttrgroupWithSpuId" resultMap="SpuItemGroupAttrVo">
|
|
|
SELECT t1.attr_group_id
|
|
|
,t1.attr_group_name
|
|
|
,t2.attr_id
|
|
|
,t3.attr_name
|
|
|
,t4.attr_value
|
|
|
FROM `pms_attr_group` t1
|
|
|
LEFT JOIN `pms_attr_attrgroup_relation` t2 ON t1.attr_group_id = t2.attr_group_id
|
|
|
LEFT JOIN `pms_attr` t3 ON t2.attr_id = t3.attr_id
|
|
|
LEFT JOIN `pms_product_attr_value` t4 ON t4.attr_id = t2.attr_id
|
|
|
WHERE t1.catelog_id = #{catalogId} AND t4.spu_id = #{spuId}
|
|
|
|
|
|
</select>
|
|
|
```
|
|
|
|
|
|
对应的AttrGroupService中的定义和实现
|
|
|
|
|
|
```java
|
|
|
List<SpuItemGroupAttrVo> getAttrgroupWithSpuId(Long spuId
|
|
|
, Long catalogId);
|
|
|
```
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
* 跟进SpuId和CatalogId查询出对应的 属性组及其属性信息
|
|
|
* @param spuId
|
|
|
* @param catalogId
|
|
|
* @return
|
|
|
*/
|
|
|
@Override
|
|
|
public List<SpuItemGroupAttrVo> getAttrgroupWithSpuId(Long spuId, Long catalogId) {
|
|
|
//
|
|
|
List<SpuItemGroupAttrVo> groupAttrVo = attrGroupDao.getAttrgroupWithSpuId(spuId,catalogId);
|
|
|
return groupAttrVo;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
然后就是对应的SkuInfoServiceImpl中的处理
|
|
|
|
|
|
```java
|
|
|
// 5.获取SPU的规格参数
|
|
|
List<SpuItemGroupAttrVo> groupAttrVo = attrGroupService.getAttrgroupWithSpuId(spuId,catalogId);
|
|
|
```
|
|
|
|
|
|
### 2.6 完成的处理
|
|
|
|
|
|
  在SkuInfoServiceImpl中的完整的处理
|
|
|
|
|
|
```java
|
|
|
@Override
|
|
|
public SpuItemVO item(Long skuId) {
|
|
|
SpuItemVO vo = new SpuItemVO();
|
|
|
// 1.sku的基本信息 pms_sku_info
|
|
|
SkuInfoEntity skuInfoEntity = getById(skuId);
|
|
|
vo.setInfo(skuInfoEntity);
|
|
|
// 获取对应的SPUID
|
|
|
Long spuId = skuInfoEntity.getSpuId();
|
|
|
// 获取对应的CatalogId 类别编号
|
|
|
Long catalogId = skuInfoEntity.getCatalogId();
|
|
|
// 2.sku的图片信息pms_sku_images
|
|
|
List<SkuImagesEntity> images = skuImagesService.getImagesBySkuId(skuId);
|
|
|
vo.setImages(images);
|
|
|
// 3.获取spu中的销售属性的组合
|
|
|
List<SkuItemSaleAttrVo> saleAttrs = skuSaleAttrValueService.getSkuSaleAttrValueBySpuId(spuId);
|
|
|
vo.setSaleAttrs(saleAttrs);
|
|
|
// 4.获取SPU的介绍
|
|
|
SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(spuId);
|
|
|
vo.setDesc(spuInfoDescEntity);
|
|
|
|
|
|
// 5.获取SPU的规格参数
|
|
|
List<SpuItemGroupAttrVo> groupAttrVo = attrGroupService.getAttrgroupWithSpuId(spuId,catalogId);
|
|
|
vo.setBaseAttrs(groupAttrVo);
|
|
|
return vo;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
然后Controller中完成商品详情的数据查询,绑定数据后调整到商品详情页。之后做页面的渲染操作
|
|
|
|
|
|
```java
|
|
|
/**
|
|
|
* 根据前端传递的SkuId我们需要查询出对有的商品信息
|
|
|
* @param skuId
|
|
|
* @return
|
|
|
*/
|
|
|
@GetMapping("/{skuId}.html")
|
|
|
public String item(@PathVariable Long skuId, Model model){
|
|
|
SpuItemVO itemVO = skuInfoService.item(skuId);
|
|
|
model.addAttribute("item",itemVO);
|
|
|
return "item";
|
|
|
}
|
|
|
```
|
|
|
|
|
|
## 3.商品详情页渲染
|
|
|
|
|
|
### 3.1 SKU标题
|
|
|
|
|
|

|
|
|
|
|
|

|
|
|
|
|
|
### 3.2 图片展示
|
|
|
|
|
|

|
|
|
|
|
|

|
|
|
|
|
|
### 3.3 价格和有货
|
|
|
|
|
|

|
|
|
|
|
|

|
|
|
|
|
|

|
|
|
|
|
|
### 3.4 销售属性
|
|
|
|
|
|
  销售属性是当前SKU对应的SPU下所有的销售属性的组合。
|
|
|
|
|
|

|
|
|
|
|
|
### 3.5 SPU详情
|
|
|
|
|
|

|
|
|
|
|
|
### 3.6 规格参数
|
|
|
|
|
|

|
|
|
|
|
|

|
|
|
|
|
|
## 4.异步处理
|
|
|
|
|
|
  然后我们就可以在商品详细信息查询的位置实现CompletableFuture的异步编排处理。
|
|
|
|
|
|

|
|
|
|
|
|
先定义线程池
|
|
|
|
|
|
```java
|
|
|
@Configuration
|
|
|
public class MyThreadPoolConfig {
|
|
|
|
|
|
@Bean
|
|
|
public ThreadPoolExecutor threadPoolExecutor()
|
|
|
{
|
|
|
return new ThreadPoolExecutor(20
|
|
|
,200
|
|
|
,10
|
|
|
, TimeUnit.SECONDS
|
|
|
,new LinkedBlockingQueue(10000)
|
|
|
, Executors.defaultThreadFactory()
|
|
|
,new ThreadPoolExecutor.AbortPolicy()
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
具体的编排处理
|
|
|
|
|
|
```java
|
|
|
|
|
|
```
|