Esempio n. 1
0
        public IActionResult GetUsers([FromRoute] Guid code, [FromQuery] Paging paging)
        {
            var repo  = _uow.GetRepository <IOrganizationRoleRepository>();
            var users = repo.GetRolesByOrganization(code);

            return(Ok(users));
        }
Esempio n. 2
0
        public async Task <byte[]> DownloadAsync(bool isDefault, Guid id)
        {
            ////lấy tên file từ db
            var repoFile = _uowCore.GetRepository <IFileRepository>();
            var file     = await repoFile.GetByIdAsync(id);

            if (file == null)
            {
                throw new Exception("Không tìm thấy file");
            }

            //lấy tên thư mục lưu file
            var folderPath = await GetFilePath(isDefault, file.CreatedAt);

            var filePath = Path.Combine(folderPath, file.Name);

            if (!System.IO.File.Exists(filePath))
            {
                throw new Exception("Không tìm thấy file vật lý");
            }

            var bytes = System.IO.File.ReadAllBytes(filePath);

            return(bytes);
        }
Esempio n. 3
0
        public async Task <Tenant> GetCurrentTenantAsync(HttpContext context)
        {
            var host = context.Request.Host.Value;

            Tenant tenant;
            var    cacheKey = $"TenantHost:{host}";

            //Lấy dữ liệu từ cache
            var bytes = await _cache.GetAsync(cacheKey);

            if (bytes != null)
            {
                var str = Encoding.UTF8.GetString(bytes);
                tenant = JsonConvert.DeserializeObject <Tenant>(str);
                return(tenant);
            }

            //Lấy dữ liệu từ DB
            var repoTenant = _uow.GetRepository <ITenantRepository>();
            var tenantHost = await repoTenant.GetByHostAsync(host);

            if (tenantHost == null)
            {
                return(null);
            }

            tenant = await repoTenant.GetByCodeAsync(tenantHost.Code);

            //Cập nhật cache
            await _cache.SetAsync(cacheKey, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(tenant)));

            return(tenant);
        }
Esempio n. 4
0
        public async Task <IActionResult> GetAsync([FromRoute] Guid code)
        {
            var idTenant = await _workContext.GetTenantCodeAsync();

            var repo = _uow.GetRepository <IFileRepository>();
            var file = await repo.GetByIdAsync(code);

            return(Ok(file));
        }
Esempio n. 5
0
        public async Task <IActionResult> TenantAsync([FromBody] InstallTenantModel model)
        {
            var repoTenant = _uow.GetRepository <ITenantRepository>();

            if (await repoTenant.AnyAsync())
            {
                return(BadRequest("Hệ thống đã được cài đặt, không thể cài lại"));
            }

            var tenantCode = Guid.NewGuid();

            //Thông tin doanh nghiệp
            await repoTenant.InsertAsync(new Tenant
            {
                Name         = model.TaxCode,
                Code         = tenantCode,
                IsEnable     = true,
                CreatedAt    = DateTime.Now,
                CreatedAtUtc = DateTime.UtcNow,
                CreatedBy    = Guid.Empty,
                Path         = tenantCode.ToString()
            });

            await repoTenant.InsertInfoAsync(new TenantInfo
            {
                TaxCode    = model.TaxCode,
                Address    = model.Address,
                City       = model.City,
                Country    = model.Country,
                District   = model.District,
                FullNameVi = model.FullNameVi,
                FullNameEn = model.FullNameEn,
                Code       = tenantCode,
                IsCurrent  = true
            });

            var splited = model.HostName.Split(';');

            foreach (var item in splited)
            {
                await repoTenant.InsertHostAsync(new TenantHost
                {
                    Code     = tenantCode,
                    HostName = model.HostName
                });
            }

            await _uow.CommitAsync();

            return(Ok(tenantCode));
        }
Esempio n. 6
0
        public async Task <IActionResult> GetAsync()
        {
            var user = await _workContext.GetUserAsync();

            var repoUser = _uow.GetRepository <IUserRepository>();
            var info     = await repoUser.FindUserInfoByIdAsync(user.Id);

            var model = new ProfileModel
            {
                Email       = user.Email,
                PhoneNumber = user.PhoneNumber,
                FullName    = info.FullName,
                AvatarPath  = info.AvatarPath
            };

            return(Ok(model));
        }
Esempio n. 7
0
        public async Task <IActionResult> GetAsync([FromQuery] Paging paging)
        {
            var context = await _workcontext.GetContextAsync(nameof(CorePolicy.LabelPolicy.Label_Read));

            var repo  = _uow.GetRepository <ILabelRepository>();
            var paged = await repo.PagingAsync(context, paging);

            var result = new Paged <LabelModel>
            {
                Data       = paged.Data.Select(x => (LabelModel)x),
                Page       = paged.Page,
                Q          = paged.Q,
                Size       = paged.Size,
                TotalItems = paged.TotalItems,
                TotalPages = paged.TotalPages
            };

            return(Ok(result));
        }
        public async Task <IActionResult> GetAsync([FromQuery] Paging paging)
        {
            var permission = await _workcontext.GetContextAsync(nameof(CorePolicy.OrganizationPolicy.OrganizationUnit_Read));

            var repo  = _uow.GetRepository <IOrganizationUnitRepository>();
            var paged = await repo.PagingAsync(permission, paging);

            var result = new Paged <OrganizationUnitModel>
            {
                Data       = paged.Data.Select(x => (OrganizationUnitModel)x),
                Page       = paged.Page,
                Q          = paged.Q,
                Size       = paged.Size,
                TotalItems = paged.TotalItems,
                TotalPages = paged.TotalPages
            };

            return(Ok(result));
        }
Esempio n. 9
0
        public async Task <IActionResult> GetAsync([FromQuery] Paging paging)
        {
            var context = await _workcontext.GetContextAsync(nameof(CorePolicy.UserPolicy.User_Read));

            var repoUser = _uow.GetRepository <IUserRepository>();
            var paged    = await repoUser.PagingAsync(context, paging);

            var userCodes = paged.Data.Select(x => x.Id).ToList();

            var repoUserInfo = _uow.GetRepository <IUserRepository>();
            var infos        = (await repoUserInfo.FindInfoByIdsAsync(userCodes)).ToDictionary(x => x.Id, x => x);

            var users = new List <UserModel>();

            foreach (var data in paged.Data)
            {
                UserModel user = data;
                if (infos.ContainsKey(data.Id))
                {
                    user.Infos = infos[data.Id];
                }
                users.Add(user);
            }

            var result = new Paged <UserModel>
            {
                Data       = users,
                Page       = paged.Page,
                Q          = paged.Q,
                Size       = paged.Size,
                TotalItems = paged.TotalItems,
                TotalPages = paged.TotalPages
            };

            return(Ok(result));
        }
Esempio n. 10
0
        /// <summary>
        /// Kiểm tra token còn hạn hay ko
        /// </summary>
        /// <param name="context"></param>
        /// <param name="configuration"></param>
        /// <param name="uow"></param>
        /// <returns></returns>
        public async Task Invoke(HttpContext context, IConfiguration configuration, ICoreUnitOfWork uow)
        {
            var auth = context.Request.Headers["Authorization"].ToString();

            if (string.IsNullOrWhiteSpace(auth))
            {
                await _next.Invoke(context);

                return;
            }

            auth = auth.Replace("Bearer ", "");
            var jwtHandler = new JwtSecurityTokenHandler();
            var token      = jwtHandler.ReadToken(auth);

            //lấy token từ cache
            var bytes = await _cache.GetAsync($"TokenInfos:{token.Id}");

            if (bytes != null)
            {
                await _next.Invoke(context);

                return;
            }

            //lấy từ DB ra xem có dữ liệu không
            var repoTenant = uow.GetRepository <ITokenInfoRepository>();
            var tokenInfo  = await repoTenant.QueryByCodeAsync(Guid.Parse(token.Id));

            if (tokenInfo != null && tokenInfo.AccessToken == auth)
            {
                //lưu vào cache
                var cacheOption = new DistributedCacheEntryOptions();
                cacheOption.AbsoluteExpiration = tokenInfo.ExpireAt;
                await _cache.SetAsync($"TokenInfos:{tokenInfo.Code}", Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(tokenInfo)), cacheOption);

                await _next.Invoke(context);

                return;
            }

            context.Response.StatusCode = 401; //UnAuthorized
            return;
        }
Esempio n. 11
0
        public async Task <Tenant> GetCurrentTenantAsync(HttpContext context)
        {
            if (!Guid.TryParse(context.Request.Query["tenantCode"].ToString(), out Guid tenantCode))
            {
                return(null);
            }

            Tenant tenant;
            var    cacheKey = $"TenantCode:{tenantCode}";

            //Lấy dữ liệu từ cache
            var bytes = await _cache.GetAsync(cacheKey);

            if (bytes != null)
            {
                var str = Encoding.UTF8.GetString(bytes);
                return(JsonConvert.DeserializeObject <Tenant>(str));
            }

            //Lấy dữ liệu từ DB
            var host       = context.Request.Host.Value;
            var repoTenant = _uow.GetRepository <ITenantRepository>();
            var current    = await repoTenant.GetByHostAsync(host);

            if (current == null)
            {
                return(null);
            }

            tenant = await repoTenant.GetByCodeAsync(tenantCode, current.Code);

            if (tenant == null)
            {
                return(null);
            }

            //Cập nhật cache
            await _cache.SetAsync(cacheKey, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(tenant)));

            return(tenant);
        }
Esempio n. 12
0
        public async Task <SessionModel> GetSessionAsync()
        {
            if (_currentToken != null)
            {
                return(_currentToken);
            }

            var tokenCode = GetTokenCode();

            if (tokenCode == null)
            {
                return(null);
            }

            var bytes = await _cache.GetAsync($"TokenInfos:{tokenCode}");

            if (bytes != null)
            {
                var data = JsonConvert.DeserializeObject <TokenInfo>(Encoding.UTF8.GetString(bytes));
                _currentToken = JsonConvert.DeserializeObject <SessionModel>(data.Metadata);
                return(_currentToken);
            }

            var repoToken = _uow.GetRepository <ITokenRepository>();
            var token     = await repoToken.GetByCodeAsync(tokenCode.Value);

            if (token == null)
            {
                throw new Exception("Phiên làm việc hết hạn hoặc đã bị đăng xuất");
            }

            _currentToken = JsonConvert.DeserializeObject <SessionModel>(token.Metadata);

            var cacheOption = new DistributedCacheEntryOptions();

            cacheOption.AbsoluteExpiration = token.ExpireAt;
            await _cache.SetAsync($"TokenInfos:{tokenCode}", Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(token)), cacheOption);

            return(_currentToken);
        }
Esempio n. 13
0
        public async Task <IActionResult> GetAsync([FromQuery] Paging paging)
        {
            var idTenant = await _workContext.GetTenantCodeAsync();

            var repoTenant = _uow.GetRepository <ITenantRepository>();
            var paged      = await repoTenant.PagingAsync(idTenant, paging);

            var tenantCodes = paged.Data.Select(x => x.Code).ToList();
            var infos       = (await repoTenant.GetInfoByCodesAsync(tenantCodes)).ToDictionary(x => x.Code, x => x);
            var hosts       = (await repoTenant.GetHostByCodesAsync(tenantCodes)).GroupBy(x => x.Code).ToDictionary(x => x.Key, x => x.ToList());

            var result = new Paged <TenantModel>
            {
                Page       = paged.Page,
                Q          = paged.Q,
                Size       = paged.Size,
                TotalItems = paged.TotalItems,
                TotalPages = paged.TotalPages,
            };

            var data = new List <TenantModel>();

            foreach (var item in paged.Data)
            {
                var model = new TenantModel();
                model.Code     = item.Code;
                model.HostName = string.Join(';', hosts[item.Code].Select(x => x.HostName));
                model.IsEnable = item.IsEnable;
                model.Theme    = item.Theme;

                if (!infos.ContainsKey(item.Code))
                {
                    data.Add(model);
                    continue;
                }

                var info = infos[item.Code];
                model.Info = new TenantInfoModel
                {
                    Id         = info.Id,
                    Code       = info.Code,
                    Address    = info.Address,
                    City       = info.City,
                    Country    = info.Country,
                    District   = info.District,
                    Emails     = info.Emails,
                    Fax        = info.Fax,
                    FullNameVi = info.FullNameVi,
                    FullNameEn = info.FullNameEn,
                    Phones     = info.Phones,
                    TaxCode    = info.TaxCode
                };
                data.Add(model);
            }
            result.Data = data;
            return(Ok(result));
        }
Esempio n. 14
0
        public async Task <IActionResult> GetAsync()
        {
            var tenantCode = await _workContext.GetTenantCodeAsync();

            var repoTenant = _uow.GetRepository <ITenantRepository>();
            var info       = await repoTenant.GetInfoByCodeAsync(tenantCode);

            if (info == null)
            {
                return(NotFound());
            }

            var model = new TenantInfoModel
            {
                Id           = info.Id,
                Code         = info.Code,
                FullNameVi   = info.FullNameVi,
                TaxCode      = info.TaxCode,
                Address      = info.Address,
                City         = info.City,
                District     = info.District,
                Country      = info.Country,
                Emails       = info.Emails,
                Fax          = info.Fax,
                FullNameEn   = info.FullNameEn,
                LegalName    = info.LegalName,
                BusinessType = info.BusinessType,
            };

            //Nếu setting ko có metadata thì trả về kết quả luôn
            var repoSetting = _uow.GetRepository <ISettingRepository>();
            var setting     = await repoSetting.GetByKeyAsync(Guid.Empty, SettingKey.ThongTinDoanhNghiep_Khac.ToString());

            if (setting == null)
            {
                return(Ok(model));
            }

            //Metadata lấy được từ DB
            var metadatas = string.IsNullOrWhiteSpace(info.Metadata) ? new List <MetadataModel>() : JsonConvert.DeserializeObject <List <MetadataModel> >(info.Metadata);

            model.Metadata = new List <MetadataModel>();
            var splited1 = setting.Value.Split("|");

            foreach (var item in splited1)
            {
                var splited2 = item.Split(":");
                var key      = splited2[0];
                var name     = splited2[1];
                var metadata = metadatas.FirstOrDefault(x => x.Key == key);

                //Nếu DB ko lưu value thì trả về null
                model.Metadata.Add(new MetadataModel {
                    Key   = key,
                    Name  = name,
                    Value = metadata == null ? null : metadata.Value
                });
            }

            return(Ok(model));
        }
Esempio n. 15
0
        public async Task <TokenModel> LoginAsync(Guid tenantCode, LoginModel model)
        {
            model.UserName = model.UserName.ToUpper();

            var repoUser = _uow.GetRepository <IUserRepository>();
            var user     = await repoUser.FindUserByUsernameAsync(tenantCode, model.UserName);

            if (user == null)
            {
                throw new Exception("Tài khoản hoặc mật khẩu không đúng");
            }

            var account = await _userManager.FindByIdAsync(user.Id.ToString());

            var result = await _signInManager.CheckPasswordSignInAsync(account, model.Password, true);

            if (result.IsLockedOut)
            {
                throw new Exception("Tài khoản bị khóa");
            }

            if (!result.Succeeded)
            {
                throw new Exception("Tài khoản hoặc mật khẩu không đúng");
            }

            //TODO: Xóa các Token đã hết hạn => Đưa vào BackgroundJob
            //var expired = tokenInfos.Where(x => x.ExpireAtUtc <= DateTime.UtcNow);
            //if (expired.Any())
            //{
            //    repoToken.DeleteRange(expired);
            //    _uowCore.SaveChanges();
            //}

            SessionModel session;
            var          token = await GetTokenAsync(user.Id);

            if (token != null)
            {
                session = JsonConvert.DeserializeObject <SessionModel>(token.Metadata);
            }
            else
            {
                var expireIn    = TimeSpan.FromDays(1);
                var tokenCode   = Guid.NewGuid();
                var expireAt    = DateTime.Now.Add(expireIn);
                var expireAtUtc = DateTime.UtcNow.Add(expireIn);
                var claims      = new List <Claim>();
                claims.Add(new Claim(JwtRegisteredClaimNames.Jti, tokenCode.ToString()));
                claims.Add(new Claim(ClaimTypes.Sid, user.Id.ToString()));
                claims.Add(new Claim(ClaimTypes.GroupSid, tenantCode.ToString()));

                var jwt = new JwtSecurityToken(
                    claims: claims,
                    expires: expireAt,
                    signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_options.SecretKey)), SecurityAlgorithms.HmacSha256));

                var accessToken = new JwtSecurityTokenHandler().WriteToken(jwt);

                session = new SessionModel
                {
                    IdUser      = user.Id,
                    UserName    = user.UserName,
                    PhoneNumber = user.PhoneNumber,
                    Email       = user.Email,
                    CreatedAt   = user.CreatedAt,
                    UserInfo    = await GetInfoAsync(user.Id),
                    TenantInfo  = await GetTenantInfoAsync(user.TenantCode),
                    Claims      = await GetClaimsAsync(user.Id)
                };

                token = new TokenInfo
                {
                    AccessToken     = accessToken,
                    CreatedAt       = DateTime.Now,
                    CreatedAtUtc    = DateTime.UtcNow,
                    ExpireAt        = expireAt,
                    ExpireAtUtc     = expireAtUtc,
                    Code            = tokenCode,
                    IdUser          = user.Id,
                    LocalIpAddress  = NetworkExtension.GetLocalIpAddress(_httpContextAccessor.HttpContext.Request).ToString(),
                    PublicIpAddress = NetworkExtension.GetRemoteIpAddress(_httpContextAccessor.HttpContext.Request).ToString(),
                    Metadata        = JsonConvert.SerializeObject(session),
                    RefreshToken    = null,
                    Source          = "Application",
                    TimeToLife      = expireIn.TotalMinutes,
                    UserAgent       = NetworkExtension.GetUserAgent(_httpContextAccessor.HttpContext.Request),
                    TenantCode      = tenantCode
                };

                var repoToken = _uow.GetRepository <ITokenRepository>();
                await repoToken.InsertAsync(token);

                await _uow.CommitAsync();
            }

            return(new TokenModel
            {
                AccessToken = token.AccessToken,
                ExpireIn = (token.ExpireAt - DateTime.Now).TotalMinutes,
                ExpireAt = token.ExpireAt,
                Timezone = TimeZoneInfo.Local.GetUtcOffset(DateTime.Now).TotalHours,
                //RefreshToken = account.SecurityStamp,
            });
        }
Esempio n. 16
0
        public async Task <IActionResult> GetAsync()
        {
            //lấy ra tất các các group default trong Db có tenantCode = Guid.Empty() hiển thị
            // setting nào có tenantCode != Guid.Empty rồi thì lấy dữ liệu db ra
            var tenantCode = await _workContext.GetTenantCodeAsync();

            var repo     = _uowCore.GetRepository <ISettingRepository>();
            var settings = await repo.GetByTenantCodesAsync(new List <Guid> {
                Guid.Empty, tenantCode
            });

            settings = settings.Where(x => !x.IsReadOnly).ToList(); //chỉ lấy setting nào đc hiển thị

            var defaulSettings = settings.Where(x => x.TenantCode == Guid.Empty);
            var entities       = settings.Except(defaulSettings);

            var groupSettingKeys = _settingService.GetGroupSettings();

            var settingGroups = new List <SettingGroupModel>();

            var defaultGroups = defaulSettings.GroupBy(x => x.Group);


            //Hiển thị toàn bộ setting theo group
            foreach (var defaultGroup in defaultGroups)
            {
                //Nếu group nào chưa có DisplayName sẽ ko hiển thị ra ngoài
                if (!groupSettingKeys.ContainsKey(defaultGroup.Key))
                {
                    continue;
                }

                var settingGroup = new SettingGroupModel
                {
                    Key      = defaultGroup.Key,
                    Name     = groupSettingKeys[defaultGroup.Key],
                    Settings = new List <SettingModel>()
                };

                var defaultSettings = defaultGroup.ToList();
                foreach (var defaultSetting in defaultSettings)
                {
                    //Lấy cấu hình mặc định
                    var setting = new SettingModel
                    {
                        Key         = defaultSetting.Key,
                        Name        = defaultSetting.Name,
                        Type        = EnumExtension.ToEnum <SettingType>(defaultSetting.Type),
                        Group       = defaultSetting.Group,
                        Options     = string.IsNullOrWhiteSpace(defaultSetting.Options) ? new List <KeyValuePair <string, string> >() : defaultSetting.Options.Split("|").Select(x => x.Split(":")).Select(x => new KeyValuePair <string, string>(x[0], x[1])).ToList(),
                        Value       = defaultSetting.Value,
                        Description = defaultSetting.Description
                    };

                    //Lấy giá trị trên CSDL
                    var entity = entities.FirstOrDefault(x => x.Key == defaultSetting.Key);
                    if (entity != null)
                    {
                        setting.Id         = entity.Id;
                        setting.Code       = entity.Code;
                        setting.TenantCode = entity.TenantCode;
                        setting.Value      = entity.Value;
                    }
                    settingGroup.Settings.Add(setting);
                }

                settingGroups.Add(settingGroup);
            }

            return(Ok(settingGroups));
        }
Esempio n. 17
0
        public async Task <IActionResult> GetTenantAsync()
        {
            //Cây phân cấp
            //         A
            //       /   \
            //     B       C
            //   /   \   /   \
            // E      F G      H
            // -----------------
            //Dữ liệu lưu trên DB
            //A = A
            //B = A|B
            //C = A|C
            //E = A|B|E
            //F = A|B|F
            //G = A|C|G
            //H = A|C|H
            // ------------------
            //Hiển thị ra UI
            //A
            //_.B
            //_._.E
            //_._.F
            //_.C
            //_._.G
            //_._.H

            var tenants = new List <HierarchyTenantModel>();
            var session = await _workContext.GetSessionAsync();

            var repoTenant = _uow.GetRepository <ITenantRepository>();
            var hierarchy  = (await repoTenant.GetHierarchyByCodeAsync(session.TenantInfo.Code)).Select(x => new HierarchyTenantModel
            {
                Id        = x.Id,
                Code      = x.Code,
                Name      = x.Name,
                Path      = x.Path,
                Codes     = x.Path.Split('|').ToList(),
                Level     = x.Path.Split('|').Length,
                IdParent  = x.IdParent,
                CreatedAt = x.CreatedAt
            }).ToList();

            if (hierarchy.Count == 0)
            {
                return(Ok(tenants));
            }

            //Nếu TenantCode là chi nhánh => Min Level != 1 => Cần giảm tất cả level cho xuất hiện Level 1 để hiển thị TenantCode hiện tại là root
            var distance = 0;
            var min      = hierarchy.Min(x => x.Level);

            if (min != 1)
            {
                distance = min - 1;
            }

            //Sắp xếp lại
            var max = hierarchy.Max(x => x.Level);

            for (int i = min; i <= max; i++)
            {
                var items = hierarchy.Where(x => x.Level == i).OrderByDescending(x => x.CreatedAt).ToList();
                foreach (var item in items)
                {
                    var index = tenants.FindIndex(x => x.Code == item.IdParent);
                    tenants.Insert(index + 1, new HierarchyTenantModel
                    {
                        Id        = item.Id,
                        Code      = item.Code,
                        Name      = item.Name,
                        Path      = item.Path,
                        Codes     = item.Codes,
                        Level     = item.Level - distance,
                        IdParent  = item.IdParent,
                        CreatedAt = item.CreatedAt
                    });
                }
            }

            ////Đổi tên thành _.
            //var infos = (await repoTenant.GetInfoByCodesAsync(hierarchy.Select(x => x.Code).ToList())).ToDictionary(x => x.Code, x => x);

            //foreach (var item in tenants)
            //{
            //    if (!infos.ContainsKey(item.Code))
            //        continue;

            //    var info = infos[item.Code];

            //    var level = new string[item.Level];
            //    for (int j = 0; j < item.Level - 1; j++)
            //    {
            //        level[j] = "_";
            //    }
            //    level[item.Level - 1] = $"{item.Name}: {info.FullNameVi}";
            //    item.FullName = info.FullNameVi;
            //    item.HierarchyName = string.Join('.', level);
            //}



            //Đổi tên thành _.
            var infos = (await repoTenant.GetInfoByCodesAsync(hierarchy.Select(x => x.Code).ToList())).ToDictionary(x => x.Code, x => x);

            //lưu lại item cuối cùng có level = 2
            var lastSecond = 0;

            for (int i = 0; i < tenants.Count; i++)
            {
                var item = tenants[i];
                if (!infos.ContainsKey(item.Code))
                {
                    continue;
                }

                var info = infos[item.Code];

                var level = new string[item.Level];

                for (int j = 0; j < item.Level - 1; j++)
                {
                    level[j] = "#####";
                }

                if (item.Level >= 2)
                {
                    level[item.Level - 2] = "|___";
                }

                if (item.Level == 2)
                {
                    lastSecond = i;
                }

                //tìm item có level cùng cấp mình gần nhất phía trước
                //nếu chưa phải  item có level cùng cấp gần nhất => cộng vào đầu item có level bé hơn 1 ký | tự để phân biệt
                for (int j = i - 1; j > 0; j--)
                {
                    if (tenants[j].Level <= item.Level)
                    {
                        break;
                    }

                    if (tenants[j].Level >= 2)
                    {
                        tenants[j].HierarchyNames[item.Level - 2] = "|&nbsp; &nbsp; &nbsp; &nbsp;";
                    }
                }

                level[item.Level - 1] = $"{item.Name}: {info.FullNameVi}";
                item.HierarchyNames   = level;
                item.FullName         = info.FullNameVi;
                item.HierarchyName    = string.Join("#", level);
            }

            //ghi lại
            foreach (var item in tenants)
            {
                item.HierarchyName = string.Join("", item.HierarchyNames).Replace("#", "&nbsp;");
            }

            var result = tenants.Select(x => new
            {
                Code          = x.Code,
                Name          = x.Name,
                FullName      = x.FullName,
                HierarchyName = x.HierarchyName
            }).ToList();

            return(Ok(result));
        }