You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

430 lines
13 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 商品详情页
# 一、服务搭建
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/b779805cebe24b8a900fb34f35fba68e.png)
## 1.配置host文件
  我们的商品详情服务是一个独立的服务我们需要在客户端的host中来配置映射
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/9a9442d1d28c4be98a56a8558dc40a54.png)
## 2.在Nginx中配置
  我们需要在Nginx中配置商品详情服务的反向代理和静态资源的管理首先看反向代理的配置
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/a6cdce0df44c4b9593cc9659959c30f6.png)
  然后就是静态资源的管理我们在es和商城首页的资源的同级目录下创建一个item目录其中保存的就是商品详情页中的资源
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/9697da3555b04fce8099d71f7a87564f.png)
## 3.网关服务路由
  当Nginx方向代理到了网关服务后网关服务得跟进host路由到商品服务我们得修改对应的配置信息
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/a0c0ae26af13460b834aa5414eefe446.png)
## 4.商品服务处理
  到请求到了商品服务中我们需要接收请求,并且跳转到商品详情页中。
```java
/**
* 商品详情的控制器
*/
@Controller
public class ItemController {
@GetMapping("/{skuId}.html")
public String item(@PathVariable Long skuId){
System.out.println(skuId);
return "item";
}
}
```
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/5557722db79c4210b7035a91d77abf14.png)
item.html页面中的内容在给大家的资源中有拷贝进去即可注意要修改相关的资源的路径
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/78f953f237964ccf93ee96a2029ca2eb.png)
## 5.检索服务跳转
  商品详情服务搭建好了之后,我们需要通过商品的检索服务调整过来,那么我们需要修改跳转的地址信息
```html
<p class="da">
<a th:href="${'http://msb.item.com/'+product.skuId+'.html'}" >
<img th:src="${product.getSkuImg()}" class="dim">
</a>
</p>
```
最终的商品详情页的展示效果
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/1d13771f64b243f0a466e2fad930169e.png)
# 二、商品详情数据
## 1.响应VO封装
&emsp;&emsp;根据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 销售属性
&emsp;&emsp;销售属性的信息是根据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对应的查询结果
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/6cd63592008d46658ec63942443c2261.png)
对应的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的规格参数
&emsp;&emsp;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
```
然后对应的查询效果
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/2e6174f065184c07971ee0a8e609e6cb.png)
具体的实现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 完成的处理
&emsp;&emsp;在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标题
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/34c58cefba9d49b9a6d6063d358baf92.png)
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/94012d961b804b9ba1a1ea4461a75e2d.png)
### 3.2 图片展示
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/b302a4b4edc449bb9c83e76b70fe3704.png)
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/46426b256df042b48a642e2075e47b64.png)
### 3.3 价格和有货
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/69cb9f0af901422296b71d8e7d6f86cb.png)
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/2f2573f515cd43b3ac06cc5e87da4067.png)
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/eeea132fbacc417593f2d64e063671a5.png)
### 3.4 销售属性
&emsp;&emsp;销售属性是当前SKU对应的SPU下所有的销售属性的组合。
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/50a00b3c7266486f83bdea51a336c7fb.png)
### 3.5 SPU详情
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/62171fb18de1461c9b8295b82e4e9923.png)
### 3.6 规格参数
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/f1510d19cb634f5e9b95b93db125984a.png)
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/630ec28f2b094d2d9a691b14207f28cf.png)
## 4.异步处理
&emsp;&emsp;然后我们就可以在商品详细信息查询的位置实现CompletableFuture的异步编排处理。
![image.png](https://fynotefile.oss-cn-zhangjiakou.aliyuncs.com/fynote/fyfile/1462/1650348980021/20b058ecebc44c3da3a0c223512ec12c.png)
先定义线程池
```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
```