diff --git a/SimpleAdmin.Application/Services/Onboarding/Employee/Dto/OnboardingEmployeeInput.cs b/SimpleAdmin.Application/Services/Onboarding/Employee/Dto/OnboardingEmployeeInput.cs
index 7e51d96..6e14320 100644
--- a/SimpleAdmin.Application/Services/Onboarding/Employee/Dto/OnboardingEmployeeInput.cs
+++ b/SimpleAdmin.Application/Services/Onboarding/Employee/Dto/OnboardingEmployeeInput.cs
@@ -46,6 +46,58 @@ public class OnboardingEmployeePageInput : BasePageInput
/// 社保状态。
///
public string SocialInsuranceStatus { get; set; }
+
+ ///
+ /// 合同即将到期筛选(30天内到期或已到期)。
+ ///
+ public bool? ContractExpiringSoon { get; set; }
+}
+
+///
+/// 正式员工档案分页数据及当前查询条件下的维度统计。
+///
+public class OnboardingEmployeePageOutput : SqlSugarPagedList
+{
+ ///
+ /// 不受快捷维度筛选影响的员工维度统计。
+ ///
+ public OnboardingEmployeeStatisticsOutput Statistics { get; set; } = new();
+}
+
+///
+/// 正式员工档案维度统计。
+///
+public class OnboardingEmployeeStatisticsOutput
+{
+ ///
+ /// 在册总数。
+ ///
+ public int Total { get; set; }
+
+ ///
+ /// 正式员工数量。
+ ///
+ public int FullTime { get; set; }
+
+ ///
+ /// 兼职员工数量。
+ ///
+ public int PartTime { get; set; }
+
+ ///
+ /// 试用期待转正数量。
+ ///
+ public int Probation { get; set; }
+
+ ///
+ /// 社保已缴纳数量。
+ ///
+ public int SocialPurchased { get; set; }
+
+ ///
+ /// 合同即将到期数量(30天内到期或已到期)。
+ ///
+ public int ExpiringContract { get; set; }
}
///
diff --git a/SimpleAdmin.Application/Services/Onboarding/Employee/IOnboardingEmployeeService.cs b/SimpleAdmin.Application/Services/Onboarding/Employee/IOnboardingEmployeeService.cs
index 52036b9..47eda1f 100644
--- a/SimpleAdmin.Application/Services/Onboarding/Employee/IOnboardingEmployeeService.cs
+++ b/SimpleAdmin.Application/Services/Onboarding/Employee/IOnboardingEmployeeService.cs
@@ -6,9 +6,9 @@ namespace SimpleAdmin.Application;
public interface IOnboardingEmployeeService : ITransient
{
///
- /// 分页查询审核通过后生成的正式员工档案。
+ /// 分页查询审核通过后生成的正式员工档案及维度统计。
///
- Task> Page(OnboardingEmployeePageInput input);
+ Task Page(OnboardingEmployeePageInput input);
///
/// 按当前列表筛选条件导出员工明细。
///
diff --git a/SimpleAdmin.Application/Services/Onboarding/Employee/OnboardingEmployeeService.cs b/SimpleAdmin.Application/Services/Onboarding/Employee/OnboardingEmployeeService.cs
index 3b63c07..5cd6d66 100644
--- a/SimpleAdmin.Application/Services/Onboarding/Employee/OnboardingEmployeeService.cs
+++ b/SimpleAdmin.Application/Services/Onboarding/Employee/OnboardingEmployeeService.cs
@@ -20,18 +20,30 @@ public class OnboardingEmployeeService : IOnboardingEmployeeService
}
///
- /// 分页查询审核通过后生成的员工档案。
+ /// 分页查询审核通过后生成的员工档案及维度统计。
///
- public async Task> Page(OnboardingEmployeePageInput input)
+ public async Task Page(OnboardingEmployeePageInput input)
{
- var result = await BuildEmployeeQuery(input)
+ var list = await BuildEmployeeQuery(input)
.OrderByDescending(item => item.CreateTime)
.ToPagedListAsync(input.PageNum, input.PageSize);
- if (result.List != null && result.List.Any())
- await FillEmployeeMetadata(result.List);
+ if (list.List != null && list.List.Any())
+ await FillEmployeeMetadata(list.List);
- return result;
+ var stats = await CalculateStatistics(input);
+
+ return new OnboardingEmployeePageOutput
+ {
+ PageNum = list.PageNum,
+ PageSize = list.PageSize,
+ Total = list.Total,
+ Pages = list.Pages,
+ List = list.List,
+ HasPrevPages = list.HasPrevPages,
+ HasNextPages = list.HasNextPages,
+ Statistics = stats
+ };
}
///
@@ -633,21 +645,31 @@ public class OnboardingEmployeeService : IOnboardingEmployeeService
await FillMissingEntryDates(employeeList);
}
+ ///
+ /// 构造基础员工查询(包含组织、岗位和姓名/手机号搜索),供列表、统计和导出复用。
+ ///
+ private ISugarQueryable BuildBaseEmployeeQuery(OnboardingEmployeePageInput input)
+ {
+ return _db.Queryable()
+ .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 == true, item => item.CompanyId == input.CompanyId.Value)
+ .WhereIF(input?.DepartmentId.HasValue == true, item => item.DepartmentId == input.DepartmentId.Value)
+ .WhereIF(input?.PositionId.HasValue == true, item => item.PositionId == input.PositionId.Value);
+ }
+
///
/// 构造列表和导出共用的员工筛选,保证页面看到的范围与导出范围一致。
///
private ISugarQueryable BuildEmployeeQuery(OnboardingEmployeePageInput input)
{
input ??= new OnboardingEmployeePageInput();
- return _db.Queryable()
- .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)
+ var expiringThreshold = DateTime.Today.AddDays(30).Date.AddDays(1).AddSeconds(-1);
+
+ return BuildBaseEmployeeQuery(input)
.WhereIF(
!string.IsNullOrWhiteSpace(input.EmploymentType),
item => item.EmploymentType == input.EmploymentType)
@@ -656,7 +678,50 @@ public class OnboardingEmployeeService : IOnboardingEmployeeService
item => item.RegularizationStatus == input.RegularizationStatus)
.WhereIF(
!string.IsNullOrWhiteSpace(input.SocialInsuranceStatus),
- item => item.SocialInsuranceStatus == input.SocialInsuranceStatus);
+ item => item.SocialInsuranceStatus == input.SocialInsuranceStatus)
+ .WhereIF(
+ input.ContractExpiringSoon == true,
+ item => item.ContractEndDate != null && item.ContractEndDate <= expiringThreshold);
+ }
+
+ ///
+ /// 计算员工档案的各项维度统计数量,受组织及关键字联动,但不受快捷状态筛选影响。
+ ///
+ private async Task CalculateStatistics(OnboardingEmployeePageInput input)
+ {
+ var expiringThreshold = DateTime.Today.AddDays(30).Date.AddDays(1).AddSeconds(-1);
+
+ var total = await BuildBaseEmployeeQuery(input).CountAsync();
+ if (total == 0)
+ {
+ return new OnboardingEmployeeStatisticsOutput();
+ }
+
+ var fullTime = await BuildBaseEmployeeQuery(input)
+ .Where(item => item.EmploymentType == EmployeeEmploymentType.FullTime)
+ .CountAsync();
+ var partTime = await BuildBaseEmployeeQuery(input)
+ .Where(item => item.EmploymentType == EmployeeEmploymentType.PartTime)
+ .CountAsync();
+ var probation = await BuildBaseEmployeeQuery(input)
+ .Where(item => item.RegularizationStatus == EmployeeRegularizationStatus.Probation)
+ .CountAsync();
+ var socialPurchased = await BuildBaseEmployeeQuery(input)
+ .Where(item => item.SocialInsuranceStatus == EmployeeSocialInsuranceStatus.Purchased)
+ .CountAsync();
+ var expiringContract = await BuildBaseEmployeeQuery(input)
+ .Where(item => item.ContractEndDate != null && item.ContractEndDate <= expiringThreshold)
+ .CountAsync();
+
+ return new OnboardingEmployeeStatisticsOutput
+ {
+ Total = total,
+ FullTime = fullTime,
+ PartTime = partTime,
+ Probation = probation,
+ SocialPurchased = socialPurchased,
+ ExpiringContract = expiringContract
+ };
}
private static void WriteEmployeeSheet(