using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Sozsoft.Platform.Entities; using Sozsoft.Platform.Extensions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Volo.Abp.Domain.Repositories; using Volo.Abp.Identity; namespace Sozsoft.Platform.OrgChart; [Authorize] [Route("api/app/org-chart")] public class OrgChartAppService : PlatformAppService { private readonly IRepository _departmentRepository; private readonly IRepository _jobPositionRepository; private readonly IIdentityUserRepository _userRepository; public OrgChartAppService( IRepository departmentRepository, IRepository jobPositionRepository, IIdentityUserRepository userRepository) { _departmentRepository = departmentRepository; _jobPositionRepository = jobPositionRepository; _userRepository = userRepository; } [HttpGet("departments")] public async Task> GetDepartmentsAsync() { var departments = await _departmentRepository.GetListAsync(); var jobPositions = await _jobPositionRepository.GetListAsync(); var users = await _userRepository.GetListAsync(); var jobById = jobPositions.ToDictionary(x => x.Id); var positionsByDepartment = jobPositions .GroupBy(position => position.DepartmentId) .ToDictionary(group => group.Key, group => group.OrderBy(position => position.Name).ToList()); var usersByJobPosition = GroupUsersByJobPosition(users); var topPositionByDepartment = new Dictionary(); foreach (var department in departments) { if (!positionsByDepartment.TryGetValue(department.Id, out var departmentPositions)) { continue; } // Top position means: no parent, parent missing, or parent belongs to another department. var topCandidates = departmentPositions .Where(position => !position.ParentId.HasValue || !jobById.ContainsKey(position.ParentId.Value) || jobById[position.ParentId.Value].DepartmentId != department.Id) .OrderBy(position => position.Name) .ToList(); var selectedTop = topCandidates.FirstOrDefault() ?? departmentPositions .OrderBy(position => position.Name) .First(); topPositionByDepartment[department.Id] = selectedTop; } var nodes = departments.Select(d => { topPositionByDepartment.TryGetValue(d.Id, out var topPosition); return new OrgChartNodeDto { Id = d.Id, Name = d.Name, ParentId = d.ParentId, JobPositionId = topPosition?.Id, JobPositionName = topPosition?.Name, Users = topPosition != null && usersByJobPosition.TryGetValue(topPosition.Id, out var positionUsers) ? positionUsers : [], }; }).ToList(); return nodes; } [HttpGet("job-positions")] public async Task> GetJobPositionsAsync() { var jobPositions = await _jobPositionRepository.GetListAsync(); var departments = await _departmentRepository.GetListAsync(); var users = await _userRepository.GetListAsync(); var deptDict = departments.ToDictionary(d => d.Id, d => d.Name); var usersByJobPosition = GroupUsersByJobPosition(users); var nodes = jobPositions.Select(jp => new OrgChartNodeDto { Id = jp.Id, Name = jp.Name, ParentId = jp.ParentId, DepartmentId = jp.DepartmentId, DepartmentName = deptDict.TryGetValue(jp.DepartmentId, out var name) ? name : null, JobPositionId = jp.Id, JobPositionName = jp.Name, Users = usersByJobPosition.TryGetValue(jp.Id, out var positionUsers) ? positionUsers : [], }).ToList(); return nodes; } private static Dictionary> GroupUsersByJobPosition( IEnumerable users) { return users .GroupBy(user => user.GetJobPositionId()) .ToDictionary( group => group.Key, group => group.Select(user => new OrgChartUserDto { Id = user.Id, FullName = user.GetFullName(), Email = user.Email, UserName = user.UserName, PhoneNumber = user.PhoneNumber }).ToList()); } }