介绍:给平台开发一个全局的系统目录树结构,可根据不同的目录属性自定义目录
主类 目录的增删改查
@Service
@Slf4j
public class CatalogServiceImpl extends ServiceImpl<CatalogMapper, Catalog> implements CatalogService {
@Resource
private CatalogMapper catalogMapper;
private final int LEVEL = 4;
/**
* 功能描述 创建实时项目时自动创建系统根目录
* 如果有多个目录 根据需求将目录加到list
* @param * @param projectType
* @param * @param projectId
* @return
* @author ZhiWen
*/
@Override
@Transactional(rollbackFor = CustomException.class)
public void initRootCatalogByProject(String projectType, Long projectId) {
//新增系统目录
//用户的信息
User user = (User) SecurityUtils.getSubject().getPrincipal();
Long userId = user.getId();
try {
//list根据自己需求确定是否需要使用
ArrayList<Catalog> list = new ArrayList<>();
//离线项目
if (projectType.equals(CatalogEnum.BATCH.getMsg())) {
//新建 "离线数据同步目录"
Catalog b1 = new Catalog()
.setCatalogUrl(CatalogEnum.BATCH_SYNC.getMsg())
.setCatalogType(CatalogEnum.CONTENT.getMsg())
.setCatalogProperty(CatalogEnum.ROOT.getMsg())
.setFatherCatalogId(0L)
.setModuleType(CatalogEnum.BATCH_SYNC_MODULE.getMsg())
.setProjectId(projectId)
.setCreateBy(userId);
list.add(b1);
} else if (projectType.equals(CatalogEnum.STREAM.getMsg())) {
//新建 "实时数据同步目录"
Catalog s1 = new Catalog()
.setCatalogUrl(CatalogEnum.STREAM_SYNC.getMsg())
.setCatalogType(CatalogEnum.CONTENT.getMsg())
.setCatalogProperty(CatalogEnum.ROOT.getMsg())
.setFatherCatalogId(0L)
.setModuleType(CatalogEnum.STREAM_SYNC_MODULE.getMsg())
.setProjectId(projectId)
.setCreateBy(userId);
list.add(s1);
}
//批量保存
saveBatch(list);
} catch (Exception e) {
log.error("创建项目新增系统根目录失败" + e);
throw new CustomException(CatalogEnum.CATALOG_ERROR_08.getCode(), CatalogEnum.CATALOG_ERROR_08.getMsg());
}
}
/**
* 功能描述 新建文件夹
*
* @param folderDTO
* @return
* @author ZhiWen
*/
@Override
public void addFolder(CatalogAddFolderDTO folderDTO) {
if (!folderDTO.getAddOrUpdate()) {
throw new CustomException(-1, "入参异常");
}
checkName(folderDTO);
Catalog fc = catalogMapper.selectById(folderDTO.getCatalogId());
if (fc == null) {
throw new CustomException(CatalogEnum.CATALOG_ERROR_01.getCode(), CatalogEnum.CATALOG_ERROR_01.getMsg());
}
String fcCatalogUrl = fc.getCatalogUrl();
String[] urlArr = fcCatalogUrl.split("/");
if (LEVEL == urlArr.length) {
throw new CustomException(CatalogEnum.CATALOG_ERROR_02.getCode(), CatalogEnum.CATALOG_ERROR_02.getMsg());
}
boolean isFile = CatalogEnum.FILE.getMsg().equals(fc.getCatalogType());
if (isFile) {
log.error("该目录不支持创建文件夹");
throw new CustomException(CatalogEnum.CATALOG_ERROR_03.getCode(), CatalogEnum.CATALOG_ERROR_03.getMsg());
}
User user = (User) SecurityUtils.getSubject().getPrincipal();
Catalog catalog = new Catalog()
.setCatalogUrl(fcCatalogUrl + "/" + folderDTO.getName())
.setCatalogType(CatalogEnum.CONTENT.getMsg())
.setCatalogProperty(CatalogEnum.FOLDER.getMsg())
.setModuleType(fc.getModuleType())
.setFatherCatalogId(fc.getId())
.setProjectId(fc.getProjectId())
.setCreateBy(user.getId());
try {
catalogMapper.insert(catalog);
} catch (Exception e) {
log.error("新增文件夹失败" + e);
throw new CustomException(CatalogEnum.CATALOG_ERROR_05.getCode(), CatalogEnum.CATALOG_ERROR_05.getMsg());
}
}
/**
* 功能描述 名称重名校验
*
* @param folderDTO
* @return
* @author ZhiWen
*/
@Override
public void checkName(CatalogAddFolderDTO folderDTO) {
Catalog fc = catalogMapper.selectById(folderDTO.getCatalogId());
if (fc == null) {
throw new CustomException(CatalogEnum.CATALOG_ERROR_01.getCode(), CatalogEnum.CATALOG_ERROR_01.getMsg());
}
String newName;
if (folderDTO.getAddOrUpdate()) {
newName = fc.getCatalogUrl() + "/" + folderDTO.getName();
} else {
//获取全路径名
String allUrl = fc.getCatalogUrl();
//作切割 最后一级名用新名代替
newName = allUrl.substring(0, allUrl.lastIndexOf("/") + 1) + folderDTO.getName();
}
QueryWrapper<Catalog> wrapper = new QueryWrapper<>();
wrapper.eq("catalog_url", newName)
.eq("module_type", fc.getModuleType())
.ne("id", fc.getId());
if (fc.getProjectId() != null) {
wrapper.eq("project_id", fc.getProjectId());
}
Integer count = catalogMapper.selectCount(wrapper);
if (count > 0) {
throw new CustomException(CatalogEnum.CATALOG_ERROR_04.getCode(), CatalogEnum.CATALOG_ERROR_04.getMsg());
}
}
@Override
public List<CatalogTreeDTO> listCatalogTree(CatalogByModuleDTO moduleDTO) {
//TODO 先确定目录展示跟项目是否有关
String moduleType = moduleDTO.getModuleType();
Long projectId = moduleDTO.getProjectId();
//目前数据服务开发不跟项目挂钩
boolean isDataService = moduleType.equals(CatalogEnum.DATA_SERVICE_DEV_MODULE.getMsg());
if (!isDataService) {
if (projectId == null) {
throw new CustomException(-1, "入参异常");
}
}
// 查询指定模块下的所有目录
QueryWrapper<Catalog> wrapper = new QueryWrapper<>();
wrapper.eq("module_type", moduleType);
if (projectId != null) {
wrapper.eq("project_id", projectId);
}
List<Catalog> catalogList = catalogMapper.selectList(wrapper);
if (CollectionUtils.isEmpty(catalogList)) {
throw new CustomException(CatalogEnum.CATALOG_ERROR_06.getCode(), CatalogEnum.CATALOG_ERROR_06.getMsg());
}
//转换成树结构
List<CatalogTreeDTO> catalogTreeList = BeanUtil.copyListProperties(catalogList, CatalogTreeDTO.class);
try {
//添加作业信息
log.info("添加作业信息>>>>>>>>>>>>>>>>");
//获取目录树
log.info("开始获取目录树结构>>>>>>>>>>>");
List<CatalogTreeDTO> catalogTree = listToTree(catalogTreeList);
//将目录树的数据结构化成前端展示数据
for (CatalogTreeDTO treeDTO : catalogTree) {
dataStructure(treeDTO);
}
return catalogTree;
} catch (Exception e) {
log.error("获取目录树失败" + e);
throw new CustomException(CatalogEnum.CATALOG_ERROR_07.getCode(), CatalogEnum.CATALOG_ERROR_07.getMsg());
}
}
/**
*功能描述 删除当前目录及所有子目录
* @author ZhiWen
* @param * @param id
* @return
*/
@Override
public void deleteCatalog(Long id){
Catalog fc = catalogMapper.selectById(id);
if (fc == null){
throw new CustomException(CatalogEnum.CATALOG_ERROR_01.getCode(),CatalogEnum.CATALOG_ERROR_01.getMsg());
}
//这里考虑根目录不可以删除
boolean isRoot = CatalogEnum.ROOT.getMsg().equals(fc.getCatalogProperty());
if (isRoot){
throw new CustomException(CatalogEnum.CATALOG_ERROR_09.getCode(),CatalogEnum.CATALOG_ERROR_09.getMsg());
}
boolean isFile = CatalogEnum.FILE.getMsg().equals(fc.getCatalogType());
//删除单个目录和层级目录
if (isFile){
catalogMapper.deleteById(id);
}else {
QueryWrapper<Catalog> wrapper = new QueryWrapper<>();
wrapper.eq("module_type",fc.getModuleType())
.likeRight("catalog_url",fc.getCatalogUrl());
if (fc.getProjectId()!=null){
wrapper.eq("project_id",fc.getProjectId());
}
catalogMapper.delete(wrapper);
}
}
/**
*功能描述 获取当前文件下所有的子目录
* @author ZhiWen
* @param * @param id
* @return
*/
@Override
public List<Catalog> selectSubcatalogList(Long id){
Catalog fc = catalogMapper.selectById(id);
if (fc == null){
throw new CustomException(CatalogEnum.CATALOG_ERROR_01.getCode(),CatalogEnum.CATALOG_ERROR_01.getMsg());
}
QueryWrapper<Catalog> wrapper = new QueryWrapper<>();
wrapper.eq("module_type",fc.getModuleType())
.likeRight("catalog_url",fc.getCatalogUrl());
if (fc.getProjectId()!=null){
wrapper.eq("project_id",fc.getProjectId());
}
return catalogMapper.selectList(wrapper);
}
/**
*功能描述 获取当前文件夹下所有的 moduleId(对应的任务id)
* @author ZhiWen
* @param * @param id
* @return
*/
@Override
public List<Long> getModuleIdList(Long id){
List<Catalog> catalogList = selectSubcatalogList(id);
return catalogList.stream().map(Catalog::getModuleId).filter(moduleId -> moduleId != null)
.collect(Collectors.toList());
}
/**
*功能描述 目录重命名
* @author ZhiWen
* @param * @param folderDTO
* @return
*/
@Override
public void updateCatalog(CatalogAddFolderDTO folderDTO){
//名称校验
if (folderDTO.getAddOrUpdate()){
throw new CustomException(-1,"入参异常");
}
checkName(folderDTO);
Catalog fc = catalogMapper.selectById(folderDTO.getCatalogId());
if (fc == null){
throw new CustomException(CatalogEnum.CATALOG_ERROR_01.getCode(),CatalogEnum.CATALOG_ERROR_01.getMsg());
}
//根据目录类型更改
User user = (User) SecurityUtils.getSubject().getPrincipal();
if (CatalogEnum.FILE.getMsg().equals(fc.getCatalogType())){
//获取全路径名
String allUrl = fc.getCatalogUrl();
//作切割 最后一级名用新名代替
String newCatalogUrl = allUrl.substring(0, allUrl.lastIndexOf("/") + 1) + folderDTO.getName();
fc.setCatalogUrl(newCatalogUrl);
fc.setUpdateBy(user.getId());
catalogMapper.updateById(fc);
}else {
//获取修改前全路径名
String oldUrl = fc.getCatalogUrl();
int length = oldUrl.split("/").length;
//查出所有需要修改的子目录集
List<Catalog> catalogList = selectSubcatalogList(folderDTO.getCatalogId());
//遍历操作每个子目录
for (Catalog catalog : catalogList) {
//每级目录的全路径名称
String allUrl = catalog.getCatalogUrl();
//子目录切割
String[] split = allUrl.split("/");
//对应目录名替换
split[length - 1] = folderDTO.getName();
String catalogUrl = String.join("/", split);
catalog.setCatalogUrl(catalogUrl);
catalog.setUpdateBy(user.getId());
}
updateBatchById(catalogList);
}
}
/*-------------------------------------------私有方法------------------------------------------*/
/**
* 功能描述 目录列表生成目录树
*
* @param * @param list
* @return
* @author ZhiWen
*/
private List<CatalogTreeDTO> listToTree(List<CatalogTreeDTO> list) {
//最好如果有多个根目录再启用
List<CatalogTreeDTO> treeList = new ArrayList<CatalogTreeDTO>();
for (CatalogTreeDTO tree : list) {
//首先判断找根目录,根目录长度
if (tree.getCatalogUrl().split("/").length == 2) {
//去找该根目录下的所有子目录,并把此根目录及其子目录添加到结果集
treeList.add(findChildren(tree, list));
//如果根目录只有一个 启用
//break;
}
}
return treeList;
}
/**
* 功能描述 找该树目录下的所有子目录
*
* @param * @param tree
* @param list
* @return
* @author ZhiWen
*/
private CatalogTreeDTO findChildren(CatalogTreeDTO tree, List<CatalogTreeDTO> list) {
for (CatalogTreeDTO node : list) {
//目录的全路径名称
String urlAll = node.getCatalogUrl();
//最后一级目录的父目录的全路径名称
String url = urlAll.substring(0, node.getCatalogUrl().lastIndexOf("/"));
//判断是否是传入目录的子目录
if (url.equals(tree.getCatalogUrl())) {
//初始空间
if (tree.getChildCatalogTree() == null) {
tree.setChildCatalogTree(new ArrayList<CatalogTreeDTO>());
}
//添加子目录,并继续找子目录的子目录
tree.getChildCatalogTree().add(findChildren(node, list));
}
}
return tree;
}
/**
* 功能描述 数据结构化 转换成前端展示数据格式
*
* @param * @param treeNode
* @return
* @author ZhiWen
*/
private void dataStructure(CatalogTreeDTO treeNode) {
//全路径
String allUrl = treeNode.getCatalogUrl();
//最后一级目录的名称
String substring = allUrl.substring(allUrl.lastIndexOf("/") + 1);
//转换数据 去掉了/ 及其父目录
treeNode.setCatalogUrl(substring);
List<CatalogTreeDTO> childTree = treeNode.getChildCatalogTree();
//子目录的数据格式化
if (childTree != null) {
for (CatalogTreeDTO node : childTree) {
dataStructure(node);
}
}
}
}
枚举类
/**
* @description 目录相关枚举
* @author: ZhiWen
* @create: 2020-04-14 14:39
**/
@Getter
public enum CatalogEnum {
/**项目类型*/
/**
* 离线
*/
BATCH("batch"),
/**
* 实时
*/
STREAM("stream"),
/**默认根目录*/
/**
* 离线同步
*/
BATCH_SYNC("/离线数据同步目录"),
/**
* 实时同步
*/
STREAM_SYNC("/实时数据同步目录"),
/**catalogType*/
/**
* 目录
*/
CONTENT("content"),
/**
* 文件
*/
FILE("file"),
/**请自定义module*/
/**
* 数据服务开发模块
*/
DATA_SERVICE_DEV_MODULE("development"),
/**
* 离线同步模块
*/
BATCH_SYNC_MODULE("batchSync"),
/**
* 实时同步模块
*/
STREAM_SYNC_MODULE("streamSync"),
/**请自定义 catalog_property*/
/**
* 根目录
*/
ROOT("root"),
/**
* 普通文件夹
*/
FOLDER("folder"),
/**
* API
*/
API("api"),
/**
* 任务类JOB
*/
JOB("job"),
SYNC_FOLDER("syncFolder"),
/**目录表错误信息*/
CATALOG_ERROR_01(6301,"目录不存在"),
CATALOG_ERROR_02(6302,"目录不可以超过三层"),
CATALOG_ERROR_03(6303,"不支持创建目录"),
CATALOG_ERROR_04(6304,"名称不可以重复"),
CATALOG_ERROR_05(6305,"新建目录失败"),
CATALOG_ERROR_06(6306,"查询目录列表为空"),
CATALOG_ERROR_07(6307,"获取目录树失败"),
CATALOG_ERROR_08(6308,"创建项目新增根目录失败"),
CATALOG_ERROR_09(6309,"根目录不可以删除"),
;
private Integer code;
private String msg;
CatalogEnum() {
}
CatalogEnum(String msg) {
this.msg = msg;
}
CatalogEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
}
表结构
CREATE TABLE `sys_catalog` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`version` int(10) DEFAULT '1' COMMENT '乐观锁版本',
`deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除字段(0:未删除,1:已删除)',
`gmt_create` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`gmt_modified` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
`create_by` bigint(20) DEFAULT NULL COMMENT '创建人',
`update_by` bigint(20) DEFAULT NULL COMMENT '修改人',
`catalog_url` varchar(255) DEFAULT NULL COMMENT '目录的全路径',
`module_type` varchar(32) DEFAULT NULL COMMENT '模块类型,(服务开发:development,...)',
`catalog_type` varchar(32) NOT NULL COMMENT '目录类型,(目录型:content,文件型:file)',
`catalog_property` varchar(32) NOT NULL COMMENT '目录属性,(根目录:root,文件夹:folder,API:api,...)',
`module_id` bigint(20) DEFAULT NULL COMMENT '该节点指向的关联表的主键id',
`father_catalog_id` bigint(20) NOT NULL COMMENT '父目录id,根目录为0',
`project_id` bigint(20) DEFAULT NULL COMMENT '所属项目id',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=455 DEFAULT CHARSET=utf8 COMMENT='系统目录表';