From 2056089ed9180dd91fc8e03c44fa037a0f168f1f Mon Sep 17 00:00:00 2001 From: xuyuxiang Date: Tue, 7 Mar 2023 17:08:06 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E6=9B=B4=E6=96=B0=E3=80=91=E5=AE=8C?= =?UTF-8?q?=E5=96=84=E5=AF=BC=E5=85=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../modular/org/service/SysOrgService.java | 14 ++- .../org/service/impl/SysOrgServiceImpl.java | 45 +++++++ .../position/service/SysPositionService.java | 10 +- .../service/impl/SysPositionServiceImpl.java | 21 +++- .../user/controller/SysUserController.java | 6 +- .../modular/user/service/SysUserService.java | 2 +- .../user/service/impl/SysUserServiceImpl.java | 114 ++++++++++++++++-- .../main/resources/userImportTemplate.xlsx | Bin 11019 -> 11200 bytes 8 files changed, 195 insertions(+), 17 deletions(-) diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/org/service/SysOrgService.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/org/service/SysOrgService.java index 235877b8..a6217572 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/org/service/SysOrgService.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/org/service/SysOrgService.java @@ -93,6 +93,14 @@ public interface SysOrgService extends IService { **/ List getCachedAllOrgList(); + /** + * 根据机构全名称获取机构id,有则返回,无则创建 + * + * @author xuyuxiang + * @date 2023/3/7 15:44 + **/ + String getOrgIdByOrgFullNameWithCreate(String orgFullName); + /** * 根据id获取父子数据列表 * @@ -123,7 +131,7 @@ public interface SysOrgService extends IService { * @author xuyuxiang * @date 2022/8/15 14:55 **/ - SysOrg getById(List originDataList, String id) ; + SysOrg getById(List originDataList, String id); /** * 根据id获取父数据 @@ -131,7 +139,7 @@ public interface SysOrgService extends IService { * @author xuyuxiang * @date 2022/8/15 14:55 **/ - SysOrg getParentById(List originDataList, String id) ; + SysOrg getParentById(List originDataList, String id); /** * 根据id获取子数据 @@ -139,7 +147,7 @@ public interface SysOrgService extends IService { * @author xuyuxiang * @date 2022/8/15 14:55 **/ - SysOrg getChildById(List originDataList, String id) ; + SysOrg getChildById(List originDataList, String id); /** * 获取组织树选择器 diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/org/service/impl/SysOrgServiceImpl.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/org/service/impl/SysOrgServiceImpl.java index 41e0471a..0bf6f836 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/org/service/impl/SysOrgServiceImpl.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/org/service/impl/SysOrgServiceImpl.java @@ -48,6 +48,7 @@ import vip.xiaonuo.sys.modular.user.entity.SysUser; import vip.xiaonuo.sys.modular.user.service.SysUserService; import javax.annotation.Resource; +import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; @@ -222,6 +223,50 @@ public class SysOrgServiceImpl extends ServiceImpl impleme return orgList; } + @Override + public String getOrgIdByOrgFullNameWithCreate(String orgFullName) { + List cachedAllOrgList = this.getCachedAllOrgList(); + List> treeList = TreeUtil.build(cachedAllOrgList.stream().map(sysOrg -> + new TreeNode<>(sysOrg.getId(), sysOrg.getParentId(), sysOrg.getName(), sysOrg.getSortCode())) + .collect(Collectors.toList()), "0"); + return findOrgIdByOrgName("0", StrUtil.split(orgFullName, StrUtil.DASHED).iterator(), cachedAllOrgList, treeList); + } + + public String findOrgIdByOrgName(String parentId, Iterator iterator, List cachedAllOrgList, List> treeList) { + String next = iterator.next(); + List> findList = treeList.stream().filter(tree -> tree.getName().equals(next)).collect(Collectors.toList()); + if(ObjectUtil.isNotEmpty(findList)) { + if(iterator.hasNext()) { + return findOrgIdByOrgName(findList.get(0).getId(), iterator, cachedAllOrgList, findList.get(0).getChildren()); + } else { + return findList.get(0).getId(); + } + } else { + //创建该机构 + SysOrg sysOrg = new SysOrg(); + sysOrg.setName(next); + sysOrg.setCode(RandomUtil.randomString(10)); + sysOrg.setParentId(parentId); + sysOrg.setCategory(parentId.equals("0")?SysOrgCategoryEnum.COMPANY.getValue():SysOrgCategoryEnum.DEPT.getValue()); + sysOrg.setSortCode(99); + this.save(sysOrg); + // 发布增加事件 + CommonDataChangeEventCenter.doAddWithData(SysDataTypeEnum.ORG.getValue(), JSONUtil.createArray().put(sysOrg)); + // 将该机构加入缓存 + cachedAllOrgList.add(sysOrg); + // 更新缓存 + commonCacheOperator.put(ORG_CACHE_ALL_KEY, cachedAllOrgList); + // 继续查找 + if(iterator.hasNext()) { + return findOrgIdByOrgName(sysOrg.getId(), iterator, cachedAllOrgList, CollectionUtil.newArrayList()); + } else { + return sysOrg.getId(); + } + } + } + + + /* ====组织部分所需要用到的选择器==== */ @Override diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/position/service/SysPositionService.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/position/service/SysPositionService.java index 3db8511e..815cd249 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/position/service/SysPositionService.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/position/service/SysPositionService.java @@ -82,7 +82,15 @@ public interface SysPositionService extends IService { * @author xuyuxiang * @date 2022/8/15 14:55 **/ - SysPosition getById(List originDataList, String id) ; + SysPosition getById(List originDataList, String id); + + /** + * 根据机构id和职位名称获取职位id,有则返回,无则创建 + * + * @author xuyuxiang + * @date 2022/8/15 14:55 + **/ + String getPositionIdByPositionNameWithCreate(String orgId, String positionName); /* ====职位部分所需要用到的选择器==== */ diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/position/service/impl/SysPositionServiceImpl.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/position/service/impl/SysPositionServiceImpl.java index 193ba901..84d6a801 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/position/service/impl/SysPositionServiceImpl.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/position/service/impl/SysPositionServiceImpl.java @@ -171,7 +171,26 @@ public class SysPositionServiceImpl extends ServiceImpl().eq(SysPosition::getOrgId, orgId).eq(SysPosition::getName, positionName)); + if(ObjectUtil.isNotEmpty(sysPosition)) { + return sysPosition.getId(); + } else { + sysPosition = new SysPosition(); + sysPosition.setOrgId(orgId); + sysPosition.setName(positionName); + sysPosition.setCode(RandomUtil.randomString(10)); + sysPosition.setCategory(SysPositionCategoryEnum.LOW.getValue()); + sysPosition.setSortCode(99); + this.save(sysPosition); + // 发布增加事件 + CommonDataChangeEventCenter.doAddWithData(SysDataTypeEnum.POSITION.getValue(), JSONUtil.createArray().put(sysPosition)); + return sysPosition.getId(); + } + } + /* ====职位部分所需要用到的选择器==== */ @Override diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/controller/SysUserController.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/controller/SysUserController.java index 6e3cb02a..125856bb 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/controller/SysUserController.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/controller/SysUserController.java @@ -13,6 +13,7 @@ package vip.xiaonuo.sys.modular.user.controller; import cn.hutool.core.lang.tree.Tree; +import cn.hutool.json.JSONObject; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import com.github.xiaoymin.knife4j.annotations.ApiSupport; @@ -283,9 +284,8 @@ public class SysUserController { @ApiOperation("用户导入") @CommonLog("用户导入") @PostMapping("/sys/user/import") - public CommonResult importUser(@RequestPart("file") @ApiParam(value="文件", required = true) MultipartFile file) { - sysUserService.importUser(file); - return CommonResult.ok(); + public CommonResult importUser(@RequestPart("file") @ApiParam(value="文件", required = true) MultipartFile file) { + return CommonResult.data(sysUserService.importUser(file)); } /** diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/service/SysUserService.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/service/SysUserService.java index 9473760c..fb191013 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/service/SysUserService.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/service/SysUserService.java @@ -354,7 +354,7 @@ public interface SysUserService extends IService { * @author xuyuxiang * @date 2022/8/8 13:16 **/ - void importUser(MultipartFile file); + JSONObject importUser(MultipartFile file); /** * 用户导出 diff --git a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/service/impl/SysUserServiceImpl.java b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/service/impl/SysUserServiceImpl.java index 1a597e48..85dd7259 100644 --- a/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/service/impl/SysUserServiceImpl.java +++ b/snowy-plugin/snowy-plugin-sys/src/main/java/vip/xiaonuo/sys/modular/user/service/impl/SysUserServiceImpl.java @@ -42,7 +42,6 @@ import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.metadata.data.WriteCellData; -import com.alibaba.excel.read.listener.PageReadListener; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.handler.context.CellWriteHandlerContext; import com.alibaba.excel.write.metadata.style.WriteCellStyle; @@ -980,21 +979,120 @@ public class SysUserServiceImpl extends ServiceImpl impl } } + @Transactional(rollbackFor = Exception.class) @Override - public void importUser(MultipartFile file) { + public JSONObject importUser(MultipartFile file) { try { - // TODO 导入 - EasyExcel.read("D://import.xlsx", SysUserImportParam.class, new PageReadListener(dataList -> { - for (SysUserImportParam sysUserImportParam : dataList) { - System.out.println(sysUserImportParam); + int successCount = 0; + int errorCount = 0; + JSONArray errorDetail = JSONUtil.createArray(); + List sysUserImportParamList = EasyExcel.read("D://import.xlsx") + .head(SysUserImportParam.class).sheet().headRowNumber(2).doReadSync(); + for (int i = 0; i < sysUserImportParamList.size(); i++) { + JSONObject jsonObject = this.doImport(sysUserImportParamList.get(i), i); + if(jsonObject.getBool("success")) { + successCount += 1; + } else{ + errorCount += 1; + errorDetail.add(jsonObject); } - })).sheet().headRowNumber(2).doRead(); + } + return JSONUtil.createObj() + .set("totalCount", sysUserImportParamList.size()) + .set("successCount", successCount) + .set("errorCount", errorCount) + .set("errorDetail", errorDetail); } catch (Exception e) { e.printStackTrace(); throw new CommonException("文件导入失败"); } } + /** + * 执行导入 + * + * @author xuyuxiang + * @date 2023/3/7 13:22 + **/ + public JSONObject doImport(SysUserImportParam sysUserImportParam, int i) { + String account = sysUserImportParam.getAccount(); + String name = sysUserImportParam.getName(); + String orgName = sysUserImportParam.getOrgName(); + String positionName = sysUserImportParam.getPositionName(); + if(ObjectUtil.hasEmpty(account, name, orgName, positionName)) { + return JSONUtil.createObj().set("index", i + 1).set("success", false).set("msg", "必填字段存在空值"); + } else { + try { + List cachedAllUserList = this.getCachedAllUserList(); + String orgId = sysOrgService.getOrgIdByOrgFullNameWithCreate(sysUserImportParam.getOrgName()); + String positionId = sysPositionService.getPositionIdByPositionNameWithCreate(orgId, sysUserImportParam.getPositionName()); + SysUser sysUser = this.getOne(new LambdaQueryWrapper().eq(SysUser::getAccount, account)); + boolean isAdd = false; + String existUserId = null; + if(ObjectUtil.isEmpty(sysUser)) { + sysUser = new SysUser(); + isAdd = true; + } else { + existUserId = sysUser.getId(); + } + String phone = sysUser.getPhone(); + String email = sysUser.getEmail(); + // 拷贝属性 + BeanUtil.copyProperties(sysUserImportParam, sysUser); + sysUser.setOrgId(orgId); + sysUser.setPositionId(positionId); + // 判断手机号是否跟系统现有的重复 + if(ObjectUtil.isNotEmpty(phone)) { + if(isAdd) { + boolean repeatPhone = cachedAllUserList.stream().anyMatch(tempSysUser -> ObjectUtil + .isNotEmpty(tempSysUser.getPhone()) && tempSysUser.getPhone().equals(phone)); + if(repeatPhone) { + sysUser.setPhone(null); + } + } else { + String finalExistUserId = existUserId; + boolean repeatPhone = cachedAllUserList.stream().anyMatch(tempSysUser -> ObjectUtil + .isNotEmpty(tempSysUser.getPhone()) && tempSysUser.getPhone() + .equals(phone) && !tempSysUser.getId().equals(finalExistUserId)); + if(repeatPhone) { + sysUser.setPhone(phone); + } + } + } + // 判断邮箱是否跟系统现有的重复 + if(ObjectUtil.isNotEmpty(email)) { + if(isAdd) { + boolean repeatPhone = cachedAllUserList.stream().anyMatch(tempSysUser -> ObjectUtil + .isNotEmpty(tempSysUser.getEmail()) && tempSysUser.getEmail().equals(email)); + if(repeatPhone) { + sysUser.setPhone(null); + } + } else { + String finalExistUserId = existUserId; + boolean repeatPhone = cachedAllUserList.stream().anyMatch(tempSysUser -> ObjectUtil + .isNotEmpty(tempSysUser.getEmail()) && tempSysUser.getEmail() + .equals(email) && !tempSysUser.getId().equals(finalExistUserId)); + if(repeatPhone) { + sysUser.setPhone(email); + } + } + } + this.saveOrUpdate(sysUser); + // 发布增加事件 + CommonDataChangeEventCenter.doAddWithData(SysDataTypeEnum.ORG.getValue(), JSONUtil.createArray().put(sysUser)); + // 将该用户加入缓存 + cachedAllUserList.add(sysUser); + // 更新缓存 + commonCacheOperator.put(USER_CACHE_ALL_KEY, cachedAllUserList); + // 返回成功 + return JSONUtil.createObj().set("success", true); + } catch (Exception e) { + e.printStackTrace(); + return JSONUtil.createObj().set("index", i + 1).set("success", false).set("msg", "数据导入异常"); + } + } + } + @Override public void exportUser(SysUserExportParam sysUserExportParam, HttpServletResponse response) throws IOException { File tempFile = null; @@ -1143,7 +1241,7 @@ public class SysUserServiceImpl extends ServiceImpl impl if(ObjectUtil.isNotEmpty(sysUser.getAvatar())) { avatarBase64 = sysUser.getAvatar(); } else { - avatarBase64 = CommonAvatarUtil.generateImg(sysUser.getAvatar()); + avatarBase64 = CommonAvatarUtil.generateImg(sysUser.getName()); } // 头像 ImageEntity imageEntity = new ImageEntity(ImgUtil.toBytes(ImgUtil.toImage(StrUtil diff --git a/snowy-plugin/snowy-plugin-sys/src/main/resources/userImportTemplate.xlsx b/snowy-plugin/snowy-plugin-sys/src/main/resources/userImportTemplate.xlsx index f5e5080f5ce430035c04eb2153c4f06b329d5366..b4a787e1388217ff4163012fde3e3e9a1f50d985 100644 GIT binary patch delta 4492 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 y9UIFA!E*BRCviM~X>^eUO5r+5Q&3^%jcUkQK delta 4341 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