Fork me on Gitee

【系统设计】统计组织架构中,各层级的人数的方案。

在业务方得到一个需求,需要统计每个组织所包含的人数。如下图所示 ,整个集团上百家公司 4000+组织。大致结构如下图所示:

 

 

 

首先,这是一个树形结构,所以要统计必然要用递归的方式。

 

 

 

分析一下表结构,然后用最简单的方法来思考一下,假如这个树从上到下只有一条路径 那么人数如图所示:

  OrgId (主键Id)  |  ParentId (父组织Id)  |  count 当前组织人数 |  sumCount 包含下级组织      

        

   人数是通过员工表 对 组织ID进行分组查询搜索出来的结果。

OK 如图所示 就统计出来了,但如果这个组织层级极其复杂的话,就没有这么容易了,比如想要算OrgId = 5 的组织人数,要把parentId = 5的子组织全找出来,然后这些子组织要继续找下级组织,直到扩散到叶子节点(指没有下级的节点)。这样计算效率低的离谱,所以需要优化一下。

我们这里引入一个深度的字段概念!这个东西最好在添加组织的时候就保存下来。然后就计算最大深度(比如10)的所有组织人数,再递归计算9、8、7......深度的组织,直到最顶层。

这样做 统计深度6的所有组织的时候,已经把7的数据都汇总完毕了,直接parentId = 深度7的组织Id,然后把数量汇总就可以了,不用继续递归,因为7已经统计完了 8 9 10深度的所有人数,以此类推

员工表1W+人 , 组织表4000+,查询时间大概是10秒左右。建议统计完所有直接把数据放入缓存中,涉及组织变更、调动、添加员工、入职、离职、都要清除缓存,下次要用的时候再重新计算。

 

代码如下(C#):

      /// <summary>
        /// 获取各组织数量
        /// </summary>
        public async Task<List<OrgCountCache>> GetOrgCountCache()
        {
            return (await _orgCountCache.GetOrAddAsync(CacheKyes.OrgCount, async () =>
            {
                var orgs = await GetOrganizationCache();

                // 计算出有人在其下的各组织节点数据
                var hasCount = await _employeeRepository.GetOrgCount();
                if (hasCount == null || !hasCount.Any())
                {
                    return new List<OrgCountCache>();
                }

                // 所有组织节点数据
                var allOrgCount = new List<OrgCountCache>();
                foreach(var org in orgs)
                {
                    var orgCountCache = new OrgCountCache();
                    orgCountCache.OrgId = org.OrgId;
                    orgCountCache.ParentId = org.ParentId;
                    orgCountCache.OrganizationLevel = (int)org.OrganizationLevel;

                    // 找出4000+个组织中挂了人的组织
                    var hasCountOrg = hasCount.FirstOrDefault(x => x.OrgId == org.OrgId);
                    if (hasCountOrg != null)
                    {
                        orgCountCache.Count = hasCountOrg.Count;
                        orgCountCache.SumCount = hasCountOrg.Count;
                    }
                    allOrgCount.Add(orgCountCache);
                }

                GetOrgCountRecursion(allOrgCount, 10);

                return allOrgCount;
            })).Clone();
        }

     /// <summary>
        /// 递归查询每个组织的数量 先查最深的,然后递归查深度其次的
        /// </summary>
        public void GetOrgCountRecursion(List<OrgCountCache> allOrgCount, int organizationLevel)
        {
            if (organizationLevel == 0) // 单独组织,没有父级,没有子集
            {
                foreach (var orgCount in allOrgCount)
                {
                    if (orgCount.OrganizationLevel != organizationLevel)
                        continue;
                    orgCount.SumCount = orgCount.Count;
                }
            }
            else if (organizationLevel < 0)  // 计算完毕,跳出循环
            {
                return;
            }
            else
            {
                foreach (var orgCount in allOrgCount)
                {
                    if (orgCount.OrganizationLevel != organizationLevel) 
                        continue;
                    // 计算总和SumCount = 本身Count  +  所有下一层深度节点的SumCount
                    orgCount.SumCount = orgCount.Count + allOrgCount.Where(x => x.ParentId == orgCount.OrgId).Select(x => x.SumCount).Sum();
                }
                // 递归调用,深度-1
                GetOrgCountRecursion(allOrgCount, organizationLevel - 1);
            }
        }

 

posted @ 2021-11-11 11:42  Roushan_IT  阅读(1102)  评论(0编辑  收藏  举报