尚品汇商城

一、商品详情相关业务介绍

l 商品详情页,简单说就是以购物者的角度展现一个sku的详情信息。
l 用户点击不同的销售属性值切换不同的商品
l 点击添加购物车,将商品放入购物车列表中

二、模板技术Thymeleaf介绍

2.1 Thymeleaf 简介

Thymeleaf是一款用于渲染XML/XHTML/HTML5内容的模板引擎。类似JSP,Velocity,FreeMaker等, 它也可以轻易的与Spring MVC等Web框架进行集成作为Web应用的模板引擎。与其它模板引擎相比, Thymeleaf最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用!

2.2 快速入门

项目创建,依赖模块web,Thymeleaf.模板。
06 商品详情页 - 图1

2.2.1 设置头文件

就想Jsp的<%@Page %>一样 ,Thymeleaf的也要引入标签规范。不加这个虽然不影响程序运行,但是你的idea会认不出标签,不方便开发。

<html xmlns=”http://www.w3.org/1999/xhtml“ xmlns:th=”http://www.thymeleaf.org>

2.2.2 赋值字符串拼接

request.setAttribute(“name”,“刘德华”);

2.2.3 循环

| List list = Arrays.asList(“郑爽”, “刘德华”, “张惠妹”, “成龙”);

request.setAttribute(“arrayList”,list);
<table>
<tr th:each=”s,stat: ${arrayList}”> <td th:text=”${s}”></td> <td th:text=”${stat.index}”></td> <td th:text=”${stat.count}”></td> <td th:text=”${stat.size}”></td> <td th:text=”${stat.even}”></td> <td th:text=”${stat.odd}”></td> <td th:text=”${stat.first}”></td> <td th:text=”${stat.last}”></td> </tr> </table> | | —- |

stat 称作状态变量,属性有
index:当前迭代对象的 index(从 0 开始计算)
count:当前迭代对象的 index(从 1 开始计算)
size:被迭代对象的大小
even/odd:布尔值,当前循环是否是偶数/奇数
first:布尔值,当前循环是否是第一个
last:布尔值,当前循环是否是最后一个

2.2.4 判断

th:if 条件成立显示
th:unless 条件不成立的时候才会显示内容

| model.addAttribute(“age”,18);<h2>判断 if</h2> <div th:if=”${age}>=18” th:text=”success”>good</div> <a th:unless=”${age != 18}” th:text=”success” >atguigu</a> <h2>判断 三元</h2> <div th:text=”${age}>=18?’success’:’failure’”></div> | | —- |

2.2.5 取session中的属性

httpSession.setAttribute(“addr”,“北京中南海”);

2.2.6 引用内嵌页

2.2.7 th:utext :解析样式

th:utext:识别html中的标签

request.setAttribute(“gname”,绿色);
<p th:utext=”${gname}”>color</p>

2.2.8 点击链接传值

<a th:href=”@{http://localhost:8080/list.html?category1Id={category1Id}(category1Id=${category1Id})}>点我带你飞</a>
@RequestMapping(“list.html”)

@ResponseBody
public String list(String category1Id){
// 接收传递过来的数据 _System.**_out.println(“获取到的数据:\t”+category1Id);
return category1Id;
}在index 控制器中先存储一个category1Id_/保存 category1Id/
_request.setAttribute(
“category1Id”,“2”**); |

2.2.9 多种存储方式

| model.addAttribute(“addr”,“北京昌平区”);
hashMap.put(“id”,“101”);
HashMap map = new HashMap<>();
map.put(“stuNo”,“1000”);
map.put(“stuName”,“张三”);
model.addAllAttributes(map);

| | —- | | <h2> 多种方式存储数据1 </h2>
<div th:text=”${addr}”></div><div th:text=”${id}”></div>
<h2> 多种方式存储数据2 </h2>
<div th:text=”${stuNo}”></div><div th:text=”${stuName}”></div>
|

三、商品详情业务需求分析

3.1 详情渲染功能介绍

06 商品详情页 - 图2
商品详情所需构建的数据如下:
1,Sku基本信息
2,Sku图片信息
3,Sku分类信息
4,Sku销售属性相关信息
5,Sku价格信息(平台可以单独修改价格,sku后续会放入缓存,为了回显最新价格,所以单独获取)
6,展示商品的海报
7,获取skuId 对应的商品规格参数

3.2 详情模块规划

模块规划思路:
1,service-item微服务模块封装详情页面所需数据接口;
2,service-item通过feign client调用其他微服务数据接口进行数据汇总;
3,pc端前台页面通过web-all调用service-item数据接口渲染页面;
4,service-item可以为ps端、H5、安卓与ios等前端应用提供数据接口,web-all为ps端页面渲染形式
5,service-item获取商品信息需要调用service-product服务sku信息等;
6,由于service各微服务可能会相互调用,调用方式都是通过feign client调用,所以我们把feign client api接口单独封装出来,需要时直接引用feign client api模块接口即可,即需创建service-client父模块,管理各service微服务feign client api接口。
06 商品详情页 - 图3

四、商品详情功能开发

4.1 搭建service-item

4.1.1 构建模块

点击service,选择New–>Module,操作如下
06 商品详情页 - 图4
下一步
06 商品详情页 - 图5
下一步
06 商品详情页 - 图6
完成,结构如下
06 商品详情页 - 图7

4.1.2 修改配置

修改配置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”
> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.atguigu.gmall</groupId> <artifactId>service</artifactId> <version>1.0</version> </parent>
<artifactId>service-item</artifactId> <version>1.0</version>
<packaging>jar</packaging> <name>service-item</name> <description>service-item</description>

<build> <finalName>service-item</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
</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.item;


@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//取消数据源自动配置 @ComponentScan({“com.atguigu.gmall”})
@EnableDiscoveryClient
@EnableFeignClients(basePackages= {“com.atguigu.gmall”})
public class ServiceItemApplication {

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

} | | —- |

4.1.3 service-item服务接口封装

| package com.atguigu.gmall.item.service; **public interface ItemService {

_/

  1. * 获取sku详情信息
  2. * **@param **_**_skuId<br />
  3. _**_* **@return<br />
  4. ***/<br />
  5. _Map<String, Object> getBySkuId(Long skuId);<br />

} | | —- | | @Service
public class ItemServiceImpl implements ItemService {

@Override
public Map getBySkuId(Long skuId) {
Map result = new HashMap<>();


return result;
}
} | | @RestController
@RequestMapping(“api/item”)
public class ItemApiController {

@Autowired
private ItemService itemService;

_/**

 * 获取sku详情信息
 * **@param **_**_skuId<br />
 _**_* **@return<br />
 ***/<br />
_@GetMapping(**"{skuId}"**)<br />
**public **Result getItem(@PathVariable Long skuId){<br />
    Map<String,Object> result = **itemService**.getBySkuId(skuId);<br />
    **return **Result._ok_(result);<br />
}} |


说明:商品详情相关信息在service-product微服务都可以获取,所以我们在service-product模块编写所需要的接口

4.2 在service-product微服务提供api接口

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

4.2.1.1 编写接口与实现类

| ManageService接口_/**

  • 根据skuId 查询skuInfo
  • @param **_skuId
    _* @return
    */
    _SkuInfo getSkuInfo(Long skuId); | | —- | | 实现类@Override
    public SkuInfo getSkuInfo(Long skuId) {
    SkuInfo skuInfo =
    skuInfoMapper.selectById(skuId);
    _// 根据skuId 查询图片列表集合 _QueryWrapper queryWrapper =
    new QueryWrapper<>();
    queryWrapper.eq(
    “sku_id”, skuId);
    List skuImageList =
    skuImageMapper.selectList(queryWrapper);

    skuInfo.setSkuImageList(skuImageList);
    return **skuInfo;
    } |

4.2.1.2 编写控制器

| package com.atguigu.gmall.product.api.ProductApiController@RestController
@RequestMapping(“api/product”)
public class ProductApiController {

@Autowired
private ManageService manageService;_
/**

 * 根据skuId获取sku信息
 * **@param **_**_skuId<br />
 _**_* **@return<br />
 ***/<br />
_@GetMapping(**"inner/getSkuInfo/{skuId}"**)<br />
**public **SkuInfo getAttrValueList(@PathVariable(**"skuId"**) Long skuId){<br />
    SkuInfo skuInfo = **manageService**.getSkuInfo(skuId);<br />
    **return **skuInfo;<br />
} |

| —- |

4.2.2 获取分类信息

4.2.2.1 需求分析:

sku是挂在三级分类下面的,我们的分类信息分别在base_category1、base_category2、base_category3这三张表里面,目前需要通过sku表的三级分类id获取一级分类名称、二级分类名称和三级分类名称
解决方案:
我们可以建立一个视图**(view)**,把三张表关联起来,视图id就是三级分类id,这样通过三级分类id就可以查询到相应数据,效果如下:
06 商品详情页 - 图8

创建视图
CREATE VIEW base_category_view AS
select
c3.id as id,
c1.id as category1_id, c1.name as category1_name,
c2.id as category2_id, c2.name as category2_name,
c3.id as category3_id, c3.name as category3_name
from base_category1 c1
inner join base_category2 c2 on c2.category1_id = c1.id
inner join base_category3 c3 on c3.category2_id = c2.id

4.2.2.2 创建mapper

| Mapper@Mapper
public interface BaseCategoryViewMapper extends BaseMapper {

} | | —- |

4.2.2.3 编写接口与实现类

| ManageService接口_/**

  • 通过三级分类id查询分类信息
    • @param **_category3Id
      _* @return
      */
      _BaseCategoryView getCategoryViewByCategory3Id(Long category3Id); | | —- | | 接口实现@Override
      public BaseCategoryView getCategoryViewByCategory3Id(Long category3Id) {
      return baseCategoryViewMapper**.selectById(category3Id);
      } |

4.2.3.4 编写控制器

| ProductApiController_/**

  • 通过三级分类id查询分类信息
  • @param **_category3Id
    _* @return
    */
    _@GetMapping(
    “inner/getCategoryView/{category3Id}”)
    public BaseCategoryView getCategoryView(@PathVariable(“category3Id”)Long category3Id){
    return manageService**.getCategoryViewByCategory3Id(category3Id);
    } | | —- |

4.2.3 获取价格信息

4.2.3.1 编写接口与实现类

| _/**

  • 获取sku价格
  • @param **_skuId
    _* @return
    */
    BigDecimal getSkuPrice(Long skuId); | | —- | | /

  • 获取sku价格
  • @param **_skuId
    _* @return
    */
    _@Override
    public BigDecimal getSkuPrice(Long skuId) {
    SkuInfo skuInfo =
    skuInfoMapper.selectById(skuId);
    if(null **!= skuInfo) {
     **return **skuInfo.getPrice();<br />
    
    }
    return new BigDecimal(“0”);
    } |

4.2.3.2 编写控制器

| _/**

  • 获取sku最新价格
  • @param **_skuId
    _* @return
    */
    _@GetMapping(
    “inner/getSkuPrice/{skuId}”)
    public BigDecimal getSkuPrice(@PathVariable Long skuId){
    return manageService**.getSkuPrice(skuId);
    } | | —- |

4.2.4 获取销售信息

思路:
1、查出该商品的spu的所有销售属性和属性值
2、标识出本商品对应的销售属性
3、点击其他销售属性值的组合,跳转到另外的sku页面

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

第1、2条通过此sql实现

| SELECT sa.id ,sa.spu_id, sa.sale_attr_name,sa.base_sale_attr_id,
sv.id sale_attr_value_id,
sv.sale_attr_value_name,
skv.sku_id,
IF(skv.sku_id IS NULL,0,1) is_checked
FROM spu_sale_attr sa
INNER JOIN spu_sale_attr_value sv ON sa.spu_id=sv.spu_id AND sa.base_sale_attr_id=sv.base_sale_attr_id
LEFT JOIN sku_sale_attr_value skv ON skv.sale_attr_value_id= sv.id AND skv.sku_id=#{skuId}
WHERE sa.spu_id=#{spuId}
ORDER BY sv.base_sale_attr_id,sv.id | | —- |

此sql列出所有该spu的销售属性和属性值,并关联某skuid如果能关联上is_check设为1,否则设为0。
在对应的实体类中【SpuSaleAttrValue】添加属性字段
@TableField(exist = false)

String isChecked;

4.2.4.2 在SpuSaleAttrMapper 接口中添加的方法

| 接口 SpuSaleAttrMapper_// 根据spuId,skuId 查询销售属性集合 _List selectSpuSaleAttrListCheckBySku(@Param(“skuId”) Long skuId, @Param(“spuId”)Long spuId); | | —- | | <select id=”selectSpuSaleAttrListCheckBySku” resultMap=”spuSaleAttrMap”> SELECT sa.id ,sa.spu_id, sa.sale_attr_name,sa.base_sale_attr_id,
sv.id sale_attr_value_id,
sv.sale_attr_value_name,
skv.sku_id,
IF(skv.sku_id IS NULL,0,1) is_checked
FROM spu_sale_attr sa
INNER JOIN spu_sale_attr_value sv ON sa.spu_id=sv.spu_id AND sa.base_sale_attr_id=sv.base_sale_attr_id
LEFT JOIN sku_sale_attr_value skv ON skv.sale_attr_value_id= sv.id AND skv.sku_id=#{skuId}
WHERE sa.spu_id=#{spuId}
ORDER BY sv.base_sale_attr_id,sv.id
</select> |

4.2.4.3 编写接口与实现类

| ManageService 接口_/**

  • 根据spuId,skuId 查询销售属性集合
  • @param **_skuId
    _* @param _spuId
    _* @return
    */
    _List getSpuSaleAttrListCheckBySku(Long skuId, Long spuId); | | —- | | 实现类@Override
    public List getSpuSaleAttrListCheckBySku(Long skuId, Long spuId) {
    return spuSaleAttrMapper**.selectSpuSaleAttrListCheckBySku(skuId, spuId);
    } |

4.2.4.4 编写控制器

| ProductApiController _/**

  • 根据spuId,skuId 查询销售属性集合
  • @param **_skuId
    _* @param _spuId
    _* @return
    */
    _@GetMapping(
    “inner/getSpuSaleAttrListCheckBySku/{skuId}/{spuId}”)
    public List getSpuSaleAttrListCheckBySku(@PathVariable(“skuId”) Long skuId,@PathVariable(“spuId”) Long spuId){
    return manageService**.getSpuSaleAttrListCheckBySku(skuId, spuId);
    } | | —- |

4.2.5 实现商品切换

实现思路:
06 商品详情页 - 图9
06 商品详情页 - 图10
1 、从页面中获得得所有选中的销售属性进行组合比如:
“属性值1|属性值2” 用这个字符串匹配一个对照表,来获得skuId。并进行跳转,或者告知无货。
2、后台要生成一个“属性值1|属性值2:skuId”的一个json串以提供页面进行匹配。如06 商品详情页 - 图11
3、需要从后台数据库查询出该spu下的所有skuId和属性值关联关系。然后加工成如上的Json串,用该json串,跟前台匹配。
实现:
使用sql 语句来解决:
GROUP_CONCAT:group_concat( [distinct] 要连接的字段 [order by 排序字段 asc/desc ] [separator ‘分隔符’] )

4.2.5.1在SkuSaleAttrValueMapper中添加接口

SkuSaleAttrValueMapper_// 根据spuId 查询map 集合数据List selectSaleAttrValuesBySpu(Long spuId);
SkuSaleAttrValueMapper.xml_

_<resultMap id=”spuValueIdsMap” type=”java.util.Map” autoMapping=”true”>
</resultMap><select id=”selectSaleAttrValuesBySpu” resultMap=”spuValueIdsMap”> SELECT sku_id , GROUP_CONCAT(sale_attr_value_id ORDER BY sp.base_sale_attr_id ASC SEPARATOR ‘|’) value_ids
FROM sku_sale_attr_value sv
INNER JOIN spu_sale_attr_value sp on sp.id = sv.sale_attr_value_id
WHERE sv.spu_id=#{spuId}
GROUP BY sku_id</select> |

4.2.5.2 编写接口与实现类

| ManageService接口_/**

  • 根据spuId 查询map 集合属性
  • @param **_spuId
    _* @return
    */
    _Map getSkuValueIdsMap(Long spuId); | | —- | | 实现类@Override
    public Map getSkuValueIdsMap(Long spuId) {
    Map map =
    new HashMap<>();
    _// key = 125|123 ,value = 37
    _List mapList =
    skuSaleAttrValueMapper.selectSaleAttrValuesBySpu(spuId);
    if (mapList != null **&& mapList.size() > 0) {
     _// 循环遍历
     _**for **(Map skuMap : mapList) {<br />
         _// key = 125&#124;123 ,value = 37<br />
         _map.put(skuMap.get(**"value_ids"**), skuMap.get(**"sku_id"**));<br />
     }<br />
    
    }
    return map;
    } |

4.2.5.3 编写控制器

| ProductApiController _/**

  • 根据spuId 查询map 集合属性
  • @param **_spuId
    _* @return
    */
    _@GetMapping(
    “inner/getSkuValueIdsMap/{spuId}”)
    public Map getSkuValueIdsMap(@PathVariable(“spuId”) Long spuId){
    return manageService**.getSkuValueIdsMap(spuId);
    } | | —- |

4.2.6 获取海报信息

4.2.6.1 编写接口与实现类

| _/**

  • 根据spuid获取商品海报
  • @param **_spuId
    _* @return
    */
    _List findSpuPosterBySpuId(Long spuId); | | —- | | @Override
    public List findSpuPosterBySpuId(Long spuId) {
    QueryWrapper spuInfoQueryWrapper =
    new QueryWrapper<>();
    spuInfoQueryWrapper.eq(
    “spu_id”,spuId);
    List spuPosterList =
    spuPosterMapper.selectList(spuInfoQueryWrapper);
    return **spuPosterList;
    } |

4.2.6.2 编写控制器

| // 根据spuId 获取海报数据
@GetMapping(“inner/findSpuPosterBySpuId/{spuId}”)
public List findSpuPosterBySpuId(@PathVariable Long spuId){
return manageService.findSpuPosterBySpuId(spuId);
} | | —- |

4.2.7 Sku对应的平台属性

4.2.7.1 编写接口与实现类

显示在商品详情规格处

| ManageService_/**

  • 通过skuId 集合来查询数据
  • @param **_skuId
    _* @return
    */
    _List getAttrList(Long skuId); | | —- | | 实现类@Override
    public List getAttrList(Long skuId) {

    return baseAttrInfoMapper.selectBaseAttrInfoListBySkuId(skuId);
    } | | BaseAttrInfoMapper 添加方法 _/


    @param **_skuId
    _*/
    _List selectBaseAttrInfoListBySkuId(@Param(
    “skuId”)Long skuId); | | BaseAttrInfoMapper.xml添加<select id=”selectBaseAttrInfoListBySkuId” resultMap=”baseAttrInfoMap”> SELECT
    bai.id,
    bai.attr_name,
    bai.category_id,
    bai.category_level,
    bav.id attr_value_id,
    bav.value_name,
    bav.attr_id
    FROM
    base_attr_info bai
    INNER JOIN base_attr_value bav ON bai.id = bav.attr_id
    INNER JOIN sku_attr_value sav ON sav.value_id = bav.id
    WHERE
    sav.sku_id = #{skuId}
    </
    select**> |

4.2.7.2 编写控制器

ProductApiController/
通过skuId 集合来查询数据
@param **
skuId
@return
/
@GetMapping(“inner/getAttrList/{skuId}”)public List getAttrList(@PathVariable(“skuId”) Long skuId){
return manageService.getAttrList(skuId);
}


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

4.3 搭建service-client父模块

改模块管理所有微服务feign client api模块

4.3.1 搭建service-client父模块

搭建方式如:common父模块

4.3.2 修改配置

修改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 https://maven.apache.org/xsd/maven-4.0.0.xsd”
> <modelVersion>4.0.0</modelVersion>
<modules> <module>service-product-client</module> <module>service-item-client</module> </modules> <parent> <groupId>com.atguigu.gmall</groupId> <artifactId>gmall-parent</artifactId> <version>1.0</version> </parent>
<artifactId>service-client</artifactId> <packaging>pom</packaging> <version>1.0</version>
<dependencies> <dependency> <groupId>com.atguigu.gmall</groupId> <artifactId>common-util</artifactId> <version>1.0</version> </dependency>
<dependency> <groupId>com.atguigu.gmall</groupId> <artifactId>model</artifactId> <version>1.0</version> </dependency>
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>

<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <scope>provided </scope> </dependency> </dependencies>
</project> | | —- |

4.4 搭建service-product-client

4.4.1 构建模块

在service-client 模块下创建

4.4.2 修改配置

修改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 https://maven.apache.org/xsd/maven-4.0.0.xsd”
> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.atguigu.gmall</groupId> <artifactId>service-client</artifactId> <version>1.0</version> </parent>
<artifactId>service-product-client</artifactId> <version>1.0</version>
<packaging>jar</packaging> <name>service-product-client</name> <description>service-product-client</description>
</project> | | —- |

4.4.3 封装service-product-client接口

| package com.atguigu.gmall.product.client; @FeignClient(value =“service-product”, fallback = ProductDegradeFeignClient.class)
public interface ProductFeignClient {

_/**

 * 根据skuId获取sku信息
 *<br />
 * **@param **_**_skuId<br />
 _**_* **@return<br />
 ***/<br />
_@GetMapping(**"/api/product/inner/getSkuInfo/{skuId}"**)<br />
SkuInfo getSkuInfo(@PathVariable(**"skuId"**) Long skuId);<br />



_/**

 * 通过三级分类id查询分类信息
 * **@param **_**_category3Id<br />
 _**_* **@return<br />
 ***/<br />
_@GetMapping(**"/api/product/inner/getCategoryView/{category3Id}"**)<br />
BaseCategoryView getCategoryView(@PathVariable(**"category3Id"**)Long category3Id);<br />


_/**

 * 获取sku最新价格
 *<br />
 * **@param **_**_skuId<br />
 _**_* **@return<br />
 ***/<br />
_@GetMapping(**"/api/product/inner/getSkuPrice/{skuId}"**)<br />
BigDecimal getSkuPrice(@PathVariable(value = **"skuId"**) Long skuId);<br />


_/**

 * 根据spuId,skuId 查询销售属性集合
 *<br />
 * **@param **_**_skuId<br />
 _**_* **@param **_**_spuId<br />
 _**_* **@return<br />
 ***/<br />
_@GetMapping(**"/api/product/inner/getSpuSaleAttrListCheckBySku/{skuId}/{spuId}"**)<br />
List<SpuSaleAttr> getSpuSaleAttrListCheckBySku(@PathVariable(**"skuId"**) Long skuId, @PathVariable(**"spuId"**) Long spuId);<br />


_/**

 * 根据spuId 查询map 集合属性
 * **@param **_**_spuId<br />
 _**_* **@return<br />
 ***/<br />
_@GetMapping(**"/api/product/inner/getSkuValueIdsMap/{spuId}"**)<br />
Map getSkuValueIdsMap(@PathVariable(**"spuId"**) Long spuId);<br />


// 根据spuId 获取海报数据
@GetMapping(“/api/product/inner/findSpuPosterBySpuId/{spuId}”)
List getSpuPosterBySpuId(@PathVariable Long spuId);_ /**

 * 通过skuId 集合来查询数据
 * **@param **_**_skuId<br />

** ** ** @return
/
@GetMapping(“/api/product/inner/getAttrList/{skuId}”)
_List getAttrList(@PathVariable(“skuId”) Long skuId);} | | —- | | @Component
public class ProductDegradeFeignClient implements ProductFeignClient {

@Override
public SkuInfo getSkuInfo(Long skuId) {
return null;
}

@Override
public BaseCategoryView getCategoryView(Long category3Id) {
return null;
}

@Override
public BigDecimal getSkuPrice(Long skuId) {
return null;
}

@Override
public List getSpuSaleAttrListCheckBySku(Long skuId, Long spuId) {
return null;
}


@Override
public Map getSkuValueIdsMap(Long spuId) {
return null;
} @Override
public List getSpuPosterBySpuId(Long spuId) {
return null;
} @Override
public List getAttrList(Long skuId) {
return null;
}
} |


说明:接下来service-item引用service-product-client模块,就可以调用相应接口
在service-item pom.xml引用依赖:
<dependency>
<groupId>com.atguigu.gmall</groupId>
<artifactId>service-product-client</artifactId>
<version>1.0</version>
</dependency>

4.5 service-item模块汇总数据

| @Service
public class ItemServiceImpl implements ItemService {

// 远程调用service-product-client
@Autowired
private ProductFeignClient productFeignClient;

@Override
public Map getItemBySkuId(Long skuId) {
// 声明对象 _Map result = new HashMap<>();

// 获取到的数据是skuInfo + skuImageList
SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);

// 判断skuInfo 不为空 if (skuInfo!=null){
// 获取分类数据 BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
result.put(“categoryView”,categoryView);
// 获取销售属性+销售属性值 List spuSaleAttrListCheckBySku = productFeignClient.getSpuSaleAttrListCheckBySku(skuId, skuInfo.getSpuId());
result.put(“spuSaleAttrList”,spuSaleAttrListCheckBySku);
// 查询销售属性值Id 与skuId 组合的map
Map skuValueIdsMap = productFeignClient.getSkuValueIdsMap(skuInfo.getSpuId());
// 将这个map 转换为页面需要的Json 对象 String valueJson = JSON._toJSONString(skuValueIdsMap);
result.put(“valuesSkuJson”,valueJson);

}
// 获取价格 _BigDecimal skuPrice = productFeignClient.getSkuPrice(skuId);
// map 中 key 对应的谁? Thymeleaf 获取数据的时候 ${skuInfo.skuName}
result.put(“skuInfo”,skuInfo);
result.put(“price”,skuPrice);
// 返回map 集合 Thymeleaf 渲染:能用map 存储数据!_ // spu海报数据
_List spuPosterList =
productFeignClient.findSpuPosterBySpuId(skuInfo.getSpuId());
result.put(“spuPosterList”, spuPosterList); List attrList = productFeignClient.getAttrList(skuId);
// 使用拉姆达表示
List> skuAttrList = attrList.stream().map((baseAttrInfo) -> {
Map attrMap = new HashMap<>();
attrMap.put(“attrName”, baseAttrInfo.getAttrName());
attrMap.put(“attrValue”, baseAttrInfo.getAttrValueList().get(0).getValueName());
return attrMap;
}).collect(Collectors._toList());
_result.put(“skuAttrList”, skuAttrList); _return result;
}
} | | —- |


4.6 商品详情页面渲染

4.6.1 搭建service-item-client模块

在service-client 目录下创建。
搭建方式同service-product-client
接口类

| package com.atguigu.gmall.item.client @FeignClient(value = “service-item”, fallback = ItemDegradeFeignClient.class)
public interface ItemFeignClient {

_/**

 * **@param **_**_skuId<br />
 _**_* **@return<br />
 ***/<br />
_@GetMapping(**"/api/item/{skuId}"**)<br />
Result getItem(@PathVariable(**"skuId"**) Long skuId);<br />


} | | —- | | @Component
public class ItemDegradeFeignClient implements ItemFeignClient {


@Override
public Result getItem(Long skuId) {
return Result.fail();
}
} |


4.6.2 搭建web-util模块

4.6.2.1 搭建web-util

搭建方式如service-util

4.6.2.2 修改配置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>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>

<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> | | —- |


导入工具类:

4.6.3 构建web父模块

构建方式如:common父模块
修改配置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”
> <modules> <module>web-item</module> </modules>
<parent> <groupId>com.atguigu.gmall</groupId> <artifactId>gmall-parent</artifactId> <version>1.0</version> </parent>
<artifactId>web</artifactId> <packaging>pom</packaging> <version>1.0</version>
<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>


<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> </dependencies>
</project>

4.6.4 构建web-all模块

4.6.4.1 搭建web-all模块

搭建方式在web模块下创建

4.6.4.2 修改pom.xml文件

| <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.atguigu.gmall</groupId> <artifactId>web</artifactId> <version>1.0</version> </parent>
<artifactId>web-all</artifactId> <version>1.0</version>
<packaging>jar</packaging> <name>web-all </name> <description>web-all </description>
<dependencies> <dependency> <groupId>com.atguigu.gmall</groupId> <artifactId>service-item-client</artifactId> <version>1.0</version> </dependency> </dependencies>

<build> <finalName>web-all</finalName> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> | | —- |

4.6.4.3 添加配置文件

bootstrap.properties

spring.application.name=web-allspring.profiles.active=devspring.cloud.nacos.discovery.server-addr=192.168.200.128:8848spring.cloud.nacos.config.server-addr=192.168.200.128:8848spring.cloud.nacos.config.prefix=${spring.application.name}spring.cloud.nacos.config.file-extension=yaml
启动类@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)_//取消数据源自动配置

@ComponentScan({“com.atguigu.gmall”})
@EnableDiscoveryClient
@EnableFeignClients(basePackages= {“com.atguigu.gmall”})
public class WebAllApplication {

public static void main(String[] args) {
SpringApplication._run
(WebAllApplication.class, args);
}
} |

4.6.4.4 将web-all 模块添加到网关

1,由于我们的微服务接口都是通过网关暴露服务,所以需要配置网关
server-gateway网关添加配置:
routes:

  • id: service-product
    uri: lb://service-product
    predicates:

    • Path=//product/ _#
      - id: service-item
      **
      uri: lb://service-item
      predicates:
      - Path=/
      /item/
      #==================web前端==========================
      -
      id**: web-item

    uri: lb://web-all
    predicates:

    • Host=item.gmall.com

      4.6.4.5 导入静态资源

      在web-all 工具类中 有static,templates ,将这两个文件夹放入到resouces 文件夹中。
      导入之后,可能发送异常警告
      06 商品详情页 - 图12
      解决方案:
      06 商品详情页 - 图13

      4.6.5 编写web-all中的控制器

      4.6.5.1 在web-all调用接口

      | Package com.atguigu.gmall.all.controller@Controller
      public class ItemController {

      @Autowired
      private ItemFeignClient itemFeignClient;

      _/**
      • sku详情页面
        • @param **_skuId
          _* @param _model
          _* @return
          */
          _@RequestMapping(
          “{skuId}.html”)
          public String getItem(@PathVariable Long skuId, Model model){
          _// 通过skuId 查询skuInfo
          _Result result =
          itemFeignClient.getItem(skuId);
          model.addAllAttributes(result.getData());
          return “item/index”**;
          }
          } | | —- |