文章目錄
- 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
,發現驗證碼不再顯示:
分析原因:
- 現在的驗證碼請求路徑為,
http://localhost:88/api/captcha.jpg?uuid=69c79f02-d15b-478a-8465-a07fd09001e6
- 原始的驗證碼請求路徑:
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 節點被點選時的回調
-->