gulimall-分布式基础篇-分类维护
一、三级分类
1.1 效果展示
1.2 导入数据
1.3 后端(tree功能)
1.3.1 CategoryEntity
pms_category表
package com.jyyy.gulimall.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;
@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;
}
1.3.2 CategoryController
@RequestMapping("list/tree")
public R list(){
List<CategoryEntity> entities = categoryService.listWithTree();
return R.ok().put("data",entities);
}
1.3.3 CategoryService
/**
* 三级分类工具类
* @return
*/
List<CategoryEntity> listWithTree();
1.3.4 CategoryServiceImpl
在gulimall-product模块,service里创建listWithTree(),impl里实现;
第一层分类要自己在数据库创建
/**
* 三级分类工具类
* @return
*/
public List<CategoryEntity> listWithTree() {
// 1.查出所有分类
List<CategoryEntity> entities = baseMapper.selectList(null);
// 2.组装成父子的树形结构
return entities.stream()
// 过滤出一级分类
.filter(categoryEntity -> categoryEntity.getParentCid() == 0)
// peek改变元素内部结构,对每个 object 执行 方法(a, b)
.peek(menu -> menu.setChildren(getChildless(menu,entities)))
// 按sort排序(过滤完后将所有排序)从上到下展示
// menu.getSort()如果为0则赋值,不为0则用本身的值
.sorted(Comparator.comparingInt(menu -> (menu.getSort() == null ? 0 : menu.getSort())))
.collect(Collectors.toList());
}
/**
* 递归查找所有菜单的子菜单
* @return
*/
private List<CategoryEntity> getChildless(CategoryEntity root,List<CategoryEntity> all){
return all.stream()
// 过滤父节点相等的
.filter(categoryEntity -> categoryEntity.getParentCid().equals(root.getCatId()))
// 处理
.peek(categoryEntity -> {
// 继续
categoryEntity.setChildren(getChildless(categoryEntity,all));
})
// 排序,Comparator比较器接口,用category的sort排序
.sorted(Comparator.comparingInt(menu -> (menu.getSort() == null ? 0: menu.getSort())))
.collect(Collectors.toList());
}
1.3.5 测试
localhost:10000/product/category/list/tree
1.4 后端(删除功能)
删除时判断有没有子菜单
1.4.1 配置逻辑删除
配置全局的逻辑删除
spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.33.10:3306/gulimall_pms?useUnicode=true&characterEncoding=UTF-8&userSSL=false&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
application:
name: gulimall-product
cloud:
nacos:
discovery:
server-addr: 192.168.33.10:8848
# MapperScan
# sql映射文件位置
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
id-type: auto
#这里是全局,如果某一个表不同,则单独配置
logic-delete-value: 1 #1表示删除
logic-not-delete-value: 0 #0表示未删除
server:
port: 10000
不使用全局逻辑删除,使用实体类注解
@TableLogic(value = "1",delval = "0")
private Integer showStatus;
1.5 后端(批量修改功能)
CategoryController
拖拽树产生的批量数据
/**
* 批量修改
* {
* "catId": 0, //菜单id
* "catLevel": 0, //菜单层级
* "parentCid": 0, //父菜单id
* "sort": 0 //排序
* }
*/
@RequestMapping("/update/sort")
// @RequiresPermissions("product:category:update")
public R update(@RequestBody CategoryEntity[] categorys){
// categoryService.updateById(category);
// 这里收集集合对象Collection<T> entityList
categoryService.updateBatchById(Arrays.asList(categorys));
return R.ok();
}
1.6、前端
1.6.1 前端脚手架路由规范
当点击侧边栏目录新增的分类维护时,会跳转到 /product-category,对应新增菜单设置的路由 product/category
页面:对应前端项目 src->views->modules->product->category.vue 页面组件
1.6.2 创建目录/菜单
1.6.3 创建页面框架
1.6.4 使用el-tree
1.6.5 参照系统页面获取数据(拉取数据)
1.6.6 分类维护新增/删除功能
1.6.7 只有一,二级分类才有新增,没有子节点才有删除
1.6.8 删除需要选择框,点击时获取到catId
1.6.9 编写删除方功能
1.6.10 删除或新增后刷新还是打开原来打开的分组
1.6.11 编写新增功能
1.6.12编写修改功能
1.6.13 关闭点击框外关闭
1.6.14 修改后获取的数据在新增时要清除
修改时都是直接获取的所以不需要清空
1.6.15 el-tree开启拖拽功能
1.6.16 验证节点是否能被拖动到指定位置
找到拖拽节点最深度
拖拽方法
1.6.17 拖拽数据搜集
拖拽功能影响的是,父节点,层级,排序
1.6.18 解决根分类拖动后没有父id
1.6.19 修改当前节点层级,包含子节点的层级
1.6.20 发送数据到后台
1.6.21 拖拽成功后要把层级,更新节点数组重新赋值
1.6.22 开关开启拖动功能
1.6.23 开启批量拖拽后再发送数据
1.6.24 改变深度求法,批量拖拽应用前端level求
1.6.25 每修改一个菜单,要展开的父节点应放数组
1.6.26 批量删除
二、配置网关
作用:这一节会解决一个前端向多个后端服务请求的问题(API网关),此外还有网关服务与前端前后分离的跨域问题。
流程:前端客户端请求88/api→后端gateway模块→再由gateway模块分去各个模块
2.1 前端工程配置API网关作为唯一接口
打开 static->config->index.js 配置统一请求地址(设置接口请求地址(唯一))
每一个服务都要注册到nacos中
// api网关作为接口请求地址,由网关分发到不同服务
window.SITE_CONFIG['baseUrl'] = 'http://localhost:88/api';
2.2 将renren-fast接入网关服务配置
2.2.1 将renren-fast注册到nacos服务
先创建独有的命名空间
renren-fast引入common依赖
<dependency>
<groupId>com.jyyy.gulimall</groupId>
<artifactId>gulimall-common</artifactId>
</dependency>
配置nacos地址
spring:
application:
name: renren-fast
cloud:
nacos:
discovery:
server-addr: 192.168.10.33:8848
主启动类
@EnableDiscoveryClient
@SpringBootApplication
public class RenrenApplication {
public static void main(String[] args) {
SpringApplication.run(RenrenApplication.class, args);
}
}
2.3 将gulimall-product注册到nacos服务
2.3.1 配置到注册中心
application:
name: gulimall-product
cloud:
nacos:
discovery:
server-addr: 192.168.33.10:8848
2.3.2 配置到配置中心
spring:
application:
name: gulimall-product
cloud:
nacos:
config:
server-addr: 192.168.33.10:8848
file-extension: yaml
namespace: d23e7bc6-dce1-4333-8119-c429c23a609a
2.3.3 主启动类
package com.jyyy.gulimall.product;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@MapperScan("com.jyyy.gulimall.product.dao")
@SpringBootApplication
@EnableDiscoveryClient
public class GulimallProductApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallProductApplication.class, args);
}
}
2.4 网关增加路由断言转发不同服务
spring:
application:
name: gulimall-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.10.33:8848
gateway:
routes:
<!--高优先级路由放前面,模糊路由放面-->
- id: product_route
<!--负载均衡到gulimall-product-->
uri: lb://gulimall-product
predicates:
- Path=/api/product/**
filters:
<!--路径重写-->
- RewritePath=/api/(?<segment>.*),/$\{segment}
- id: admin_route
<!--负载均衡到renren-fast-->
uri: lb://renren-fast
<!-- 断言-->
predicates:
<!--所有api的地址都会拦截-->
- Path=/api/**
filters:
<!--路径重写-->
- RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}
三、跨域
当前网站不允许运行其它网站的脚本,报错,所以要解决
3.1 简介
Method:OPTIONS预请求
3.2 方法
方法一
方法二
3.3 配置跨域
在网关跨域就行,因为前端是请求网关
package com.jyyy.gulimall.gateway.config;
@Configuration
public class MallCorsConfiguration {
@Bean
public CorsWebFilter corsWebFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
// 配置跨域
corsConfiguration.addAllowedHeader("*");
corsConfiguration.addAllowedMethod("*");
corsConfiguration.addAllowedOrigin("*");
// 运行携带cookie不然丢失信息
corsConfiguration.setAllowCredentials(true);
// 注册
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsWebFilter(source);
}
}
3.4 删除renren-fast的跨域
有两个会报错
删除 src/main/java/io/renren/config/CorsConfig.java 中的配置内容
这里会导致请求头添加重复,导致跨域失败