Преглед на файлове

feature(机构增量同步功能开发): 机构增量同步功能开发

机构增量同步功能开发
mazq преди 3 години
родител
ревизия
579739fa44

+ 5 - 0
dcuc-auth-model/src/main/java/com/dragoninfo/dcuc/auth/sub/dto/OrgTreeNodeDTO.java

@@ -37,6 +37,11 @@ public class OrgTreeNodeDTO {
      */
     private String path;
 
+    /**
+     * 同级机构排序
+     */
+    private Integer sort;
+
     /**
      * 是否是父节点
      */

+ 1 - 1
dcuc-auth-service/src/main/java/com/dragoninfo/dcuc/auth/sub/business/impl/AuthOrgBusinessImpl.java

@@ -139,7 +139,7 @@ public class AuthOrgBusinessImpl implements IAuthOrgBusiness {
             verifyOrgInfo(list);
             //保存数据
             saveImport(list);
-            authOrgTreeService.reInitTrees();
+            authOrgTreeService.reInitTrees(false);
         } catch (IOException e) {
             return ResponseStatus.fail("文件格式不对");
         } catch (NumberFormatException e){

+ 234 - 9
dcuc-auth-service/src/main/java/com/dragoninfo/dcuc/auth/sub/business/impl/SubSyncBusinessImpl.java

@@ -650,7 +650,7 @@ public class SubSyncBusinessImpl implements ISubSyncBusiness {
 
                     watch.start("reInitTrees");
                     setPathAndUpId();
-                    orgTreeService.reInitTrees();
+                    orgTreeService.reInitTrees(false);
                     watch.stop();
 
                     log.info("orgSync total time:{}", watch.prettyPrint());
@@ -986,7 +986,233 @@ public class SubSyncBusinessImpl implements ISubSyncBusiness {
 
     @Override
     public ResponseStatus orgAddSync() {
-        return null;
+        SecurityUser currentUser = UserContextUtils.getCurrentUser();
+        String idcard = null;
+        if(null != currentUser) {
+            idcard = currentUser.getIdcard();
+        }
+        List<Header> headers = getHeads(idcard);
+        Runnable r = getOrgAddSyncTask(currentUser, headers);
+        Thread t = new Thread(r);
+        t.start();
+        return ResponseStatus.success();
+    }
+
+    private Runnable getOrgAddSyncTask(SecurityUser currentUser, List<Header> headers) {
+        return ()->{
+            RLock lock = redissonClient.getLock(AddSyncContance.ORG_SYNC_KEY);
+            try {
+                UserContextUtils.setCurrentUser(currentUser);
+
+                boolean tryLock = lock.tryLock(0, 5, TimeUnit.MINUTES);
+                if (!tryLock) {
+                    log.info("try lock fail");
+                    return ;
+                }
+
+                StopWatch watch = new StopWatch("orgAddSync");
+                watch.start("onOrgSyncStart");
+                //获取上次同步成功的时间
+                Date syncStartTime = getSyncStartTime(AddSyncContance.ADD_SYNC_TYPE_ORG);
+                if(null == syncStartTime) {
+                    log.info("orgAddSync syncStartTime is null");
+                    return ;
+                }
+                //删除增量同步数据
+                deleteOrgAddInfo();
+                watch.stop();
+
+                //在同步前记录时间
+                //避免同步时用户中心更新的数据未被同步过来
+                //被同步的会在下次同步时再更新一次
+                Date updateTime = new Date();
+
+                watch.start("getOrgInfoAddFromRemote");
+                Integer total = getOrgInfoAddFromRemote(subSyncConfig.getPageSize(), syncStartTime, headers);
+                watch.stop();
+
+                if(total == null) {
+                    log.info("getOrgInfoAddFromRemote total is :{}", total);
+                    updateAddSyncRecord(AddSyncContance.ADD_SYNC_TYPE_ORG, false, updateTime, BooleanEnum.FALSE.value);
+                    return ;
+                }
+
+                if(total == 0) {
+                    updateAddSyncRecord(AddSyncContance.ADD_SYNC_TYPE_ORG, true, updateTime, BooleanEnum.TRUE.value);
+                    return ;
+                }
+
+                watch.start("orgInfoAddSync");
+                orgInfoAddSync();
+                watch.stop();
+
+                watch.start("Set path And ReInitTrees");
+                List<String> updateOrgCodes = setAddPathAndUpId();
+                orgTreeService.updateTreeByCode(updateOrgCodes, false);
+                watch.stop();
+
+                watch.start("onOrgSyncEnd");
+                //修改人员、人员机构关系中的数据
+                updateOrgInfoInUser();
+                //记录同步成功时间
+                updateAddSyncRecord(AddSyncContance.ADD_SYNC_TYPE_ORG, true, updateTime, BooleanEnum.TRUE.value);
+                watch.stop();
+                log.info("orgAddSync total time:{}", watch.prettyPrint());
+            } catch (Exception e) {
+                log.error("orgAddSync error.", e);
+                //记录同步失败时间
+                updateAddSyncRecord(AddSyncContance.ADD_SYNC_TYPE_ORG, false, null, BooleanEnum.FALSE.value);
+            }finally {
+                lock.unlock();
+            }
+        };
+    }
+
+    private List<String> setAddPathAndUpId() {
+        List<String> addOrgCodes = orgAddOriginalService.getCodes();
+        //获取新增或修改的机构
+        List<AuthOrgInfo> addOrgInfos = authOrgInfoService.getOrgByCodes(addOrgCodes);
+        List<String> upOrgInfoCodes = addOrgInfos.stream()
+                .map(AuthOrgInfo::getUpGovCode)
+                .distinct()
+                .collect(Collectors.toList());
+        //查询增量同步机构在已存在机构中的上级机构
+        List<String> collect = upOrgInfoCodes.stream().filter(e -> !addOrgCodes.contains(e)).collect(Collectors.toList());
+        List<AuthOrgInfo> upOrgInfos = authOrgInfoService.getOrgByCodes(collect);
+        //设置path和upGovId
+        Map<String, AuthOrgInfo> upOrgMap = upOrgInfos.stream()
+                .collect(Collectors.toMap(AuthOrgInfo::getCode, e -> e, (old, last) -> last));
+        Map<String, List<AuthOrgInfo>> addOrgGroup = addOrgInfos.stream()
+                .collect(Collectors.groupingBy(AuthOrgInfo::getUpGovCode));
+        Set<String> upOrgCodes = upOrgMap.keySet();
+        Deque<String> deque = new LinkedList<>(upOrgCodes);
+        while (!deque.isEmpty()) {
+            String upOrgCode = deque.pollFirst();
+            AuthOrgInfo upOrgInfo = upOrgMap.get(upOrgCode);
+            List<AuthOrgInfo> orgInfos = addOrgGroup.get(upOrgCode);
+            if(CollectionUtils.isNotEmpty(orgInfos)) {
+                String path = upOrgInfo.getPath();
+                if (null == path) {
+                    path = "";
+                } else {
+                    path += "-";
+                }
+                String finalPath = path;
+                orgInfos.forEach(e->{
+                    e.setUpGovId(upOrgInfo.getId());
+                    e.setUpGovName(upOrgInfo.getFullName());
+                    e.setPath(finalPath + upOrgInfo.getId());
+                    upOrgMap.put(e.getCode(), e);
+                    deque.addLast(e.getCode());
+                });
+            }
+        }
+        //批量更新机构path和upGovId、upGovName
+        authOrgInfoService.batchUpdatePathUpGovId(addOrgInfos);
+
+        //过滤被删除的机构code
+        Set<String> addOrgCodeSet = addOrgInfos.stream().map(AuthOrgInfo::getCode).collect(Collectors.toSet());
+        List<String> delOrgCodes = addOrgCodes.stream().filter(e -> !addOrgCodeSet.contains(e)).distinct().collect(Collectors.toList());
+        //返回所有需要修改树节点信息的机构code
+        //这里只添加能串树节点的机构code和删除的机构code
+        List<String> list = new ArrayList<>(upOrgCodes);
+        list.addAll(delOrgCodes);
+        return list;
+    }
+
+    private void orgInfoAddSync() {
+        int currentPage = 0;
+        Searchable searchable = Searchable.newSearchable();
+        searchable.addSort(Sort.Direction.ASC, "orgUpdateTime");
+        do {
+            searchable.setPage(currentPage, 1000);
+            Page<AuthOrgAddOriginal> page = orgAddOriginalService.pageSearch(searchable);
+            if(page.isEmpty()) {
+                return;
+            }
+            List<AuthOrgAddOriginal> content = page.getContent();
+            List<AuthOrgInfo> orgInfoList = content.stream().map(item -> {
+                String orgDeleted = item.getOrgDeleted();
+                if (BooleanEnum.FALSE.value.equals(orgDeleted)) {
+                    String jsonData = item.getJsonData();
+                    return JSON.parseObject(jsonData, AuthOrgInfo.class);
+                } else {
+                    String orgCode = item.getCode();
+                    AuthOrgInfo authOrgInfo = new AuthOrgInfo();
+                    authOrgInfo.setDeleted(BooleanEnum.TRUE.value);
+                    authOrgInfo.setCode(orgCode);
+                    return authOrgInfo;
+                }
+            }).collect(Collectors.toList());
+            authOrgInfoService.batchSaveByOrgCode(orgInfoList);
+            ++currentPage;
+        }while (true);
+
+    }
+
+    private Integer getOrgInfoAddFromRemote(Integer pageSize, Date syncStartTime, List<Header> headers) {
+        int currentPage = 1;
+        Integer totalPage = null;
+        Integer total = null;
+        ApiSearchReq apiSearchReq = new ApiSearchReq();
+        //设置分页参数
+        ApiPageReq pageReq = new ApiPageReq();
+        pageReq.setSize(pageSize);
+        //设置同步时间
+        SearchParam searchParam = new SearchParam();
+        Map<String, SearchParam> filters = new HashMap<>();
+        filters.put("updateTime", searchParam);
+        searchParam.setOperator(SearchOperator.gt.name());
+        searchParam.setValue(syncStartTime);
+        apiSearchReq.setFilters(filters);
+        apiSearchReq.setPage(pageReq);
+        String url = Joiner.on("").join(authConfig.getUserCenterUrl(), UserApiConstance.ORG_SYNC_API);
+        logger.info("-------------getOrgInfoAddFromRemote START-----------");
+        do {
+            pageReq.setFrom(currentPage);
+            String result = HttpUtil.postJSON(url, JSON.toJSONString(apiSearchReq), headers,null);
+            if(StringUtils.isBlank(result)) {
+                logger.info("result is null");
+                break;
+            }
+            //从结果解析用户数据保存到原始数据表
+            ApiResult apiResult = JSON.parseObject(result, ApiResult.class);
+            if(!ResultEnum.SUCCESS.getKey().equals(apiResult.getStatusCode())) {
+                logger.info("getOrgInfoFromRemote ERROR:{}, PAGE NO:{}",apiResult.getMessage(), currentPage);
+                break;
+            }
+            JSONObject obj = (JSONObject) apiResult.getResult();
+            ApiResultPage<JSONObject> page = JSON.parseObject(obj.toJSONString(), new TypeReference<ApiResultPage<JSONObject>>(){});
+            //从结果解析总页数
+            totalPage = getTotalPage(pageSize, totalPage, page.getTotal().intValue());
+            //解析请求结果
+            saveAddOrgOri(page);
+        } while (++currentPage <= totalPage);
+        logger.info("-------------getOrgInfoAddFromRemote END-----------");
+        return total;
+    }
+
+    private void saveAddOrgOri(ApiResultPage<JSONObject> page) {
+        List<JSONObject> content = page.getContent();
+        List<AuthOrgAddOriginal> collect = content.stream().map(json -> {
+            AuthOrgAddOriginal original = new AuthOrgAddOriginal();
+            String orgCode = json.getString("code");
+            String orgDeleted = json.getString("deleted");
+            Date updateTime = json.getDate("updateTime");
+            original.setCode(orgCode);
+            original.setOrgUpdateTime(updateTime);
+            original.setOrgDeleted(orgDeleted);
+            if (BooleanEnum.FALSE.value.equals(orgDeleted)) {
+                String jsonString = json.toJSONString();
+                original.setJsonData(jsonString);
+            }
+            return original;
+        }).collect(Collectors.toList());
+        orgAddOriginalService.batchSave(collect);
+    }
+
+    private void deleteOrgAddInfo() {
+        orgAddOriginalService.deleteAll();
     }
 
     /**
@@ -1066,19 +1292,18 @@ public class SubSyncBusinessImpl implements ISubSyncBusiness {
 
     private void saveOrgOri(Date now, ApiResultPage<JSONObject> apiResultPage) {
         List<JSONObject> content = apiResultPage.getContent();
-        List<AuthOrgOriginalData> originals = new ArrayList<>();
-        for (JSONObject jsonObject : content) {
-            String jsonString = jsonObject.toJSONString();
+        List<AuthOrgOriginalData> collect = content.stream().map(json -> {
+            String jsonString = json.toJSONString();
             AuthOrgOriginalData oriOrgInfo = new AuthOrgOriginalData();
-            oriOrgInfo.setOrgCode(jsonObject.getString("orgCode"));
+            oriOrgInfo.setOrgCode(json.getString("orgCode"));
             oriOrgInfo.setDeleted(BooleanEnum.FALSE.value);
             oriOrgInfo.setSync(BooleanEnum.FALSE.value);
             oriOrgInfo.setSyncDate(now);
             oriOrgInfo.setJsonData(jsonString);
-            originals.add(oriOrgInfo);
-        }
+            return oriOrgInfo;
+        }).collect(Collectors.toList());
         //过程数据保存
-        authOrgOriginalDataService.batchSaveByCode(originals);
+        authOrgOriginalDataService.batchSaveByCode(collect);
     }
 
     /**

+ 6 - 0
dcuc-auth-service/src/main/java/com/dragoninfo/dcuc/auth/sub/entity/AuthOrgInfo.java

@@ -79,6 +79,12 @@ public class AuthOrgInfo implements LogicDeleteable, IdEntity<String> {
     @Column(name = "ORG_TYPE")
     private String orgType;
 
+    /**
+     * 同级机构排序字段
+     */
+    @Column(name = "SORT")
+    private Integer sort;
+
     /**
      * 机构层级
      */

+ 24 - 0
dcuc-auth-service/src/main/java/com/dragoninfo/dcuc/auth/sub/repo/AuthOrgAddOriRepo.java

@@ -0,0 +1,24 @@
+package com.dragoninfo.dcuc.auth.sub.repo;
+
+import com.dragoninfo.dcuc.auth.sub.entity.AuthOrgAddOriginal;
+import com.dragonsoft.duceap.core.persistent.repository.BaseRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.List;
+
+/**
+ * @author mazq
+ * @date 2021/12/17
+ */
+@Repository
+public interface AuthOrgAddOriRepo extends BaseRepository<AuthOrgAddOriginal, String> {
+
+    /**
+     * 获取增量同同步的机构code
+     * @return
+     */
+    @Query("SELECT DISTINCT code FROM AuthOrgAddOriginal")
+    List<String> getCodes();
+
+}

+ 24 - 0
dcuc-auth-service/src/main/java/com/dragoninfo/dcuc/auth/sub/repo/AuthOrgInfoRepository.java

@@ -51,4 +51,28 @@ public interface AuthOrgInfoRepository extends BaseRepository<AuthOrgInfo, Strin
     @Query(value = "SELECT new com.dragoninfo.dcuc.auth.sub.entity.AuthOrgInfo(id, code) from AuthOrgInfo where code in :orgCodes")
     List<AuthOrgInfo> convertCodesToIds(@Param("orgCodes") List<String> orgCodes);
 
+    /**
+     * 根据code删除机构
+     * @param code
+     */
+    @Modifying
+    @Query("DELETE FROM AuthOrgInfo WHERE code = :code")
+    void deleteByCode(@Param("code") String code);
+
+    /**
+     * 获取机构最大的排序号
+     * @return
+     */
+    @Query("SELECT max(sort) from AuthOrgInfo")
+    Integer getMaxSort();
+
+    /**
+     * 更新机构path和upGovId、upGovName
+     * @param authOrgInfo
+     */
+    @Modifying
+    @Query("UPDATE AuthOrgInfo SET upGovId = :#{#authOrgInfo.upGovId}, path = :#{#authOrgInfo.path}, upGovName = :#{#authOrgInfo.upGovName}" +
+            " WHERE id = :#{#authOrgInfo.id}")
+    void updatePathUpGovId(@Param("authOrgInfo") AuthOrgInfo authOrgInfo);
+
 }

+ 13 - 0
dcuc-auth-service/src/main/java/com/dragoninfo/dcuc/auth/sub/service/IAuthOrgInfoService.java

@@ -124,4 +124,17 @@ public interface IAuthOrgInfoService{
      * @return
      */
     List<AuthOrgInfo> convertCodesToIds(List<String> orgCodes);
+
+    /**
+     * 批量更新机构path和upGovId、upGovName
+     * @param orgInfos
+     */
+    void batchUpdatePathUpGovId(List<AuthOrgInfo> orgInfos);
+
+    /**
+     * 根据机构code批量获取子机构id
+     * @param orgCodes
+     * @return
+     */
+    List<AuthOrgInfo> getChildByCodes(List<String> orgCodes);
 }

+ 11 - 2
dcuc-auth-service/src/main/java/com/dragoninfo/dcuc/auth/sub/service/IAuthOrgTreeService.java

@@ -1,8 +1,10 @@
 package com.dragoninfo.dcuc.auth.sub.service;
 
 import com.dragoninfo.dcuc.auth.sub.dto.OrgTreeNodeDTO;
+import com.dragoninfo.dcuc.auth.sub.entity.AuthOrgInfo;
 
 import java.util.Collection;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
 
@@ -17,7 +19,7 @@ public interface IAuthOrgTreeService {
      * 机构数据懒加载获取子节点
      *
      * @param id 本级节点id
-     * @param level 需要的子节点深度(从本级节点算起)
+     * @param level 需要的子节点深度(从本级节点算起, 1表示第一层的子节点, 2表示子节点的所有子节点
      * @return 子级机构数据
      */
     List<OrgTreeNodeDTO> getChildById(String id, Integer level);
@@ -59,7 +61,14 @@ public interface IAuthOrgTreeService {
 
     /**
      * 重新构建机构树
+     * @param async 是否同步
      */
+    void reInitTrees(boolean async);
 
-    void reInitTrees();
+    /**
+     * 部分更新树节点
+     * @param updateOrgCodes
+     * @param async
+     */
+    void updateTreeByCode(List<String> updateOrgCodes, boolean async);
 }

+ 48 - 0
dcuc-auth-service/src/main/java/com/dragoninfo/dcuc/auth/sub/service/impl/AuthOrgAddOriginalServiceImpl.java

@@ -0,0 +1,48 @@
+package com.dragoninfo.dcuc.auth.sub.service.impl;
+
+import com.dragoninfo.dcuc.auth.sub.entity.AuthOrgAddOriginal;
+import com.dragoninfo.dcuc.auth.sub.repo.AuthOrgAddOriRepo;
+import com.dragoninfo.dcuc.auth.sub.service.IAuthOrgAddOriginalService;
+import com.dragonsoft.duceap.commons.util.collections.CollectionUtils;
+import com.dragonsoft.duceap.core.search.Searchable;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * 机构增量同步业务类
+ * @author mazq
+ * @date 2021-12-27
+ */
+@Service
+public class AuthOrgAddOriginalServiceImpl implements IAuthOrgAddOriginalService {
+
+    @Autowired
+    private AuthOrgAddOriRepo repository;
+
+    @Override
+    public void batchSave(List<AuthOrgAddOriginal> originals) {
+        if(CollectionUtils.isEmpty(originals)) {
+            return ;
+        }
+        repository.saveAll(originals);
+    }
+
+    @Override
+    public Page<AuthOrgAddOriginal> pageSearch(Searchable searchable) {
+        return repository.paging(searchable);
+    }
+
+    @Override
+    public void deleteAll() {
+        repository.deleteAll();
+    }
+
+    @Override
+    public List<String> getCodes() {
+        return repository.getCodes();
+    }
+
+}

+ 39 - 5
dcuc-auth-service/src/main/java/com/dragoninfo/dcuc/auth/sub/service/impl/AuthOrgInfoService.java

@@ -21,11 +21,9 @@ import org.springframework.data.jpa.domain.Specification;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import javax.persistence.criteria.CriteriaQuery;
 import javax.persistence.criteria.Predicate;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -108,21 +106,34 @@ public class AuthOrgInfoService implements IAuthOrgInfoService {
         List<AuthOrgInfo> existList = getOrgByCodes(codes);
         Map<String, AuthOrgInfo> existMap = existList.stream()
                 .collect(Collectors.toMap(AuthOrgInfo::getCode, e -> e, (old, last) -> last));
+        Integer maxSort = getMaxSort();
         for (AuthOrgInfo authOrgInfo : orgInfoList) {
             String code = authOrgInfo.getCode();
             AuthOrgInfo exist = existMap.get(code);
+            //删除机构信息
+            if(BooleanEnum.TRUE.value.equals(authOrgInfo.getDeleted())) {
+                orgInfoRepository.deleteByCode(code);
+                continue ;
+            }
+            //新增或修改机构
             if(null == exist) {
                 authOrgInfo.setDeleted(BooleanEnum.FALSE.value);
+                authOrgInfo.setSort(++maxSort);
                 authOrgInfo.setCreateTime(new Date());
                 orgInfoRepository.save(authOrgInfo);
             } else {
                 authOrgInfo.setUpdateTime(new Date());
-                BeanUtils.copyProperties(authOrgInfo, exist, "id","createTime","createUser");
+                BeanUtils.copyProperties(authOrgInfo, exist, "id","createTime","createUser", "sort");
                 orgInfoRepository.update(exist);
             }
         }
     }
 
+    private Integer getMaxSort() {
+        return Optional.ofNullable(orgInfoRepository.getMaxSort())
+                .orElse(0);
+    }
+
     @Override
     public List<AuthOrgInfo> batchSave(List<AuthOrgInfo> collect) {
         return orgInfoRepository.saveAll(collect);
@@ -194,4 +205,27 @@ public class AuthOrgInfoService implements IAuthOrgInfoService {
         return orgInfoRepository.convertCodesToIds(orgCodes);
     }
 
+    @Override
+    public void batchUpdatePathUpGovId(List<AuthOrgInfo> orgInfos) {
+        if(CollectionUtils.isEmpty(orgInfos)) {
+            return ;
+        }
+        for (AuthOrgInfo authOrgInfo : orgInfos) {
+            orgInfoRepository.updatePathUpGovId(authOrgInfo);
+        }
+    }
+
+    @Override
+    public List<AuthOrgInfo> getChildByCodes(List<String> orgCodes) {
+        if(CollectionUtils.isEmpty(orgCodes)) {
+            return Collections.emptyList();
+        }
+        Specification<AuthOrgInfo> specification = (root, query, cb)->{
+            CriteriaQuery<?> multiSelect = query.multiselect(root.get("id"), root.get("upGovId"));
+            Predicate orgCode = root.get("upGovCode").in(orgCodes);
+            return multiSelect.where(orgCode).getRestriction();
+
+        };
+        return orgInfoRepository.findAll(specification);
+    }
 }

+ 324 - 179
dcuc-auth-service/src/main/java/com/dragoninfo/dcuc/auth/sub/service/impl/AuthOrgTreeServiceImpl.java

@@ -13,6 +13,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import javax.annotation.PostConstruct;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 import java.util.stream.Collectors;
@@ -32,7 +33,6 @@ public class AuthOrgTreeServiceImpl implements IAuthOrgTreeService {
 
     /**
      * holder不能对外暴露
-     * 通过holder获取的树节点不允许调用set方法修改树节点的属性值
      * 接口给出去的对象使用拷贝后的对象,不要破坏树节点的内部结构
      * 在使用holder时尽量只获取一次,然后都使用获取到的引用。可以避免在一个方法中多次获取树结构,而树结构又被重新构建导致数据不一致的情况
      */
@@ -47,175 +47,119 @@ public class AuthOrgTreeServiceImpl implements IAuthOrgTreeService {
 
     @Override
     public List<OrgTreeNodeDTO> getChildById(String id, Integer level) {
-        if(StringUtils.isBlank(id)) {
-            return deepCopy(holder.getTopNodesList());
-        } else {
-            Map<String, OrgTreeNodeDTO> treeNodeMap = holder.getTreeNodeMap();
-            OrgTreeNodeDTO dto = treeNodeMap.get(id);
-            if(null == dto) {
-                return new ArrayList<>();
-            }
-            Set<String> childIds = getAllChildIds(id, treeNodeMap, level);
-            if(CollectionUtils.isEmpty(childIds)) {
-                return new ArrayList<>();
-            }
-            List<OrgTreeNodeDTO> collect = childIds
-                    .stream()
-                    .map(treeNodeMap::get)
-                    .collect(Collectors.toList());
-            return deepCopy(collect);
-        }
+        return holder.getChildById(id, level);
     }
 
     @Override
     public List<OrgTreeNodeDTO> getSelfAndChildByIds(List<String> orgIds) {
-        if(CollectionUtils.isEmpty(orgIds)) {
-            return new ArrayList<>();
-        }
-        Map<String, OrgTreeNodeDTO> treeNodeMap = holder.getTreeNodeMap();
-        List<OrgTreeNodeDTO> selfNodes = orgIds.stream().map(treeNodeMap::get).collect(Collectors.toList());
-        List<OrgTreeNodeDTO> collect = selfNodes
-                .stream()
-                .map(OrgTreeNodeDTO::getChildIds)
-                .flatMap(List::stream)
-                .distinct()
-                .map(treeNodeMap::get)
-                .collect(Collectors.toList());
-        selfNodes.addAll(collect);
-        return deepCopy(selfNodes);
+        return holder.getSelfAndChildByIds(orgIds);
     }
 
     @Override
     public List<OrgTreeNodeDTO> getAllChildNodes(String orgId) {
-        if(StringUtils.isBlank(orgId)) {
-            return new ArrayList<>();
-        }
-        Map<String, OrgTreeNodeDTO> treeNodeMap = holder.getTreeNodeMap();
-        Set<String> allChildIds = getAllChildIds(orgId, treeNodeMap, null);
-        return getOrgTreeNodeByIds(allChildIds);
+        return holder.getAllChildNodes(orgId);
     }
 
     @Override
     public OrgTreeNodeDTO getOrgTreeNode(String id) {
-        if(StringUtils.isBlank(id)) {
-            return null;
-        }
-        Map<String, OrgTreeNodeDTO> treeNodeMap = holder.getTreeNodeMap();
-        OrgTreeNodeDTO orgTreeNodeDTO = treeNodeMap.get(id);
-        if(null == orgTreeNodeDTO) {
-            return null;
-        }
-        return deepCopy(orgTreeNodeDTO);
+        return holder.getOrgTreeNode(id);
     }
 
     @Override
     public List<OrgTreeNodeDTO> getOrgTreeNodeByIds(Collection<String> ids) {
-        if(CollectionUtils.isEmpty(ids)) {
-            return new ArrayList<>();
-        }
-        Map<String, OrgTreeNodeDTO> treeNodeMap = holder.getTreeNodeMap();
-        List<OrgTreeNodeDTO> collect = ids
-                .stream()
-                .map(treeNodeMap::get)
-                .collect(Collectors.toList());
-
-        return deepCopy(collect);
+        return holder.getOrgTreeNodeByIds(ids);
     }
 
     @Override
     public Set<String> getAllMtOrgIds(String mtIds) {
-        Set<String> resultSet = new HashSet<>();
-        String[] mtIdStrs = mtIds.split(",");
-        Map<String, OrgTreeNodeDTO> treeNodeMap = holder.getTreeNodeMap();
-        for (String mtIdStr : mtIdStrs) {
-            if (mtIdStr.contains(":")) {
-                String[] split = mtIdStr.split(":");
-                String orgId = split[0];
-                resultSet.add(orgId);
-                int rang = Integer.parseInt(split[1]);
-                //1 半选 2 全选
-                if (rang == 2) {
-                    Set<String> allChildIds = getAllChildIds(orgId, treeNodeMap, null);
-                    resultSet.addAll(allChildIds);
-                }
-            } else {
-                resultSet.add(mtIdStr);
-            }
-        }
-        return resultSet;
+        return holder.getAllMtOrgIds(mtIds);
     }
 
-    /**
-     * 获取机构下的子机构节点id
-     * @param orgId 机构id
-     * @param treeNodeMap 机构树map
-     * @param level 深度(从该机构节点算起)。需要所有子节点id,传入null
-     * @return
-     */
-    private Set<String> getAllChildIds(String orgId, Map<String, OrgTreeNodeDTO> treeNodeMap, Integer level) {
-        Set<String> childIds = new HashSet<>();
-        OrgTreeNodeDTO orgTreeNodeDTO = treeNodeMap.get(orgId);
-        if(null == orgTreeNodeDTO) {
-            return childIds;
-        }
-        if(CollectionUtils.isEmpty(orgTreeNodeDTO.getChildIds())) {
-            return childIds;
-        }
-        //初始化双端队列
-        Deque<OrgTreeNodeDTO> deque = new LinkedList<>();
-        deque.add(orgTreeNodeDTO);
-
-        //初始化深度计算指针
-        int currentLevelIndex = deque.size();
-        int currentNodeIndex = 0;
-
-        while (!deque.isEmpty()) {
-            //往双端队列中添加下一层的子节点
-            OrgTreeNodeDTO node = deque.pollFirst();
-            List<String> nodeChildIds = node.getChildIds();
-            if(CollectionUtils.isNotEmpty(nodeChildIds)) {
-                childIds.addAll(nodeChildIds);
-                List<OrgTreeNodeDTO> childNodes = nodeChildIds
-                        .stream()
-                        .map(treeNodeMap::get)
-                        .collect(Collectors.toList());
-                deque.addAll(childNodes);
-            }
-            //判断是否达到深度
-            if(null != level && ++currentNodeIndex == currentLevelIndex) {
-                --level;
-                if(level == 0) {
-                    break;
-                }
-                //重置index
-                currentNodeIndex = 0;
-                currentLevelIndex = deque.size();
-            }
+    @Override
+    public void reInitTrees(boolean async) {
+        Runnable runnable = ()-> holder.init();
+        if(async) {
+            Thread thread = new Thread(runnable);
+            thread.start();
+        } else {
+            runnable.run();
         }
-        return childIds;
     }
 
     @Override
-    public void reInitTrees() {
-        holder.init();
+    public void updateTreeByCode(List<String> updateOrgCodes, boolean async) {
+        Runnable runnable = ()-> updateTask(updateOrgCodes);
+        if(async) {
+            Thread thread = new Thread(runnable);
+            thread.start();
+        } else {
+            runnable.run();
+        }
     }
 
-    private OrgTreeNodeDTO deepCopy(OrgTreeNodeDTO orgTreeNodeDTO) {
-        OrgTreeNodeDTO dto = new OrgTreeNodeDTO();
-        BeanUtils.copyProperties(orgTreeNodeDTO, dto);
-        dto.setChildIds(new ArrayList<>(orgTreeNodeDTO.getChildIds()));
-        return dto;
-    }
+    private void updateTask(List<String> updateOrgCodes) {
+        List<AuthOrgInfo> updateOrgInfos = authOrgInfoService.getOrgByCodes(updateOrgCodes);
+        Set<String> existOrgCodes = updateOrgInfos.stream().map(AuthOrgInfo::getCode).collect(Collectors.toSet());
+        //构建需要更新的树节点
+        List<OrgTreeNodeDTO> treeNods = getTreeNods(updateOrgInfos);
 
-    private List<OrgTreeNodeDTO> deepCopy(List<OrgTreeNodeDTO> list) {
-        return Optional.ofNullable(list)
-                .orElse(new ArrayList<>())
+        List<AuthOrgInfo> childInfos = authOrgInfoService.getChildByCodes(updateOrgCodes);
+        Map<String, List<AuthOrgInfo>> childIdGroup = childInfos
                 .stream()
-                .map(this::deepCopy).collect(Collectors.toList());
+                .collect(Collectors.groupingBy(AuthOrgInfo::getUpGovId));
+
+        treeNods.forEach(e->{
+            List<AuthOrgInfo> list = childIdGroup.get(e.getId());
+            List<String> collect = list.stream()
+                    .sorted(Comparator.comparing(AuthOrgInfo::getSort,
+                            Comparator.nullsLast(Integer::compareTo)))
+                    .map(AuthOrgInfo::getId).collect(Collectors.toList());
+            if(CollectionUtils.isEmpty(collect)) {
+                e.setIsParent(false);
+                e.setChildIds(Collections.emptyList());
+            } else {
+                e.setIsParent(true);
+                e.setChildIds(collect);
+            }
+        });
+        //过滤删除的机构
+        List<String> delCodes = updateOrgCodes.stream()
+                .filter(e -> !existOrgCodes.contains(e))
+                .collect(Collectors.toList());
+        holder.updateTreeNodes(treeNods, delCodes);
+    }
+
+    /**
+     * 将机构数据转为树节点对象
+     * @param orgInfos 机构信息
+     * @return
+     */
+    private List<OrgTreeNodeDTO> getTreeNods(List<AuthOrgInfo> orgInfos) {
+        return orgInfos.stream().map(e -> {
+            OrgTreeNodeDTO nodeDTO = new OrgTreeNodeDTO();
+            nodeDTO.setId(e.getId());
+            nodeDTO.setName(e.getFullName());
+            nodeDTO.setCode(e.getCode());
+            nodeDTO.setPid(e.getUpGovId());
+            nodeDTO.setPath(e.getPath());
+            nodeDTO.setOrgLevel(e.getOrgLevel());
+            nodeDTO.setOrgType(e.getOrgType());
+            nodeDTO.setOrgKind(e.getOrgKind());
+            nodeDTO.setOrgRank(e.getOrgRank());
+            nodeDTO.setUnitClass(e.getUnitClass());
+            nodeDTO.setSort(e.getSort());
+            return nodeDTO;
+        }).collect(Collectors.toList());
     }
 
     private class AuthOrgTreeHolder {
 
+        /**
+         * 全量构建树标识
+         * true 正在构建中 false 未在构建中
+         */
+        private volatile AtomicBoolean initIng = new AtomicBoolean(false);
+
         /**
          * 深度为1的树节点map
          * childIds字段只存子节点id
@@ -223,6 +167,13 @@ public class AuthOrgTreeServiceImpl implements IAuthOrgTreeService {
          */
         private volatile Map<String, OrgTreeNodeDTO> treeNodeMap;
 
+        /**
+         * 树节点id和code对应值Map
+         * 便于根据code值操作树节点
+         * key:orgCode value:orgId
+         */
+        private volatile Map<String, OrgTreeNodeDTO> codeIdMap;
+
         /**
          * 所有顶级节点
          */
@@ -234,6 +185,20 @@ public class AuthOrgTreeServiceImpl implements IAuthOrgTreeService {
          */
         private ReadWriteLock lock = new ReentrantReadWriteLock();
 
+        private OrgTreeNodeDTO deepCopy(OrgTreeNodeDTO orgTreeNodeDTO) {
+            OrgTreeNodeDTO dto = new OrgTreeNodeDTO();
+            BeanUtils.copyProperties(orgTreeNodeDTO, dto);
+            dto.setChildIds(new ArrayList<>(orgTreeNodeDTO.getChildIds()));
+            return dto;
+        }
+
+        private List<OrgTreeNodeDTO> deepCopy(List<OrgTreeNodeDTO> list) {
+            return Optional.ofNullable(list)
+                    .orElse(new ArrayList<>())
+                    .stream()
+                    .map(this::deepCopy).collect(Collectors.toList());
+        }
+
         /**
          * 构建树结构
          * @param treeNodeMap
@@ -259,28 +224,6 @@ public class AuthOrgTreeServiceImpl implements IAuthOrgTreeService {
 
         }
 
-        /**
-         * 将机构数据转为树节点对象
-         * @param orgInfos 机构信息
-         * @return
-         */
-        private List<OrgTreeNodeDTO> getTreeNods(List<AuthOrgInfo> orgInfos) {
-            return orgInfos.stream().map(e -> {
-                OrgTreeNodeDTO nodeDTO = new OrgTreeNodeDTO();
-                nodeDTO.setId(e.getId());
-                nodeDTO.setName(e.getFullName());
-                nodeDTO.setCode(e.getCode());
-                nodeDTO.setPid(e.getUpGovId());
-                nodeDTO.setPath(e.getPath());
-                nodeDTO.setOrgLevel(e.getOrgLevel());
-                nodeDTO.setOrgType(e.getOrgType());
-                nodeDTO.setOrgKind(e.getOrgKind());
-                nodeDTO.setOrgRank(e.getOrgRank());
-                nodeDTO.setUnitClass(e.getUnitClass());
-                return nodeDTO;
-            }).collect(Collectors.toList());
-        }
-
         /**
          * 将树节点组装成深度为1的树结构
          * 子节点只保留id集合
@@ -301,11 +244,13 @@ public class AuthOrgTreeServiceImpl implements IAuthOrgTreeService {
                 List<OrgTreeNodeDTO> childNodes = upIdGroupMap.get(headNode.getId());
                 if(CollectionUtils.isEmpty(childNodes)) {
                     headNode.setIsParent(false);
-                    headNode.setChildIds(ListUtils.EMPTY_LIST);
+                    headNode.setChildIds(Collections.emptyList());
                 } else {
                     headNode.setIsParent(true);
                     List<String> childIds = childNodes
                             .stream()
+                            .sorted(Comparator.comparing(OrgTreeNodeDTO::getSort,
+                                    Comparator.nullsLast(Integer::compareTo)))
                             .map(OrgTreeNodeDTO::getId)
                             .distinct()
                             .collect(Collectors.toList());
@@ -316,46 +261,246 @@ public class AuthOrgTreeServiceImpl implements IAuthOrgTreeService {
             }
         }
 
-        Map<String, OrgTreeNodeDTO> getTreeNodeMap() {
+        /**
+         * 初始化
+         */
+        void init() {
+            try {
+                //cas减少并发量
+                boolean init = initIng.compareAndSet(false, true);
+                if (!init) {
+                    log.info("cas set fail");
+                    return ;
+                }
+                Map<String, OrgTreeNodeDTO> orgMap = new HashMap<>();
+                List<OrgTreeNodeDTO> orgList = new ArrayList<>();
+                initIng.getAndSet(false);
+
+                //锁控制数据一致
+                lock.writeLock().lock();
+                constructTrees(orgMap, orgList);
+                treeNodeMap = orgMap;
+                topNodesList = orgList;
+                codeIdMap = orgList
+                        .stream()
+                        .collect(Collectors.toMap(OrgTreeNodeDTO::getCode, e->e, (old, last)-> last));
+            }catch (Exception e) {
+                log.error("reInitTree error.", e);
+            } finally {
+                lock.writeLock().unlock();
+            }
+        }
+
+        void updateTreeNodes(List<OrgTreeNodeDTO> treeNods, List<String> delCodes) {
+            try {
+                lock.writeLock().lock();
+                treeNods.forEach(e->{
+                    treeNodeMap.put(e.getId(), e);
+                    codeIdMap.put(e.getCode(), e);
+                });
+                delCodes.forEach(e->{
+                    OrgTreeNodeDTO node = codeIdMap.remove(e);
+                    if(null != node) {
+                        treeNodeMap.remove(node.getId());
+                    }
+                });
+            }catch (Exception e) {
+                log.error("updateTreeNodes error.", e);
+            } finally {
+                lock.writeLock().unlock();
+            }
+        }
+
+        private List<OrgTreeNodeDTO> getChildById(String id, Integer level) {
+            try {
+                lock.readLock().lock();
+                if(StringUtils.isBlank(id)) {
+                    return deepCopy(this.topNodesList);
+                } else {
+                    Map<String, OrgTreeNodeDTO> treeNodeMap = this.treeNodeMap;
+                    OrgTreeNodeDTO dto = treeNodeMap.get(id);
+                    if(null == dto) {
+                        return new ArrayList<>();
+                    }
+                    Set<String> childIds = getAllChildIds(id, treeNodeMap, level);
+                    if(CollectionUtils.isEmpty(childIds)) {
+                        return new ArrayList<>();
+                    }
+                    List<OrgTreeNodeDTO> collect = childIds
+                            .stream()
+                            .map(treeNodeMap::get)
+                            .collect(Collectors.toList());
+                    return deepCopy(collect);
+                }
+            }catch (Exception e) {
+                log.error("updateTreeNodes error.", e);
+                return Collections.emptyList();
+            } finally {
+                lock.readLock().unlock();
+            }
+        }
+
+        private List<OrgTreeNodeDTO> getAllChildNodes(String orgId) {
+            try {
+                lock.readLock().lock();
+                if(StringUtils.isBlank(orgId)) {
+                    return Collections.emptyList();
+                }
+                Map<String, OrgTreeNodeDTO> treeNodeMap = this.treeNodeMap;
+                Set<String> allChildIds = getAllChildIds(orgId, treeNodeMap, null);
+                return getOrgTreeNodeByIds(allChildIds);
+            }catch (Exception e) {
+                log.error("getAllChildNodes error.", e);
+                return Collections.emptyList();
+            }finally {
+                lock.readLock().unlock();
+            }
+
+        }
+
+        private List<OrgTreeNodeDTO> getSelfAndChildByIds(List<String> orgIds) {
             try {
                 lock.readLock().lock();
-                return treeNodeMap;
+                if(CollectionUtils.isEmpty(orgIds)) {
+                    return Collections.emptyList();
+                }
+                Map<String, OrgTreeNodeDTO> treeNodeMap = this.treeNodeMap;
+                List<OrgTreeNodeDTO> selfNodes = orgIds.stream().map(treeNodeMap::get).collect(Collectors.toList());
+                List<OrgTreeNodeDTO> collect = selfNodes
+                        .stream()
+                        .map(OrgTreeNodeDTO::getChildIds)
+                        .flatMap(List::stream)
+                        .distinct()
+                        .map(treeNodeMap::get)
+                        .collect(Collectors.toList());
+                selfNodes.addAll(collect);
+                return deepCopy(selfNodes);
             } catch (Exception e) {
-                log.error("getTreeNodeMap error.", e);
-                return null;
+                log.error("getSelfAndChildByIds error.", e);
+                return Collections.emptyList();
             } finally {
                 lock.readLock().unlock();
             }
         }
 
-        List<OrgTreeNodeDTO> getTopNodesList() {
+        private OrgTreeNodeDTO getOrgTreeNode(String id) {
             try {
                 lock.readLock().lock();
-                return topNodesList;
+                if(StringUtils.isBlank(id)) {
+                    return null;
+                }
+                Map<String, OrgTreeNodeDTO> treeNodeMap = this.treeNodeMap;
+                OrgTreeNodeDTO orgTreeNodeDTO = treeNodeMap.get(id);
+                if(null == orgTreeNodeDTO) {
+                    return null;
+                }
+                return deepCopy(orgTreeNodeDTO);
             }catch (Exception e) {
-                log.error("getTreeNodeMap error.", e);
+                log.error("getOrgTreeNode error.", e);
                 return null;
             }finally {
                 lock.readLock().unlock();
             }
         }
 
-        /**
-         * 初始化
-         */
-        void init() {
-            Map<String, OrgTreeNodeDTO> orgMap = new HashMap<>();
-            List<OrgTreeNodeDTO> orgList = new ArrayList<>();
-            constructTrees(orgMap, orgList);
+        private List<OrgTreeNodeDTO> getOrgTreeNodeByIds(Collection<String> ids) {
             try {
-                lock.writeLock().lock();
-                treeNodeMap = orgMap;
-                topNodesList = orgList;
+                lock.readLock().lock();
+                if(CollectionUtils.isEmpty(ids)) {
+                    return new ArrayList<>();
+                }
+                Map<String, OrgTreeNodeDTO> treeNodeMap = this.treeNodeMap;
+                List<OrgTreeNodeDTO> collect = ids
+                        .stream()
+                        .map(treeNodeMap::get)
+                        .collect(Collectors.toList());
+                return deepCopy(collect);
             }catch (Exception e) {
-                log.error("reInitTree error.", e);
-            } finally {
-                lock.writeLock().unlock();
+                log.error("getOrgTreeNode error.", e);
+                return Collections.emptyList();
+            }finally {
+                lock.readLock().unlock();
+            }
+
+        }
+
+        private Set<String> getAllMtOrgIds(String mtIds) {
+            try {
+                lock.readLock().lock();
+                Set<String> resultSet = new HashSet<>();
+                String[] mtIdStrs = mtIds.split(",");
+                Map<String, OrgTreeNodeDTO> treeNodeMap = this.treeNodeMap;
+                for (String mtIdStr : mtIdStrs) {
+                    if (mtIdStr.contains(":")) {
+                        String[] split = mtIdStr.split(":");
+                        String orgId = split[0];
+                        resultSet.add(orgId);
+                        int rang = Integer.parseInt(split[1]);
+                        //1 半选 2 全选
+                        if (rang == 2) {
+                            Set<String> allChildIds = getAllChildIds(orgId, treeNodeMap, null);
+                            resultSet.addAll(allChildIds);
+                        }
+                    } else {
+                        resultSet.add(mtIdStr);
+                    }
+                }
+                return resultSet;
+            }catch (Exception e) {
+                return Collections.emptySet();
+            }finally {
+                lock.readLock().unlock();
+            }
+        }
+
+        /**
+         * 获取机构下的子机构节点id
+         * @param orgId 机构id
+         * @param treeNodeMap 机构树map
+         * @param level 深度(从该机构节点算起)。需要所有子节点id,传入null
+         * @return
+         */
+        private Set<String> getAllChildIds(String orgId, Map<String, OrgTreeNodeDTO> treeNodeMap, Integer level) {
+            Set<String> childIds = new HashSet<>();
+            OrgTreeNodeDTO orgTreeNodeDTO = treeNodeMap.get(orgId);
+            if(null == orgTreeNodeDTO) {
+                return childIds;
+            }
+            if(CollectionUtils.isEmpty(orgTreeNodeDTO.getChildIds())) {
+                return childIds;
+            }
+            //初始化双端队列
+            Deque<OrgTreeNodeDTO> deque = new LinkedList<>();
+            deque.add(orgTreeNodeDTO);
+
+            //初始化深度计算指针
+            int currentLevelIndex = deque.size();
+            int currentNodeIndex = 0;
+
+            while (!deque.isEmpty()) {
+                //往双端队列中添加下一层的子节点
+                OrgTreeNodeDTO node = deque.pollFirst();
+                List<String> nodeChildIds = node.getChildIds();
+                if(CollectionUtils.isNotEmpty(nodeChildIds)) {
+                    childIds.addAll(nodeChildIds);
+                    List<OrgTreeNodeDTO> childNodes = nodeChildIds
+                            .stream()
+                            .map(treeNodeMap::get)
+                            .collect(Collectors.toList());
+                    deque.addAll(childNodes);
+                }
+                //判断是否达到深度
+                if(null != level && ++currentNodeIndex == currentLevelIndex) {
+                    --level;
+                    if(level == 0) {
+                        break;
+                    }
+                    //重置index
+                    currentNodeIndex = 0;
+                    currentLevelIndex = deque.size();
+                }
             }
+            return childIds;
         }
     }
 }

+ 14 - 0
dcuc-auth-service/src/main/resources/config/mysql/V4_3_0026__OrgSort.sql

@@ -0,0 +1,14 @@
+ALTER TABLE `t_auth_org_info` ADD COLUMN `SORT` INT(7) COMMENT 'SORT 排序' AFTER `PATH`;
+
+-- 机构排序历史数据处理
+-- CREATE TABLE `t_org_sort` (
+--   `id` int(11) NOT NULL AUTO_INCREMENT,
+--   `org_id` varchar(32) NOT NULL,
+--   PRIMARY KEY (`id`)
+-- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
+--
+-- INSERT into t_org_sort SELECT NULL,id from t_auth_org_info where DELETED = '0';
+--
+-- UPDATE t_auth_org_info o JOIN t_org_sort s on o.id = s.org_id set o.SORT = s.id WHERE o.DELETED = '0';
+--
+-- DROP TABLE t_org_sort;