diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java index 906ab646a..9ab668f54 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java @@ -12,9 +12,11 @@ import io.swagger.v3.oas.annotations.Operation; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; import javax.validation.Valid; +import java.io.IOException; import java.util.Comparator; import java.util.List; @@ -83,4 +85,11 @@ public class DeptController { return success(DeptConvert.INSTANCE.convert(deptService.getDept(id))); } + @Operation(summary = "批量组织架构导入") + @PostMapping(value = "/batch/import", name = "批量组织架构导入") + public CommonResult batchImport(@RequestParam("file") MultipartFile file) throws IOException { + deptService.batchImport(file); + return success(true); + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/BatchImportVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/BatchImportVO.java new file mode 100644 index 000000000..dd99b3eb6 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/BatchImportVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept; + +import com.alibaba.excel.annotation.ExcelProperty; +import lombok.Data; + +import java.io.Serializable; + +/** + * @Title:BatchImportVO + * @Description: TODO + * @author: tangqian + * @date: 2023/5/30 11:11 + * @version: V1.0.0 + */ +@Data +public class BatchImportVO implements Serializable { + private static final long serialVersionUID = -1235962811424997478L; + @ExcelProperty("名称") + private String depName; + @ExcelProperty("层级") + private String hierarchy; + @ExcelProperty("关系") + private String relation; + @ExcelProperty("标识(唯一)") + private String characteristic; +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java index 2c9ad9581..3b8434176 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java @@ -71,4 +71,6 @@ public class DeptDO extends TenantBaseDO { */ private String parentOrganizationName; + + private String characteristic; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java index 6d68c3d51..9008610a2 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptService.java @@ -7,7 +7,9 @@ import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptCreateRe import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -115,4 +117,5 @@ public interface DeptService { */ void validateDeptList(Collection ids); + void batchImport(MultipartFile file) throws IOException; } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java index 0fb718f13..31edc3097 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java @@ -2,9 +2,11 @@ package cn.iocoder.yudao.module.system.service.dept; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.BatchImportVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptUpdateReqVO; @@ -13,6 +15,11 @@ import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; import cn.iocoder.yudao.module.system.enums.dept.DeptIdEnum; import cn.iocoder.yudao.module.system.mq.producer.dept.DeptProducer; +import cn.iocoder.yudao.module.system.util.TransactionalService; +import com.alibaba.excel.EasyExcel; +import com.alibaba.excel.context.AnalysisContext; +import com.alibaba.excel.event.AnalysisEventListener; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.IdWorker; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.google.common.collect.ImmutableMap; @@ -20,12 +27,18 @@ import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Multimap; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; +import org.springframework.web.multipart.MultipartFile; import javax.annotation.PostConstruct; import javax.annotation.Resource; +import java.io.IOException; import java.util.*; +import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; @@ -43,7 +56,7 @@ public class DeptServiceImpl implements DeptService { /** * 部门缓存 * key:部门编号 {@link DeptDO#getId()} - * + *

* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 */ @Getter @@ -52,7 +65,7 @@ public class DeptServiceImpl implements DeptService { * 父部门缓存 * key:部门编号 {@link DeptDO#getParentId()} * value: 直接子部门列表 - * + *

* 这里声明 volatile 修饰的原因是,每次刷新时,直接修改指向 */ @Getter @@ -63,6 +76,8 @@ public class DeptServiceImpl implements DeptService { @Resource private DeptProducer deptProducer; + @Resource + private TransactionalService transactionalService; /** * 初始化 {@link #parentDeptCache} 和 {@link #deptCache} 缓存 @@ -98,13 +113,13 @@ public class DeptServiceImpl implements DeptService { // 插入部门 DeptDO dept = DeptConvert.INSTANCE.convert(reqVO); dept.setId(IdWorker.getId()); - if(DeptIdEnum.ROOT.getId().equals(dept.getParentId())){ - dept.setParentOrganizationIds(dept.getId()+""); + if (DeptIdEnum.ROOT.getId().equals(dept.getParentId())) { + dept.setParentOrganizationIds(dept.getId() + ""); dept.setParentOrganizationName(dept.getName()); - }else{ + } else { DeptDO parent = deptMapper.selectById(reqVO.getParentId()); - dept.setParentOrganizationIds(parent.getParentOrganizationIds()+","+dept.getId()); - dept.setParentOrganizationName(parent.getParentOrganizationName()+">"+dept.getName()); + dept.setParentOrganizationIds(parent.getParentOrganizationIds() + "," + dept.getId()); + dept.setParentOrganizationName(parent.getParentOrganizationName() + ">" + dept.getName()); } deptMapper.insert(dept); @@ -122,13 +137,13 @@ public class DeptServiceImpl implements DeptService { validateForCreateOrUpdate(reqVO.getId(), reqVO.getParentId(), reqVO.getName()); // 更新部门 DeptDO updateObj = DeptConvert.INSTANCE.convert(reqVO); - if(DeptIdEnum.ROOT.getId().equals(updateObj.getParentId())){ - updateObj.setParentOrganizationIds(updateObj.getId()+""); + if (DeptIdEnum.ROOT.getId().equals(updateObj.getParentId())) { + updateObj.setParentOrganizationIds(updateObj.getId() + ""); updateObj.setParentOrganizationName(updateObj.getName()); - }else{ + } else { DeptDO parent = deptMapper.selectById(reqVO.getParentId()); - updateObj.setParentOrganizationIds(parent.getParentOrganizationIds()+","+updateObj.getId()); - updateObj.setParentOrganizationName(parent.getParentOrganizationName()+">"+updateObj.getName()); + updateObj.setParentOrganizationIds(parent.getParentOrganizationIds() + "," + updateObj.getId()); + updateObj.setParentOrganizationName(parent.getParentOrganizationName() + ">" + updateObj.getName()); } deptMapper.updateById(updateObj); // 发送刷新消息 @@ -167,8 +182,8 @@ public class DeptServiceImpl implements DeptService { */ @Override public DeptDO findParentDept(Long tenantId) { - List deptDOs = deptMapper.selectList(Wrappers.lambdaQuery(DeptDO.class).eq(DeptDO::getParentId,DeptIdEnum.ROOT.getId()).eq(DeptDO::getTenantId,tenantId)); - if(deptDOs!=null && deptDOs.size() > 0){ + List deptDOs = deptMapper.selectList(Wrappers.lambdaQuery(DeptDO.class).eq(DeptDO::getParentId, DeptIdEnum.ROOT.getId()).eq(DeptDO::getTenantId, tenantId)); + if (deptDOs != null && deptDOs.size() > 0) { return deptDOs.get(0); } return null; @@ -181,19 +196,19 @@ public class DeptServiceImpl implements DeptService { } List result = new ArrayList<>(); // 递归,简单粗暴 - getDeptsByParentIdFromCache(result, parentId, - recursive ? Integer.MAX_VALUE : 1, // 如果递归获取,则无限;否则,只递归 1 次 - parentDeptCache); + getDeptsByParentIdFromCache(result, parentId, + recursive ? Integer.MAX_VALUE : 1, // 如果递归获取,则无限;否则,只递归 1 次 + parentDeptCache); return result; } /** * 递归获取所有的子部门,添加到 result 结果 * - * @param result 结果 - * @param parentId 父编号 + * @param result 结果 + * @param parentId 父编号 * @param recursiveCount 递归次数 - * @param parentDeptMap 父部门 Map,使用缓存,避免变化 + * @param parentDeptMap 父部门 Map,使用缓存,避免变化 */ private void getDeptsByParentIdFromCache(List result, Long parentId, int recursiveCount, Multimap parentDeptMap) { @@ -305,4 +320,99 @@ public class DeptServiceImpl implements DeptService { }); } + private static List deptDOS2 = new ArrayList<>(); + private static List deptDOS3 = new ArrayList<>(); + private static List deptDOS4 = new ArrayList<>(); + + @Override + public void batchImport(MultipartFile file) throws IOException { + List list = ExcelUtils.read(file, BatchImportVO.class); + try { + EasyExcel.read(file.getInputStream(), BatchImportVO.class, new AnalysisEventListener() { + + private final List deptList = new ArrayList<>(10000); + + @Override + public void invoke(BatchImportVO o, AnalysisContext analysisContext) { + deptList.add(o); + if (deptList.size() % 15 == 0) { + batchInsert(); + deptList.clear(); + } + } + + @Override + public void doAfterAllAnalysed(AnalysisContext analysisContext) { + if (CollectionUtils.isNotEmpty(deptList)) { + batchInsert(); + } + } + + private void batchInsert() { + Map> collect = deptList.stream().collect(Collectors.groupingBy(BatchImportVO::getHierarchy)); + List batchImportVOS1 = collect.get("1"); + List batchImportVOS2 = collect.get("2"); + List batchImportVOS3 = collect.get("3"); + List batchImportVOS4 = collect.get("4"); + transactionalService.run(() -> { + // 装入层级1 + BatchImportVO batchImportVO1 = batchImportVOS1.get(0); + DeptDO deptDO1 = new DeptDO(); + deptDO1.setParentId(0L); + deptDO1.setName(batchImportVO1.getDepName()); + deptDO1.setParentOrganizationName(batchImportVO1.getDepName()); + deptDO1.setCharacteristic(batchImportVO1.getCharacteristic()); + deptMapper.insert(deptDO1); + deptDO1.setParentOrganizationIds(deptDO1.getId().toString()); + deptMapper.updateById(deptDO1); + // 装入层级2 + for (BatchImportVO batchImportVO : batchImportVOS2) { + DeptDO deptDO2 = new DeptDO(); + deptDO2.setName(batchImportVO.getDepName()); + deptDO2.setParentId(deptDO1.getId()); + deptDO2.setParentOrganizationName(deptDO1.getName() + "," + batchImportVO.getDepName()); + deptDO2.setCharacteristic(batchImportVO.getCharacteristic()); + deptMapper.insert(deptDO2); + deptDO2.setParentOrganizationIds(deptDO1.getId() + "," + deptDO2.getId()); + deptMapper.updateById(deptDO2); + deptDOS2.add(deptDO2); + } + // 装入层级3 + for (BatchImportVO batchImportVO : batchImportVOS3) { + DeptDO deptDO3 = new DeptDO(); + deptDO3.setName(batchImportVO.getDepName()); + List collect1 = deptDOS2.stream().filter(e -> e.getCharacteristic().equals(batchImportVO.getCharacteristic())).collect(Collectors.toList()); + DeptDO deptDO = collect1.get(0); + deptDO3.setParentId(deptDO.getId()); + deptDO3.setParentOrganizationName(deptDO.getName() + "," + batchImportVO.getDepName()); + deptMapper.insert(deptDO3); + deptDO3.setParentOrganizationIds(deptDO.getId() + "," + deptDO3.getId()); + deptMapper.updateById(deptDO3); + deptDOS3.add(deptDO3); + } + // 装入层级4 + for (BatchImportVO batchImportVO : batchImportVOS4) { + DeptDO deptDO4 = new DeptDO(); + deptDO4.setName(batchImportVO.getDepName()); + List collect1 = deptDOS3.stream().filter(e -> e.getCharacteristic().equals(batchImportVO.getCharacteristic())).collect(Collectors.toList()); + DeptDO deptDO = collect1.get(0); + deptDO4.setParentId(deptDO.getId()); + deptDO4.setParentOrganizationName(deptDO.getName() + "," + batchImportVO.getDepName()); + deptMapper.insert(deptDO4); + deptDO4.setParentOrganizationIds(deptDO.getId() + "," + deptDO4.getId()); + deptMapper.updateById(deptDO4); + deptDOS3.add(deptDO4); + } + }); + } + }).sheet().headRowNumber(1).doRead(); + } catch (IOException e) { + log.error("导入组织架构失败!", e); + } finally { + deptDOS2.removeAll(deptDOS2); + deptDOS3.removeAll(deptDOS3); + deptDOS4.removeAll(deptDOS4); + } + } + } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/TransactionalService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/TransactionalService.java new file mode 100644 index 000000000..d908e4976 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/util/TransactionalService.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.system.util; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; + +import java.util.concurrent.Callable; + +/** + *

Title: TransactionalService

+ *

Description: 事务

+ *

Package: com.cmx.activity.service

+ *

Date: 2023年01月12日 21:10

+ * + * @author wanglong [atva725@qq.com] + * @version V1.0.0 + */ +@Component +@RequiredArgsConstructor +public class TransactionalService { + + private final TransactionDefinition transactionDefinition; + private final PlatformTransactionManager platformTransactionManager; + + public void run(Runnable runnable) { + final TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition); + try { + runnable.run(); + platformTransactionManager.commit(transaction); + } catch (Exception e) { + platformTransactionManager.rollback(transaction); + throw new RuntimeException(e); + } + } + + public R run(Callable callable) { + R rev; + final TransactionStatus transaction = platformTransactionManager.getTransaction(transactionDefinition); + try { + rev = callable.call(); + platformTransactionManager.commit(transaction); + } catch (Exception e) { + platformTransactionManager.rollback(transaction); + throw new RuntimeException(e); + } + return rev; + } +}