第5章-商品详情-页面静态化.md 51 KB

第5章-商品详情-页面静态化

学习目标:

  • 能够说出商品详情业务需求
  • 梳理商品详情页面所需数据(Thymeleaf模板html页面)
  • 掌握Feign调用最佳实践方式
  • 完成商品详情页面渲染

1. 商品详情业务需求分析

1.1 详情渲染功能介绍

image-20221201011123762

商品详情所需构建的数据如下:

1,Sku基本信息

2,Sku图片信息

3,Sku分类信息

4,Sku销售属性相关信息

5,Sku价格信息(平台可以单独修改价格,sku后续会放入缓存,为了回显最新价格,所以单独获取)

6,展示商品的海报

7,获取skuId 对应的商品规格参数

...

1.2 详情模块规划

模块规划思路:

1,service-item微服务模块封装详情页面所需数据接口;

2,service-item通过feign client调用其他微服务数据接口进行数据汇总;

3,pc端前台页面通过gmall-web调用service-item数据接口渲染页面;

4,service-item可以为pc端、H5、安卓与ios等前端应用提供数据接口,gmall-web为pc端页面渲染形式

5,service-item获取商品信息需要调用service-product服务sku信息等;

6,由于service各微服务可能会相互调用,调用方式都是通过feign client调用,所以我们把feign client api接口单独封装出来,需要时直接引用feign client api模块接口即可,即需创建service-client父模块,管理各service微服务feign client api接口;

http://item.gmall.com/25.html

img

2. 商品详情页面数据分析

​ 首先我们一起来分析一下,在商品详情页面中需要哪些数据,通过web前端项目item/item.html模板页面中的Thymeleaf语法得知,例如有下面的部分:

  1. 面包屑分类数据

image-20221213142113696

  1. Sku商品信息以及商品图片列表

image-20221213142401007

  1. 商品SKU价格

image-20221213142512379

  1. 销售属性名称,属性值集合

image-20221213143031394

  1. 切换SKU转换SKU商品Json字符串信息

image-20221213143518283

  1. 商品海报图片集合

image-20221213143637364

  1. 商品基本属性名称以及属性值集合

image-20221213143910863

逐个查看,发现需要下面的变量:

  • skuInfo:当前商品SKU信息包含SKU图片列表
  • categoryView:当前商品所属的分类信息(包含三级)
  • price:当前商品最新价格
  • spuPosterList:当前商品海报图片集合
  • skuAttrList:当前商品平台属性及属性值集合--- 规格与参数
  • spuSaleAttrList:当前商品销售属性集合选中效果
  • valuesSkuJson:切换SKU转换SKU商品json字符串信息

因此接下来,我们需要在商品微服务补充RestFul接口实现 并且 提供远程调用Feign API接口给商品详情微服务来调用获取。

3. 服务提供方(商品微服务)

service-product微服务提供restful接口实现以下接口均为微服务模块内部之间的调用。

3.1 获取sku基本信息与图片信息

YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/587

3.1.1 编写控制器

service-product模块中新建包:com.atguigu.gmall.product.api 这个包下提供服务内部调用的restful 接口实现

package com.atguigu.gmall.product.api;

import com.atguigu.gmall.product.model.SkuInfo;
import com.atguigu.gmall.product.service.SkuManageService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 商品模块提供用于内部服务间调用Restful接口实现
 *
 * @author: atguigu
 * @create: 2023-09-02 10:26
 */
@RestController
@RequestMapping("/api/product")
public class ProductApiController {

    @Autowired
    private SkuManageService skuManageService;


    /**
     * 根据SkuID查询商品SKU信息包含图片列表
     *
     * @param skuId
     * @return
     */
    @ApiOperation("根据SkuID查询商品SKU信息包含图片列表")
    @GetMapping("/inner/getSkuInfo/{skuId}")
    public SkuInfo getSkuInfo(@PathVariable("skuId") Long skuId) {
        return skuManageService.getSkuInfo(skuId);
    }

}

3.1.2 业务接口与实现类

SkuManageService

/**
 * 根据SkuID查询商品SKU信息包含图片列表
 *
 * @param skuId
 * @return
 */
SkuInfo getSkuInfo(Long skuId);

SkuManageServiceImpl

/**
 * 根据SkuID查询商品SKU信息包含图片列表
 *
 * @param skuId
 * @return
 */
@Override
public SkuInfo getSkuInfo(Long skuId) {
    //1.根据商品SKU主键查询商品SKU基本信息
    SkuInfo skuInfo = skuInfoService.getById(skuId);

    //2.根据SKUID查询商品图片列表
    if (skuInfo != null) {
        LambdaQueryWrapper<SkuImage> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SkuImage::getSkuId, skuId);
        List<SkuImage> skuImageList = skuImageService.list(queryWrapper);
        skuInfo.setSkuImageList(skuImageList);
        return skuInfo;
    }
    return null;
}

访问Knife4j接口文档进行测试:

image-20221227095555197

3.2 获取分类信息

YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/635

3.2.1 需求分析

sku是挂在三级分类下面的,我们的分类信息分别在base_category1、base_category2、base_category3这三张表里面,目前需要通过sku表的三级分类id获取一级分类名称、二级分类名称和三级分类名称

解决方案:

我们可以建立一个视图(view),把三张表关联起来,视图id就是三级分类id,这样通过三级分类id就可以查询到相应数据,效果如下:

img

创建视图,使用代码生成器生成基础代码

CREATE 
	OR REPLACE VIEW base_category_view AS SELECT
	bc3.id id,
	bc1.id category1_id,
	bc1.NAME category1_name,
	bc2.id category2_id,
	bc2.NAME category2_name,
	bc3.id category3_id,
	bc3.NAME category3_name 
FROM
	base_category1 bc1
	INNER JOIN base_category2 bc2 ON bc2.category1_Id = bc1.id
	INNER JOIN base_category3 bc3 ON bc3.category2_id = bc2.id 
WHERE
	bc1.is_deleted = 0 
	AND bc2.is_deleted = 0 
	AND bc3.is_deleted = 0

3.2.2 控制器

ProductApiController

/**
 * 根据三级分类ID查询分类视图获取三级分类信息
 *
 * @param category3Id
 * @return
 */
@ApiOperation("根据三级分类ID查询分类视图获取三级分类信息")
@GetMapping("/inner/getCategoryView/{category3Id}")
public BaseCategoryView getCategoryView(@PathVariable("category3Id") Long category3Id) {
    return baseCategoryViewService.getById(category3Id);
}

3.2.3 业务接口与实现类

BaseCategoryViewService接口

package com.atguigu.gmall.product.service;

import com.atguigu.gmall.product.model.BaseCategoryView;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * VIEW 业务接口类
 * @author atguigu
 * @since 2023-06-10
 */
public interface BaseCategoryViewService extends IService<BaseCategoryView> {

}

BaseCategoryViewServiceImpl

package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.product.model.BaseCategoryView;
import com.atguigu.gmall.product.mapper.BaseCategoryViewMapper;
import com.atguigu.gmall.product.service.BaseCategoryViewService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * VIEW 业务实现类
 *
 * @author atguigu
 * @since 2023-06-10
 */
@Service
public class BaseCategoryViewServiceImpl extends ServiceImpl<BaseCategoryViewMapper, BaseCategoryView> implements BaseCategoryViewService {

}

3.2.4 持久层Mapper

package com.atguigu.gmall.product.mapper;

import com.atguigu.gmall.product.model.BaseCategoryView;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

/**
 * VIEW Mapper 接口
 *
 * @author atguigu
 * @since 2023-06-10
 */
public interface BaseCategoryViewMapper extends BaseMapper<BaseCategoryView> {

}

测试:

image-20221227103439673

3.3. 获取价格信息

YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/563

3.3.1 编写控制器

ProductApiController

/**
 * 获取SKU商品实时价格
 * @param skuId
 * @return
 */
@ApiOperation("获取SKU商品实时价格")
@GetMapping("/inner/getSkuPrice/{skuId}")
public BigDecimal getSkuPrice(@PathVariable("skuId") Long skuId){
    return skuManageService.getSkuPrice(skuId);
}

3.3.2 业务接口与实现类

SkuManageService

/**
 * 获取SKU商品实时价格
 * @param skuId
 * @return
 */
BigDecimal getSkuPrice(Long skuId);

SkuManageServiceImpl

/**
 * 获取SKU商品实时价格
 *
 * @param skuId
 * @return
 */
@Override
public BigDecimal getSkuPrice(Long skuId) {
    LambdaQueryWrapper<SkuInfo> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(SkuInfo::getId, skuId);
    queryWrapper.select(SkuInfo::getPrice);
    SkuInfo skuInfo = skuInfoService.getOne(queryWrapper);
    if (skuInfo != null) {
        return skuInfo.getPrice();
    }
    return new BigDecimal("0");
}

3.4 获取海报信息

YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/603

3.4.1 编写控制器

ProductApiController

/**
 * 根据SpuId查询海报图片列表
 * @param spuId
 * @return
 */
@ApiOperation("根据SpuId查询海报图片列表")
@GetMapping("/inner/findSpuPosterBySpuId/{spuId}")
public List<SpuPoster> getSpuPosterBySpuId(@PathVariable("spuId") Long spuId){
    return spuManageService.getSpuPosterBySpuId(spuId);
}

3.4.2 业务接口与实现类

SpuManageService接口

/**
 * 根据SpuId查询海报图片列表
 * @param spuId
 * @return
 */
List<SpuPoster> getSpuPosterBySpuId(Long spuId);

SpuManageServiceImpl实现类

/**
 * 根据SpuId查询海报图片列表
 *
 * @param spuId
 * @return
 */
@Override
public List<SpuPoster> getSpuPosterBySpuId(Long spuId) {
    LambdaQueryWrapper<SpuPoster> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(SpuPoster::getSpuId, spuId);
    return spuPosterService.list(queryWrapper);
}

3.5 Sku对应的平台属性

需求:显示在商品详情规格处

YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/555

3.5.1 编写控制器

ProductApiController

/**
 * 根据skuId查询当前商品的平台属性列表-包含平台属性值
 * @param skuId
 * @return
 */
@ApiOperation("根据skuId查询当前商品的平台属性列表-包含平台属性值")
@GetMapping("/inner/getAttrList/{skuId}")
public List<BaseAttrInfo> getAttrListBySkuId(@PathVariable("skuId") Long skuId){
    return skuManageService.getAttrListBySkuId(skuId);

}

3.5.2 业务接口与实现类

BaseAttrInfo实体类中增加attrValue属性

package com.atguigu.gmall.product.model;

@Data
@ApiModel(description = "平台属性")
@TableName("base_attr_info")
public class BaseAttrInfo extends BaseEntity {
    //...省略其他字段
    /**
     * 当前商品SKU中对应属性值 包含属性值名称
     */
    @TableField(exist = false)
    private String attrValue;
}


SkuManageService接口

/**
 * 根据skuId查询当前商品的平台属性列表-包含平台属性值
 * @param skuId
 * @return
 */
List<BaseAttrInfo> getAttrListBySkuId(Long skuId);

SkuManageServiceImpl实现类

/**
 * 根据skuId查询当前商品的平台属性列表-包含平台属性值
 *
 * @param skuId
 * @return
 */
@Override
public List<BaseAttrInfo> getAttrListBySkuId(Long skuId) {
    SkuAttrValueMapper skuAttrValueMapper = (SkuAttrValueMapper) skuAttrValueService.getBaseMapper();
    return skuAttrValueMapper.getAttrListBySkuId(skuId);
}

注意:BaseAttrInfo实体类中增加属性值名称变量:attrValue

3.5.3 持久层

BaseAttrInfoMapper

package com.atguigu.gmall.product.mapper;

import com.atguigu.gmall.product.model.BaseAttrInfo;
import com.atguigu.gmall.product.model.SkuAttrValue;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * sku平台属性值关联表 Mapper 接口
 *
 * @author atguigu
 * @since 2023-09-01
 */
public interface SkuAttrValueMapper extends BaseMapper<SkuAttrValue> {


    /**
     *根据skuId查询当前商品的平台属性列表-包含平台属性值
     * @param skuId
     * @return
     */
    List<BaseAttrInfo> getAttrListBySkuId(@Param("skuId") Long skuId);
}

SkuAttrValueMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.gmall.product.mapper.SkuAttrValueMapper">

    <!--根据skuId查询当前商品的平台属性列表-包含平台属性值-->
    <select id="getAttrListBySkuId" resultType="com.atguigu.gmall.product.model.BaseAttrInfo">
        SELECT
            bai.id,
            bai.attr_name,
            bai.category_id,
            bai.category_level,
            bav.value_name attr_value
        FROM
            base_attr_info bai
                INNER JOIN sku_attr_value sav ON sav.attr_id = bai.id
                INNER JOIN base_attr_value bav ON sav.value_id = bav.id
        WHERE
            sav.sku_id = #{skuId}
          AND bai.is_deleted = 0
          AND bav.is_deleted = 0
    </select>
</mapper>

3.6 获取销售属性选中

思路:

1、查出该商品的spu的所有销售属性和属性值

2、标识出本商品对应的销售属性

3、点击其他销售属性值的组合,跳转到另外的sku页面

YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/611

SQL实现

#第一步:加载当前SPU商品所有销售属性以及属性值,根据spuId查询所有销售属性跟值

SELECT 
  ssa.id,
	ssa.spu_id,
	ssa.base_sale_attr_id,
	ssa.sale_attr_name,
	ssav.id spu_sale_attr_value_id,
	ssav.sale_attr_value_name
FROM spu_sale_attr ssa INNER JOIN spu_sale_attr_value ssav
on ssa.spu_id = ssav.spu_id and ssa.base_sale_attr_id = ssav.base_sale_attr_id
WHERE ssa.spu_id = 10;


#第二步:单独查询指定SKU商品销售属性值
SELECT * from sku_sale_attr_value where sku_id = 24;
SELECT * from sku_sale_attr_value where sku_id = 25;
SELECT * from sku_sale_attr_value where sku_id = 26;
SELECT * from sku_sale_attr_value where sku_id = 27;


# 第三步:将spu包含所有销售属性以及值查询SQL,当成一张查询基准表使用采用左外连接
SELECT * from
(SELECT 
  ssa.id,
	ssa.spu_id,
	ssa.base_sale_attr_id,
	ssa.sale_attr_name,
	ssav.id spu_sale_attr_value_id,
	ssav.sale_attr_value_name
FROM spu_sale_attr ssa INNER JOIN spu_sale_attr_value ssav
on ssa.spu_id = ssav.spu_id and ssa.base_sale_attr_id = ssav.base_sale_attr_id and ssa.spu_id = 10) t
LEFT JOIN sku_sale_attr_value skussav on skussav.sale_attr_value_id = t.spu_sale_attr_value_id and skussav.sku_id = 24;



SELECT 
  ssa.id,
	ssa.spu_id,
	ssa.base_sale_attr_id,
	ssa.sale_attr_name,
	ssav.id spu_sale_attr_value_id,
	ssav.sale_attr_value_name,
	skussav.*
FROM spu_sale_attr ssa INNER JOIN spu_sale_attr_value ssav
on ssa.spu_id = ssav.spu_id and ssa.base_sale_attr_id = ssav.base_sale_attr_id and ssa.spu_id = 10 LEFT JOIN sku_sale_attr_value skussav on skussav.sale_attr_value_id = ssav.id and skussav.sku_id = 24;


SELECT 
  ssa.id,
	ssa.spu_id,
	ssa.base_sale_attr_id,
	ssa.sale_attr_name,
	ssav.id spu_sale_attr_value_id,
	ssav.sale_attr_value_name,
	IF(skussav.id is null,0,1) is_checked
FROM spu_sale_attr ssa INNER JOIN spu_sale_attr_value ssav
on ssa.spu_id = ssav.spu_id and ssa.base_sale_attr_id = ssav.base_sale_attr_id and ssa.spu_id = 10 LEFT JOIN sku_sale_attr_value skussav on skussav.sale_attr_value_id = ssav.id and skussav.sku_id = 25 and ssa.is_deleted=0 and ssav.is_deleted=0 and skussav.is_deleted =0
ORDER BY ssa.id,ssav.id;


SELECT IF(1=2,'true','false')

3.6.1 编写控制器

ProductApiController

/**
 * SPU商品所有销售属性以及属性值,根据SKUID将当前SKU销售属性值选中
 * @param skuId
 * @param spuId
 * @return
 */
@ApiOperation("SPU商品所有销售属性以及属性值,根据SKUID将当前SKU销售属性值选中")
@GetMapping("/inner/getSpuSaleAttrListCheckBySku/{skuId}/{spuId}")
public List<SpuSaleAttr> getSpuSaleAttrListCheckBySku(@PathVariable("skuId") Long skuId, @PathVariable("spuId") Long spuId){
    return spuManageService.getSpuSaleAttrListCheckBySku(skuId, spuId);
}

3.6.2 业务接口与实现类

SkuManageService接口

/**
 * SPU商品所有销售属性以及属性值,根据SKUID将当前SKU销售属性值选中
 * @param skuId
 * @param spuId
 * @return
 */
List<SpuSaleAttr> getSpuSaleAttrListCheckBySku(Long skuId, Long spuId);

SkuManageServiceImpl实现类

/**
 * SPU商品所有销售属性以及属性值,根据SKUID将当前SKU销售属性值选中
 *
 * @param skuId
 * @param spuId
 * @return
 */
@Override
public List<SpuSaleAttr> getSpuSaleAttrListCheckBySku(Long skuId, Long spuId) {
    SpuSaleAttrMapper spuSaleAttrMapper = (SpuSaleAttrMapper) spuSaleAttrService.getBaseMapper();
    return spuSaleAttrMapper.getSpuSaleAttrListCheckBySku(skuId, spuId);
}

3.6.3 查询出sku对应spu的销售属性

第1、2条通过此sql实现

SELECT
    ssa.id,
    ssa.spu_id,
    ssa.base_sale_attr_id,
    ssa.sale_attr_name,
    ssav.id spu_sale_attr_value_id,
    ssav.sale_attr_value_name,
    IF( s.id IS NULL, '0', '1' ) is_checked
FROM
    spu_sale_attr ssa
        INNER JOIN spu_sale_attr_value ssav ON ssa.spu_id = ssav.spu_id
            AND ssa.base_sale_attr_id = ssav.base_sale_attr_id
            AND ssa.spu_id = #{spuId}
            AND ssa.is_deleted = '0'
            AND ssav.is_deleted = '0'
        LEFT JOIN sku_sale_attr_value s ON s.sale_attr_value_id = ssav.id
            AND s.is_deleted = '0'
            AND s.sku_id = #{skuId}
ORDER BY ssa.id , ssav.id

此sql列出所有该spu的销售属性和属性值,并关联某skuid如果能关联上is_check设为1,否则设为0。

在对应的实体类中【SpuSaleAttrValue】添加属性字段

@TableField(exist = false)
String isChecked;

3.6.4 在SpuSaleAttrMapper 接口中添加的方法

package com.atguigu.gmall.product.mapper;

import com.atguigu.gmall.product.model.SpuSaleAttr;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * spu销售属性 Mapper 接口
 *
 * @author atguigu
 * @since 2023-07-25
 */
public interface SpuSaleAttrMapper extends BaseMapper<SpuSaleAttr> {


    /**
     * SPU商品所有销售属性以及属性值,根据SKUID将当前SKU销售属性值选中
     *
     * @param skuId
     * @param spuId
     * @return
     */
    List<SpuSaleAttr> getSpuSaleAttrListCheckBySku(@Param("skuId") Long skuId, @Param("spuId") Long spuId);
}

SpuSaleAttrMapper.xml 注意:复用以前的结果集,要保证销售属性值ID别名跟以前ResultMap里配置一样

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.gmall.product.mapper.SpuSaleAttrMapper">

    <!--销售属性销售属性一对多配置-->
    <resultMap id="spuSaleAttrMap" type="com.atguigu.gmall.product.model.SpuSaleAttr" autoMapping="true">
        <id column="id" property="id"></id>
        <!--配置多方集合-->
        <collection property="spuSaleAttrValueList" ofType="com.atguigu.gmall.product.model.SpuSaleAttrValue" autoMapping="true">
            <id column="spu_sale_attr_value_id" property="id"></id>
        </collection>
    </resultMap>

    <!--SPU商品所有销售属性以及属性值,根据SKUID将当前SKU销售属性值选中-->
    <select id="getSpuSaleAttrListCheckBySku" resultMap="spuSaleAttrMap">
        SELECT
            ssa.id,
            ssa.spu_id,
            ssa.base_sale_attr_id,
            ssa.sale_attr_name,
            ssav.id spu_sale_attr_value_id,
            ssav.sale_attr_value_name,
            IF
                ( skussav.id IS NULL, 0, 1 ) is_checked
        FROM
            spu_sale_attr ssa
                INNER JOIN spu_sale_attr_value ssav ON ssa.spu_id = ssav.spu_id
                AND ssa.base_sale_attr_id = ssav.base_sale_attr_id
                AND ssa.spu_id = #{spuId}
                LEFT JOIN sku_sale_attr_value skussav ON skussav.sale_attr_value_id = ssav.id
                AND skussav.sku_id = #{skuId}
                AND ssa.is_deleted = 0
                AND ssav.is_deleted = 0
                AND skussav.is_deleted = 0
        ORDER BY
            ssa.id,
            ssav.id
    </select>
</mapper>

3.7 实现商品切换

YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/595

实现思路:

img

1 、从页面中获得得所有选中的销售属性进行组合比如:

“属性值1|属性值2” 用这个字符串匹配一个对照表,来获得skuId。并进行跳转,或者告知无货。

2、后台要生成一个“属性值1|属性值2:skuId”的一个json串以提供页面进行匹配。如img

3、需要从后台数据库查询出该spu下的所有skuId和属性值关联关系。然后加工成如上的Json串,用该json串,跟前台匹配。

实现:

使用sql 语句来解决:

GROUP_CONCAT:group_concat( [distinct] 要连接的字段 [order by 排序字段 asc/desc ] [separator '分隔符'] )

#展示所有销售属性以及值 根据skuId选中销售属性值 根据skuId=24得到销售属性值-3733+3734

颜色: 【曜金黑-3732】     冰霜银-3733】
版本: 【8G+128G-3734】   【8G+256G-3735】


#当用户选择一组销售属性值后,根据销售属性值确定skuId
后端响应JSON数据   {"3733|3734":24,"3733|3735":25,"3732|3734":26,"3732|3735":27}

#第一步:先得到所有SPU商品销售属性值记录
SELECT *
FROM sku_sale_attr_value where spu_id = 10;

#第二步:对以上查询结果按照skuId进行分组 语法错:严格模式下"only_full_group_by"规则:如果有分组select中只能出现分组字段或者聚合函数
SELECT *
FROM sku_sale_attr_value where spu_id = 10
GROUP BY sku_id;
#修正
SELECT sku_id #可以使用聚合函数
FROM sku_sale_attr_value where spu_id = 10
GROUP BY sku_id;

#第三步:在select 使用聚合函数 将分组后记录中指定字段进行拼接
SELECT ssav.sku_id,GROUP_CONCAT(ssav.sale_attr_value_id separator '|') value_ids #可以使用聚合函数
FROM sku_sale_attr_value ssav where ssav.spu_id = 10
GROUP BY ssav.sku_id;

#GROUP_CONCAT 
# 语法 group_concat( [distinct] 要连接的字段 [order by 排序字段 asc/desc ] [separator '分隔符'] )

#第四步:为了避免出现销售属性值ID顺序不一致,加入排序1.先按照销售属性ID排序,2.再按照销售属性值ID排序

SELECT
	ssav.sku_id,
	GROUP_CONCAT( ssav.sale_attr_value_id ORDER BY ssa.id, ssav.sale_attr_value_id SEPARATOR '|' ) value_ids #可以使用聚合函数
FROM
	sku_sale_attr_value ssav
	LEFT JOIN spu_sale_attr_value spussav ON spussav.id = ssav.sale_attr_value_id 
	AND spussav.spu_id = ssav.spu_id
	LEFT JOIN spu_sale_attr ssa ON ssa.spu_id = spussav.spu_id 
	AND ssa.base_sale_attr_id = spussav.base_sale_attr_id 
WHERE
	ssav.spu_id = 10 
GROUP BY
	ssav.sku_id;

3.7.1 编写控制器

ProductApiController

/**
 * 在一组SPU商品下 切换不同SKU字符串
 * @param spuId "{'销售属性1|销售属性2':'sku商品ID','销售属性1|销售属性3':'sku商品ID'}"
 * @return
 */
@GetMapping("/inner/getSkuValueIdsMap/{spuId}")
public String getSkuValueIdsMap(@PathVariable("spuId") Long spuId){
    return skuManageService.getSkuValueIdsMap(spuId);
}

3.7.2 编写接口与实现类

SkuManageService接口

/**
 * 在一组SPU商品下 切换不同SKU字符串
 * @param spuId "{'销售属性1|销售属性2':'sku商品ID','销售属性1|销售属性3':'sku商品ID'}"
 * @return
 */
String getSkuValueIdsMap(Long spuId);

SpuManageServiceImpl实现类

/**
 * 在一组SPU商品下 切换不同SKU字符串
 * @param spuId "{'销售属性1|销售属性2':'sku商品ID','销售属性1|销售属性3':'sku商品ID'}"
 * @return
 */
@Override
public String getSkuValueIdsMap(Long spuId) {
    SkuSaleAttrValueMapper skuSaleAttrValueMapper = (SkuSaleAttrValueMapper) skuSaleAttrValueService.getBaseMapper();
    //1.调用动态SQL得到多条 销售属性跟SKUID对照关系
    List<Map> list = skuSaleAttrValueMapper.getSkuValueIdsMap(spuId);
    //2.遍历List将所有销售属性ID作为jsonKey 商品SKUID作为val
    HashMap<Object, Object> mapResult    = new HashMap<>();
    for (Map map : list) {
        Object value_ids = map.get("value_ids");
        Object skuId = map.get("sku_id");
        mapResult.put(value_ids, skuId);
    }
    return JSON.toJSONString(mapResult);
}

3.7.3 在SkuSaleAttrValueMapper中添加接口

package com.atguigu.gmall.product.mapper;

import com.atguigu.gmall.product.model.SkuSaleAttrValue;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Map;

/**
 * sku销售属性值 Mapper 接口
 *
 * @author atguigu
 * @since 2023-06-09
 */
public interface SkuSaleAttrValueMapper extends BaseMapper<SkuSaleAttrValue> {

    /**
     * 在一组SPU商品下 切换不同SKU字符串
     * @param spuId "{'销售属性1|销售属性2':'sku商品ID','销售属性1|销售属性3':'sku商品ID'}"
     * @return
     */
    List<Map> getSkuValueIdsMap(@Param("spuId") Long spuId);
}

SkuSaleAttrValueMapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.gmall.product.mapper.SkuSaleAttrValueMapper">

    <select id="getSkuValueIdsMap" resultType="java.util.Map">
        select
            group_concat(ssav.sale_attr_value_id order by sa.id,sp.id separator '|') value_ids,
            ssav.sku_id
        from sku_sale_attr_value ssav
                 left join spu_sale_attr_value sp on sp.id= ssav.sale_attr_value_id
                 left join spu_sale_attr sa on sa.spu_id = sp.spu_id and sa.base_sale_attr_id = sp.base_sale_attr_id
        where ssav.spu_id = #{spuId}
        group by ssav.sku_id
    </select>
</mapper>

说明:目前我们在service-product里面把数据模型已经封装好了,接下封装feign client api接口,提供给service-item微服务调用汇总数据模型

4. 服务调用方(详情微服务)

4.1 Feign远程调用模块

4.1.1 搭建gmall-client父模块

该模块管理所有微服务远程调用的feign client api模块。搭建方式如:gmall-common父模块

  1. 选中gmall-parent新增子模块

image-20221201153711505

  1. 填写模块名称:gmall-client

image-20221201153751518

image-20221201153809246

  1. 当前模块为父工程,固删除src

image-20221201153915864

  1. 提供pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
       <parent>
           <artifactId>gmall-parent</artifactId>
           <groupId>com.atguigu.gmall</groupId>
           <version>1.0</version>
       </parent>
       <modelVersion>4.0.0</modelVersion>
       
       <artifactId>gmall-client</artifactId>
           
    	<packaging>pom</packaging>
           
       <dependencies>
           <dependency>
               <groupId>com.atguigu.gmall</groupId>
               <artifactId>common-util</artifactId>
               <version>1.0</version>
           </dependency>
       
           <dependency>
               <groupId>com.atguigu.gmall</groupId>
               <artifactId>gmall-model</artifactId>
               <version>1.0</version>
           </dependency>
       
           <dependency>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-starter-web</artifactId>
           </dependency>
       
           <!-- 服务调用feign -->
           <dependency>
               <groupId>org.springframework.cloud</groupId>
               <artifactId>spring-cloud-starter-openfeign</artifactId>
               <scope>provided </scope>
           </dependency>
       </dependencies>
    </project>
    

4.1.2 搭建service-product-client

4.1.2.1 构建模块

gmall-client 模块下创建:service-product-client

image-20221201154051421

image-20221201154108978

4.1.2.2 封装service-product-client接口

  1. service-product-client模块新建包名:com.atguigu.gmall.product.client 包下创建Feign接口

    package com.atguigu.gmall.product.client;
       
    import com.atguigu.gmall.product.client.impl.ProductDegradeFeignClient;
    import com.atguigu.gmall.product.model.*;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
       
    import java.math.BigDecimal;
    import java.util.List;
    import java.util.Map;
       
    @FeignClient(value = "service-product", fallback = ProductDegradeFeignClient.class)  // http://service-product
    public interface ProductFeignClient {
       
       /**
        * 获取商品信息 包含商品图片
        *
        * @param skuId
        * @return
        */
       @GetMapping("/api/product/inner/getSkuInfo/{skuId}")
       public SkuInfo getSkuInfo(@PathVariable("skuId") Long skuId);
       
       
       /**
        * 根据三级分类ID 查询所属所有三级分类信息
        *
        * @param category3Id
        * @return
        */
       @GetMapping("/api/product/inner/getCategoryView/{category3Id}")
       public BaseCategoryView getCategoryView(@PathVariable("category3Id") Long category3Id);
       
       
       /**
        * 根据skuID查询商品价格
        *
        * @param skuId
        * @return
        */
       @GetMapping("/api/product/inner/getSkuPrice/{skuId}")
       public BigDecimal getSkuPrice(@PathVariable("skuId") Long skuId);
       
       
       /**
        * 根据SPUID查询商品所有海报列表
        * @param spuId
        * @return
        */
       @GetMapping("/api/product/inner/findSpuPosterBySpuId/{spuId}")
       public List<SpuPoster> getSpuPosterBySpuId(@PathVariable("spuId") Long spuId);
       
       
       
       /**
        * 根据商品SkuID查询当前sku商品所有平台属性名称包含属性值
        *
        * @param skuId
        * @return
        */
       @GetMapping("/api/product/inner/getAttrList/{skuId}")
       public List<BaseAttrInfo> getAttrList(@PathVariable("skuId") Long skuId);
       
       
       /**
        * 查询SPU所有销售属性,将指定传入SKU商品销售属性区分是否选中
        *
        * @param skuId
        * @param spuId
        * @return
        */
       @GetMapping("/api/product/inner/getSpuSaleAttrListCheckBySku/{skuId}/{spuId}")
       public List<SpuSaleAttr> getSpuSaleAttrListCheckBySku(
               @PathVariable("skuId") Long skuId,
               @PathVariable("spuId") Long spuId);
       
       
       /**
        * 查询每个商品SKU 销售属性 跟 SKUID 对照关系
        *
        * @param spuId
        * @return {"3732|3734":24,"3732|3735":25}
        */
       @GetMapping("/api/product/inner/getSkuValueIdsMap/{spuId}")
       public Map getSkuValueIdsMap(@PathVariable("spuId") Long spuId);
    }
    
  2. 为了保护上游服务,进一步提供服务降级类

    package com.atguigu.gmall.product.client;
       
    import com.atguigu.gmall.product.client.impl.ProductDegradeFeignClient;
    import com.atguigu.gmall.product.model.*;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
       
    import java.math.BigDecimal;
    import java.util.List;
    import java.util.Map;
       
    @FeignClient(value = "service-product", fallback = ProductDegradeFeignClient.class)  // http://service-product
    public interface ProductFeignClient {
       
       /**
        * 获取商品信息 包含商品图片
        *
        * @param skuId
        * @return
        */
       @GetMapping("/api/product/inner/getSkuInfo/{skuId}")
       public SkuInfo getSkuInfo(@PathVariable("skuId") Long skuId);
       
       
       /**
        * 根据三级分类ID 查询所属所有三级分类信息
        *
        * @param category3Id
        * @return
        */
       @GetMapping("/api/product/inner/getCategoryView/{category3Id}")
       public BaseCategoryView getCategoryView(@PathVariable("category3Id") Long category3Id);
       
       
       /**
        * 根据skuID查询商品价格
        *
        * @param skuId
        * @return
        */
       @GetMapping("/api/product/inner/getSkuPrice/{skuId}")
       public BigDecimal getSkuPrice(@PathVariable("skuId") Long skuId);
       
       
       /**
        * 根据SPUID查询商品所有海报列表
        * @param spuId
        * @return
        */
       @GetMapping("/api/product/inner/findSpuPosterBySpuId/{spuId}")
       public List<SpuPoster> getSpuPosterBySpuId(@PathVariable("spuId") Long spuId);
       
       
       
       /**
        * 根据商品SkuID查询当前sku商品所有平台属性名称包含属性值
        *
        * @param skuId
        * @return
        */
       @GetMapping("/api/product/inner/getAttrList/{skuId}")
       public List<BaseAttrInfo> getAttrList(@PathVariable("skuId") Long skuId);
       
       
       /**
        * 查询SPU所有销售属性,将指定传入SKU商品销售属性区分是否选中
        *
        * @param skuId
        * @param spuId
        * @return
        */
       @GetMapping("/api/product/inner/getSpuSaleAttrListCheckBySku/{skuId}/{spuId}")
       public List<SpuSaleAttr> getSpuSaleAttrListCheckBySku(
               @PathVariable("skuId") Long skuId,
               @PathVariable("spuId") Long spuId);
       
       
       /**
        * 查询每个商品SKU 销售属性 跟 SKUID 对照关系
        *
        * @param spuId
        * @return {"3732|3734":24,"3732|3735":25}
        */
       @GetMapping("/api/product/inner/getSkuValueIdsMap/{spuId}")
       public Map getSkuValueIdsMap(@PathVariable("spuId") Long spuId);
    }
       
    

说明:接下来service-item引用service-product-client模块,就可以调用相应接口

4.2 商品详情服务(service-item)

4.2.1 构建模块

点击gmall-service,新建子模块:service-item 。选择New–>Module,操作如下

下一步

下一步

完成,结构如下

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>gmall-service</artifactId>
        <groupId>com.atguigu.gmall</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>service-item</artifactId>


</project>

添加配置文件:bootstrap.properties

spring.application.name=service-item
spring.profiles.active=dev
spring.cloud.nacos.discovery.server-addr=192.168.200.128:8848
spring.cloud.nacos.config.server-addr=192.168.200.128:8848
spring.cloud.nacos.config.prefix=${spring.application.name}
spring.cloud.nacos.config.file-extension=yaml
spring.cloud.nacos.config.shared-configs[0].data-id=common.yaml

添加启动类

exclude = DataSourceAutoConfiguration.class 排除数据库链接jar表示当前项目{service-item} 不参与数据库查询

package com.atguigu.gmall;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源自动配置
@EnableFeignClients
public class ItemApp {
    public static void main(String[] args) {
        SpringApplication.run(ItemApp.class, args);
    }
}

4.2.2 item服务汇总数据

YAPI接口地址:http://192.168.200.128:3000/project/11/interface/api/699

  1. service-item模块pom.xml中引入service-product-client依赖

    <dependencies>
       <dependency>
           <groupId>com.atguigu.gmall</groupId>
           <artifactId>service-product-client</artifactId>
           <version>1.0</version>
       </dependency>
    </dependencies>
    
  2. 提供获取商品详情相关数据RestFul接口实现

    package com.atguigu.gmall.item.api;
       
    import ch.qos.logback.core.pattern.util.RegularEscapeUtil;
    import com.atguigu.gmall.common.result.Result;
    import com.atguigu.gmall.item.service.ItemService;
    import org.apache.ibatis.annotations.Param;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
       
    import java.util.Map;
       
    /**
    * @author: atguigu
    * @create: 2023-09-02 16:13
    */
    @RestController
    @RequestMapping("/api/item")
    public class ItemApiController {
       
       @Autowired
       private ItemService itemService;
       
       /**
        * 汇总渲染商品详情页面所需要数据模型
        *
        * @param skuId
        * @return
        */
       @GetMapping("/{skuId}")
       public Result getSkuInfo(@PathVariable("skuId") Long skuId) {
           Map<String, Object> mapResult = itemService.getSkuInfo(skuId);
           return Result.ok(mapResult);
       }
    }
       
    
  3. 业务层完成数据汇总

    package com.atguigu.gmall.item.service.impl;
       
    import com.atguigu.gmall.item.service.ItemService;
    import com.atguigu.gmall.product.client.ProductFeignClient;
    import com.atguigu.gmall.product.model.*;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.util.CollectionUtils;
       
    import java.math.BigDecimal;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
       
    /**
    * @author: atguigu
    * @create: 2023-09-02 16:15
    */
    @Service
    public class ItemServiceImpl implements ItemService {
       
       @Autowired
       private ProductFeignClient productFeignClient;
       
       /**
        * 汇总渲染商品详情页面所需要数据模型
        * 远程调用商品服务获取七项商品相关信息
        * ${categoryView}-三级分类
        * ${skuInfo}-商品sku信息
        * ${price}-商品价格
        * ${spuPosterList} 海报图片
        * ${skuAttrList} sku平台属性信息
        * ${spuSaleAttrList} 销售属性列表
        * ${valuesSkuJson} 切换商品SKU
        *
        * @param skuId
        * @return
        */
       @Override
       public Map<String, Object> getSkuInfo(Long skuId) {
           Map<String, Object> mapResult = new HashMap<>();
           //1.根据商品SkuId查询商品信息
           SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
           if (skuInfo == null) {
               throw new RuntimeException("远程调用商品不存在!");
           }
           mapResult.put("skuInfo", skuInfo);
           //2.根据商品分类ID查询分类信息
           BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
           if (categoryView != null) {
               mapResult.put("categoryView", categoryView);
           }
       
           //3.根据商品SkuId查询商品价格
           BigDecimal skuPrice = productFeignClient.getSkuPrice(skuId);
           if (skuPrice != null) {
               mapResult.put("price", skuPrice);
           }
       
           //4.根据商品SpuId查询海报图片列表
           List<SpuPoster> spuPosterList = productFeignClient.getSpuPosterBySpuId(skuInfo.getSpuId());
           if (!CollectionUtils.isEmpty(spuPosterList)) {
               mapResult.put("spuPosterList", spuPosterList);
       
           }
       
           //5.根据skuId查询商品平台属性列表
           List<BaseAttrInfo> baseAttrInfoList = productFeignClient.getAttrListBySkuId(skuId);
           if (!CollectionUtils.isEmpty(baseAttrInfoList)) {
               mapResult.put("skuAttrList", baseAttrInfoList);
       
           }
       
           //6.根据skuId,spuId获取所有销售属性,带选中当前sku销售属性值
           List<SpuSaleAttr> spuSaleAttrListCheckBySku = productFeignClient.getSpuSaleAttrListCheckBySku(skuId, skuInfo.getSpuId());
           if (!CollectionUtils.isEmpty(spuSaleAttrListCheckBySku)) {
               mapResult.put("spuSaleAttrList", spuSaleAttrListCheckBySku);
           }
       
           //7.切换商品SKU字符串
           String changeSkuValueIdsString = productFeignClient.getChangeSkuValueIdsMap(skuInfo.getSpuId());
           if (StringUtils.isNotBlank(changeSkuValueIdsString)) {
               mapResult.put("valuesSkuJson", changeSkuValueIdsString);
           }
           return mapResult;
       }
    }
    

5. 商品详情页面渲染

5.1 商品详情Feign模块

gmall-client 父工程下创建:service-item-client 子模块。搭建方式同service-product-client

image-20221213175122463

提供远程调用Feign API接口

package com.atguigu.gmall.item.client;

import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.item.client.impl.ItemDegradeFeignClient;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import java.util.Map;

/**
 * @author: atguigu
 * @create: 2023-09-02 16:30
 */
@FeignClient(value = "service-item", path = "/api/item", fallback = ItemDegradeFeignClient.class)
public interface ItemFeignClient {

    /**
     * 汇总渲染商品详情页面所需要数据模型
     *
     * @param skuId
     * @return
     */
    @GetMapping("/{skuId}")
    public Result<Map> getSkuInfo(@PathVariable("skuId") Long skuId);
}

服务降级类

package com.atguigu.gmall.item.client.impl;

import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.item.client.ItemFeignClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.test.annotation.Commit;

/**
 * @author: atguigu
 * @create: 2023-09-02 16:30
 */
@Slf4j
@Component
public class ItemDegradeFeignClient implements ItemFeignClient {
    @Override
    public Result getSkuInfo(Long skuId) {
        log.error("[详情服务]getSkuInfo远程调用失败,服务降级");
        return null;
    }
}

5.2. 搭建前端web服务

5.2.1 搭建web-util

选中gmall-common父工程新建子模块:web-util,搭建方式如service-util

image-20221201155708798

image-20221201155721866

配置:pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>gmall-common</artifactId>
        <groupId>com.atguigu.gmall</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>web-util</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.atguigu.gmall</groupId>
            <artifactId>common-util</artifactId>
            <version>1.0</version>
        </dependency>

        <!-- 服务调用feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>provided </scope>
        </dependency>
    </dependencies>
</project>

导入工具类,将目录中工具类复制到web-util模块中:

image-20221201155903158

5.2.1 构建gmall-web

  1. 选中gmall-parent父工程新建子模块:gmall-web

image-20221201160254082

image-20221201160305383

  1. 修改配置pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>gmall-parent</artifactId>
        <groupId>com.atguigu.gmall</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    
    <artifactId>gmall-web</artifactId>
    
    <dependencies>
        <dependency>
            <groupId>com.atguigu.gmall</groupId>
            <artifactId>web-util</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    
        <!-- 服务注册 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <!-- 服务配置-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
    
    
        <!-- 服务调用feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    
        <!-- 流量控制 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <!--导入详情微服务feign模块-->
        <dependency>
            <groupId>com.atguigu.gmall</groupId>
            <artifactId>service-item-client</artifactId>
            <version>1.0</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>
    </dependencies>
    </project>
    
    

启动类

package com.atguigu.gmall;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源自动配置
@EnableFeignClients
public class WebAll {

    public static void main(String[] args) {
        SpringApplication.run(WebAll.class, args);
    }
}

gmall-web 模块中resources目录下新建:bootstrap.properties 文件

spring.application.name=web-all
spring.profiles.active=dev
spring.cloud.nacos.discovery.server-addr=192.168.200.128:8848
spring.cloud.nacos.config.server-addr=192.168.200.128:8848
spring.cloud.nacos.config.prefix=${spring.application.name}
spring.cloud.nacos.config.file-extension=yaml
spring.cloud.nacos.config.shared-configs[0].data-id=common.yaml

5.2.2.2 导入静态资源

在配套资料中02 工具类中gmall-web 文件夹下 有static,templates ,将这两个文件夹放入到resouces文件夹中。

导入之后,可能发生异常警告

img

解决方案:将JS规范版本设置为ECMAScript6版本

image-20221201161257586

5.2.3 编写gmall-web中的控制器

gmall-web的控制器中远程调用商品详情微服务(service-item)中汇总商品SKU方法

package com.atguigu.gmall.web.controller;

import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.item.client.ItemFeignClient;
import org.springframework.beans.factory.annotation.Autowired;
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 java.util.Map;

/**
 * @author: atguigu
 * @create: 2023-09-02 16:35
 */
@Controller
public class ItemHtmlController {


    @Autowired
    private ItemFeignClient itemFeignClient;

    /**
     * 渲染商品详情页面
     *
     * @param model
     * @return
     */
    @GetMapping("/{skuId}.html")
    public String itemHtml(Model model, @PathVariable("skuId") Long skuId) {
        //1.远程调用详情服务获取七项数据
        Result<Map> result = itemFeignClient.getSkuInfo(skuId);

        //2.通过Model封装数据模型
        model.addAllAttributes(result.getData());

        //3.返回要渲染模板页面名称
        return "/item/item";
    }
}

5.3 联调测试

浏览器中直接方法前端gmall-web微服务测试:

通过网关域名映射访问测试:

image-20221229115821331