256 lines
10 KiB
C#
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];
|
|
}
|
|
}
|
|
}
|