目录
1、简单表结构示例:
结果展示:
{
"success": true,
"message": "操作成功!",
"code": 200,
"result": [
{
"id": 1,
"pid": 0,
"name": "中国",
"code": "0001",
"children": [
{
"id": 2,
"pid": 1,
"name": "河南",
"code": "0002",
"children": [
{
"id": 4,
"pid": 2,
"name": "周口",
"code": "0021",
"children": []
},
{
"id": 5,
"pid": 2,
"name": "郑州",
"code": "0022",
"children": []
}
]
},
{
"id": 3,
"pid": 1,
"name": "台湾",
"code": "0003",
"children": [
{
"id": 6,
"pid": 3,
"name": "台北",
"code": "0031",
"children": []
}
]
},
{
"id": 7,
"pid": 1,
"name": "北京",
"code": "0004",
"children": []
}
]
}
],
"resultTwo": null,
"resultThree": null,
"timestamp": 1687765801212
}
实体类TestTree
@Data
@TableName("test_tree")
@Accessors(chain = true)
@ApiModel(value="测试树", description="测试树")
public class TestTree {
@ApiModelProperty(value = "ID")
private Integer id;
@ApiModelProperty(value = "父id")
private Integer pid;
@ApiModelProperty(value = "姓名")
private String name;
@ApiModelProperty(value = "编码")
private String code;
@TableField(exist = false)
private List<TestTree> children;
}
controller
@Slf4j
@Api(tags = "测试树")
@RestController
@RequestMapping("/test")
public class TestTreeController {
@Resource
private TestTreeService testTreeService;
@ApiOperation(value = "测试树",notes = "测试树")
@GetMapping("/selectTree")
public Result<Object> selectTree() {
Result<Object> result = testTreeService.selectTree();
return Result.ok(result);
}
}
service
public interface TestTreeService {
Result<Object> selectTree();
}
@Service
public class TestTreeServiceImpl implements TestTreeService {
@Resource
private TestTreeMapper testTreeMapper;
@Override
public Result<Object> selectTree() {
List<TestTree> testTreeList=this.testTreeMapper.selectTree();
List<TestTree> testTrees = testTreeList.stream().filter(testTree -> 0 == testTree.getPid())
.peek(testTree -> testTree.setChildren(createChildList(testTree, testTreeList)))
.collect(Collectors.toList());
return Result.ok(testTrees);
}
/**
* @param testTree 父级
* @param testTreeList 对应的list
* @return
*/
private static List<TestTree> createChildList(TestTree testTree, List<TestTree> testTreeList) {
return testTreeList.stream().filter(model -> testTree.getId().equals(model.getPid()))
.peek(model -> model.setChildren(createChildList(model, testTreeList)))
.collect(Collectors.toList());
}
}
mapper
sql:select * from test_tree
2、多层次使用此方法也可以查询出树形结构
示例:
{
"success": true,
"message": "操作成功!",
"code": 200,
"result": [
{
"id": 1,
"pid": 0,
"name": "中国",
"code": "0001",
"children": [
{
"id": 2,
"pid": 1,
"name": "河南",
"code": "0002",
"children": [
{
"id": 4,
"pid": 2,
"name": "周口",
"code": "0021",
"children": [
{
"id": 10,
"pid": 4,
"name": "郸城县",
"code": "0211",
"children": [
{
"id": 12,
"pid": 10,
"name": "汲冢镇",
"code": "2111",
"children": []
},
{
"id": 13,
"pid": 10,
"name": "李楼乡",
"code": "2112",
"children": []
}
]
},
{
"id": 11,
"pid": 4,
"name": "沈丘县",
"code": "0212",
"children": []
}
]
},
{
"id": 5,
"pid": 2,
"name": "郑州",
"code": "0022",
"children": []
}
]
},
{
"id": 3,
"pid": 1,
"name": "台湾",
"code": "0003",
"children": [
{
"id": 6,
"pid": 3,
"name": "台北",
"code": "0031",
"children": []
}
]
},
{
"id": 7,
"pid": 1,
"name": "北京",
"code": "0004",
"children": []
}
]
},
{
"id": 8,
"pid": 0,
"name": "俄罗斯",
"code": "1001",
"children": [
{
"id": 9,
"pid": 8,
"name": "莫斯科",
"code": "1011",
"children": []
}
]
}
],
"resultTwo": null,
"resultThree": null,
"timestamp": 1687766628068
}
3、根据根节点分页
TreeEntity<T>
@Data
@EqualsAndHashCode(callSuper = true)
public class TreeEntity<T> extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 父菜单名称
*/
@TableField(exist = false)
private String parentName;
/**
* 父菜单ID
*/
private Long parentId;
/**
* 子部门
*/
@TableField(exist = false)
private List<T> children = new ArrayList<>();
}
BaseEntity
@Data
public class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 搜索值
*/
@JsonIgnore
@TableField(exist = false)
private String searchValue;
/**
* 创建者
*/
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "创建人")
private String createName;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
@ApiModelProperty(value = "创建时间")
private LocalDateTime createTime;
/**
* 更新者
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(value = "更新人")
private String updateName;
/**
* 更新时间
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@TableField(fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(value = "更新时间")
private LocalDateTime updateTime;
/**
* 开始时间
*/
@TableField(exist = false)
@ApiModelProperty(value = "时间查询开始时间")
private String beginCreateTime;
/**
* 结束时间
*/
@TableField(exist = false)
@ApiModelProperty(value = "时间查询结束时间")
private String endCreateTime;
/**
* 页码
*/
@TableField(exist = false)
@ApiModelProperty(value = "页码")
private Integer pageNum = 1;
/**
* 条数
*/
@TableField(exist = false)
@ApiModelProperty(value = "每页条数")
private Integer pageSize = 10;
/**
* 请求参数
*/
@JsonInclude(JsonInclude.Include.NON_EMPTY)
// 在序列化Java对象为JSON时,只有属性值不为null,或者如果属性是集合或数组,则至少包含一个元素,或者如果属性是字符串,则不是空字符串,这些属性才会被包含在生成的JSON中。
@TableField(exist = false)
private Map<String, Object> params = new HashMap<>();
}
sysDept
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_dept")
@NoArgsConstructor
@AllArgsConstructor
public class SysDept extends TreeEntity<SysDept> {
private static final long serialVersionUID = 1L;
/**
* 部门ID
*/
@TableId(value = "dept_id")
private Long deptId;
/**
* 部门编码
*/
@NotBlank(message = "部门编码不能为空")
@Size(min = 0, max = 30, message = "部门编码长度不能超过{max}个字符")
private String deptCode;
/**
* 部门名称
*/
@NotBlank(message = "部门名称不能为空")
@Size(min = 0, max = 30, message = "部门名称长度不能超过{max}个字符")
private String deptName;
/**
* 部门类型 0机器人 1标件公司 2非标件公司
*/
private String deptType;
/**
* 显示顺序
*/
@NotNull(message = "显示顺序不能为空")
private Integer orderNum;
/**
* 负责人
*/
private String leader;
/**
* 联系电话
*/
@Size(min = 0, max = 11, message = "联系电话长度不能超过{max}个字符")
private String phone;
/**
* 邮箱
*/
@Email(message = "邮箱格式不正确")
@Size(min = 0, max = 50, message = "邮箱长度不能超过{max}个字符")
private String email;
/**
* 部门状态:0正常,1停用
*/
private String status;
/**
* 删除标志(0代表存在 2代表删除)
*/
@TableLogic
private String delFlag;
/**
* 祖级列表
*/
private String ancestors;
}
SysdeptPlus
/**
* @author ChenSir
* @description
* @date 2023/10/26
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class SysdeptPlus {
private SysDept sysDept;
private List<SysDept> rows;
private int total;
}
serviceImpl
@Override
public SysdeptPlus getTreePage(SysDept sysDept, PageQuery pageQuery) {
int pageNum = pageQuery.getPageNum();
int pageSize = pageQuery.getPageSize();
// 1、查询所有分类
QueryWrapper<SysDept> wrapper = Wrappers.query();
wrapper.eq(StrUtil.isNotBlank(sysDept.getStatus()), "status", sysDept.getStatus());
List<SysDept> sysDeptList = treePageMapper.selectList(wrapper);
// 2、组装成父子树形结构 每条均为parentId=0 的根节点,其children包含其子节点
List<SysDept> testTrees = sysDeptList.stream().filter(testTree -> 0 == testTree.getParentId())
.peek(testTree -> testTree.setChildren(createChildList(testTree, sysDeptList)))
.collect(Collectors.toList());
// 3、分页
List<SysDept> page = new ArrayList<>();
int start = (pageNum - 1) * pageSize;
for (int i = start; i < (start + pageSize > testTrees.size() ? testTrees.size() : start + pageSize); i++) {
page.add(testTrees.get(i));
}
SysdeptPlus sysdeptPlus = new SysdeptPlus();
sysdeptPlus.setRows(page);
sysdeptPlus.setTotal(testTrees.size());
return sysdeptPlus;
}
/**
* @param testTree 父级
* @param testTreeList 对应的list
* @return
*/
private static List<SysDept> createChildList(SysDept testTree, List<SysDept> testTreeList) {
return testTreeList.stream().filter(model -> testTree.getDeptId().equals(model.getParentId()))
.peek(model -> model.setChildren(createChildList(model, testTreeList)))
.collect(Collectors.toList());
}