sozsoft-platform/api/src/Sozsoft.Platform.Application/OrgChart/OrgChartAppService.cs

163 lines
6.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Sozsoft.Platform.Entities;
using Sozsoft.Platform.Extensions;
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<Branch, Guid> _branchRepository;
private readonly IRepository<Department, Guid> _departmentRepository;
private readonly IRepository<JobPosition, Guid> _jobPositionRepository;
private readonly IIdentityUserRepository _userRepository;
public OrgChartAppService(
IRepository<Branch, Guid> branchRepository,
IRepository<Department, Guid> departmentRepository,
IRepository<JobPosition, Guid> jobPositionRepository,
IIdentityUserRepository userRepository)
{
_branchRepository = branchRepository;
_departmentRepository = departmentRepository;
_jobPositionRepository = jobPositionRepository;
_userRepository = userRepository;
}
[HttpGet("departments")]
public async Task<List<OrgChartNodeDto>> 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<Guid, JobPosition>();
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,
NodeType = OrgChartNodeType.Department,
ParentId = ResolveParent(d.ParentId, d.BranchId, departments.Select(x => x.Id)),
JobPositionId = topPosition?.Id,
JobPositionName = topPosition?.Name,
Users = topPosition != null && usersByJobPosition.TryGetValue(topPosition.Id, out var positionUsers)
? positionUsers
: [],
};
}).ToList();
return await WithBranchRootsAsync(nodes);
}
[HttpGet("job-positions")]
public async Task<List<OrgChartNodeDto>> 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,
NodeType = OrgChartNodeType.JobPosition,
ParentId = ResolveParent(jp.ParentId, jp.BranchId, jobPositions.Select(x => x.Id)),
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 await WithBranchRootsAsync(nodes);
}
// Üstü olmayan (ya da üstü silinmiş) kayıt şubesinin altına bağlanır; şube ağacın köküdür.
private static Guid? ResolveParent(Guid? parentId, Guid branchId, IEnumerable<Guid> knownIds)
=> parentId.HasValue && knownIds.Contains(parentId.Value) ? parentId : branchId;
// Yalnızca altında kayıt bulunan şubeler düğüm olur; Branch IMultiTenant değildir, kiracı süzgeci elle yazılır.
private async Task<List<OrgChartNodeDto>> WithBranchRootsAsync(List<OrgChartNodeDto> nodes)
{
var nodeIds = nodes.Select(n => n.Id).ToHashSet();
var branchIds = nodes
.Where(n => n.ParentId.HasValue && !nodeIds.Contains(n.ParentId.Value))
.Select(n => n.ParentId!.Value)
.ToHashSet();
if (branchIds.Count == 0)
{
return nodes;
}
var tenantId = CurrentTenant.Id;
var branches = await _branchRepository.GetListAsync(b => b.TenantId == tenantId && branchIds.Contains(b.Id));
var branchNodes = branches
.OrderBy(b => b.Name)
.Select(b => new OrgChartNodeDto { Id = b.Id, Name = b.Name, NodeType = OrgChartNodeType.Branch });
return branchNodes.Concat(nodes).ToList();
}
private static Dictionary<Guid, List<OrgChartUserDto>> GroupUsersByJobPosition(
IEnumerable<IdentityUser> 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());
}
}