添加递归查询全国行政区域功能,优化数据库查询性能
1. 新增`getAllChildren`和`getAllChildrenTree`接口,支持根据ID递归查询所有下级区域数据。 2. 实现SQL递归查询和Java递归查询的双重策略,提升查询效率。 3. 新增`DictRegionTreeVO`类,构建树形结构以便于展示层级关系。 4. 更新相关控制器和服务层,完善接口文档和注释。 影响范围:全国行政区域模块,提升数据查询的灵活性和性能。
This commit is contained in:
43
.gitignore
vendored
Normal file
43
.gitignore
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
# Java
|
||||
*.class
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# Maven
|
||||
target/
|
||||
!.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
# Gradle
|
||||
.gradle/
|
||||
build/
|
||||
|
||||
# IntelliJ IDEA
|
||||
.idea/
|
||||
*.iml
|
||||
*.iws
|
||||
out/
|
||||
|
||||
# VSCode
|
||||
.vscode/
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Others
|
||||
*.swp
|
||||
*.swo
|
||||
|
||||
# FastRequest plugin
|
||||
.fastRequest/
|
||||
|
||||
# Ignore generated sources
|
||||
**/generated-sources/
|
||||
**/generated-test-sources/
|
||||
|
||||
# Ignore test classes
|
||||
**/test-classes/
|
@ -13,12 +13,14 @@ import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
|
||||
@SpringBootApplication(exclude = {
|
||||
DataSourceAutoConfiguration.class,
|
||||
DruidDataSourceAutoConfigure.class
|
||||
// DataSourceAutoConfiguration.class,
|
||||
// DruidDataSourceAutoConfigure.class
|
||||
})
|
||||
@EnableFeignClients(basePackages = {"com.coscoshipping.ebtp.*","com.chinaunicom.mall.ebtp.cloud.*","com.chinaunicom.mall.ebtp.*"})
|
||||
@MapperScan({"com.coscoshipping.ebtp.project.**.dao","com.chinaunicom.mall.ebtp.**.dao"})
|
||||
@ComponentScan(basePackages = {"com.coscoshipping.ebtp.*","com.chinaunicom.mall.ebtp.cloud.*","com.chinaunicom.mall.ebtp.*"})
|
||||
@EnableFeignClients(basePackages = { "com.coscoshipping.ebtp.*", "com.chinaunicom.mall.ebtp.cloud.*",
|
||||
"com.chinaunicom.mall.ebtp.*" })
|
||||
@MapperScan({ "com.coscoshipping.ebtp.project.**.dao", "com.chinaunicom.mall.ebtp.**.dao" })
|
||||
@ComponentScan(basePackages = { "com.coscoshipping.ebtp.*", "com.chinaunicom.mall.ebtp.cloud.*",
|
||||
"com.chinaunicom.mall.ebtp.*" })
|
||||
public class SysManagerEbtpProjectApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
225
src/main/java/com/coscoshipping/ebtp/project/dict/README.md
Normal file
225
src/main/java/com/coscoshipping/ebtp/project/dict/README.md
Normal file
@ -0,0 +1,225 @@
|
||||
# 全国行政区域递归查询功能说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
本模块新增了递归查询功能,可以根据传入的ID查询出包括自己在内的所有下级数据。支持两种查询方式:
|
||||
1. 平铺列表查询
|
||||
2. 树形结构查询
|
||||
|
||||
## 性能优化
|
||||
|
||||
### 数据库查询优化
|
||||
- **减少数据库调用次数**:树形结构构建时,先一次性获取所有下级数据,然后在内存中构建树形结构
|
||||
- **使用Map提高查找效率**:构建父ID到子节点的映射,避免在列表中循环查找子节点
|
||||
- **双重实现策略**:优先使用SQL递归查询(CTE),失败时自动降级到Java递归查询
|
||||
|
||||
### 性能对比
|
||||
- **优化前**:构建树形结构时,每个节点都会查询数据库,N个节点需要N次数据库查询
|
||||
- **优化后**:构建树形结构时,只需要1次数据库查询获取所有数据,然后在内存中处理
|
||||
|
||||
## 新增接口
|
||||
|
||||
### 1. 递归查询所有下级数据(平铺列表)
|
||||
|
||||
**接口地址:** `GET /v1/dictRegion/getAllChildren/{id}`
|
||||
|
||||
**功能描述:** 根据传入的ID递归查询所有下级数据,包括自己,返回平铺的列表结构。
|
||||
|
||||
**参数说明:**
|
||||
- `id` (路径参数): 当前节点ID,必填
|
||||
|
||||
**返回示例:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": [
|
||||
{
|
||||
"id": "110000",
|
||||
"pId": "0",
|
||||
"name": "北京市",
|
||||
"level": "0",
|
||||
"ab": "京"
|
||||
},
|
||||
{
|
||||
"id": "110100",
|
||||
"pId": "110000",
|
||||
"name": "北京市",
|
||||
"level": "1",
|
||||
"ab": "京"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 2. 构建树形结构(性能优化版)
|
||||
|
||||
**接口地址:** `GET /v1/dictRegion/getAllChildrenTree/{id}`
|
||||
|
||||
**功能描述:** 根据传入的ID构建树形结构,包含层级关系和子节点信息。**已优化性能,减少数据库查询次数。**
|
||||
|
||||
**参数说明:**
|
||||
- `id` (路径参数): 当前节点ID,必填
|
||||
|
||||
**返回示例:**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"data": {
|
||||
"id": "110000",
|
||||
"pId": "0",
|
||||
"name": "北京市",
|
||||
"level": "0",
|
||||
"ab": "京",
|
||||
"children": [
|
||||
{
|
||||
"id": "110100",
|
||||
"pId": "110000",
|
||||
"name": "北京市",
|
||||
"level": "1",
|
||||
"ab": "京",
|
||||
"children": [],
|
||||
"isLeaf": true,
|
||||
"depth": 1
|
||||
}
|
||||
],
|
||||
"isLeaf": false,
|
||||
"depth": 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 1. SQL递归查询
|
||||
|
||||
使用MySQL的CTE(Common Table Expression)递归查询:
|
||||
|
||||
```sql
|
||||
WITH RECURSIVE region_tree AS (
|
||||
-- 基础查询:查询当前节点
|
||||
SELECT id, p_id, name, level, ab
|
||||
FROM dict_region
|
||||
WHERE id = #{id}
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- 递归查询:查询所有子节点
|
||||
SELECT dr.id, dr.p_id, dr.name, dr.level, dr.ab
|
||||
FROM dict_region dr
|
||||
INNER JOIN region_tree rt ON dr.p_id = rt.id
|
||||
)
|
||||
SELECT * FROM region_tree
|
||||
ORDER BY id
|
||||
```
|
||||
|
||||
### 2. Java递归查询(备选方案)
|
||||
|
||||
如果数据库不支持CTE,系统会自动降级使用Java递归查询:
|
||||
|
||||
```java
|
||||
private List<DictRegion> getAllChildrenRecursive(String parentId) {
|
||||
List<DictRegion> children = new ArrayList<>();
|
||||
|
||||
// 查询直接子节点
|
||||
QueryWrapper<DictRegion> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("p_id", parentId);
|
||||
queryWrapper.orderByAsc("id");
|
||||
List<DictRegion> directChildren = this.list(queryWrapper);
|
||||
|
||||
for (DictRegion child : directChildren) {
|
||||
children.add(child);
|
||||
// 递归查询子节点的子节点
|
||||
children.addAll(getAllChildrenRecursive(child.getId()));
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 优化的树形结构构建
|
||||
|
||||
**优化前的问题:**
|
||||
```java
|
||||
// 每次递归都会查询数据库
|
||||
private DictRegionTreeVO buildTreeRecursive(DictRegion node, int depth) {
|
||||
// 查询子节点 - 每次都会访问数据库
|
||||
QueryWrapper<DictRegion> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("p_id", node.getId());
|
||||
List<DictRegion> children = this.list(queryWrapper);
|
||||
|
||||
// 递归处理每个子节点
|
||||
for (DictRegion child : children) {
|
||||
childNodes.add(buildTreeRecursive(child, depth + 1)); // 再次查询数据库
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**优化后的实现:**
|
||||
```java
|
||||
// 一次性获取所有数据,在内存中构建树形结构
|
||||
public DictRegionTreeVO getAllChildrenTree(String id) {
|
||||
// 1. 获取当前节点
|
||||
DictRegion current = this.getById(id);
|
||||
|
||||
// 2. 一次性获取所有下级数据
|
||||
List<DictRegion> allChildren = getAllChildrenIncludingSelfByJava(id);
|
||||
|
||||
// 3. 构建父ID到子节点的映射,提高查找效率
|
||||
Map<String, List<DictRegion>> parentToChildrenMap = new HashMap<>();
|
||||
for (DictRegion node : allChildren) {
|
||||
parentToChildrenMap.computeIfAbsent(node.getPId(), k -> new ArrayList<>()).add(node);
|
||||
}
|
||||
|
||||
// 4. 在内存中构建树形结构
|
||||
return buildTreeFromMap(current, parentToChildrenMap, 0);
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 树形结构工具类
|
||||
|
||||
提供了`TreeUtil`工具类,支持:
|
||||
- 将平铺列表转换为树形结构
|
||||
- 将树形结构转换为平铺列表
|
||||
- 递归构建树形结构
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 查询北京市及其所有下级区域
|
||||
|
||||
```bash
|
||||
# 平铺列表查询
|
||||
GET /v1/dictRegion/getAllChildren/110000
|
||||
|
||||
# 树形结构查询(性能优化版)
|
||||
GET /v1/dictRegion/getAllChildrenTree/110000
|
||||
```
|
||||
|
||||
### 查询上海市及其所有下级区域
|
||||
|
||||
```bash
|
||||
# 平铺列表查询
|
||||
GET /v1/dictRegion/getAllChildren/310000
|
||||
|
||||
# 树形结构查询
|
||||
GET /v1/dictRegion/getAllChildrenTree/310000
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **性能考虑:**
|
||||
- 对于层级较深的数据,建议使用SQL递归查询以获得更好的性能
|
||||
- 树形结构构建已优化,从N次数据库查询减少到1次查询
|
||||
2. **数据完整性:** 确保数据库中的父子关系正确,避免循环引用
|
||||
3. **内存使用:** 对于大量数据的递归查询,注意内存使用情况
|
||||
4. **数据库兼容性:** 如果数据库不支持CTE,系统会自动使用Java递归查询
|
||||
|
||||
## 扩展功能
|
||||
|
||||
可以根据需要扩展以下功能:
|
||||
1. 添加缓存机制提高查询性能
|
||||
2. 支持按层级深度限制查询
|
||||
3. 支持按条件过滤子节点
|
||||
4. 添加批量查询接口
|
||||
5. 支持异步查询大数据量的树形结构
|
@ -1,8 +1,9 @@
|
||||
package com.coscoshipping.ebtp.project.dict.controller;
|
||||
|
||||
import com.coscoshipping.ebtp.project.dict.entity.DictRegion;
|
||||
import com.coscoshipping.ebtp.project.dict.service.IDictRegionService;
|
||||
import com.chinaunicom.mall.ebtp.common.base.entity.BaseResponse;
|
||||
import com.coscoshipping.ebtp.project.dict.entity.DictRegion;
|
||||
import com.coscoshipping.ebtp.project.dict.entity.DictRegionTreeVO;
|
||||
import com.coscoshipping.ebtp.project.dict.service.IDictRegionService;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import io.swagger.annotations.ApiParam;
|
||||
@ -54,6 +55,17 @@ public class DictRegionController {
|
||||
|
||||
return BaseResponse.success(idictRegionService.updateById(dictRegion));
|
||||
}
|
||||
/**
|
||||
* 查询所有数据
|
||||
*
|
||||
* @return 返回结果
|
||||
*/
|
||||
@ApiOperation("查询所有数据")
|
||||
@GetMapping("/all")
|
||||
public BaseResponse<List<DictRegion>> listAll(){
|
||||
List<DictRegion> list = idictRegionService.list();
|
||||
return BaseResponse.success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据
|
||||
@ -87,5 +99,36 @@ public class DictRegionController {
|
||||
return BaseResponse.success(dictRegion);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归查询所有下级数据(包括自己)
|
||||
*
|
||||
* @param id 当前节点id值
|
||||
*
|
||||
* @return 返回结果
|
||||
*/
|
||||
@ApiOperation("递归查询所有下级数据")
|
||||
@GetMapping("/getAllChildren/{id}")
|
||||
public BaseResponse<List<DictRegion>> getAllChildren(@ApiParam(value = "当前节点id", required = true) @PathVariable String id){
|
||||
|
||||
List<DictRegion> dictRegionList = idictRegionService.getAllChildren(id);
|
||||
|
||||
return BaseResponse.success(dictRegionList);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建树形结构
|
||||
*
|
||||
* @param id 当前节点id值
|
||||
*
|
||||
* @return 返回结果
|
||||
*/
|
||||
@ApiOperation("递归查询所有下级数据树形结构")
|
||||
@GetMapping("/getAllChildrenTree/{id}")
|
||||
public BaseResponse<DictRegionTreeVO> getAllChildrenTree(@ApiParam(value = "当前节点id", required = true) @PathVariable String id){
|
||||
|
||||
DictRegionTreeVO treeStructure = idictRegionService.getAllChildrenTree(id);
|
||||
|
||||
return BaseResponse.success(treeStructure);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package com.coscoshipping.ebtp.project.dict.controller;
|
||||
|
||||
import com.coscoshipping.ebtp.project.dict.entity.DictRegion;
|
||||
import com.coscoshipping.ebtp.project.dict.entity.DictRegionInternational;
|
||||
import com.coscoshipping.ebtp.project.dict.service.IDictRegionInternationalService;
|
||||
import com.chinaunicom.mall.ebtp.common.base.entity.BaseResponse;
|
||||
@ -14,6 +15,7 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* 全球国家信息
|
||||
*
|
||||
* @author 自动生成
|
||||
*/
|
||||
@RestController
|
||||
@ -33,7 +35,8 @@ public class DictRegionInternationalController {
|
||||
*/
|
||||
@ApiOperation("插入新数据")
|
||||
@PostMapping("")
|
||||
public BaseResponse<Boolean> insert(@ApiParam(value = "对象数据", required = true) @RequestBody @Valid DictRegionInternational dictRegionInternational){
|
||||
public BaseResponse<Boolean> insert(
|
||||
@ApiParam(value = "对象数据", required = true) @RequestBody @Valid DictRegionInternational dictRegionInternational) {
|
||||
boolean save = iDictRegionInternationalService.save(dictRegionInternational);
|
||||
return BaseResponse.success(save);
|
||||
}
|
||||
@ -47,10 +50,23 @@ public class DictRegionInternationalController {
|
||||
*/
|
||||
@ApiOperation("修改数据")
|
||||
@PostMapping("/update")
|
||||
public BaseResponse<Boolean> update(@ApiParam(value = "对象数据", required = true) @RequestBody DictRegionInternational dictRegionInternational){
|
||||
public BaseResponse<Boolean> update(
|
||||
@ApiParam(value = "对象数据", required = true) @RequestBody DictRegionInternational dictRegionInternational) {
|
||||
return BaseResponse.success(iDictRegionInternationalService.updateById(dictRegionInternational));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有数据
|
||||
*
|
||||
* @return 返回结果
|
||||
*/
|
||||
@ApiOperation("查询所有数据")
|
||||
@GetMapping("/all")
|
||||
public BaseResponse<List<DictRegionInternational>> listAll(){
|
||||
List<DictRegionInternational> list = iDictRegionInternationalService.list();
|
||||
return BaseResponse.success(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询数据
|
||||
*
|
||||
@ -60,7 +76,8 @@ public class DictRegionInternationalController {
|
||||
*/
|
||||
@ApiOperation("查询数据")
|
||||
@GetMapping("/{id}")
|
||||
public BaseResponse<DictRegionInternational> get(@ApiParam(value = "主键id", required = true) @PathVariable String id){
|
||||
public BaseResponse<DictRegionInternational> get(
|
||||
@ApiParam(value = "主键id", required = true) @PathVariable String id) {
|
||||
DictRegionInternational dictRegionInternational = iDictRegionInternationalService.getById(id);
|
||||
return BaseResponse.success(dictRegionInternational);
|
||||
}
|
||||
@ -74,8 +91,9 @@ public class DictRegionInternationalController {
|
||||
*/
|
||||
@ApiOperation("查询子数据")
|
||||
@GetMapping("/getChild")
|
||||
public BaseResponse<List<DictRegionInternational>> getChild(@ApiParam(value = "主键id", required = true) @RequestParam(name = "pId") String pId){
|
||||
public BaseResponse<List<DictRegionInternational>> getChild(
|
||||
@ApiParam(value = "主键id", required = true) @RequestParam(name = "pId") String pId) {
|
||||
List<DictRegionInternational> dictRegionInternational = iDictRegionInternationalService.getChild(pId);
|
||||
return BaseResponse.success(dictRegionInternational);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,8 +3,17 @@ package com.coscoshipping.ebtp.project.dict.dao;
|
||||
|
||||
import com.chinaunicom.mall.ebtp.common.base.dao.IBaseMapper;
|
||||
import com.coscoshipping.ebtp.project.dict.entity.DictRegion;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface DictRegionMapper extends IBaseMapper<DictRegion> {
|
||||
|
||||
/**
|
||||
* 递归查询所有下级数据(包括自己)
|
||||
* @param id 当前节点ID
|
||||
* @return 所有下级数据列表
|
||||
*/
|
||||
List<DictRegion> getAllChildrenIncludingSelf(@Param("id") String id);
|
||||
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
<result column="p_id" jdbcType="VARCHAR" property="pId"/>
|
||||
<result column="name" jdbcType="VARCHAR" property="name"/>
|
||||
<result column="level" jdbcType="VARCHAR" property="level"/>
|
||||
<result column="ab" jdbcType="VARCHAR" property="ab"/>
|
||||
</resultMap>
|
||||
|
||||
<!--逻辑删除方法 此方法为代码生成器生成 不允许修改 如有特殊需求 请自行新建SQL语句-->
|
||||
@ -17,4 +18,23 @@
|
||||
delete_flag="deleted"
|
||||
where ID=#{id}
|
||||
</update>
|
||||
|
||||
<!-- 递归查询所有下级数据(包括自己) -->
|
||||
<select id="getAllChildrenIncludingSelf" resultMap="BaseResultMap">
|
||||
WITH RECURSIVE region_tree AS (
|
||||
-- 基础查询:查询当前节点
|
||||
SELECT id, p_id, name, level, ab
|
||||
FROM dict_region
|
||||
WHERE id = #{id}
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- 递归查询:查询所有子节点
|
||||
SELECT dr.id, dr.p_id, dr.name, dr.level, dr.ab
|
||||
FROM dict_region dr
|
||||
INNER JOIN region_tree rt ON dr.p_id = rt.id
|
||||
)
|
||||
SELECT * FROM region_tree
|
||||
ORDER BY id
|
||||
</select>
|
||||
</mapper>
|
||||
|
@ -0,0 +1,71 @@
|
||||
package com.coscoshipping.ebtp.project.dict.entity;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 全国行政区域树形结构VO
|
||||
*
|
||||
* @author daixc
|
||||
* @date 2020/10/29
|
||||
*/
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
@ApiModel("全国行政区域树形结构VO")
|
||||
public class DictRegionTreeVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* id
|
||||
*/
|
||||
@ApiModelProperty(value = "id")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 上级id
|
||||
*/
|
||||
@ApiModelProperty(value = "上级id")
|
||||
private String pId;
|
||||
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
@ApiModelProperty(value = "名称")
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 类型:0-省,1-市,2-县
|
||||
*/
|
||||
@ApiModelProperty(value = "类型:0-省,1-市,2-县")
|
||||
private String level;
|
||||
|
||||
/**
|
||||
* 缩写
|
||||
*/
|
||||
@ApiModelProperty(value = "缩写")
|
||||
private String ab;
|
||||
|
||||
/**
|
||||
* 子节点列表
|
||||
*/
|
||||
@ApiModelProperty(value = "子节点列表")
|
||||
private List<DictRegionTreeVO> children;
|
||||
|
||||
/**
|
||||
* 是否叶子节点
|
||||
*/
|
||||
@ApiModelProperty(value = "是否叶子节点")
|
||||
private Boolean isLeaf;
|
||||
|
||||
/**
|
||||
* 层级深度
|
||||
*/
|
||||
@ApiModelProperty(value = "层级深度")
|
||||
private Integer depth;
|
||||
}
|
@ -3,6 +3,7 @@ package com.coscoshipping.ebtp.project.dict.service;
|
||||
|
||||
import com.chinaunicom.mall.ebtp.common.base.service.IBaseService;
|
||||
import com.coscoshipping.ebtp.project.dict.entity.DictRegion;
|
||||
import com.coscoshipping.ebtp.project.dict.entity.DictRegionTreeVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -20,4 +21,18 @@ public interface IDictRegionService extends IBaseService<DictRegion>{
|
||||
* @return
|
||||
*/
|
||||
List<DictRegion> getChild(String pId);
|
||||
|
||||
/**
|
||||
* 递归查询所有下级数据(包括自己)
|
||||
* @param id 当前节点ID
|
||||
* @return 所有下级数据列表
|
||||
*/
|
||||
List<DictRegion> getAllChildren(String id);
|
||||
|
||||
/**
|
||||
* 递归查询所有下级数据并构建树形结构
|
||||
* @param id 当前节点ID
|
||||
* @return 树形结构数据
|
||||
*/
|
||||
DictRegionTreeVO getAllChildrenTree(String id);
|
||||
}
|
||||
|
@ -5,11 +5,15 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.chinaunicom.mall.ebtp.common.base.service.impl.BaseServiceImpl;
|
||||
import com.coscoshipping.ebtp.project.dict.dao.DictRegionMapper;
|
||||
import com.coscoshipping.ebtp.project.dict.entity.DictRegion;
|
||||
import com.coscoshipping.ebtp.project.dict.entity.DictRegionTreeVO;
|
||||
import com.coscoshipping.ebtp.project.dict.service.IDictRegionService;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 对数据表 dict_region 操作的 serviceImpl
|
||||
@ -32,4 +36,123 @@ public class DictRegionServiceImpl extends BaseServiceImpl<DictRegionMapper, Dic
|
||||
queryWrapper.orderByAsc("id");
|
||||
return this.list(queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DictRegion> getAllChildren(String id) {
|
||||
if (StringUtils.isBlank(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// 优先使用SQL递归查询
|
||||
return this.baseMapper.getAllChildrenIncludingSelf(id);
|
||||
} catch (Exception e) {
|
||||
// 如果SQL递归查询失败,使用Java递归查询作为备选方案
|
||||
return getAllChildrenIncludingSelfByJava(id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DictRegionTreeVO getAllChildrenTree(String id) {
|
||||
if (StringUtils.isBlank(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取当前节点
|
||||
DictRegion current = this.getById(id);
|
||||
if (current == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 获取所有下级数据(包括自己)
|
||||
List<DictRegion> allChildren = getAllChildrenIncludingSelfByJava(id);
|
||||
if (allChildren == null || allChildren.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 构建父ID到子节点的映射,提高查找效率
|
||||
Map<String, List<DictRegion>> parentToChildrenMap = new HashMap<>();
|
||||
for (DictRegion node : allChildren) {
|
||||
parentToChildrenMap.computeIfAbsent(node.getPId(), k -> new ArrayList<>()).add(node);
|
||||
}
|
||||
|
||||
// 在内存中构建树形结构
|
||||
return buildTreeFromMap(current, parentToChildrenMap, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Java版本递归查询所有下级数据(包括自己)
|
||||
* @param id 当前节点ID
|
||||
* @return 所有下级数据列表
|
||||
*/
|
||||
private List<DictRegion> getAllChildrenIncludingSelfByJava(String id) {
|
||||
List<DictRegion> result = new ArrayList<>();
|
||||
|
||||
// 查询当前节点
|
||||
DictRegion current = this.getById(id);
|
||||
if (current != null) {
|
||||
result.add(current);
|
||||
// 递归查询子节点
|
||||
result.addAll(getAllChildrenRecursive(id));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归查询所有子节点
|
||||
* @param parentId 父节点ID
|
||||
* @return 所有子节点列表
|
||||
*/
|
||||
private List<DictRegion> getAllChildrenRecursive(String parentId) {
|
||||
List<DictRegion> children = new ArrayList<>();
|
||||
|
||||
// 查询直接子节点
|
||||
QueryWrapper<DictRegion> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("p_id", parentId);
|
||||
queryWrapper.orderByAsc("id");
|
||||
List<DictRegion> directChildren = this.list(queryWrapper);
|
||||
|
||||
for (DictRegion child : directChildren) {
|
||||
children.add(child);
|
||||
// 递归查询子节点的子节点
|
||||
children.addAll(getAllChildrenRecursive(child.getId()));
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从Map中构建树形结构(内存中处理,使用Map提高效率)
|
||||
* @param rootNode 根节点
|
||||
* @param parentToChildrenMap 父ID到子节点的映射
|
||||
* @param depth 当前深度
|
||||
* @return 树形结构节点
|
||||
*/
|
||||
private DictRegionTreeVO buildTreeFromMap(DictRegion rootNode, Map<String, List<DictRegion>> parentToChildrenMap, int depth) {
|
||||
DictRegionTreeVO treeNode = new DictRegionTreeVO()
|
||||
.setId(rootNode.getId())
|
||||
.setPId(rootNode.getPId())
|
||||
.setName(rootNode.getName())
|
||||
.setLevel(rootNode.getLevel())
|
||||
.setAb(rootNode.getAb())
|
||||
.setDepth(depth);
|
||||
|
||||
// 从Map中获取当前节点的直接子节点
|
||||
List<DictRegion> directChildren = parentToChildrenMap.get(rootNode.getId());
|
||||
|
||||
if (directChildren == null || directChildren.isEmpty()) {
|
||||
treeNode.setIsLeaf(true);
|
||||
treeNode.setChildren(null);
|
||||
} else {
|
||||
treeNode.setIsLeaf(false);
|
||||
List<DictRegionTreeVO> childNodes = new ArrayList<>();
|
||||
for (DictRegion child : directChildren) {
|
||||
childNodes.add(buildTreeFromMap(child, parentToChildrenMap, depth + 1));
|
||||
}
|
||||
treeNode.setChildren(childNodes);
|
||||
}
|
||||
|
||||
return treeNode;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,108 @@
|
||||
package com.coscoshipping.ebtp.project.dict.util;
|
||||
|
||||
import com.coscoshipping.ebtp.project.dict.entity.DictRegion;
|
||||
import com.coscoshipping.ebtp.project.dict.entity.DictRegionTreeVO;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 树形结构工具类
|
||||
*
|
||||
* @author daixc
|
||||
* @date 2020/10/29
|
||||
*/
|
||||
public class TreeUtil {
|
||||
|
||||
/**
|
||||
* 将平铺的列表转换为树形结构
|
||||
*
|
||||
* @param list 平铺的列表
|
||||
* @return 树形结构列表
|
||||
*/
|
||||
public static List<DictRegionTreeVO> buildTreeFromList(List<DictRegion> list) {
|
||||
if (list == null || list.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 按父ID分组
|
||||
Map<String, List<DictRegion>> parentMap = list.stream()
|
||||
.collect(Collectors.groupingBy(DictRegion::getPId));
|
||||
|
||||
// 构建树形结构
|
||||
return buildTreeRecursive(parentMap, "0", 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归构建树形结构
|
||||
*
|
||||
* @param parentMap 父ID映射
|
||||
* @param parentId 父ID
|
||||
* @param depth 当前深度
|
||||
* @return 树形结构列表
|
||||
*/
|
||||
private static List<DictRegionTreeVO> buildTreeRecursive(Map<String, List<DictRegion>> parentMap, String parentId, int depth) {
|
||||
List<DictRegion> children = parentMap.get(parentId);
|
||||
if (children == null || children.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<DictRegionTreeVO> result = new ArrayList<>();
|
||||
for (DictRegion child : children) {
|
||||
DictRegionTreeVO treeNode = new DictRegionTreeVO()
|
||||
.setId(child.getId())
|
||||
.setPId(child.getPId())
|
||||
.setName(child.getName())
|
||||
.setLevel(child.getLevel())
|
||||
.setAb(child.getAb())
|
||||
.setDepth(depth);
|
||||
|
||||
// 递归构建子节点
|
||||
List<DictRegionTreeVO> childNodes = buildTreeRecursive(parentMap, child.getId(), depth + 1);
|
||||
if (childNodes.isEmpty()) {
|
||||
treeNode.setIsLeaf(true);
|
||||
treeNode.setChildren(null);
|
||||
} else {
|
||||
treeNode.setIsLeaf(false);
|
||||
treeNode.setChildren(childNodes);
|
||||
}
|
||||
|
||||
result.add(treeNode);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将树形结构转换为平铺列表
|
||||
*
|
||||
* @param tree 树形结构
|
||||
* @return 平铺列表
|
||||
*/
|
||||
public static List<DictRegionTreeVO> flattenTree(DictRegionTreeVO tree) {
|
||||
List<DictRegionTreeVO> result = new ArrayList<>();
|
||||
flattenTreeRecursive(tree, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归将树形结构转换为平铺列表
|
||||
*
|
||||
* @param node 当前节点
|
||||
* @param result 结果列表
|
||||
*/
|
||||
private static void flattenTreeRecursive(DictRegionTreeVO node, List<DictRegionTreeVO> result) {
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
result.add(node);
|
||||
if (node.getChildren() != null) {
|
||||
for (DictRegionTreeVO child : node.getChildren()) {
|
||||
flattenTreeRecursive(child, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -125,4 +125,17 @@ public class SysOrgController{
|
||||
public BaseResponse<List<SysOrgVO>> queryAll(SysOrg sysOrg){
|
||||
return BaseResponse.success(iSysOrgService.getTreeByNode(sysOrg));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询组织信息(当前组织及下级组织列表)
|
||||
*
|
||||
* @param sysOrg 查询条件
|
||||
* @return 返回当前组织信息以及下级组织列表
|
||||
*/
|
||||
@ApiOperation("查询组织信息(当前组织及下级组织列表)")
|
||||
@GetMapping("/queryOrgWithChildren")
|
||||
public BaseResponse<List<SysOrg>> queryOrgWithChildren(@ApiParam(value = "查询条件", required = false) SysOrg sysOrg) {
|
||||
List<SysOrg> result = iSysOrgService.queryOrgWithChildren(sysOrg);
|
||||
return BaseResponse.success(result);
|
||||
}
|
||||
}
|
||||
|
@ -8,4 +8,12 @@ import com.coscoshipping.ebtp.project.org.entity.SysOrg;
|
||||
|
||||
@Repository
|
||||
public interface SysOrgMapper extends IBaseMapper<SysOrg> {
|
||||
|
||||
/**
|
||||
* 查询指定组织的所有下级组织(包括多级下级)
|
||||
*
|
||||
* @param parentOrgId 父组织ID
|
||||
* @return 所有下级组织列表
|
||||
*/
|
||||
List<SysOrg> selectAllChildrenByOrgId(String parentOrgId);
|
||||
}
|
||||
|
@ -38,4 +38,41 @@
|
||||
delete_flag="1"
|
||||
where ID=#{id }
|
||||
</update>
|
||||
</mapper>
|
||||
|
||||
<!-- 递归查询指定组织的所有下级组织 -->
|
||||
<select id="selectAllChildrenByOrgId" resultMap="orgResultMap">
|
||||
WITH RECURSIVE org_tree AS (
|
||||
-- 基础查询:直接下级组织
|
||||
SELECT
|
||||
org_id, org_name, org_num, org_full_id, org_full_name,
|
||||
up_org_id, org_category, leader_name, leader_oa_code, leader_user_id,
|
||||
top_leader_name, top_leader_oa_code, top_leader_user_id, sort, status,
|
||||
branch, site, cu_company_number, create_by, create_date, tenant_id,
|
||||
tenant_name, update_by, update_date, delete_flag, last_update_time,
|
||||
1 as level
|
||||
FROM sys_org
|
||||
WHERE up_org_id = #{parentOrgId}
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- 递归查询:下级的下级组织
|
||||
SELECT
|
||||
o.org_id, o.org_name, o.org_num, o.org_full_id, o.org_full_name,
|
||||
o.up_org_id, o.org_category, o.leader_name, o.leader_oa_code, o.leader_user_id,
|
||||
o.top_leader_name, o.top_leader_oa_code, o.top_leader_user_id, o.sort, o.status,
|
||||
o.branch, o.site, o.cu_company_number, o.create_by, o.create_date, o.tenant_id,
|
||||
o.tenant_name, o.update_by, o.update_date, o.delete_flag, o.last_update_time,
|
||||
ot.level + 1
|
||||
FROM sys_org o
|
||||
INNER JOIN org_tree ot ON o.up_org_id = ot.org_id
|
||||
)
|
||||
SELECT
|
||||
org_id, org_name, org_num, org_full_id, org_full_name,
|
||||
up_org_id, org_category, leader_name, leader_oa_code, leader_user_id,
|
||||
top_leader_name, top_leader_oa_code, top_leader_user_id, sort, status,
|
||||
branch, site, cu_company_number, create_by, create_date, tenant_id,
|
||||
tenant_name, update_by, update_date, delete_flag, last_update_time
|
||||
FROM org_tree
|
||||
ORDER BY sort ASC, org_name ASC
|
||||
</select>
|
||||
</mapper>
|
||||
|
@ -41,4 +41,12 @@ public interface SysOrgService extends IBaseService<SysOrg>{
|
||||
Boolean deleteById(String orgId);
|
||||
|
||||
SysOrg getCompanyByOrgId(String orgId);
|
||||
|
||||
/**
|
||||
* 查询组织信息(当前组织及下级组织列表)
|
||||
*
|
||||
* @param sysOrg 查询条件
|
||||
* @return 返回当前组织信息以及下级组织列表
|
||||
*/
|
||||
List<SysOrg> queryOrgWithChildren(SysOrg sysOrg);
|
||||
}
|
||||
|
@ -1,29 +1,33 @@
|
||||
package com.coscoshipping.ebtp.project.org.service.impl;
|
||||
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.chinaunicom.mall.ebtp.common.base.entity.BasePageRequest;
|
||||
import com.chinaunicom.mall.ebtp.common.exception.common.CommonExceptionEnum;
|
||||
import com.chinaunicom.mall.ebtp.common.util.JsonUtils;
|
||||
import com.chinaunicom.mall.ebtp.common.util.PropertyUtils;
|
||||
import com.coscoshipping.ebtp.project.org.entity.vo.SysOrgVO;
|
||||
import com.coscoshipping.ebtp.project.org.util.CommonUtil;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.stereotype.Service;
|
||||
import com.chinaunicom.mall.ebtp.common.base.service.impl.BaseServiceImpl;
|
||||
import com.chinaunicom.mall.ebtp.common.exception.common.CommonExceptionEnum;
|
||||
import com.chinaunicom.mall.ebtp.common.util.PropertyUtils;
|
||||
import com.coscoshipping.ebtp.project.org.dao.SysOrgMapper;
|
||||
import com.coscoshipping.ebtp.project.org.entity.SysOrg;
|
||||
import com.coscoshipping.ebtp.project.org.entity.vo.SysOrgVO;
|
||||
import com.coscoshipping.ebtp.project.org.service.SysOrgService;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import com.coscoshipping.ebtp.project.org.util.CommonUtil;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
|
||||
/**
|
||||
* 对数据表 sys_org 操作的 serviceImpl
|
||||
@ -48,53 +52,67 @@ public class SysOrgServiceImpl extends BaseServiceImpl<SysOrgMapper, SysOrg> imp
|
||||
|
||||
@Override
|
||||
public List<SysOrgVO> getTreeByNode(SysOrg org) {
|
||||
SysOrgVO sysOrgVO = new SysOrgVO();
|
||||
List<SysOrg> sysOrgList = new ArrayList<>();
|
||||
if (!StringUtils.isEmpty(org.getOrgName()) || !StringUtils.isEmpty(org.getOrgNum())) {
|
||||
SysOrgVO vo = BeanUtil.toBean(org, SysOrgVO.class);
|
||||
IPage<SysOrg> page = this.getPage(vo);
|
||||
sysOrgList = page.getRecords();
|
||||
return sysOrgList.stream().map(entity -> BeanUtil.toBean(entity, SysOrgVO.class)).collect(Collectors.toList());
|
||||
} else {
|
||||
sysOrgList = this.list();
|
||||
List<SysOrgVO> voList = new ArrayList<>();
|
||||
// 1. 先查询出所有的组织数据
|
||||
List<SysOrg> allOrgList = this.list();
|
||||
|
||||
if (CollectionUtils.isEmpty(sysOrgList)) {
|
||||
return voList;
|
||||
}
|
||||
|
||||
// 预处理所有节点为SysOrgVO,并构建子节点映射表
|
||||
List<SysOrgVO> allOrgVOs = sysOrgList.stream()
|
||||
.map(entity -> BeanUtil.toBean(entity, SysOrgVO.class))
|
||||
.map(this::setInfoForTree) // 提前应用树形结构转换
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 构建父节点到子节点的映射(key: upOrgId,value: 子节点列表)
|
||||
Map<String, List<SysOrgVO>> parentToChildrenMap = new HashMap<>();
|
||||
String rootOrgCode = "10000000";
|
||||
for (SysOrgVO orgVO : allOrgVOs) {
|
||||
String parentId = StringUtils.isBlank(orgVO.getUpOrgId()) ? rootOrgCode : orgVO.getUpOrgId();
|
||||
parentToChildrenMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(orgVO);
|
||||
}
|
||||
|
||||
// 获取根节点ID
|
||||
String rootNode = StringUtils.isNotBlank(org.getOrgId()) ? org.getOrgId() : rootOrgCode;
|
||||
|
||||
// 使用StringBuilder记录所有部门ID
|
||||
StringBuilder allDepartmentIdBuilder = new StringBuilder(rootNode);
|
||||
|
||||
// 构建多级树结构(从根节点开始递归)
|
||||
List<SysOrgVO> rootChildren = parentToChildrenMap.getOrDefault(rootNode, new ArrayList<>());
|
||||
for (SysOrgVO rootChild : rootChildren) {
|
||||
buildMultiLevelTree(rootChild, parentToChildrenMap, allDepartmentIdBuilder);
|
||||
voList.add(rootChild);
|
||||
}
|
||||
|
||||
// 设置全量部门ID
|
||||
voList.forEach(vo -> vo.setAllDepartmentId(allDepartmentIdBuilder.toString()));
|
||||
|
||||
return voList;
|
||||
// 如果没有数据,直接返回空列表
|
||||
if (CollectionUtils.isEmpty(allOrgList)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// 2. 构建完整的树形结构
|
||||
List<SysOrgVO> fullTree = buildOrgTree(allOrgList, org);
|
||||
|
||||
// 3. 如果有查询条件,则根据条件过滤树形结构
|
||||
if (!StringUtils.isEmpty(org.getOrgName()) || !StringUtils.isEmpty(org.getOrgNum())) {
|
||||
return filterTreeByCondition(fullTree, org);
|
||||
}
|
||||
|
||||
// 4. 没有查询条件,直接返回完整树形结构
|
||||
return fullTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建组织机构树形结构
|
||||
*
|
||||
* @param sysOrgList 组织机构列表
|
||||
* @param org 查询条件对象
|
||||
* @return 树形结构的组织机构列表
|
||||
*/
|
||||
private List<SysOrgVO> buildOrgTree(List<SysOrg> sysOrgList, SysOrg org) {
|
||||
List<SysOrgVO> voList = new ArrayList<>();
|
||||
|
||||
// 预处理所有节点为SysOrgVO,并构建子节点映射表
|
||||
List<SysOrgVO> allOrgVOs = sysOrgList.stream()
|
||||
.map(entity -> BeanUtil.toBean(entity, SysOrgVO.class))
|
||||
.map(this::setInfoForTree) // 提前应用树形结构转换
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// 构建父节点到子节点的映射(key: upOrgId,value: 子节点列表)
|
||||
Map<String, List<SysOrgVO>> parentToChildrenMap = new HashMap<>();
|
||||
for (SysOrgVO orgVO : allOrgVOs) {
|
||||
String parentId = StringUtils.isBlank(orgVO.getUpOrgId()) ? CommonUtil.ROOT_NODE : orgVO.getUpOrgId();
|
||||
parentToChildrenMap.computeIfAbsent(parentId, k -> new ArrayList<>()).add(orgVO);
|
||||
}
|
||||
|
||||
// 获取根节点ID
|
||||
String rootNode = StringUtils.isNotBlank(org.getOrgId()) ? org.getOrgId() : CommonUtil.ROOT_NODE;
|
||||
|
||||
// 使用StringBuilder记录所有部门ID,暂不知道这个参数有何作用,先保留
|
||||
// StringBuilder allDepartmentIdBuilder = new StringBuilder(rootNode);
|
||||
StringBuilder allDepartmentIdBuilder = new StringBuilder("");
|
||||
|
||||
// 构建多级树结构(从根节点开始递归)
|
||||
List<SysOrgVO> rootChildren = parentToChildrenMap.getOrDefault(rootNode, new ArrayList<>());
|
||||
for (SysOrgVO rootChild : rootChildren) {
|
||||
buildMultiLevelTree(rootChild, parentToChildrenMap, allDepartmentIdBuilder);
|
||||
voList.add(rootChild);
|
||||
}
|
||||
|
||||
// 设置全量部门ID
|
||||
// voList.forEach(vo -> vo.setAllDepartmentId(allDepartmentIdBuilder.toString()));
|
||||
|
||||
return voList;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,7 +138,7 @@ public class SysOrgServiceImpl extends BaseServiceImpl<SysOrgMapper, SysOrg> imp
|
||||
currentNode.setChildren(sortedChildren);
|
||||
|
||||
// 记录当前节点ID,累加所有层级ID
|
||||
allDepartmentIdBuilder.append(",").append(currentNode.getOrgId());
|
||||
// allDepartmentIdBuilder.append(",").append(currentNode.getOrgId());
|
||||
|
||||
// 递归处理子节点
|
||||
for (SysOrgVO child : sortedChildren) {
|
||||
@ -244,4 +262,109 @@ public class SysOrgServiceImpl extends BaseServiceImpl<SysOrgMapper, SysOrg> imp
|
||||
lqw.orderByDesc(SysOrg::getCreateDate);
|
||||
return lqw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据查询条件过滤树形结构
|
||||
*
|
||||
* @param fullTree 完整的树形结构
|
||||
* @param org 查询条件对象
|
||||
* @return 过滤后的树形结构
|
||||
*/
|
||||
private List<SysOrgVO> filterTreeByCondition(List<SysOrgVO> fullTree, SysOrg org) {
|
||||
List<SysOrgVO> filteredTree = new ArrayList<>();
|
||||
|
||||
for (SysOrgVO node : fullTree) {
|
||||
// 检查当前节点是否匹配条件
|
||||
boolean isMatch = isNodeMatchCondition(node, org);
|
||||
|
||||
// 递归检查子节点
|
||||
List<SysOrgVO> filteredChildren = filterTreeByCondition(node.getChildren(), org);
|
||||
|
||||
// 如果当前节点匹配条件或有匹配的子节点,则保留该节点
|
||||
if (isMatch || !filteredChildren.isEmpty()) {
|
||||
SysOrgVO filteredNode = BeanUtil.toBean(node, SysOrgVO.class);
|
||||
filteredNode.setChildren(filteredChildren);
|
||||
filteredTree.add(filteredNode);
|
||||
}
|
||||
}
|
||||
|
||||
return filteredTree;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断节点是否匹配查询条件
|
||||
*
|
||||
* @param node 待检查的节点
|
||||
* @param org 查询条件对象
|
||||
* @return 是否匹配
|
||||
*/
|
||||
private boolean isNodeMatchCondition(SysOrgVO node, SysOrg org) {
|
||||
// 检查组织名称
|
||||
if (StringUtils.isNotBlank(org.getOrgName()) &&
|
||||
StringUtils.isNotBlank(node.getOrgName()) &&
|
||||
node.getOrgName().contains(org.getOrgName())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 检查组织编号
|
||||
if (StringUtils.isNotBlank(org.getOrgNum()) &&
|
||||
StringUtils.isNotBlank(node.getOrgNum()) &&
|
||||
node.getOrgNum().contains(org.getOrgNum())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SysOrg> queryOrgWithChildren(SysOrg sysOrg) {
|
||||
List<SysOrg> result = new ArrayList<>();
|
||||
|
||||
// 如果没有指定组织ID,则查询所有组织
|
||||
if (StringUtils.isBlank(sysOrg.getOrgId())) {
|
||||
LambdaQueryWrapper<SysOrg> queryWrapper = buildOrgQueryWrapper(sysOrg);
|
||||
result = this.list(queryWrapper);
|
||||
} else {
|
||||
// 查询指定的组织
|
||||
SysOrg currentOrg = this.getById(sysOrg.getOrgId());
|
||||
if (currentOrg != null) {
|
||||
result.add(currentOrg);
|
||||
|
||||
// 使用SQL递归查询所有下级组织
|
||||
List<SysOrg> children = baseMapper.selectAllChildrenByOrgId(sysOrg.getOrgId());
|
||||
result.addAll(children);
|
||||
}
|
||||
}
|
||||
|
||||
// 按排序字段排序
|
||||
result.sort(Comparator.comparing(SysOrg::getSort, Comparator.nullsLast(String::compareTo)));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建组织查询条件
|
||||
*
|
||||
* @param sysOrg 查询条件对象
|
||||
* @return 查询包装器
|
||||
*/
|
||||
private LambdaQueryWrapper<SysOrg> buildOrgQueryWrapper(SysOrg sysOrg) {
|
||||
LambdaQueryWrapper<SysOrg> queryWrapper = Wrappers.lambdaQuery();
|
||||
|
||||
// 添加查询条件
|
||||
queryWrapper.like(StringUtils.isNotBlank(sysOrg.getOrgName()), SysOrg::getOrgName, sysOrg.getOrgName());
|
||||
queryWrapper.like(StringUtils.isNotBlank(sysOrg.getOrgNum()), SysOrg::getOrgNum, sysOrg.getOrgNum());
|
||||
queryWrapper.eq(StringUtils.isNotBlank(sysOrg.getOrgCategory()), SysOrg::getOrgCategory, sysOrg.getOrgCategory());
|
||||
queryWrapper.eq(StringUtils.isNotBlank(sysOrg.getStatus()), SysOrg::getStatus, sysOrg.getStatus());
|
||||
queryWrapper.eq(StringUtils.isNotBlank(sysOrg.getUpOrgId()), SysOrg::getUpOrgId, sysOrg.getUpOrgId());
|
||||
queryWrapper.like(StringUtils.isNotBlank(sysOrg.getLeaderName()), SysOrg::getLeaderName, sysOrg.getLeaderName());
|
||||
queryWrapper.like(StringUtils.isNotBlank(sysOrg.getTopLeaderName()), SysOrg::getTopLeaderName, sysOrg.getTopLeaderName());
|
||||
queryWrapper.eq(StringUtils.isNotBlank(sysOrg.getSite()), SysOrg::getSite, sysOrg.getSite());
|
||||
queryWrapper.eq(StringUtils.isNotBlank(sysOrg.getCuCompanyNumber()), SysOrg::getCuCompanyNumber, sysOrg.getCuCompanyNumber());
|
||||
|
||||
// 按排序字段排序
|
||||
queryWrapper.orderByAsc(SysOrg::getSort);
|
||||
|
||||
return queryWrapper;
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public class CommonUtil {
|
||||
/**
|
||||
* 组织结构书根节点
|
||||
*/
|
||||
public static final String ROOT_NODE = "0";
|
||||
public static final String ROOT_NODE = "10000000";
|
||||
|
||||
/**
|
||||
* 系统编码
|
||||
|
@ -13,8 +13,8 @@ spring:
|
||||
writeTimeout: 35000
|
||||
nacos:
|
||||
discovery:
|
||||
server-addr: 127.0.0.1:18848
|
||||
group: EBTP_GROUP # 例如:EBTP_GROUP
|
||||
server-addr: 10.60.161.59:8848
|
||||
|
||||
aop:
|
||||
auto: true #开启spring的aop配置
|
||||
proxy-target-class: true
|
||||
@ -22,30 +22,29 @@ spring:
|
||||
application:
|
||||
name: sys-manager-ebtp-project
|
||||
|
||||
shardingsphere:
|
||||
datasource:
|
||||
names: ds0
|
||||
ds0:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
username: root
|
||||
password: Unicom@2024
|
||||
jdbc-url: jdbc:mysql://59.110.10.99:53306/ebtp_sys_manager?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
|
||||
url: jdbc:mysql://59.110.10.99:53306/ebtp_sys_manager?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
|
||||
filters: stat,wall,log4j
|
||||
maxActive: 20
|
||||
initialSize: 1
|
||||
maxWait: 60000
|
||||
minIdle: 1
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
validationQuery: select 'x'
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
poolPreparedStatements: true
|
||||
maxOpenPreparedStatements: 20
|
||||
connection-properties: druid.stat.merggSql=ture;druid.stat.slowSqlMillis=5000
|
||||
datasource:
|
||||
type: com.alibaba.druid.pool.DruidDataSource
|
||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||
username: root@test
|
||||
# username: root
|
||||
password: Unicom@2024
|
||||
# url: jdbc:mysql://59.110.10.99:53306/ebtp_sys_manager?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
|
||||
url: jdbc:mysql://localhost:2881/ebtp_sys_manager?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
|
||||
druid:
|
||||
# filters: stat,wall,log4j
|
||||
maxActive: 20
|
||||
initialSize: 1
|
||||
maxWait: 60000
|
||||
minIdle: 1
|
||||
timeBetweenEvictionRunsMillis: 60000
|
||||
minEvictableIdleTimeMillis: 300000
|
||||
validationQuery: select 'x'
|
||||
testWhileIdle: true
|
||||
testOnBorrow: false
|
||||
testOnReturn: false
|
||||
poolPreparedStatements: true
|
||||
maxOpenPreparedStatements: 20
|
||||
connection-properties: druid.stat.merggSql=ture;druid.stat.slowSqlMillis=5000
|
||||
props:
|
||||
sql:
|
||||
show: true
|
||||
@ -83,7 +82,8 @@ spring:
|
||||
redis:
|
||||
sentinel:
|
||||
master: mymaster
|
||||
nodes: 10.60.161.59:26379, 10.60.161.59:26380, 10.60.161.59:26381
|
||||
# nodes: 10.60.161.59:26379, 10.60.161.59:26380, 10.60.161.59:26381
|
||||
nodes: localhost:26379
|
||||
password: pass
|
||||
database:
|
||||
sharding: 1
|
||||
@ -91,6 +91,14 @@ spring:
|
||||
idempotent: 3
|
||||
userinfo: 4
|
||||
|
||||
thymeleaf:
|
||||
prefix: classpath:/templates/
|
||||
suffix: .html
|
||||
cache: false
|
||||
mode: HTML
|
||||
encoding: UTF-8
|
||||
servlet:
|
||||
content-type: text/html
|
||||
|
||||
|
||||
mybatis-plus:
|
||||
@ -99,7 +107,7 @@ mybatis-plus:
|
||||
map-underscore-to-camel-case: true
|
||||
auto-mapping-behavior: full
|
||||
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
|
||||
#log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
mapper-locations: classpath*:com/coscoshipping/ebtp/**/mapper/*Mapper.xml,com/chinaunicom/mall/ebtp/**/mapper/*Mapper.xml
|
||||
global-config:
|
||||
# 逻辑删除配置
|
||||
|
@ -2,9 +2,9 @@
|
||||
<configuration scan="true" scanPeriod="60 seconds" debug="true">
|
||||
|
||||
<property name="logback.logdir" value="/log" />
|
||||
<property name="logback.appname" value="${APP_NAME}" />
|
||||
<property name="logback.appname" value="sys-manager-ebtp-project" />
|
||||
|
||||
<contextName>${logback.appname}</contextName>
|
||||
<!-- <contextName>${logback.appname}</contextName> -->
|
||||
|
||||
<!--输出到控制台 ConsoleAppender-->
|
||||
<appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
|
||||
@ -39,7 +39,7 @@
|
||||
<pattern>%d ${MY_POD_IP} [%thread] %-5level %logger{64} %line - [ppTraceId: %X{PtxId}, ppSpanId: %X{PspanId}] - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
|
||||
<!--指定最基础的日志输出级别-->
|
||||
<root level="INFO">
|
||||
<appender-ref ref="consoleLog"/>
|
||||
@ -47,4 +47,14 @@
|
||||
<appender-ref ref="fileInfoLog"/>
|
||||
</root>
|
||||
|
||||
<!-- MyBatis SQL日志配置 -->
|
||||
<logger name="com.coscoshipping.ebtp.project" level="DEBUG"/>
|
||||
<logger name="com.chinaunicom.mall.ebtp" level="DEBUG"/>
|
||||
<logger name="org.apache.ibatis" level="DEBUG"/>
|
||||
<logger name="java.sql" level="DEBUG"/>
|
||||
<logger name="java.sql.Connection" level="DEBUG"/>
|
||||
<logger name="java.sql.Statement" level="DEBUG"/>
|
||||
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
|
||||
<logger name="java.sql.ResultSet" level="DEBUG"/>
|
||||
|
||||
</configuration>
|
||||
|
Reference in New Issue
Block a user