corps/src/MilitaryTrainingManagement/Services/Implementations/PersonnelService.cs
2026-01-14 20:52:59 +08:00

579 lines
22 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.Text.Json;
using Microsoft.EntityFrameworkCore;
using MilitaryTrainingManagement.Data;
using MilitaryTrainingManagement.Models.Entities;
using MilitaryTrainingManagement.Models.Enums;
using MilitaryTrainingManagement.Services.Interfaces;
namespace MilitaryTrainingManagement.Services.Implementations;
/// <summary>
/// 人员管理服务实现
/// </summary>
public class PersonnelService : IPersonnelService
{
private readonly ApplicationDbContext _context;
private readonly IOrganizationService _organizationService;
private readonly IFileUploadService _fileUploadService;
private readonly IWorkflowService _workflowService;
private readonly ILogger<PersonnelService> _logger;
public PersonnelService(
ApplicationDbContext context,
IOrganizationService organizationService,
IFileUploadService fileUploadService,
IWorkflowService workflowService,
ILogger<PersonnelService> logger)
{
_context = context;
_organizationService = organizationService;
_fileUploadService = fileUploadService;
_workflowService = workflowService;
_logger = logger;
}
public async Task<Personnel?> GetByIdAsync(int id)
{
return await _context.Personnel
.Include(p => p.SubmittedByUnit)
.Include(p => p.ApprovedByUnit)
.FirstOrDefaultAsync(p => p.Id == id);
}
public async Task<IEnumerable<Personnel>> GetByUnitAsync(int unitId, bool includeSubordinates = false)
{
if (includeSubordinates)
{
var subordinates = await _organizationService.GetSubordinatesAsync(unitId);
var unitIds = subordinates.Select(s => s.Id).ToList();
unitIds.Add(unitId);
return await _context.Personnel
.Include(p => p.SubmittedByUnit)
.Include(p => p.ApprovedByUnit)
.Where(p => unitIds.Contains(p.SubmittedByUnitId))
.OrderByDescending(p => p.SubmittedAt)
.ToListAsync();
}
return await _context.Personnel
.Include(p => p.SubmittedByUnit)
.Include(p => p.ApprovedByUnit)
.Where(p => p.SubmittedByUnitId == unitId)
.OrderByDescending(p => p.SubmittedAt)
.ToListAsync();
}
public async Task<Personnel> CreateAsync(Personnel personnel)
{
personnel.Status = PersonnelStatus.Pending;
personnel.SubmittedAt = DateTime.UtcNow;
_context.Personnel.Add(personnel);
await _context.SaveChangesAsync();
return personnel;
}
public async Task<Personnel> UpdateAsync(Personnel personnel)
{
_context.Personnel.Update(personnel);
await _context.SaveChangesAsync();
return personnel;
}
public async Task<Personnel> ApproveAsync(int personnelId, int approvedByUnitId, PersonnelLevel level)
{
var personnel = await _context.Personnel.FindAsync(personnelId);
if (personnel == null)
throw new ArgumentException("人员记录不存在");
var approvedByUnit = await _context.OrganizationalUnits.FindAsync(approvedByUnitId);
if (approvedByUnit == null)
throw new ArgumentException("审批单位不存在");
// 验证审批单位层级必须高于或等于人员等级(数值越小层级越高)
var unitLevelValue = (int)approvedByUnit.Level;
var personnelLevelValue = (int)level;
if (unitLevelValue > personnelLevelValue)
throw new ArgumentException("审批单位层级不足以审批该等级人才");
var previousStatus = personnel.Status;
var previousLevel = personnel.ApprovedLevel;
personnel.Status = PersonnelStatus.Approved;
personnel.ApprovedByUnitId = approvedByUnitId;
personnel.ApprovedLevel = level;
personnel.ApprovedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
// 记录审批历史
var userId = await GetUserIdByUnitAsync(approvedByUnitId);
await RecordApprovalHistoryAsync(personnelId, PersonnelApprovalAction.Approved,
previousStatus, PersonnelStatus.Approved, previousLevel, level,
userId, approvedByUnitId, "审批通过");
_logger.LogInformation("人员 {PersonnelId} 已被单位 {UnitId} 审批通过,等级:{Level}",
personnelId, approvedByUnitId, level);
return personnel;
}
public async Task<Personnel> RejectAsync(int personnelId, int reviewedByUserId)
{
var personnel = await _context.Personnel.FindAsync(personnelId);
if (personnel == null)
throw new ArgumentException("人员记录不存在");
var previousStatus = personnel.Status;
personnel.Status = PersonnelStatus.Rejected;
await _context.SaveChangesAsync();
// 记录审批历史
await RecordApprovalHistoryAsync(personnelId, PersonnelApprovalAction.Rejected,
previousStatus, PersonnelStatus.Rejected, null, null,
reviewedByUserId, null, "审批拒绝");
_logger.LogInformation("人员 {PersonnelId} 已被用户 {UserId} 拒绝", personnelId, reviewedByUserId);
return personnel;
}
public async Task DeleteAsync(int id)
{
var personnel = await _context.Personnel.FindAsync(id);
if (personnel == null)
throw new ArgumentException("人员记录不存在");
// 删除关联的文件
if (!string.IsNullOrEmpty(personnel.PhotoPath))
{
await _fileUploadService.DeleteFileAsync(personnel.PhotoPath);
}
if (!string.IsNullOrEmpty(personnel.SupportingDocuments))
{
await _fileUploadService.DeleteFileAsync(personnel.SupportingDocuments);
}
_context.Personnel.Remove(personnel);
await _context.SaveChangesAsync();
_logger.LogInformation("人员记录 {PersonnelId} 已删除", id);
}
public async Task<Personnel> SubmitPersonnelAsync(Personnel personnel, IFormFile? photo, IFormFile? supportingDocument)
{
// 验证人员数据完整性
var validation = await ValidatePersonnelDataAsync(personnel);
if (!validation.IsValid)
{
throw new ArgumentException($"人员数据验证失败:{string.Join(", ", validation.Errors)}");
}
// 处理照片上传
if (photo != null)
{
try
{
personnel.PhotoPath = await _fileUploadService.UploadPhotoAsync(photo);
_logger.LogInformation("人员照片上传成功:{PhotoPath}", personnel.PhotoPath);
}
catch (Exception ex)
{
_logger.LogError(ex, "人员照片上传失败");
throw new InvalidOperationException($"照片上传失败:{ex.Message}");
}
}
// 处理支持文档上传
if (supportingDocument != null)
{
try
{
personnel.SupportingDocuments = await _fileUploadService.UploadDocumentAsync(supportingDocument);
_logger.LogInformation("支持文档上传成功:{DocumentPath}", personnel.SupportingDocuments);
}
catch (Exception ex)
{
_logger.LogError(ex, "支持文档上传失败");
throw new InvalidOperationException($"支持文档上传失败:{ex.Message}");
}
}
// 设置提交状态和时间
personnel.Status = PersonnelStatus.Pending;
personnel.SubmittedAt = DateTime.UtcNow;
_context.Personnel.Add(personnel);
await _context.SaveChangesAsync();
_logger.LogInformation("人员数据提交成功ID{PersonnelId},提交单位:{UnitId}",
personnel.Id, personnel.SubmittedByUnitId);
return personnel;
}
public async Task<PersonnelValidationResult> ValidatePersonnelDataAsync(Personnel personnel)
{
var errors = new List<string>();
// 验证必填字段
if (string.IsNullOrWhiteSpace(personnel.Name))
errors.Add("姓名不能为空");
if (string.IsNullOrWhiteSpace(personnel.Position))
errors.Add("职位不能为空");
if (string.IsNullOrWhiteSpace(personnel.Rank))
errors.Add("军衔不能为空");
if (string.IsNullOrWhiteSpace(personnel.Gender))
errors.Add("性别不能为空");
if (string.IsNullOrWhiteSpace(personnel.IdNumber))
errors.Add("身份证号不能为空");
if (personnel.Age <= 0 || personnel.Age > 100)
errors.Add("年龄必须在1-100之间");
if (personnel.SubmittedByUnitId <= 0)
errors.Add("提交单位不能为空");
// 验证身份证号格式(简单验证)
if (!string.IsNullOrWhiteSpace(personnel.IdNumber))
{
if (personnel.IdNumber.Length != 18)
errors.Add("身份证号必须为18位");
}
// 验证身份证号唯一性
if (!string.IsNullOrWhiteSpace(personnel.IdNumber))
{
var existingPersonnel = await _context.Personnel
.FirstOrDefaultAsync(p => p.IdNumber == personnel.IdNumber && p.Id != personnel.Id);
if (existingPersonnel != null)
errors.Add("身份证号已存在");
}
// 验证提交单位是否存在
if (personnel.SubmittedByUnitId > 0)
{
var unit = await _context.OrganizationalUnits.FindAsync(personnel.SubmittedByUnitId);
if (unit == null)
errors.Add("提交单位不存在");
}
return errors.Any() ? PersonnelValidationResult.Failure(errors.ToArray()) : PersonnelValidationResult.Success();
}
public async Task<OrganizationalUnit> GetApprovalTargetUnitAsync(int submittingUnitId)
{
var submittingUnit = await _context.OrganizationalUnits
.Include(u => u.Parent)
.FirstOrDefaultAsync(u => u.Id == submittingUnitId);
if (submittingUnit == null)
throw new ArgumentException("提交单位不存在");
// 审批路由逻辑:提交给上级单位审批
if (submittingUnit.Parent != null)
{
return submittingUnit.Parent;
}
// 如果是师团级别,则自己审批
if (submittingUnit.Level == OrganizationalLevel.Division)
{
return submittingUnit;
}
throw new InvalidOperationException("无法确定审批目标单位");
}
public async Task<bool> CanModifyPersonnelAsync(int personnelId)
{
var personnel = await _context.Personnel.FindAsync(personnelId);
if (personnel == null)
return false;
// 只有待审批状态的人员可以直接修改
if (personnel.Status == PersonnelStatus.Pending)
return true;
// 已审批的人员需要通过工作流修改
return false;
}
public async Task<ApprovalRequest> RequestPersonnelModificationAsync(int personnelId, int requestedByUserId, string reason, Personnel modifiedPersonnel)
{
var originalPersonnel = await _context.Personnel
.Include(p => p.SubmittedByUnit)
.FirstOrDefaultAsync(p => p.Id == personnelId);
if (originalPersonnel == null)
throw new ArgumentException("人员记录不存在");
if (originalPersonnel.Status != PersonnelStatus.Approved)
throw new InvalidOperationException("只能修改已审批的人员记录");
var user = await _context.UserAccounts
.Include(u => u.OrganizationalUnit)
.FirstOrDefaultAsync(u => u.Id == requestedByUserId);
if (user == null)
throw new ArgumentException("请求用户不存在");
// 序列化原始数据和修改数据
var originalData = JsonSerializer.Serialize(new
{
originalPersonnel.Name,
originalPersonnel.Position,
originalPersonnel.Rank,
originalPersonnel.Gender,
originalPersonnel.IdNumber,
originalPersonnel.ProfessionalTitle,
originalPersonnel.EducationLevel,
originalPersonnel.Age,
originalPersonnel.Height,
originalPersonnel.ContactInfo,
originalPersonnel.Hometown,
originalPersonnel.TrainingParticipation,
originalPersonnel.Achievements
});
var requestedChanges = JsonSerializer.Serialize(new
{
modifiedPersonnel.Name,
modifiedPersonnel.Position,
modifiedPersonnel.Rank,
modifiedPersonnel.Gender,
modifiedPersonnel.IdNumber,
modifiedPersonnel.ProfessionalTitle,
modifiedPersonnel.EducationLevel,
modifiedPersonnel.Age,
modifiedPersonnel.Height,
modifiedPersonnel.ContactInfo,
modifiedPersonnel.Hometown,
modifiedPersonnel.TrainingParticipation,
modifiedPersonnel.Achievements
});
var request = await _workflowService.CreateModificationRequestAsync(
ApprovalRequestType.PersonnelModification,
personnelId,
requestedByUserId,
user.OrganizationalUnitId,
reason,
originalData,
requestedChanges);
_logger.LogInformation("人员修改请求已创建ID{RequestId}人员ID{PersonnelId}",
request.Id, personnelId);
return request;
}
public async Task<IEnumerable<Personnel>> GetVisiblePersonnelAsync(int unitId, OrganizationalLevel userLevel)
{
// 根据组织层级获取可见的人员
// 用户只能查看当前和下级组织单位的人员
var subordinates = await _organizationService.GetSubordinatesAsync(unitId);
var visibleUnitIds = subordinates.Select(s => s.Id).ToList();
visibleUnitIds.Add(unitId);
var query = _context.Personnel
.Include(p => p.SubmittedByUnit)
.Include(p => p.ApprovedByUnit)
.Where(p => visibleUnitIds.Contains(p.SubmittedByUnitId));
// 根据人员等级进一步过滤
// 用户只能查看等级不高于自己组织层级的人员
var maxPersonnelLevel = (PersonnelLevel)(int)userLevel;
query = query.Where(p => p.ApprovedLevel == null || p.ApprovedLevel >= maxPersonnelLevel);
return await query.OrderByDescending(p => p.SubmittedAt).ToListAsync();
}
public async Task<Personnel> TransferPersonnelAsync(int personnelId, int targetUnitId, int approvedByUnitId)
{
var personnel = await _context.Personnel
.Include(p => p.SubmittedByUnit)
.FirstOrDefaultAsync(p => p.Id == personnelId);
if (personnel == null)
throw new ArgumentException("人员记录不存在");
if (personnel.Status != PersonnelStatus.Approved)
throw new InvalidOperationException("只能转移已审批的人员");
var targetUnit = await _context.OrganizationalUnits.FindAsync(targetUnitId);
if (targetUnit == null)
throw new ArgumentException("目标单位不存在");
var approvedByUnit = await _context.OrganizationalUnits.FindAsync(approvedByUnitId);
if (approvedByUnit == null)
throw new ArgumentException("审批单位不存在");
// 验证审批单位有权限进行跨层级转移
// 审批单位层级必须高于或等于目标单位层级
if ((int)approvedByUnit.Level > (int)targetUnit.Level)
throw new UnauthorizedAccessException("审批单位层级不足以进行此转移");
// 更新人员所属单位
personnel.SubmittedByUnitId = targetUnitId;
// 根据新的审批单位重新设置人员等级
var newLevel = (PersonnelLevel)(int)approvedByUnit.Level;
personnel.ApprovedLevel = newLevel;
personnel.ApprovedByUnitId = approvedByUnitId;
personnel.ApprovedAt = DateTime.UtcNow;
await _context.SaveChangesAsync();
_logger.LogInformation("人员 {PersonnelId} 已从单位 {FromUnitId} 转移到单位 {ToUnitId},审批单位:{ApprovedByUnitId}",
personnelId, personnel.SubmittedByUnit.Id, targetUnitId, approvedByUnitId);
return personnel;
}
public async Task<IEnumerable<Personnel>> GetPendingApprovalPersonnelAsync(int unitId)
{
// 获取需要当前单位审批的人员(来自下级单位的提交)
var subordinates = await _organizationService.GetSubordinatesAsync(unitId);
var subordinateIds = subordinates.Select(s => s.Id).ToList();
return await _context.Personnel
.Include(p => p.SubmittedByUnit)
.Where(p => subordinateIds.Contains(p.SubmittedByUnitId) && p.Status == PersonnelStatus.Pending)
.OrderBy(p => p.SubmittedAt)
.ToListAsync();
}
public async Task<IEnumerable<Personnel>> BatchApprovePersonnelAsync(int[] personnelIds, int approvedByUnitId, PersonnelLevel level)
{
var approvedPersonnel = new List<Personnel>();
foreach (var personnelId in personnelIds)
{
try
{
var personnel = await ApproveAsync(personnelId, approvedByUnitId, level);
approvedPersonnel.Add(personnel);
// 记录审批历史
await RecordApprovalHistoryAsync(personnelId, PersonnelApprovalAction.Approved,
PersonnelStatus.Pending, PersonnelStatus.Approved, null, level,
await GetUserIdByUnitAsync(approvedByUnitId), approvedByUnitId, "批量审批通过");
}
catch (Exception ex)
{
_logger.LogError(ex, "批量审批人员 {PersonnelId} 失败", personnelId);
// 继续处理其他人员,不中断整个批量操作
}
}
return approvedPersonnel;
}
public async Task<IEnumerable<Personnel>> BatchRejectPersonnelAsync(int[] personnelIds, int reviewedByUserId, string reason)
{
var rejectedPersonnel = new List<Personnel>();
foreach (var personnelId in personnelIds)
{
try
{
var personnel = await RejectAsync(personnelId, reviewedByUserId);
rejectedPersonnel.Add(personnel);
// 记录审批历史
await RecordApprovalHistoryAsync(personnelId, PersonnelApprovalAction.Rejected,
PersonnelStatus.Pending, PersonnelStatus.Rejected, null, null,
reviewedByUserId, null, reason);
}
catch (Exception ex)
{
_logger.LogError(ex, "批量拒绝人员 {PersonnelId} 失败", personnelId);
// 继续处理其他人员,不中断整个批量操作
}
}
return rejectedPersonnel;
}
public async Task<IEnumerable<PersonnelApprovalHistory>> GetPersonnelApprovalHistoryAsync(int personnelId)
{
return await _context.PersonnelApprovalHistories
.Include(h => h.ReviewedByUser)
.Include(h => h.ReviewedByUnit)
.Where(h => h.PersonnelId == personnelId)
.OrderByDescending(h => h.ReviewedAt)
.ToListAsync();
}
public async Task<bool> CanApprovePersonnelAsync(int userId, int personnelId)
{
var user = await _context.UserAccounts
.Include(u => u.OrganizationalUnit)
.FirstOrDefaultAsync(u => u.Id == userId);
if (user == null)
return false;
var personnel = await _context.Personnel
.Include(p => p.SubmittedByUnit)
.FirstOrDefaultAsync(p => p.Id == personnelId);
if (personnel == null || personnel.Status != PersonnelStatus.Pending)
return false;
// 同一单位可以审批自己提交的人员
if (user.OrganizationalUnitId == personnel.SubmittedByUnitId)
return true;
// 检查用户的组织单位是否是提交单位的上级
var isParent = await _organizationService.IsParentUnitAsync(user.OrganizationalUnitId, personnel.SubmittedByUnitId);
return isParent;
}
/// <summary>
/// 记录人员审批历史
/// </summary>
private async Task RecordApprovalHistoryAsync(int personnelId, PersonnelApprovalAction action,
PersonnelStatus? previousStatus, PersonnelStatus newStatus,
PersonnelLevel? previousLevel, PersonnelLevel? newLevel,
int reviewedByUserId, int? reviewedByUnitId, string? comments)
{
var history = new PersonnelApprovalHistory
{
PersonnelId = personnelId,
Action = action,
PreviousStatus = previousStatus,
NewStatus = newStatus,
PreviousLevel = previousLevel,
NewLevel = newLevel,
ReviewedByUserId = reviewedByUserId,
ReviewedByUnitId = reviewedByUnitId,
Comments = comments,
ReviewedAt = DateTime.UtcNow
};
_context.PersonnelApprovalHistories.Add(history);
await _context.SaveChangesAsync();
}
/// <summary>
/// 根据单位ID获取用户ID用于审批历史记录
/// </summary>
private async Task<int> GetUserIdByUnitAsync(int unitId)
{
var user = await _context.UserAccounts
.FirstOrDefaultAsync(u => u.OrganizationalUnitId == unitId);
return user?.Id ?? 0; // 如果找不到用户返回0作为系统操作
}
}