天天看點

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

文章目錄

  • ​​1.三級分類​​
  • ​​2.遞歸樹形結構擷取資料​​
  • ​​3.配置Vue項目​​
  • ​​4.網關配置​​

1.三級分類

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

一級分類查出二級分類資料,二級分類中查詢出三級分類資料

資料庫設計

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

2.遞歸樹形結構擷取資料

在注冊中心中“product”命名空間中,建立“goshop-product.properties”配置檔案:

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路
《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路
《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路
《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

在本地建立“bootstrap.properties”檔案,指明配置中心的位置和使用到的配置檔案:

spring.application.name=goshop-product
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=1c4754cb-987f-4362-a871-7ab52136f7ee

spring.cloud.nacos.config.ext-config[0].data-id=mybatis.yml
spring.cloud.nacos.config.ext-config[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.ext-config[0].refresh=true

spring.cloud.nacos.config.ext-config[1].data-id=datasource.yml
spring.cloud.nacos.config.ext-config[1].group=DEFAULT_GROUP
spring.cloud.nacos.config.ext-config[1].refresh=true
        
spring.cloud.nacos.config.ext-config[2].data-id=other.yml
spring.cloud.nacos.config.ext-config[2].group=DEFAULT_GROUP
spring.cloud.nacos.config.ext-config[2].refresh=true      

然後啟動goshop-product,檢視到該服務已經出現在了nacos的注冊中心中了

修改“com.itxiongmao.goshop.product.controller.CategoryController”類,添加如下代碼:

/**
  * 查出所有分類以及子分類,以樹形結構組裝起來
  */
 @RequestMapping("/list/tree")
 public R list(){
     List<CategoryEntity> entities = categoryService.listWithTree();
     return R.ok().put("data", entities);
 }      
@Override
 public List<CategoryEntity> listWithTree() {
     //1、查出所有分類
     List<CategoryEntity> entities = baseMapper.selectList(null);
     //2、組裝成父子的樹形結構
     //2.1)、找到所有的一級分類
     List<CategoryEntity> level1Menus = entities.stream().filter(categoryEntity -> categoryEntity.getParentCid() == 0
     ).map((menu) -> {
         menu.setChildren(getChildrens(menu, entities));
         return menu;
     }).sorted((menu1, menu2) -> {
         return (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort());
     }).collect(Collectors.toList());
     return level1Menus;
 }


 //遞歸查找所有菜單的子菜單
 private List<CategoryEntity> getChildrens(CategoryEntity root,List<CategoryEntity> all){

     List<CategoryEntity> children = all.stream().filter(categoryEntity -> {
         return categoryEntity.getParentCid() == root.getCatId();
     }).map(categoryEntity -> {
         //1、找到子菜單
         categoryEntity.setChildren(getChildrens(categoryEntity,all));
         return categoryEntity;
     }).sorted((menu1,menu2)->{
         //2、菜單的排序
         return (menu1.getSort()==null?0:menu1.getSort()) - (menu2.getSort()==null?0:menu2.getSort());
     }).collect(Collectors.toList());

     return children;
 }      

分類實體類:

package com.itxiongmao.goshop.product.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.Data;

/**
 * 商品三級分類
 * 
 * @author bruce
 * @email
 * @date 2021-04-18 23:40:14
 */
@Data
@TableName("pms_category")
public class CategoryEntity implements Serializable {
  private static final long serialVersionUID = 1L;

  /**
   * 分類id
   */
  @TableId
  private Long catId;
  /**
   * 分類名稱
   */
  private String name;
  /**
   * 父分類id
   */
  private Long parentCid;
  /**
   * 層級
   */
  private Integer catLevel;
  /**
   * 是否顯示[0-不顯示,1顯示]
   */
  private Integer showStatus;
  /**
   * 排序
   */
  private Integer sort;
  /**
   * 圖示位址
   */
  private String icon;
  /**
   * 計量機關
   */
  private String productUnit;
  /**
   * 商品數量
   */
  private Integer productCount;


  @JsonInclude(JsonInclude.Include.NON_EMPTY)
  @TableField(exist=false)
  private List<CategoryEntity> children;

}      

測試結果:

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

3.配置Vue項目

啟動後端項目​

​renren-fast​

​​

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

啟動前端項目renren-fast-vue:

npm run dev      
《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路
《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

建立一級菜單:

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

建立完成後,在背景的管理系統中會建立一條記錄:

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

然後建立子菜單:

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路
《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

建立​​

​renren-fast-vue\src\views\modules\product​

​​目錄,之是以是這樣來建立,是因為​

​product/category​

​​,對應于​

​product-category​

在該目錄下,建立“​

​category.vue​

​”檔案:

<!--  -->
<template>
  <el-tree :data="menus" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
</template>

<script>
//這裡可以導入其他檔案(比如:元件,工具js,第三方插件js,json檔案,圖檔檔案等等)
//例如:import 《元件名稱》 from '《元件路徑》';

export default {
  //import引入的元件需要注入到對象中才能使用
  components: {},

  //監聽屬性 類似于data概念
  computed: {},
  //監控data中的資料變化
  watch: {},
  data() {
    return {
      menus: [],
      defaultProps: {
        children: "childrens",
        label: "name"
      }
    };
  },

  methods: {
    handleNodeClick(data) {
      console.log(data);
    },
    getMenus() {
      this.dataListLoading = true;
      this.$http({
        url: this.$http.adornUrl("/product/category/list/tree"),
        method: "get"
      }).then(({ data }) => {
        console.log("擷取到資料", data);
        this.menus = data;
      });
    }
  },

  //生命周期 - 建立完成(可以通路目前this執行個體)
  created() {
    this.getMenus();
  },
  //生命周期 - 挂載完成(可以通路DOM元素)
  mounted(){},
  beforeCreate(){}, //生命周期 - 建立之前
  beforeMount(){}, //生命周期 - 挂載之前
  beforeUpdate(){}, //生命周期 - 更新之前
  updated(){}, //生命周期 - 更新之後
  beforeDestroy(){}, //生命周期 - 銷毀之前
  destroyed(){}, //生命周期 - 銷毀完成
  activated(){} //如果頁面有keep-alive緩存功能,這個函數會觸發
};
</script>
<style scoped>      

重新整理頁面出現404異常,檢視請求發現,請求的是“​​http://localhost:8080/renren-fast/product/category/list/tree​​​”

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

這個請求是不正确的,正确的請求是:​​http://localhost:10000/product/category/list/tree​​,

修正這個問題:

替換“​

​static\config\index.js​

​​”檔案中的“​

​window.SITE_CONFIG['baseUrl']​

​”

替換前:

window.SITE_CONFIG['baseUrl'] = 'http://localhost:8080/renren-fast';      

替換後:

window.SITE_CONFIG['baseUrl'] = 'http://localhost:88/api';      

​​http://localhost:88​​,這個位址是我們網關微服務的接口。

4.網關配置

這裡我們需要通過網關來完成路徑的映射,是以将​

​renren-fast​

​​注冊到​

​nacos​

​注冊中心中,并添加配置中心

<dependency>
  <groupId>com.itxiongmao.goshop</groupId>
  <artifactId>goshop-common</artifactId>
  <version>0.0.1-SNAPSHOT</version>
</dependency>      
application:
    name: renren-fast
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
      config:
        name: renren-fast
        server-addr: 127.0.0.1:8848
        namespace: 5f33d11b-d560-4aaf-ad58-333ed653fbc4      
《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

啟動類:

@SpringBootApplication
@EnableDiscoveryClient
public class RenrenApplication {

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

注冊中心:

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

配置網關路由,前台的所有請求都是經由“​​

​http://localhost:88/api​

​​”來轉發的,在“​

​goshop-gateway​

​”中添加路由規則:

- id: admin_route
          uri: lb://renren-fast
          predicates:
            - Path=/api/**      

但是這樣做也引入了另外的一個問題,再次通路:​

​http://localhost:8001/#/login​

​,發現驗證碼不再顯示:

分析原因:

  1. 現在的驗證碼請求路徑為,​

    ​http://localhost:88/api/captcha.jpg?uuid=69c79f02-d15b-478a-8465-a07fd09001e6​

  2. 原始的驗證碼請求路徑:​

    ​http://localhost:8001/renren-fast/captcha.jpg?uuid=69c79f02-d15b-478a-8465-a07fd09001e6​

在admin_route的路由規則下,在通路路徑中包含了“​

​api​

​​”,是以它會将它轉發到​

​renren-fast​

​,網關在轉發的時候,會使用網關的字首資訊,為了能夠正常的取得驗證碼,我們需要對請求路徑進行重寫

關于請求路徑重寫:

​​6.16. The ​

​RewritePath​

​ ​

​GatewayFilter​

​ Factory​​

The ​

​RewritePath​

​​ ​

​GatewayFilter​

​​ factory takes a path ​

​regexp​

​​ parameter and a ​

​replacement​

​​ parameter. This uses Java regular expressions for a flexible way to rewrite the request path. The following listing configures a ​

​RewritePath​

​​ ​

​GatewayFilter​

​:

Example 41. application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: rewritepath_route
        uri: https://example.org
        predicates:
        - Path=/foo/**
        filters:
        - RewritePath=/red(?<segment>/?.*), $\{segment}      

For a request path of ​

​/red/blue​

​​, this sets the path to ​

​/blue​

​​ before making the downstream request. Note that the ​

​$​

​​ should be replaced with ​

​$\​

​ because of the YAML specification.

修改“admin_route”路由規則:

- id: admin_route
          uri: lb://renren-fast
          predicates:
            - Path=/api/**
          filters:
            - RewritePath=/api/(?<segment>/?.*), /renren-fast/$\{segment}      

再次通路:​

​http://localhost:8001/#/login​

​​,驗證碼能夠正常的加載了。

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

但是很不幸新的問題又産生了,通路被拒絕了

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

問題描述:已攔截跨源請求:同源政策禁止讀取位于 ​

​http://localhost:88/api/sys/login​

​​ 的遠端資源。(原因:CORS 頭缺少 ‘​

​Access-Control-Allow-Origin​

​’)。

問題分析:這是一種跨域問題。通路的域名和端口和原來的請求不同,請求就會被限制

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

跨域流程

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

參考位址:​​https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS​​

解決跨域

方式1: 使用nginx部署為同一域

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

方式2: 配置當次請求允許跨域

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

解決方法:在網關中定義“​

​GoShopCorsConfiguration​

​”類,該類用來做過濾,允許所有的請求跨域。

package com.itxiongmao.goshop.gateway.config;


import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

/**
 * @BelongsProject: goshop
 * @BelongsPackage: com.itxiongmao.goshop.gateway.config
 * @CreateTime: 2021-05-18 17:31
 * @Description: TODO
 */
@Configuration
public class GoShopCorsConfiguration {

    @Bean
    public CorsWebFilter corsWebFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

        CorsConfiguration corsConfiguration = new CorsConfiguration();

        //1、配置跨域
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.setAllowCredentials(true);

        source.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsWebFilter(source);
    }
}      

再次通路:​

​http://localhost:8001/#/login​

​​

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

​http://localhost:8001/renre​

​已攔截跨源請求:同源政策禁止讀取位于 ​

​http://localhost:88/api/sys/login​

​ 的遠端資源。(原因:不允許有多個 ‘Access-Control-Allow-Origin’ CORS 頭)​

​renren-fast/captcha.jpg?uuid=69c79f02-d15b-478a-8465-a07fd09001e6​

出現了多個請求,并且也存在多個跨源請求。

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

為了解決這個問題,需要修改renren-fast項目,注釋掉“​

​io.renren.config.CorsConfig​

​​”類。然後再次進行通路。

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

在顯示分類資訊的時候,出現了404異常,請求的​​​http://localhost:88/api/product/category/list/tree​​​不存在

《分布式微服務電商》專題(十四)-電商項目商品類目背景頁面通路

這是因為網關上所做的路徑映射不正确,映射後的路徑為​​http://localhost:8001/renren-fast/product/category/list/tree​​

但是隻有通過​​http://localhost:10000/product/category/list/tree​​路徑才能夠正常通路,是以會報404異常。

解決方法就是定義一個​

​product​

​路由規則,進行路徑重寫:

server:
  port: 88
spring:
  application:
    name: goshop-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      routes:
        - id: test_route
          uri: http://baidu.com:80/
          predicates:
            - Query=url,baidu
        - id: qq_route
          uri: http://qq.com:80/
          predicates:
            - Query=url,qq
        #商品路由
        - id: product_route
          uri: lb://goshop-product
          predicates:
            - Path=/api/product/**
          filters:
            - RewritePath=/api/(?<segment>/?.*),/$\{segment}
        #将路徑為Path=/api/**轉發至背景管理
        - id: admin_route
          uri: lb://renren-fast
          predicates:
            - Path=/api/**
          filters:
            - RewritePath=/api/(?<segment>/?.*), /renren-fast/$\{segment}
logging:
  level:
    org.springframework.cloud.gateway: TRACE
    org.springframework.http.server.reactive: DEBUG
    org.springframework.web.reactive: DEBUG
    reactor.ipc.netty: DEBUG      
<!--  -->
<template>
  <el-tree :data="menus" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
</template>

<script>
//這裡可以導入其他檔案(比如:元件,工具js,第三方插件js,json檔案,圖檔檔案等等)
//例如:import 《元件名稱》 from '《元件路徑》';

export default {
  //import引入的元件需要注入到對象中才能使用
  components: {},

  //監聽屬性 類似于data概念
  computed: {},
  //監控data中的資料變化
  watch: {},
  data() {
    return {
      menus: [],
      defaultProps: {
        children: "children",
        label: "name"
      }
    };
  },

  methods: {
    handleNodeClick(data) {
      console.log(data);
    },
    getMenus() {
      this.dataListLoading = true;
      this.$http({
        url: this.$http.adornUrl("/product/category/list/tree"),
        method: "get"
      }).then(({ data }) => {
        console.log("成功擷取到菜單資料...", data.data);
        this.menus = data.data;
      });
    }
  },

  //生命周期 - 建立完成(可以通路目前this執行個體)
  created() {
    this.getMenus();
  },
  //生命周期 - 挂載完成(可以通路DOM元素)
  mounted() {},
  beforeCreate() {}, //生命周期 - 建立之前
  beforeMount() {}, //生命周期 - 挂載之前
  beforeUpdate() {}, //生命周期 - 更新之前
  updated() {}, //生命周期 - 更新之後
  beforeDestroy() {}, //生命周期 - 銷毀之前
  destroyed() {}, //生命周期 - 銷毀完成
  activated() {} //如果頁面有keep-alive緩存功能,這個函數會觸發
};
</script>
<style scoped>      
<el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
<!--
data    展示資料
props 配置選項
    children 指定子樹為節點對象的某個屬性值
    label 指定節點标簽為節點對象的某個屬性值
    disabled 節點選擇框是否禁用為節點對象的某個屬性值
@node-click 節點被點選時的回調
-->      

繼續閱讀