zoukankan      html  css  js  c++  java
  • 【系统设计】统计组织架构中,各层级的人数的方案。

    在业务方得到一个需求,需要统计每个组织所包含的人数。如下图所示 ,整个集团上百家公司 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);
                }
            }
  • 相关阅读:
    字符串的长度 -- js
    导入drupal中文语言包
    ubuntu修改iP地址
    人生需要苦难和敌人
    Drupal.theme未解之谜
    如何定义带下标的js数组
    smtp admin email 似乎可以考虑在
    js中的apply 和 call
    js 点号 中括号
    代码调试
  • 原文地址:https://www.cnblogs.com/simawenbo/p/15539120.html
Copyright © 2011-2022 走看看