分布式基础篇
csdn机制问题导致图片丢失,可以查看本人的个人博客:谷粒商城-P45-P58
1. 三级分类
1.1 sql脚本
三级分类数据脚本
1.2 查出所有分类以及子分类
1.2.1 后段代码编写
在gulimall-product的src/main/java/com/rabbitboss/gulimall/product/controller/CategoryController.java`?以下代码
@RequestMapping("/list/tree")
public R list(@RequestParam Map<String, Object> params){
List<CategoryEntity> entities = categoryService.listWithTree();
return R.ok().put("data", entities);
}
src/main/java/com/rabbitboss/gulimall/product/service/CategoryService.java 添加以下代码
List<CategoryEntity> listWithTree();
src/main/java/com/rabbitboss/gulimall/product/service/impl/CategoryServiceImpl.java 添加以下代码
@Override
public List<CategoryEntity> listWithTree() {
List<CategoryEntity> entities = baseMapper.selectList(null);
List<CategoryEntity> level1Menu = entities.stream().filter(categoryEntity ->
categoryEntity.getParentCid() == 0
).map(menu -> {
menu.setChildren(getChildrens(menu, entities));
return menu;
}).sorted((menu1, menu2) ->
(menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort())
).collect(Collectors.toList());
return level1Menu;
}
private List<CategoryEntity> getChildrens(CategoryEntity root, List<CategoryEntity> all) {
List<CategoryEntity> children = all.stream().filter((categoryEntity) -> {
return categoryEntity.getParentCid() == root.getCatId();
}).map(categoryEntity -> {
categoryEntity.setChildren(getChildrens(categoryEntity, all));
return categoryEntity;
}).sorted((menu1, menu2) ->
(menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort())
).collect(Collectors.toList());
return children;
}
1.2.2前端代码编写
1.2.2.1配置网关路由与路径重写
接着操作后台
localhost:8001 , 点击系统管理,菜单管理,新增
目录
商品系统
一级菜单
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-a2SMyzJ5-1651674156447)(https://b3logfile.com/file/2022/05/solo-fetchupload-2215375846762372900-191c3030.png)]
注意地址栏http://localhost:8001/#/product-category 可以注意到product-category我们的/被替换为了-
比如sys-role具体的视图在renren-fast-vue/views/modules/sys/role.vue
所以要自定义我们的product/category视图的话,就是创建mudules/product/category.vue
1.2.2.2前端代码
<template>
<el-tree :data="data" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
</template>
<script>
export default {
components: {},
props: {},
data () {
return {
data: [],
defaultProps: {
children: 'children',
label: 'label'
}
}
},
computed: {},
watch: {},
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)
})
}
},
created () {
this.getMenus()
},
mounted () {
},
beforeCreate () {
},
beforeMount () {
},
beforeUpdate () {
},
updated () {
},
beforeDestroy () {
},
destroyed () {
},
activated () {
}
}
</script>
<style scoped>
</style>
他要给8080发请求读取数据,但是数据是在10000端口上,如果找到了这个请求改端口那改起来很麻烦。
那么怎么将88:/api 转发到renren-fast上呢?看下面的步骤
<dependency>
<groupId>com.rabbitboss.gulimall</groupId>
<artifactId>gulimall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
application:
name: renren-fast
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
spring:
cloud:
gateway:
routes:
- id: admin_route
uri: lb://renren-fast
predicates:
- Path=/api/**
filters:
- RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}
在重启renren-fast的时候可能会出错,具体错误请看下面解决过程
1.2.3renren-fast相关报错
renren-fast启动报错:
Your project setup is incompatible with our requirements due to following reasons:
- Spring Boot [2.2.4.RELEASE] is not compatible with this Spring Cloud release train
Action:
Consider applying the following actions:
- Change Spring Boot version to one of the following versions [2.6.x, 2.7.x]
因为在Common模块里面是用的3.1的Nacos,最低支持SpringBoot2.6的版本
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xqp6Z97p-1651674156448)(https://b3logfile.com/file/2022/05/solo-fetchupload-2673391811449110828-19d9724b.png)]
所以需要将renren-fast模块里面的pom.xml把SpringBoot版本指定成2.6.6和谷粒商城其他模块的版本保持一致
renren-fast编译报错
javax.validation.constraints不存在
请看另外一篇博客javax.validation.constraints 不存在
启动报错
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-04-30 16:25:02.551 ERROR 35690 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter :
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
sysLoginController (field private io.renren.modules.sys.service.SysUserService io.renren.modules.sys.controller.SysLoginController.sysUserService)
┌─────┐
| sysUserService (field private io.renren.modules.sys.service.SysRoleService io.renren.modules.sys.service.impl.SysUserServiceImpl.sysRoleService)
↑ ↓
| sysRoleService (field private io.renren.modules.sys.service.SysUserService io.renren.modules.sys.service.impl.SysRoleServiceImpl.sysUserService)
└─────┘
Action:
Relying upon circular references is discouraged and they are prohibited by default. Update your application to remove the dependency cycle between beans. As a last resort, it may be possible to break the cycle automatically by setting spring.main.allow-circular-references to true.
按照提示在renren-fast项目下的: src/main/resources/application.yml 加入以下配置
spring.main.allow-circular-references=true
启动报错
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-04-30 16:35:10.693 ERROR 40671 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'scheduleJobController': Unsatisfied dependency expressed through field 'scheduleJobService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'scheduleJobService': Unsatisfied dependency expressed through field 'scheduler'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'schedulerFactoryBean' defined in class path resource [io/renren/modules/job/config/ScheduleConfig.class]: Invocation of init method failed; nested exception is org.quartz.SchedulerConfigException: DataSource name not set.
特别强调一下:
因为谷粒商城其他模块是SpringBoot2.6.6的版本,所以应该采取第二种写法
解决步骤
修改renren-fast项目下的:src/main/java/io/renren/modules/job/config/ScheduleConfig.java 这个文件
按照下面的修改:
prop.put("org.quartz.jobStore.class", "org.quartz.impl.jdbcjobstore.JobStoreTX");
prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore");
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o7PsMIlX-1651674156449)(https://b3logfile.com/file/2022/05/solo-fetchupload-1206516960012513860-cf207e0c.png)]
启动报错
报错原因:SpringBoot 2.6.x高版本和swagger2版本冲突
Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2022-04-30 15:29:19.138 ERROR 15886 --- [ main] o.s.boot.SpringApplication : Application run failed
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
解决步骤
- 在renren-fast项目下的:
src/main/resources/application.yml 添加以下配置
spring.mvc.pathmatch.matching-strategy=ant-path-matcher
1.2.4 请求验证码接口返回200,但是不显示出来
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lZc4UWuh-1651674156450)(https://b3logfile.com/file/2022/05/solo-fetchupload-283096653150069449-8e461ea9.png)]
1.2.5 跨域问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bnckr0F7-1651674156452)(https://b3logfile.com/file/2022/05/solo-fetchupload-3501653427239191771-31a311e8.png)]
1.2.5.1 简单请求
简单请求
某些请求不会触发 CORS 预检请求。本文称这样的请求为“简单请求”,请注意,该术语并不属于 Fetch(其中定义了 CORS)规范。若请求 满足所有下述条件,则该请求可视为“简单请求”:
使用下列方法之一:
除了被用户代理自动设置的首部字段(例如 Connection ,User-Agent )和在 Fetch 规范中定义为 禁用首部名称 的其他首部,允许人为设置的字段为 Fetch 规范定义的 对 CORS 安全的首部字段集合。该集合为:
Content-Type 的值仅限于下列三者之一:
text/plain multipart/form-data application/x-www-form-urlencoded
请求中的任意 XMLHttpRequest 对象均没有注册任何事件监听器;XMLHttpRequest 对象可以使用 XMLHttpRequest.upload 属性访问。
请求中没有使用 ReadableStream 对象。
1.2.6解决跨域
1.2.6.1 跨域流程
1.2.6.2 方式一: 使用nginx部署为同一域
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dVAMamFc-1651674156453)(https://b3logfile.com/file/2022/05/solo-fetchupload-1558048554553530534-464c03a2.png)]
生产环境 的话 推荐使用这种方法解决跨域,原因如下
- 访问静态 域名 可以通过 nginx 直接转到后台管理系统
- 后台登录等其他的增删改查功能,直接通过 nginx 转到 gateway 服务
1.2.6.3 方式二: 配置当次请求允许跨域
添加响应头
-
Access-Control-Allow-Origin:支持哪些来源的请求跨域 -
Access-Control-Allow-Methods:支持哪些方法跨域 -
Access-Control-Allow-Credentials:跨域请求默认不包含cookie,设置为true可以包含 cookie -
Access-Control-Expose-Headers:跨域请求暴露的字段
-
CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段: Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如 果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。 -
Access-Control-Max-Age:表明该响应的有效时间为多少秒。在有效时间内,浏览器无 须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果 该首部字段的值超过了最大有效时间,将不会生效。
后端跨域配置
第一步:注释掉 renrenfast 配置的路由规则
注释掉renrenfast 配置的路由规则的类:src/main/java/io/renren/config/CorsConfig.java
第二步
在gulimall-gateway 模块的 src/main/resources/application.yml 下加入以下代码
spring:
cloud:
gateway:
routes:
- id: baidu_route
uri: https://www.baidu.com
predicates:
- Query=url,baidu
- id: qq_route
uri: https://www.qq.com
predicates:
- Query=url,qq
- id: admin_route
uri: lb://renren-fast
predicates:
- Path=/api/**
filters:
- RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}
第三步
配置跨域详细信息
1.3 三级分类-查询-树形结构
访问http://localhost:8001/#/product-category 报404是因为还没有在 nacos 中并没有把 gulimall-product 服务注册进去
1.3.1 将 gulimall-product 注册到nacos 中 具体配置如下:
- 修改product 服务的
src/main/resources/application.yml 文件
spring:
datasource:
username: root
password: root
url: jdbc:mysql://192.168.1.17:3306/gulimall_pms
driver-class-name: com.mysql.jdbc.Driver
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
application:
name: gulimall-product
mybatis-plus:
mapper-locations: classpath:/mapper/**/*.xml
global-config:
db-config:
id-type: auto
server:
port: 10000
- 在统计目录下创建
bootstrap.properties 配置 nacos 以及绑定命名空间 id
spring.application.name=gulimall-product
spring.cloud.nacos.config.server-addr=127.0.0.1:8848
spring.cloud.nacos.config.namespace=f0882d03-cf30-47f9-92e5-19a362b937b6
- 在 gulimall-product 模块的启动类加上
@EnableDiscoveryClient 这个注解
前面在解决跨域问题的时候已经加了只要是 api 打头的请求都会转发到 renren-fast模块
- id: admin_route
uri: lb://renren-fast
predicates:
- Path=/api/**
filters:
- RewritePath=/api/(?<segment>.*),/renren-fast/$\{segment}
现在需要做的是 http://localhost:8001/#/product-category 这个请求是要转发到 gulimall-product 模块去的,之前配置的断言已经不符合要求了,需要再添加断言规则
- id: product_route
uri: lb://gulimall-product
predicates:
- Path=/api/product/**
filters:
- RewritePath=/api/(?<segment>.*),/$\{segment}
1.4 前端代码,树形结构的增删改查(P51-P58)
<template>
<div>
<el-switch
v-model="draggable"
active-text="开启拖拽"
inactive-text="关闭拖拽">
</el-switch>
<el-button v-show="draggable" @click="batchSave">批量保存</el-button>
<el-button type="danger" @click="batchDelete">批量删除</el-button>
<el-tree
:data="menus"
:props="defaultProps"
:expand-on-click-node="false"
show-checkbox
node-key="catId"
:default-expanded-keys="expandedKey"
:draggable="draggable"
:allow-drop="allowDrop"
@node-drop="handleDrop"
ref="menuTree"
>
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ node.label }}</span>
<span>
<el-button
v-if="node.level <= 2"
type="text"
size="mini"
@click="() => append(data)">
Append
</el-button>
<el-button
type="text"
size="mini"
@click="edit(data)"
>
Edit
</el-button>
<el-button
v-if="node.childNodes.length == 0"
type="text"
size="mini"
@click="() => remove(node, data)">
Delete
</el-button>
</span>
</span>
</el-tree>
<el-dialog
:title="title"
:visible.sync="dialogVisible"
width="30%"
:close-on-click-modal="false"
>
<el-form :model="category">
<el-form-item label="分类名称">
<el-input v-model="category.name" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="图标地址">
<el-input v-model="category.icon" autocomplete="off"></el-input>
</el-form-item>
<el-form-item label="计量单位">
<el-input v-model="category.productUnit" autocomplete="off"></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">取 消</el-button>
<el-button type="primary" @click="submitData">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
export default {
components: {},
props: {},
data () {
return {
pCid: [],
draggable: false,
updateNodes: [],
maxLeve: 0,
title: '',
dialogType: '',
category: {
name: '',
parentCid: 0,
catLevel: 0,
showStatus: 1,
sort: 0,
catId: null,
icon: '',
productUnit: ''
},
dialogVisible: false,
menus: [],
expandedKey: [],
defaultProps: {
children: 'children',
label: 'name'
}
}
},
computed: {},
watch: {},
methods: {
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
})
},
batchSave () {
this.$http({
url: this.$http.adornUrl('/product/category/update/sort'),
method: 'post',
data: this.$http.adornData(this.updateNodes, false)
}).then(({data}) => {
this.$message({
type: 'success',
message: '菜单等修改成功!'
})
this.getMenus()
this.expandedKey = this.pCid
this.updateNodes = []
this.maxLeve = 0
})
},
batchDelete () {
let catIds = []
let nodesName = []
let checkedNodes = this.$refs.menuTree.getCheckedNodes()
console.log('checkedNodes:', checkedNodes)
for (let i = 0; i < checkedNodes.length; i++) {
catIds.push(checkedNodes[i].catId)
nodesName.push(checkedNodes[i].name)
}
this.$confirm(`是否批量删除「${nodesName}」?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/product/category/delete'),
method: 'post',
data: this.$http.adornData(catIds, false)
}).then(({data}) => {
this.$message({
type: 'success',
message: ' 菜单批量删除成功!'
})
this.getMenus()
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
})
})
},
handleDrop (draggingNode, dropNode, dropType, ev) {
console.log('handleDrop: ', draggingNode, dropNode, dropType, ev)
let pCid = 0
let sibling = null
if (dropType === 'before' || dropType === 'after') {
pCid = dropNode.parent.data.catId === undefined ? 0 : dropNode.parent.data.catId
sibling = dropNode.parent.childNodes
} else {
pCid = dropNode.data.catId
sibling = dropNode.childNodes
}
this.pCid.push(pCid)
for (let i = 0; i < sibling.length; i++) {
if (sibling[i].data.catId === draggingNode.data.catId) {
let catLevel = draggingNode.level
if (sibling[i].level !== draggingNode.level) {
catLevel = sibling[i].level
this.updateChildNodeLevel(sibling[i])
}
this.updateNodes.push({catId: sibling[i].data.catId, sort: i, parentCid: pCid, catLevel: catLevel})
} else {
this.updateNodes.push({catId: sibling[i].data.catId, sort: i})
}
}
console.log(this.updateNodes)
},
updateChildNodeLevel (node) {
if (node.childNodes.length > 0) {
for (let i = 0; i < node.childNodes.length; i++) {
var cNodes = node.childNodes[i].data
this.updateNodes.push({catId: cNodes.catId, catLevel: node.childNodes[i].level})
this.updateChildNodeLevel(node.childNodes[i])
}
}
},
allowDrop (draggingNode, dropNode, type) {
console.log('当前节点,xxx,节点所在位置', draggingNode, dropNode, type)
this.countNodeLevel(draggingNode)
let deep = Math.abs(this.maxLeve - draggingNode.level) + 1
console.log('深度', deep)
if (type === 'inner') {
return deep + draggingNode.level <= 3
} else {
return deep + draggingNode.parent.level <= 3
}
},
countNodeLevel (node) {
if (node.childNodes != null && node.childNodes.length > 0) {
for (let i = 0; i < node.childNodes.length; i++) {
if (node.childNodes[i].level > this.maxLeve) {
this.maxLeve = node.childNodes[i].level
}
this.countNodeLevel(node.childNodes[i])
}
}
},
append (data) {
console.log('append', data)
this.dialogType = 'add'
this.title = '添加分类'
this.category.parentCid = data.catId
this.category.catLevel = data.catLevel * 1 + 1
this.category.catId = null
this.category.name = ''
this.category.showStatus = 1
this.category.productUnit = ''
this.category.icon = ''
this.category.sort = 0
this.dialogVisible = true
},
addCategory () {
console.log('提交三级分类的数据:', this.category)
this.$http({
url: this.$http.adornUrl('/product/category/save'),
method: 'post',
data: this.$http.adornData(this.category, false)
}).then(({data}) => {
this.$message({
type: 'success',
message: '菜单保存成功!'
})
this.dialogVisible = false
this.getMenus()
this.expandedKey = [this.category.parentCid]
})
},
edit (data) {
console.log('要修改的数据:', data)
this.dialogType = 'edit'
this.title = '修改分类'
this.dialogVisible = true
this.$http({
url: this.$http.adornUrl(`/product/category/info/${data.catId}`),
method: 'get',
params: this.$http.adornParams({})
}).then(({data}) => {
console.log('要回显的数据:', data)
this.category.name = data.data.name
this.category.catId = data.data.catId
this.category.icon = data.data.icon
this.category.productUnit = data.data.productUnit
this.category.parentCid = data.data.parentCid
})
},
editCategory () {
console.log('提交三级分类的数据:', this.category)
var {catId, name, icon, productUnit} = this.category
this.$http({
url: this.$http.adornUrl('/product/category/update'),
method: 'post',
data: this.$http.adornData({catId, name, icon, productUnit}, false)
}).then(({data}) => {
this.$message({
type: 'success',
message: '菜单修改成功!'
})
this.dialogVisible = false
this.getMenus()
this.expandedKey = [this.category.parentCid]
})
},
submitData () {
if (this.dialogType === 'add') {
this.addCategory()
}
if (this.dialogType === 'edit') {
this.editCategory()
}
},
remove (node, data) {
console.log('remove', node, data)
var ids = [data.catId]
this.$confirm(`是否删除「${data.name}」菜单?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.$http({
url: this.$http.adornUrl('/product/category/delete'),
method: 'post',
data: this.$http.adornData(ids, false)
}).then(({data}) => {
this.$message({
type: 'success',
message: '菜单删除成功!'
})
this.getMenus()
this.expandedKey = [node.parent.data.catId]
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
})
})
}
},
created () {
this.getMenus()
},
mounted () {
},
beforeCreate () {
},
beforeMount () {
},
beforeUpdate () {
},
updated () {
},
beforeDestroy () {
},
destroyed () {
},
activated () {
}
}
</script>
<style scoped>
</style>
1.5 后端代码,树形结构增删改查
@RestController
@RequestMapping("product/category")
public class CategoryController {
@Autowired
private CategoryService categoryService;
@RequestMapping("/list/tree")
public R list(@RequestParam Map<String, Object> params){
List<CategoryEntity> entities = categoryService.listWithTree();
return R.ok().put("data", entities);
}
@RequestMapping("/info/{catId}")
public R info(@PathVariable("catId") Long catId){
CategoryEntity category = categoryService.getById(catId);
return R.ok().put("data", category);
}
@RequestMapping("/save")
public R save(@RequestBody CategoryEntity category){
categoryService.save(category);
return R.ok();
}
@RequestMapping("/update")
public R update(@RequestBody CategoryEntity category){
categoryService.updateById(category);
return R.ok();
}
@RequestMapping("/update/sort")
public R update(@RequestBody CategoryEntity[] category){
categoryService.updateBatchById(Arrays.asList(category));
return R.ok();
}
@RequestMapping("/delete")
public R delete(@RequestBody Long[] catIds){
categoryService.removeMenuByIds(Arrays.asList(catIds));
return R.ok();
}
}
public interface CategoryService extends IService<CategoryEntity> {
PageUtils queryPage(Map<String, Object> params);
List<CategoryEntity> listWithTree();
void removeMenuByIds(List<Long> asList);
}
@Service("categoryService")
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {
@Override
public PageUtils queryPage(Map<String, Object> params) {
IPage<CategoryEntity> page = this.page(
new Query<CategoryEntity>().getPage(params),
new QueryWrapper<CategoryEntity>()
);
return new PageUtils(page);
}
@Override
public List<CategoryEntity> listWithTree() {
List<CategoryEntity> entities = baseMapper.selectList(null);
List<CategoryEntity> level1Menu = entities.stream().filter(categoryEntity ->
categoryEntity.getParentCid() == 0
).map(menu -> {
menu.setChildren(getChildrens(menu, entities));
return menu;
}).sorted((menu1, menu2) ->
(menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort())
).collect(Collectors.toList());
return level1Menu;
}
@Override
public void removeMenuByIds(List<Long> asList) {
baseMapper.deleteBatchIds(asList);
}
private List<CategoryEntity> getChildrens(CategoryEntity root, List<CategoryEntity> all) {
List<CategoryEntity> children = all.stream().filter((categoryEntity) -> {
return categoryEntity.getParentCid() == root.getCatId();
}).map(categoryEntity -> {
categoryEntity.setChildren(getChildrens(categoryEntity, all));
return categoryEntity;
}).sorted((menu1, menu2) ->
(menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort())
).collect(Collectors.toList());
return children;
}
}
@Data
@TableName("pms_category")
public class CategoryEntity implements Serializable {
private static final long serialVersionUID = 1L;
@TableId
private Long catId;
private String name;
private Long parentCid;
private Integer catLevel;
@TableLogic(value = "1",delval = "0")
private Integer showStatus;
private Integer sort;
private String icon;
private String productUnit;
private Integer productCount;
@TableField(exist = false)
private List<CategoryEntity> children;
}
|