Files
OnboardXAdmin/SimpleAdmin.Application/Services/Onboarding/Employee/OnboardingEmployeeService.cs
T
2026-09-07 16:17:49 +08:00

256 lines
10 KiB
C#

namespace SimpleAdmin.Application;
/// <summary>
/// 实现正式员工档案查询业务能力,仅展示审核通过后创建的正式员工。
/// </summary>
public class OnboardingEmployeeService : IOnboardingEmployeeService
{
private readonly SqlSugarScope _db;
public OnboardingEmployeeService()
{
_db = DbContext.DB;
}
/// <summary>
/// 分页查询审核通过后生成的正式员工档案。
/// </summary>
public async Task<SqlSugarPagedList<ObEmployee>> Page(OnboardingEmployeePageInput input)
{
var result = await _db.Queryable<ObEmployee>()
.WhereIF(
!string.IsNullOrWhiteSpace(input.EmployeeName),
item => item.EmployeeName.Contains(input.EmployeeName)
|| item.IdCardNumber.Contains(input.EmployeeName))
.WhereIF(!string.IsNullOrWhiteSpace(input.Phone), item => item.Phone.Contains(input.Phone))
.WhereIF(input.CompanyId.HasValue, item => item.CompanyId == input.CompanyId.Value)
.WhereIF(input.DepartmentId.HasValue, item => item.DepartmentId == input.DepartmentId.Value)
.WhereIF(input.PositionId.HasValue, item => item.PositionId == input.PositionId.Value)
.OrderByDescending(item => item.CreateTime)
.ToPagedListAsync(input.PageNum, input.PageSize);
if (result.List != null && result.List.Any())
await FillEmployeeMetadata(result.List);
return result;
}
/// <summary>
/// 查询正式员工及其来源任务、合同和审核记录。
/// </summary>
public async Task<OnboardingEmployeeDetailOutput> Detail(BaseIdInput input)
{
var employee = await _db.Queryable<ObEmployee>().InSingleAsync(input.Id)
?? throw Oops.Bah("正式员工不存在");
var task = await _db.Queryable<ObOnboardingTask>().FirstAsync(x => x.EmployeeId == employee.Id);
await FillEmployeeMetadata(new[] { employee });
var employeeInfo = task == null
? null
: await _db.Queryable<ObTaskEmployeeInfo>()
.FirstAsync(item => item.TaskId == task.Id);
if (employeeInfo == null && !string.IsNullOrWhiteSpace(employee.ExtraInfoJson))
{
try
{
employeeInfo = global::System.Text.Json.JsonSerializer.Deserialize<ObTaskEmployeeInfo>(
employee.ExtraInfoJson);
}
catch (global::System.Text.Json.JsonException)
{
// 兼容历史异常快照:员工主档仍可正常查看,其余信息显示为空。
}
}
var contracts = task == null
? new List<ObTaskContract>()
: await _db.Queryable<ObTaskContract>()
.Where(item => item.TaskId == task.Id)
.ToListAsync();
var materials = await GetEmployeeMaterials(task?.Id);
var electronicSignProcess = task == null
? null
: await _db.Queryable<ObElectronicSignProcess>()
.Where(item => item.TaskId == task.Id && item.ArchiveFileId.HasValue)
.OrderByDescending(item => item.Id)
.FirstAsync();
var audits = task == null
? new List<ObTaskAudit>()
: await _db.Queryable<ObTaskAudit>()
.Where(item => item.TaskId == task.Id)
.OrderByDescending(item => item.AuditedAt)
.ToListAsync();
return new OnboardingEmployeeDetailOutput
{
Employee = employee,
Task = task,
EmployeeInfo = employeeInfo,
Contracts = contracts,
Materials = materials,
ElectronicSignArchiveFileId = electronicSignProcess?.ArchiveFileId,
Audits = audits
};
}
/// <summary>
/// 查询员工来源任务中上传的全部材料,并补充原始文件名、后缀和文件大小。
/// </summary>
private async Task<List<OnboardingTaskMaterialDetailOutput>> GetEmployeeMaterials(long? taskId)
{
if (!taskId.HasValue)
return new List<OnboardingTaskMaterialDetailOutput>();
var materials = await _db.Queryable<ObTaskMaterial>()
.Where(item => item.TaskId == taskId.Value)
.ToListAsync();
if (materials.Count == 0)
return new List<OnboardingTaskMaterialDetailOutput>();
var fileIds = materials
.Select(item => item.FileId)
.Distinct()
.ToList();
var files = await _db.Queryable<SysFile>()
.Where(item => fileIds.Contains(item.Id))
.ToListAsync();
var fileMap = files.ToDictionary(item => item.Id);
var requirements = await _db.Queryable<ObTaskMaterialRequirement>()
.Where(item => item.TaskId == taskId.Value)
.ToListAsync();
var sortMap = requirements
.GroupBy(item => item.MaterialTypeId)
.ToDictionary(group => group.Key, group => group.Min(item => item.SortCode));
return materials
.Select(material =>
{
var output = material.Adapt<OnboardingTaskMaterialDetailOutput>();
if (fileMap.TryGetValue(material.FileId, out var file))
{
output.FileName = file.Name;
output.FileSuffix = file.Suffix;
output.FileSizeInfo = file.SizeInfo;
}
return output;
})
.OrderBy(item => sortMap.GetValueOrDefault(item.MaterialTypeId, int.MaxValue))
.ThenBy(item => item.UploadedAt)
.ToList();
}
/// <summary>
/// 批量补充员工所属公司、部门和岗位名称,避免列表逐行查询数据库。
/// </summary>
private async Task FillEmployeeMetadata(IEnumerable<ObEmployee> employees)
{
var employeeList = employees.ToList();
if (employeeList.Count == 0)
return;
var companyIds = employeeList.Select(item => item.CompanyId).Distinct().ToList();
var departmentIds = employeeList
.Where(item => item.DepartmentId.HasValue)
.Select(item => item.DepartmentId.Value)
.Distinct()
.ToList();
var positionIds = employeeList.Select(item => item.PositionId).Distinct().ToList();
var companies = await _db.Queryable<ObCompany>()
.Where(item => companyIds.Contains(item.Id))
.ToListAsync();
var departments = departmentIds.Count == 0
? new List<ObDepartment>()
: await _db.Queryable<ObDepartment>()
.Where(item => departmentIds.Contains(item.Id))
.ToListAsync();
var positions = await _db.Queryable<ObPosition>()
.Where(item => positionIds.Contains(item.Id))
.ToListAsync();
var companyMap = companies.ToDictionary(item => item.Id, item => item.CompanyName);
var departmentMap = departments.ToDictionary(item => item.Id, item => item.DepartmentName);
var positionMap = positions.ToDictionary(item => item.Id, item => item.PositionName);
foreach (var employee in employeeList)
{
if (companyMap.TryGetValue(employee.CompanyId, out var companyName))
employee.CompanyName = companyName;
if (employee.DepartmentId.HasValue
&& departmentMap.TryGetValue(employee.DepartmentId.Value, out var departmentName))
{
employee.DepartmentName = departmentName;
}
if (positionMap.TryGetValue(employee.PositionId, out var positionName))
employee.PositionName = positionName;
}
await FillMissingEntryDates(employeeList);
}
/// <summary>
/// 兼容已建档但入职日期为空的历史员工,从来源任务的合同开始日期进行只读回填。
/// 新审核通过的员工会直接持久化该日期,不会进入此兼容分支。
/// </summary>
private async Task FillMissingEntryDates(List<ObEmployee> employees)
{
var missingEmployees = employees
.Where(item => !item.EntryDate.HasValue || !item.ContractStartDate.HasValue)
.ToList();
if (missingEmployees.Count == 0)
return;
var employeeIds = missingEmployees.Select(item => item.Id).ToList();
var tasks = await _db.Queryable<ObOnboardingTask>()
.Where(item => item.EmployeeId.HasValue
&& employeeIds.Contains(item.EmployeeId.Value))
.ToListAsync();
if (tasks.Count == 0)
return;
var taskIds = tasks.Select(item => item.Id).ToList();
var contracts = await _db.Queryable<ObTaskContract>()
.Where(item => taskIds.Contains(item.TaskId))
.ToListAsync();
if (contracts.Count == 0)
return;
var contractIds = contracts.Select(item => item.Id).ToList();
var startDateVariables = await _db.Queryable<ObTaskContractVariable>()
.Where(item => contractIds.Contains(item.TaskContractId)
&& item.VariableCode == "hr.ContractStartDate")
.ToListAsync();
var employeeIdByTaskId = tasks
.Where(item => item.EmployeeId.HasValue)
.ToDictionary(item => item.Id, item => item.EmployeeId.Value);
var taskIdByContractId = contracts.ToDictionary(item => item.Id, item => item.TaskId);
var datesByEmployeeId = startDateVariables
.Where(item => !string.IsNullOrWhiteSpace(item.VariableValue)
&& taskIdByContractId.ContainsKey(item.TaskContractId))
.Select(item => new
{
EmployeeId = employeeIdByTaskId[taskIdByContractId[item.TaskContractId]],
EntryDate = DateTime.TryParse(item.VariableValue, out var value)
? value.Date
: (DateTime?)null
})
.Where(item => item.EntryDate.HasValue)
.GroupBy(item => item.EmployeeId)
.ToDictionary(
group => group.Key,
group => group.Select(item => item.EntryDate.Value).Distinct().ToList());
foreach (var employee in missingEmployees)
{
if (!datesByEmployeeId.TryGetValue(employee.Id, out var dates) || dates.Count != 1)
continue;
employee.EntryDate ??= dates[0];
employee.ContractStartDate ??= dates[0];
}
}
}