【业务功能篇76】微服务网关路由predicates断言条件-filters路由转换地址-跨域问题-多级目录树化层级设计-mybatisPlus逻辑删除

业务开发-基础业务-分类管理

启动renren-fast如果出现如下错误

-Djps.track.ap.dependencies=false

添加相关配置即可

分类管理

1.后端分类接口

JDK8特性:https://blog.csdn.net/qq_38526573/category_11113126.html

在后端服务中我们需要查询出所有的三级分类信息,并将这些信息组合为有父子关系的数据,所以首先我们需要在对应的entity中添加关联字段 childrens

java 复制代码
	/**
	 * 当前类别所拥有的所有的子类
	 */
	@TableField(exist = false)
	private List<CategoryEntity> childrens;

然后我们在service中完成对应的数据处理的逻辑,具体实现逻辑参考注释

java 复制代码
    /**
     * 查询所有的类别数据,然后将数据封装为树形结构,便于前端使用
     *
     * @param params
     * @return
     */
    @Override
    public List<CategoryEntity> queryPageWithTree(Map<String, Object> params) {
        // 1.查询所有的商品分类信息
        List<CategoryEntity> categoryEntities = baseMapper.selectList(null);
        // 2.将商品分类信息拆解为树形结构【父子关系】
        // 第一步遍历出所有的大类  parent_cid = 0
        List<CategoryEntity> list = categoryEntities.stream().filter(categoryEntity -> categoryEntity.getParentCid() == 0)
                .map(categoryEntity -> {
                    // 根据大类找到多有的小类  递归的方式实现
                    categoryEntity.setChildrens(getCategoryChildrens(categoryEntity,categoryEntities));
                    return categoryEntity;
                }).sorted((entity1, entity2) -> {
                    return (entity1.getSort() == null ? 0 : entity1.getSort()) - (entity2.getSort() == null ? 0 : entity2.getSort());
                }).collect(Collectors.toList());
        // 第二步根据大类找到对应的所有的小类
        return list;
    }

    /**
     *  查找该大类下的所有的小类  递归查找
     * @param categoryEntity 某个大类
     * @param categoryEntities 所有的类别数据
     * @return
     */
    private List<CategoryEntity> getCategoryChildrens(CategoryEntity categoryEntity
            , List<CategoryEntity> categoryEntities) {
        List<CategoryEntity> collect = categoryEntities.stream().filter(entity -> {
            // 根据大类找到他的直属的小类
            return entity.getParentCid().equals(categoryEntity.getCatId());
        }).map(entity -> {
            // 根据这个小类递归找到对应的小小类
            entity.setChildrens(getCategoryChildrens(entity, categoryEntities));
            return entity;
        }).sorted((entity1, entity2) -> {
            return (entity1.getSort() == null ? 0 : entity1.getSort()) - (entity2.getSort() == null ? 0 : entity2.getSort());
        }).collect(Collectors.toList());
        return collect;
    }

CategoryService中同步定义对应的接口方法

java 复制代码
public interface CategoryService extends IService<CategoryEntity> {

    PageUtils queryPage(Map<String, Object> params);

    List<CategoryEntity> queryPageWithTree(Map<String, Object> params);
}

然后在CategoryController中新增对应处理的方法

java 复制代码
    @GetMapping("/listTree")
    public R listTree(@RequestParam Map<String, Object> params){
        List<CategoryEntity> list = categoryService.queryPageWithTree(params);
        return R.ok().put("data", list);
    }

启动服务访问测试

2. 前端服务串联

2.1 新增菜单

首先我们新增一个 商品系统的目录

按照上图的操作完成即可

然后在商品系统下添加 类别管理的菜单

对应的三级分类的页面 product/catagory--> src/views/modules/product/category.vue

2.2 类别数据

ElementUI官网:https://element.eleme.cn/#/zh-CN

第一步:展示静态的数据,直接从ElementUI官网拷贝Tree相关案例代码

html 复制代码
<template>
  <div>
      <el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
  </div>
</template>

<script>
/* eslint-disable */
export default {
    data() {
      return {
        data: [{
          label: '一级 1',
          children: [{
            label: '二级 1-1',
            children: [{
              label: '三级 1-1-1'
            }]
          }]
        }, {
          label: '一级 2',
          children: [{
            label: '二级 2-1',
            children: [{
              label: '三级 2-1-1'
            }]
          }, {
            label: '二级 2-2',
            children: [{
              label: '三级 2-2-1'
            }]
          }]
        }, {
          label: '一级 3',
          children: [{
            label: '二级 3-1',
            children: [{
              label: '三级 3-1-1'
            }]
          }, {
            label: '二级 3-2',
            children: [{
              label: '三级 3-2-1'
            }]
          }]
        }],
        defaultProps: {
          children: 'children',
          label: 'label'
        }
      };
    },
    methods: {
      handleNodeClick(data) {
        console.log(data);
      }
    }
  };
</script>

<style>

</style>

页面效果

第二步:动态获取后台服务的数据

html 复制代码
<template>
  <div>
      <el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
  </div>
</template>

<script>
/* eslint-disable */
export default {
    data() {
      return {
        data: [],
        defaultProps: {
          children: 'children',
          label: 'label'
        }
      };
    },
    methods: {
      getCategory(){
        this.$http({
          url: this.$http.adornUrl('/product/category/listTree'),
          method: 'get'
        }).then(({data}) => {
          console.log("成功获取的类别数据:",data.data)
          this.data = data.data
        })
      },
      handleNodeClick(data) {
        console.log(data);
      }
    },created(){
      this.getCategory();
    }
  };
</script>

<style>

</style>

访问三级分类数据并没有得到我们期望的结果。出现了404错误:http://localhost:8080/renren-fast/product/category/listTree

针对这个错误提示我们需要通过网关服务来实现统一的路由处理

修改了前端统一的后端服务地址为路由服务后

访问后这个验证码出不来了

验证码出不来的原因是请求的地址:http://localhost:8070/captcha.jpg?uuid=a496be9e-d916-4f3e-813d-d396c13a8b87 跑到网关服务获取验证码了,这里网关服务就应该要将这个请求路由到renren-fast服务中。

首先renren-fast服务没有在注册中心中注册,网关发现不了,先注册renren-fast服务

在renren-fast中依赖commons

xml 复制代码
		<dependency>
			<groupId>com.msb.mall</groupId>
			<artifactId>mall-commons</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>

添加注册中心和配置中心相关的信息

然后放开注册中心

最后启动服务提示如下错误

原因是因为SpringBoot我们把版本升级到了2.4.12那么validation默认被移除了,我们需要收到的添加依赖

xml 复制代码
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
			<version>2.4.12</version>
		</dependency>

启动服务,注册成功

解决验证码图片不显示的问题,我们需要在网关服务中添加对renren-fast服务访问的路由

yml 复制代码
# 注册中心的信息
spring:
  application:
    name: mall-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.56.100:8848
    gateway:
      routes:
        - id: route1
          uri: http://www.baidu.com
          predicates:
            - Query=url,baidu
        - id: route2
          uri: http://www.jd.com
          predicates:
            - Query=url,jd
        - id: app_route
          uri: lb://renren-fast
          predicates:
            - Path=/app/**
          filters:
            - RewritePath=/app/(?<segment>/?.*), /renren-fast/$\{segment}
# localhost:8070/app/captcha.jpg -->
# localhost:8080/app/captcha.jpg localhost:8080/renren-fast/captcha.jpg
# 指定注册中心的服务端口
server:
  port: 8070

然后测试访问验证码出现了503的错误

出现503错误的原因是Gateway网关服务中会根据loadbanlance负载均衡路由到renren-fast但是缺少了对应的依赖,在Gateway服务中添加即可

xml 复制代码
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-loadbalancer</artifactId>
        </dependency>

2.3 跨域问题

同源策略

由于浏览器的同源策略,即属于不同域的页面之间不能相互访问各自的页面内容
:同源策略,单说来就是同协议,同域名,同端口

xml 复制代码
URL 说明 是否允许通信
http://www.a.com/a.js
http://www.a.com/b.js 同一域名下 允许
http://www.a.com/lab/a.js
http://www.a.com/script/b.js 同一域名下不同文件夹 允许
http://www.a.com:8000/a.js
http://www.a.com/b.js 同一域名,不同端口 不允许
http://www.a.com/a.js
https://www.a.com/b.js 同一域名,不同协议 不允许
http://www.a.com/a.js
http://70.32.92.74/b.js 域名和域名对应ip 不允许
http://www.a.com/a.js
http://script.a.com/b.js 主域相同,子域不同 不允许
http://www.a.com/a.js
http://a.com/b.js 同一域名,不同二级域名(同上) 不允许(cookie这种情况下也不允许访问)
http://www.cnblogs.com/a.js
http://www.a.com/b.js 不同域名 不允许

跨域网站介绍:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS![image.png](https://file.jishuzhan.net/article/1694647524196880386/c2d77218102b4bfea83332622e16eb31.png)

对于跨域问题的解决我们统一在Gateway中设定。注意删除掉在renren-fast中配置的跨域问题

java 复制代码
package com.msb.mall.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.CorsConfigurationSource;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;

@Configuration
public class MallCorsConfiguration {

    @Bean
    public CorsWebFilter corsWebFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration configuration = new CorsConfiguration();
        // 配置跨域的信息
        configuration.addAllowedHeader("*");
        configuration.addAllowedMethod("*");
        // SpringBoot升级到2.4.0 之后需要使用该配置
        configuration.addAllowedOriginPattern("*");
        configuration.setAllowCredentials(true);
        source.registerCorsConfiguration("/**",configuration);
        return new CorsWebFilter(source);
    }
}

然后登录操作即可

2.4 查看类别数据

首先需要在Gateway中配置商品服务的路由信息,同时要注意配置规则的先后顺序

然后服务端响应的数据的字段要在Vue文件中显示的对应,才能正确的显示

访问测试

3 删除类别

2> 实现后台服务逻辑删除的操作

在实际开发中针对数据删除这块我们一般都会采用逻辑删除的方法来操作。在本项目中我们可以通过mybatis-Puls中提供的逻辑删除方式来实现

mybatis-plus:https://mp.baomidou.com/guide/

首先配置全局的逻辑删除逻辑

然后在entity的字段中显示的标明:

然后我们就可以在service中继续使用delete相关的方法来操作了

java 复制代码
    /**
     * 逻辑批量删除操作
     * @param ids
     */
    @Override
    public void removeCategoryByIds(List<Long> ids) {
        // TODO  1.检查类别数据是否在其他业务中使用

        // 2.批量逻辑删除操作
        baseMapper.deleteBatchIds(ids);

    }

然后通过postman测试

然后在数据库中反应的效果

3> 串联前后端的逻辑

要实现该效果,首先需要通过ajax异步的来提交请求到后端服务

javascript 复制代码
this.$http({
            url: this.$http.adornUrl("/product/category/delete"),
            method: "post",
            data: this.$http.adornData(ids, false),
          }).then(({ data }) => {
            if (data && data.code === 0) {
              this.$message({
                message: "操作成功",
                type: "success",
              });
              // 重新加载所有的菜单数据
              this.getCategory();
              // 设置默认展开的父节点信息
              this.expandKeys=[node.parent.data.catId]
            } else {
              this.$message.error(data.msg);
            }

然后删除需要必要的提示信息

javascript 复制代码
this.$confirm(`是否确认删除【${data.name}】记录?`, "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          // 传递的数据
          let ids = [data.catId];
          // 把删除的请求提交到后台服务
          this.$http({
            url: this.$http.adornUrl("/product/category/delete"),
            method: "post",
            data: this.$http.adornData(ids, false),
          }).then(({ data }) => {
            if (data && data.code === 0) {
              this.$message({
                message: "操作成功",
                type: "success",
              });
              // 重新加载所有的菜单数据
              this.getCategory();
              // 设置默认展开的父节点信息
              this.expandKeys=[node.parent.data.catId]
            } else {
              this.$message.error(data.msg);
            }
          });
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "已取消删除",
          });
        });
      console.log("删除", data, node);

最后删除成功数据后Tree数据刷新及对应的Tree的默认展示父节点信息

到此,三级分类数据的删除操作搞定

相关推荐
向上的车轮3 小时前
云边端架构的优势是什么?面临哪些挑战?
架构·云边端
FHYAAAX4 小时前
灾备方案和架构类型、跨区域
架构·华为云
北京_宏哥4 小时前
python接口自动化(四十二)- 项目架构设计之大结局(超详解)
python·架构·前端框架
运维&陈同学4 小时前
【Beats01】企业级日志分析系统ELK之Metricbeat与Heartbeat 监控
运维·elk·elasticsearch·云原生·kibana·heartbeat·metricbeat
车载诊断技术13 小时前
电子电气架构 --- 什么是EPS?
网络·人工智能·安全·架构·汽车·需求分析
武子康13 小时前
大数据-258 离线数仓 - Griffin架构 配置安装 Livy 架构设计 解压配置 Hadoop Hive
java·大数据·数据仓库·hive·hadoop·架构
有一个好名字16 小时前
zookeeper分布式锁模拟12306买票
分布式·zookeeper·云原生
9527华安18 小时前
FPGA多路MIPI转FPD-Link视频缩放拼接显示,基于IMX327+FPD953架构,提供2套工程源码和技术支持
fpga开发·架构·音视频
噢,我明白了18 小时前
同源策略:为什么XMLHttpRequest不能跨域请求资源?
javascript·跨域
Anna_Tong19 小时前
云原生大数据计算服务 MaxCompute 是什么?
大数据·阿里云·云原生·maxcompute·odps