跳至主要內容

云商城-商品详情页实战

soulballad实践项目SpringCloudAlibaba 云商城SpringCloudAlibaba约 5132 字大约 17 分钟

第5章 搜索+商品详情页实战

课程目标

1、搜索页面Thymeleaf渲染实战

​ 1)商品搜索页模板渲染

​ 2)搜索页条件搜索实现

2、商品详情页静态化处理

​ 1)商品详情页静态化

​ 2)Vue+Thymeleaf静态页属性切换

3、静态页实时更新

​ 1)Canal实时监听数据库变更

​ 2)实时更新静态页

1 搜索页面Thymeleaf渲染实战

1.1 Thymeleaf模板引擎

Thymeleaf 是一种模板语言,它包含数据模型(Data)、模板(Template)、模板引擎(Template Engine)和结果文档(Result Documents)。

  • 数据模型
    数据是信息的表现形式和载体,可以是符号、文字、数字、语音、图像、视频等。数据和信息是不可分离的,数据是信息的表达,信息是数据的内涵。数据本身没有意义,数据只有对实体行为产生影响时才成为信息。
  • 模板
    模板,是一个蓝图,即一个与类型无关的类。编译器在使用模板时,会根据模板实参对模板进行实例化,得到一个与类型相关的类。
  • 模板引擎
    模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。
  • 结果文档
    一种特定格式的文档,比如用于网站的模板引擎就会生成一个标准的HTML文档。

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

1.2 Springboot整合thymeleaf

1)工程创建

mall-web下创建搜索页面渲染工程mall-search-web坐标如下:

<groupId>com.gupaoedu.vip.mall</groupId>
<version>0.0.1-SNAPSHOT</version>
<artifactId>mall-search-web</artifactId>

mall-web中引入如下依赖:

<dependencies>
    <!--web起步依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!--thymeleaf配置-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>

    <!--Nacos-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
    </dependency>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
</dependencies>

bootstrap.yml:

server:
  port: 8085
spring:
  application:
    name: mall-websearch
  cloud:
    nacos:
      config:
        file-extension: yaml
        server-addr: 192.168.100.130:8848
      discovery:
        #Nacos的注册地址
        server-addr: 192.168.100.130:8848
  thymeleaf:
    cache: false
    suffix: .html
    encoding: UTF-8
    prefix: classpath:/templates/
#日志配置
logging:
  pattern:
    console: "%msg%n"

启动类创建com.gupaoedu.vip.mall.WebSearchApplication

@SpringBootApplication
public class WebSearchApplication {

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

2)访问案例

参考资料中的前台\frant下的所有样式、图片、js拷贝到工程的resources/static目录下,search.html拷贝到resources/templates目录下,如下图:

1604047331708

search.html中所有相对路径换成绝对路径,也就是将"./"换成"/",把search.html中头部的<html>换成<html xmlns:th="http://www.thymeleaf.org">

创建控制器com.gupaoedu.vip.mall.search.controller.SearchController,代码如下:

@Controller
@RequestMapping(value = "/web/search")
public class SearchController {

    /****
     * 搜索页面跳转
     * @return
     */
    @GetMapping
    public String search(){
        return "search";
    }
}

访问http://localhost:8085/web/search效果如下:

1604047457887

1.3 搜索渲染

搜索数据渲染我们需要在mall-search-web中调用mall-search-service,因此需要在mall-search-web引入mall-search-api依赖,并且在com.gupaoedu.vip.mall.search.feign.SkuSearchFeign添加搜索调用方法,代码如下:

/****
 * 商品搜索
 */
@GetMapping
RespResult<Map<String,Object>> search(@RequestParam Map<String,Object> searchMap);

mall-search-web依赖引入:

<dependencies>
    <!--mall-search-api-->
    <dependency>
        <groupId>com.gupaoedu.vip.mall</groupId>
        <artifactId>mall-search-api</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

在启动类上添加@EnableFeignClients注解

@EnableFeignClients(basePackages = "com.gupaoedu.vip.mall.search.feign")

1.3.1 列表数据加载

Thymeleaf语法库:

th:abbrth:acceptth:accept-charset
th:accesskeyth:actionth:align
th:altth:archiveth:audio
th:autocompleteth:axisth:background
th:bgcolorth:borderth:cellpadding
th:cellspacingth:challengeth:charset
th:citeth:classth:classid
th:codebaseth:codetypeth:cols
th:colspanth:compactth:content
th:contenteditableth:contextmenuth:data
th:datetimeth:dirth:draggable
th:dropzoneth:enctypeth:for
th:formth:formactionth:formenctype
th:formmethodth:formtargetth:fragment
th:frameth:frameborderth:headers
th:heightth:highth:href
th:hreflangth:hspaceth:http-equiv
th:iconth:idth:inline
th:keytypeth:kindth:label
th:langth:listth:longdesc
th:lowth:manifestth:marginheight
th:marginwidthth:maxth:maxlength
th:mediath:methodth:min
th:nameth:onabortth:onafterprint
th:onbeforeprintth:onbeforeunloadth:onblur
th:oncanplayth:oncanplaythroughth:onchange
th:onclickth:oncontextmenuth:ondblclick
th:ondragth:ondragendth:ondragenter
th:ondragleaveth:ondragoverth:ondragstart
th:ondropth:ondurationchangeth:onemptied
th:onendedth:onerrorth:onfocus
th:onformchangeth:onforminputth:onhashchange
th:oninputth:oninvalidth:onkeydown
th:onkeypressth:onkeyupth:onload
th:onloadeddatath:onloadedmetadatath:onloadstart
th:onmessageth:onmousedownth:onmousemove
th:onmouseoutth:onmouseoverth:onmouseup
th:onmousewheelth:onofflineth:ononline
th:onpauseth:onplayth:onplaying
th:onpopstateth:onprogressth:onratechange
th:onreadystatechangeth:onredoth:onreset
th:onresizeth:onscrollth:onseeked
th:onseekingth:onselectth:onshow
th:onstalledth:onstorageth:onsubmit
th:onsuspendth:ontimeupdateth:onundo
th:onunloadth:onvolumechangeth:onwaiting
th:optimumth:patternth:placeholder
th:posterth:preloadth:radiogroup
th:relth:revth:rows
th:rowspanth:rulesth:sandbox
th:schemeth:scopeth:scrolling
th:sizeth:sizesth:span
th:spellcheckth:srcth:srclang
th:standbyth:startth:step
th:styleth:summaryth:tabindex
th:targetth:titleth:type
th:usemapth:valueth:valuetype
th:vspaceth:widthth:wrap
th:xmlbaseth:xmllangth:xmlspace
1.3.1.1 语法说明

数据结果集循环语法:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#using-theach

th:each对象遍历,功能类似jstl中的<c:forEach>标签。

<tr th:each="user,userStat:${users}">
    <td>
    	下标:<span th:text="${userStat.index}"></span>,
    </td>
    <td th:text="${user.id}"></td>
    <td th:text="${user.name}"></td>
    <td th:text="${user.address}"></td>
</tr>

th:text:输出指定数据,例如th:text="${user.address}"表示输出user对象中的address属性。

th:src:加载指定图片

1.3.1.2 列表加载

修改search.html商品部分,输出商品列表信息,代码如下:

1604051439447

效果如下:

1604051585704

1.3.2 关键词搜索回显

1.3.2.1 语法说明

th:value给指定表单赋值。

#maps.containsKey(searchMap,'keywords'):判断map对象searchMap中是否包含keywords的key。

三元表达式:th:value="${#maps.containsKey(searchMap,'keywords')}? ${searchMap.keywords}:''"

1.3.2.2 搜索实现

用户输入关键词,后台会用searchMap接收,接收后,前台需要显示,我们可以把searchMap再存入到model中,在页面搜索框中回显搜索条件。

1)搜索条件存储:

修改com.gupaoedu.vip.mall.search.controller.SearchControllersearch方法,将搜索条件存储到model中:

1604052496368

2)页面搜索框配置

修改search.html的搜索框:

1604052625566

3)展示优化

关键词搜索后,效果如下:

1604052169225

我们只需要把之前商品名字展示标签换成th:utext即可,这样就能识别html标签了。

<a target="_blank" href="item.html" th:utext="${item.name}"></a>

1.3.3 搜索条件回显

1.3.3.1 语法说明

th:if:条件成立,则显示

th:unless:条件不成立则显示

th:each:循环(前面用过)

th:text:文本显示

1.3.3.2 条件回显

1)分类条件回显

分类条件在result.categoryList中,可以直接在页面回显,如果没有该对象,则不回显。

1604062597350

2)品牌回显

品牌条件在result.brandList中,可以直接在页面回显,如果没有该对象,则不回显。

1604067736676

3)规格回显

属性回显需要注意,如果用户没有输入该属性,才回显,如果输入了该属性,则不回显,属性是以attr_开始传入后台。

1604067837611

4)价格回显

1604067947598

页面效果如下:

1604067987304

1.3.4 搜索条件记录

1604068314211

这些条件其实都已经存在在searchMap中了,只需要取出显示即可,但如果是属性回显,就需要把attr_去掉,回显如下:

1604070398911

1.3.5 动态搜索实现

1.5.3.1 语法说明

${#strings.replace(str,x,y)字符串替换成指定的y

th:href:a标签的超链接,th:href="@{${#strings.replace(url,'price='+searchMap.price,'')}}"

1.3.5.2 动态搜索分析

1604070898941

进行搜索的时候,我们可以发现一个规律,选择搜索条件的时候,其实就是将搜索条件作为参数追加到搜索地址后面,移除某个搜索条件的时候,其实就是把搜索参数从搜索路径上移除就可以了。

我们可以在后台定义一个基础的搜索地址/web/search,每次执行搜索的时候,搜索参数会存入到searchMap中,我们可以将searchMap中的参数拼接到基础搜索地址后面作为参数,如果下次增加搜索条件,直接在它后面追加搜索条件即可,如果是减少搜索条件,在它后面移除指定条件即可。

1.3.5.3 动态搜索

1)当前URL生成

用户每次请求,我们需要根据当前提交的搜索条件生成当前的URL地址,在参考资料中提供了UrlUtils,该工具类中有三个方法①将Map参数转成URL的参数②提供baseUrl和Map,组装一个完整的Url③去掉Url中指定参数,代码如下:

public class UrlUtils {

    /**
     * 去掉URL中指定的参数
     */
    public static String replateUrlParameter(String url,String... names){
        for (String name : names) {
            url = url.replaceAll("(&"+name+"=([0-9\\w]+))|("+name+"=([0-9\\w]+)&)|("+name+"=([0-9\\w]+))", "");
        }
        return url;
    }

    /***
     * 当前请求地址组装
     */
    public static String map2url(String baseUrl,Map<String,Object> searchMap){
        //参数获取
        String parm = map2parm(searchMap);
        if(!StringUtils.isEmpty(parm)){
            baseUrl+="?"+parm;
        }
        return baseUrl;
    }

    /**
     * 将map转换成url参数
     * @param map
     * @return
     */
    public static String map2parm(Map<String, Object> map) {
        if (map == null) {
            return "";
        }
        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            sb.append(entry.getKey() + "=" + entry.getValue());
            sb.append("&");
        }
        String parameters = sb.toString();
        if (parameters.endsWith("&")) {
            parameters = StringUtils.substringBeforeLast(parameters ,"&");
        }
        return parameters;
    }
}

我们把该类导入到mall-commoncom.gupaoedu.mall.util.UrlUtils,同时添加commons-lang3依赖:

<dependencies>
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
    </dependency>
</dependencies>

修改com.gupaoedu.vip.mall.search.controller.SearchController#search添加当前地址生成调用:

1604185734200

2)页面动态调用

每次动态调用其实就是在当前url上添加指定条件,修改search.html的搜索条件,给每个搜索条件添加url,代码如下:

1604186060722

3)条件移除

1604186176567

条件移除是指每次搜索的条件中如果想移除掉某个条件,我们可以把指定参数从当前url中替换成空即可。修改search.html当前已选中的条件,代码如下:

1604186376009

1.3.5.4 排序实现

1604186672470

无论是哪种排序方式,我们可以直接传递2个参数sfieldsm到后台即可:

综合:不带这2个参数的url
新品:sfield=updateTime,sm=DESC
价格+:sfield=price,sm=ASC
价格-:sfield=price,sm=DESC

我们需要一个不带sfield和sm这两个参数的url,可以在后台生成一个,修改com.gupaoedu.vip.mall.search.controller.SearchController#search,添加一个没有排序参数的url:

1604187146705

页面排序修改:

1604189638743

1.3.5.5 分页

每次分页携带的分页参数是page,而page每次必须和上次不同,所以我们需要将url中上一次搜索的page参数去掉,而且每次需要计算分页,我们可以将参考资料中的PageInfo拷贝到mall-common中,实现对分页的封装。

1)分页封装

mall-search-servicecom.gupaoedu.vip.mall.search.service.impl.SkuSearchServiceImpl#search实现分页,代码如下红色部分:

1604190067840

2)分页url处理

修改com.gupaoedu.vip.mall.search.controller.SearchController#search,代码如下:

1604189563356

3)页面分页处理

1604189888553

2 商品详情页静态化处理

在做网站的时候,为了提升网站数据加载效率同时降低数据库负载,一般会将变更频率较低的数据做特殊处理,比如做成静态页、添加缓存,通常网站门户会这么做。商品详情页访问频率非常高,而且数据变更频率非常低,所以完全可以做成静态页。

静态页生成流程如下图:

1604191173412

2.1 商品详情页静态化

2.1.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>mall-web</artifactId>
        <groupId>com.gupaoedu.vip.mall</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>mall-page-web</artifactId>
    <description>
        商品详情页生成
    </description>
    <dependencies>
        <dependency>
            <groupId>com.gupaoedu.vip.mall</groupId>
            <artifactId>mall-goods-api</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

bootstrap.yml

server:
  port: 8086
spring:
  application:
    name: mall-web-page
  cloud:
    nacos:
      config:
        file-extension: yaml
        server-addr: 192.168.100.130:8848
      discovery:
        #Nacos的注册地址
        server-addr: 192.168.100.130:8848
  thymeleaf:
    cache: false
    encoding: UTF-8

  main:
    allow-bean-definition-overriding: true
#页面生成存储路径
pagepath: D:/pages/items/

#日志配置
logging:
  pattern:
    console: "%msg%n"

启动类:com.gupaoedu.vip.mall.MallPageApplication

@SpringBootApplication
public class MallPageApplication {

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

参考资料中的前台\frant\item.html拷贝到工程templates路径下。

2.1.2 页面数据分析

1604206024594

页面的图片是Spu中的图片集合,标题是当前选中的Sku标题,属性数据来源于Spu中的attribute_list属性,它记录了当前Spu所有Sku的属性集合,如:{"就业薪资":["6K起","10K起"],"学习费用":["1万","2万"]}

我们要想生成静态页,需要同时查询Spu和对应Sku集合以及三级分类。

2.1.3 详情页数据加载

1)分类查询

mall-goods-servicecom.gupaoedu.vip.mall.goods.controller.CategoryController中添加根据ID查询分类信息:

/****
 * 根据ID查询
 */
@GetMapping(value = "/{id}")
public RespResult<Category> one(@PathVariable(value = "id")Integer id){
    Category category = categoryService.getById(id);
    return RespResult.ok(category);
}

mall-goods-api中创建com.gupaoedu.vip.mall.goods.feign.CategoryFeign

@FeignClient(value = "mall-goods")
public interface CategoryFeign {
    /****
     * 根据ID查询
     */
    @GetMapping(value = "/category/{id}")
    RespResult<Category> one(@PathVariable(value = "id")Integer id);
}

2)Product查询

mall-goods-servicecom.gupaoedu.vip.mall.goods.service.SpuService中添加根据SpuId查询Product

//查询商品详情
Product findBySpuId(String id);

mall-goods-servicecom.gupaoedu.vip.mall.goods.service.impl.SpuServiceImpl实现查询Product

/***
 * 查询商品详情
 * @param id
 * @return
 */
@Override
public Product findBySpuId(String id) {
    //查询Spu
    Spu spu = spuMapper.selectById(id);

    //查询Sku集合
    List<Sku> skus = skuMapper.selectList(new QueryWrapper<Sku>().eq("spu_id",id));
    Product product = new Product();
    product.setSpu(spu);
    product.setSkus(skus);
    return product;
}

mall-goods-servicecom.gupaoedu.vip.mall.goods.controller.SpuController中调用查询Product

/***
 * 根据ID查询
 */
@GetMapping(value = "/product/{id}")
public RespResult<Product> one(@PathVariable(value = "id")String id){
    Product product = spuService.findBySpuId(id);
    return RespResult.ok(product);
}

mall-goods-api中创建com.gupaoedu.vip.mall.goods.feign.SpuFeign

@FeignClient(value = "mall-goods")
public interface SpuFeign {

    /***
     * 根据ID查询
     */
    @GetMapping(value = "/spu/product/{id}")
    RespResult<Product> one(@PathVariable(value = "id")String id);
}

2.1.4 静态页生成

1)Service

mall-page-web创建com.gupaoedu.vip.mall.page.service.PageService,代码如下:

public interface PageService {
    //生成静态页
    void html(String id) throws FileNotFoundException, UnsupportedEncodingException;
}

mall-page-web中创建com.gupaoedu.vip.mall.page.service.impl.PageServiceImpl,代码如下:

@Service
public class PageServiceImpl implements PageService {

    @Autowired
    private SpuFeign spuFeign;

    @Autowired
    private CategoryFeign categoryFeign;

    @Autowired
    private TemplateEngine templateEngine;

    @Value("${pagepath}")
    private String pagepah;

    /****
     * 生成静态页
     * @param id
     */
    @Override
    public void html(String id) throws FileNotFoundException, UnsupportedEncodingException {
        //加载数据
        Map<String,Object> dataMap = dataLoad(id);

        //创建Thymeleaf容器对象
        Context context = new Context();
        //设置页面数据模型
        context.setVariables(dataMap);
        //文件名字  id.html
        File dest = new File(pagepah, id + ".html");
        PrintWriter writer = new PrintWriter(dest, "UTF-8");
        //生成页面
        templateEngine.process("item", context, writer);
    }

    /***
     * 数据加载
     * @param id
     * @return
     */
    public Map<String, Object> dataLoad(String id) {
        //查询商品数据
        RespResult<Product> respProduct = spuFeign.one(id);
        Product product = respProduct.getData();
        if (product != null) {
            //数据模型
            Map<String, Object> dataMap = new HashMap<String, Object>();
            //Spu
            dataMap.put("spu", product.getSpu());
            //图片
            dataMap.put("images", product.getSpu().getImages().split(","));
            //属性
            dataMap.put("attrs",JSON.parseObject(product.getSpu().getAttributeList()));

            //三级分类查询
            RespResult<Category> one = categoryFeign.one(product.getSpu().getCategoryOneId());
            RespResult<Category> two = categoryFeign.one(product.getSpu().getCategoryTwoId());
            RespResult<Category> three = categoryFeign.one(product.getSpu().getCategoryThreeId());
            dataMap.put("one", one.getData());
            dataMap.put("two", two.getData());
            dataMap.put("three", three.getData());

            //Sku集合转JSON
            List<Sku> skus = product.getSkus();
            List<Map<String, Object>> skuList = new ArrayList<Map<String, Object>>();
            for (Sku sku : skus) {
                Map<String, Object> skuMap = new HashMap<String, Object>();
                skuMap.put("id", sku.getId());
                skuMap.put("name", sku.getName());
                skuMap.put("price", sku.getPrice());
                skuMap.put("attr", sku.getSkuAttribute());
                //添加到集合中
                skuList.add(skuMap);
            }
            dataMap.put("skulist", skuList);
            return dataMap;
        }
        return null;
    }
}

2)Controller

mall-page-web中创建com.gupaoedu.vip.mall.page.controller.PageController

@RestController
@RequestMapping(value = "/page")
public class PageController {

    @Autowired
    private PageService pageService;

    /***
     * 生成静态页
     */
    @GetMapping(value = "/{id}")
    public RespResult html(@PathVariable(value = "id")String id) throws Exception{
        pageService.html(id);
        return RespResult.ok();
    }
}

2.1.5 数据绑定

1)分类面包屑

1604207043070

代码如下:

1604207291210

2)图片列表

1604207835064

3)属性列表

1604208489758

生成静态页效果如下:

1604208517327

2.2 Vue+Thymeleaf静态页属性切换

2.2.1 数据动态切换分析

1604208691607

我们可以把华为商城打开,商品详情如上图,每次点击不同属性时,页面根本没有跳动,其实是静态页已经把静态数据加载好了,每次选择不同产品时,直接从页面指定对象中找对应的数据即可,那么每次是怎么匹配的呢?

我们目前已经加载了Sku集合,Sku集合中有一个属性sku_attribute,它记录了每个Sku的属性集合,用户在页面选择不同Sku组合的时候,其实最终组合起来一定是某个Sku的sku_attribute的值,而且该值不可能重复,所以我们可以利用这个特性来实现对应Sku的查找。

1604208906789

2.2.2 Vue+Thymeleaf数据绑定

1)默认数据

我们将所有Sku集合中第一个商品作为默认Sku,可以点定义一个集合skulist接收所有skulist,再定义一个sku存储当前选中的Sku,定义一个cattr存储当前选中的sku的属性。

1604210455270

上图代码如下:

<script th:inline="javascript">
    new Vue({
        el: '#app',
        data() {
            return {
                //Sku集合
                skulist: [[${skulist}]],
                //当前Sku
                sku:{},
                //当前选中的属性
                cattr:{}
            }
        },
        created:function () {
            //默认选中第1个sku
            this.sku=JSON.parse(JSON.stringify(this.skulist[0]))
            //选中的属性
            this.cattr =JSON.parse(this.skulist[0].attr)
        }
    })
</script>

2)选中Sku属性匹配

选中某一个Sku后,我们需要根据用户选择的属性从skulist中所有Sku的attr进行匹配,如果匹配上了,则表示用户选择是该商品,如果匹配失败,表示不是该商品,继续匹配。

我们需要先编写一个方法,实现2个Map对象匹配:

1604210812501

上图代码如下:

//匹配2个map是否相同
sameMap(map1,map2){
    //循环第1个map
    for(var key in map1){
        //匹配当前相同key的值是否相同
        if(map1[key]!=map2[key]){
            return false;
        }
    }
    return true;
}

3)选中Sku匹配

用户每次选择不同属性,我们把属性存入到当前选中sku的属性cattr中,然后从skulist中进行匹配:

//sku匹配
choosesku(key,value){
    //将key和value填充到cattr中
    this.$set(this.cattr,key,value)
    //循环匹配
    for(var i=0;i<this.skulist.length;i++){
        //匹配,则返回true
        if(this.sameMap(JSON.parse(this.skulist[i].attr),this.cattr)){
            this.sku=JSON.parse(JSON.stringify(this.skulist[i]))
            return;
        }
    }
    //没有找到,给默认值
    this.sku.id=0;
    this.sku.name="该商品已下架";
    this.sku.price=0;
},

choosesku方法调用:

1604211508269

4)样式切换

样式为class="selected",我们可以写一个方法,将当前的属性名和属性值传入到方法中,在cattr中判断是否存在,如果存在,则表示要选中它,否则不选中。

//样式匹配
ischoose(key,value){
    if(this.cattr!=undefined && this.cattr[key]==value){
        return true;
    }
    return false;
},

页面选中样式:

1604211662164

生成静态页,效果如下

1604211856079

注意:如果没有该样式,记得把参考资料前台\frant的样式、图片、js拷贝到静态页目录下即可。

3 静态页实时同步

3.1 静态页同步分析

静态页同步,我们可以在监听sku表变化,一旦发生变更,可以直接通过feign调用实现静态页生成和删除。

3.2 静态页实时更新

1)Feign

创建mall-page-api工程,并创建Feign接口com.gupaoedu.vip.mall.page.feign.PageFeign实现生成静态页调用,代码如下:

@FeignClient(value = "mall-web-page")
public interface PageFeign {

    /***
     * 生成静态页
     */
    @GetMapping(value = "/page/{id}")
    RespResult html(@PathVariable(value = "id")String id) throws Exception;
}

2)Canal监听调用

mall-canal-service中监听调用,修改com.gupaoedu.vip.canal.listener.SkuHandler的add和update方法,增加feign接口调用,代码如下:

1604213029257

上图代码如下:

@Component
public class SkuHandler implements EntryHandler<Sku>{

    @Autowired
    private SkuSearchFeign skuSearchFeign;

    @Autowired
    private PageFeign pageFeign;

    /***
     * 增加产品
     * @param sku
     */
    @SneakyThrows
    @Override
    public void insert(Sku sku) {
        if(sku.getStatus().intValue()==1){
            //导入索引
            skuSearchFeign.add(JSON.parseObject(JSON.toJSONString(sku), SkuEs.class));
        }
        //生成静态页
        pageFeign.html(sku.getSpuId());
    }

    /***
     * 修改
     * @param before
     * @param after
     */
    @SneakyThrows
    @Override
    public void update(Sku before, Sku after) {
        if(after.getStatus().intValue()==2){
            //导入索引
            skuSearchFeign.del(after.getId());
        }else{
            skuSearchFeign.add(JSON.parseObject(JSON.toJSONString(after), SkuEs.class));
        }

        //生成静态页
        pageFeign.html(after.getSpuId());
    }

    /***
     *
     * @param sku
     */
    @Override
    public void delete(Sku sku) {
        skuSearchFeign.del(sku.getId());
    }
}

不要忘记在主方法上添加@EnableFeignClients

@EnableFeignClients(basePackages = {"com.gupaoedu.vip.mall.goods.feign","com.gupaoedu.vip.mall.search.feign","com.gupaoedu.vip.mall.page.feign"})

以后但凡出现如下错误,都不要过度担心,这种情况都是对象名字的重复,整个工程里即便null也是一个重复对象,这种情况我们允许覆盖重复名字即可。

1604213113394

在bootstrap.yml中添加如下配置:

  main:
    allow-bean-definition-overriding: true
上次编辑于:
贡献者: soulballad