782 lines
31 KiB
C#
782 lines
31 KiB
C#
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 = null)
|
||
{
|
||
var personnel = await _context.Personnel
|
||
.Include(p => p.SubmittedByUnit)
|
||
.Include(p => p.ApprovedByUnit)
|
||
.FirstOrDefaultAsync(p => p.Id == personnelId);
|
||
if (personnel == null)
|
||
throw new ArgumentException("人员记录不存在");
|
||
|
||
var approvedByUnit = await _context.OrganizationalUnits.FindAsync(approvedByUnitId);
|
||
if (approvedByUnit == null)
|
||
throw new ArgumentException("审批单位不存在");
|
||
|
||
var previousStatus = personnel.Status;
|
||
var previousLevel = personnel.ApprovedLevel;
|
||
|
||
PersonnelLevel actualLevel;
|
||
if (level.HasValue)
|
||
{
|
||
actualLevel = level.Value;
|
||
}
|
||
else
|
||
{
|
||
// 区分首次审批和向上申报两种场景
|
||
if (previousStatus == PersonnelStatus.Pending)
|
||
{
|
||
// 首次审批:人员等级根据提交单位的层级确定
|
||
// 例如:营级单位提交的人才,团级审批后应为"营级人才"
|
||
actualLevel = (PersonnelLevel)(int)personnel.SubmittedByUnit!.Level;
|
||
|
||
// 验证审批单位层级必须高于提交单位层级(数值越小层级越高)
|
||
var unitLevelValue = (int)approvedByUnit.Level;
|
||
var submittedUnitLevelValue = (int)personnel.SubmittedByUnit!.Level;
|
||
if (unitLevelValue >= submittedUnitLevelValue)
|
||
throw new ArgumentException("审批单位层级必须高于提交单位层级");
|
||
}
|
||
else if (previousStatus == PersonnelStatus.Approved)
|
||
{
|
||
// 向上申报:人员等级升级为上报单位(即之前的审批单位)的层级
|
||
// 例如:营级人才由团级向上申报,师级审批后应为"团级人才"
|
||
if (personnel.ApprovedByUnit == null)
|
||
throw new ArgumentException("无法确定上报单位");
|
||
|
||
actualLevel = (PersonnelLevel)(int)personnel.ApprovedByUnit.Level;
|
||
|
||
// 验证审批单位层级必须高于当前审批单位层级(数值越小层级越高)
|
||
var unitLevelValue = (int)approvedByUnit.Level;
|
||
var currentApprovedUnitLevelValue = (int)personnel.ApprovedByUnit.Level;
|
||
if (unitLevelValue >= currentApprovedUnitLevelValue)
|
||
throw new ArgumentException("审批单位层级必须高于当前审批单位层级");
|
||
}
|
||
else
|
||
{
|
||
throw new ArgumentException("该人员状态不允许审批");
|
||
}
|
||
}
|
||
|
||
personnel.Status = PersonnelStatus.Approved;
|
||
personnel.ApprovedByUnitId = approvedByUnitId;
|
||
personnel.ApprovedLevel = actualLevel;
|
||
personnel.ApprovedAt = DateTime.UtcNow;
|
||
|
||
await _context.SaveChangesAsync();
|
||
|
||
// 记录审批历史
|
||
var userId = await GetUserIdByUnitAsync(approvedByUnitId);
|
||
var actionType = previousStatus == PersonnelStatus.Approved
|
||
? PersonnelApprovalAction.LevelUpgraded
|
||
: PersonnelApprovalAction.Approved;
|
||
var comments = previousStatus == PersonnelStatus.Approved
|
||
? $"向上申报通过,等级从{GetLevelName(previousLevel)}升级为{GetLevelName(actualLevel)}"
|
||
: "审批通过";
|
||
|
||
await RecordApprovalHistoryAsync(personnelId, actionType,
|
||
previousStatus, PersonnelStatus.Approved, previousLevel, actualLevel,
|
||
userId, approvedByUnitId, comments);
|
||
|
||
_logger.LogInformation("人员 {PersonnelId} 已被单位 {UnitId} 审批通过,等级:{Level}",
|
||
personnelId, approvedByUnitId, actualLevel);
|
||
|
||
return personnel;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取等级名称
|
||
/// </summary>
|
||
private static string GetLevelName(PersonnelLevel? level)
|
||
{
|
||
return level switch
|
||
{
|
||
PersonnelLevel.Division => "师级人才",
|
||
PersonnelLevel.Regiment => "团级人才",
|
||
PersonnelLevel.Battalion => "营级人才",
|
||
PersonnelLevel.Company => "连级人才",
|
||
_ => "未定级"
|
||
};
|
||
}
|
||
|
||
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("军衔不能为空");
|
||
|
||
// Gender和Age已设置默认值,不需要验证
|
||
// IdNumber(士兵证号)是可选的,不需要验证必填
|
||
|
||
if (personnel.SubmittedByUnitId <= 0)
|
||
errors.Add("提交单位不能为空");
|
||
|
||
// 验证士兵证号格式(如果提供了)
|
||
// 注意:士兵证号不是身份证号,格式可能不同,这里移除18位的限制
|
||
// 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)
|
||
.Include(p => p.ApprovedByUnit)
|
||
.FirstOrDefaultAsync(p => p.Id == personnelId);
|
||
|
||
if (personnel == null)
|
||
return false;
|
||
|
||
// 已拒绝的人员不能审批
|
||
if (personnel.Status == PersonnelStatus.Rejected)
|
||
return false;
|
||
|
||
// 本单位不能审批自己提交的人员,必须由上级单位审批
|
||
if (user.OrganizationalUnitId == personnel.SubmittedByUnitId)
|
||
return false;
|
||
|
||
// 如果是已审批的人员(向上申报场景)
|
||
if (personnel.Status == PersonnelStatus.Approved && personnel.ApprovedByUnitId.HasValue)
|
||
{
|
||
// 用户单位层级必须高于已审批单位层级(数值越小层级越高)
|
||
if ((int)user.OrganizationalUnit!.Level >= (int)personnel.ApprovedByUnit!.Level)
|
||
return false;
|
||
|
||
// 向上申报场景:检查用户单位是否是当前审批单位的上级
|
||
var isParentOfApprovedUnit = await _organizationService.IsParentUnitAsync(
|
||
user.OrganizationalUnitId, personnel.ApprovedByUnitId.Value);
|
||
return isParentOfApprovedUnit;
|
||
}
|
||
|
||
// 首次审批场景:检查用户的组织单位是否是提交单位的上级
|
||
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作为系统操作
|
||
}
|
||
|
||
/// <summary>
|
||
/// 发起向上申报请求
|
||
/// </summary>
|
||
public async Task<Personnel> RequestUpgradeAsync(int personnelId, int requestByUnitId)
|
||
{
|
||
var personnel = await _context.Personnel
|
||
.Include(p => p.SubmittedByUnit)
|
||
.Include(p => p.ApprovedByUnit)
|
||
.FirstOrDefaultAsync(p => p.Id == personnelId);
|
||
|
||
if (personnel == null)
|
||
throw new ArgumentException("人员记录不存在");
|
||
|
||
if (personnel.Status != PersonnelStatus.Approved)
|
||
throw new ArgumentException("只有已审批的人员才能向上申报");
|
||
|
||
if (personnel.PendingUpgradeByUnitId.HasValue)
|
||
throw new ArgumentException("该人员已有待处理的向上申报请求");
|
||
|
||
var requestByUnit = await _context.OrganizationalUnits.FindAsync(requestByUnitId);
|
||
if (requestByUnit == null)
|
||
throw new ArgumentException("申报单位不存在");
|
||
|
||
// 师级人才不能再向上申报
|
||
if (personnel.ApprovedLevel == PersonnelLevel.Division)
|
||
throw new ArgumentException("师级人才不能再向上申报");
|
||
|
||
// 特殊情况:师部可以直接升级团级人才为师级
|
||
// 此时师部既是申报单位也是审批单位
|
||
if (requestByUnit.Level == OrganizationalLevel.Division &&
|
||
personnel.ApprovedLevel == PersonnelLevel.Regiment)
|
||
{
|
||
// 师部直接升级团级人才,不需要验证是否是审批单位
|
||
// 标记为待向上申报(由师部自己审批)
|
||
personnel.PendingUpgradeByUnitId = requestByUnitId;
|
||
|
||
await _context.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("团级人才 {PersonnelId} 已由师部 {UnitId} 发起直接升级请求",
|
||
personnelId, requestByUnitId);
|
||
|
||
return personnel;
|
||
}
|
||
|
||
// 验证申报单位必须是当前审批单位
|
||
if (personnel.ApprovedByUnitId != requestByUnitId)
|
||
throw new ArgumentException("只有当前审批单位才能发起向上申报");
|
||
|
||
// 标记为待向上申报
|
||
personnel.PendingUpgradeByUnitId = requestByUnitId;
|
||
|
||
await _context.SaveChangesAsync();
|
||
|
||
_logger.LogInformation("人员 {PersonnelId} 已由单位 {UnitId} 发起向上申报请求",
|
||
personnelId, requestByUnitId);
|
||
|
||
return personnel;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 审批向上申报请求
|
||
/// </summary>
|
||
public async Task<Personnel> ApproveUpgradeAsync(int personnelId, int approvedByUnitId)
|
||
{
|
||
var personnel = await _context.Personnel
|
||
.Include(p => p.SubmittedByUnit)
|
||
.Include(p => p.ApprovedByUnit)
|
||
.Include(p => p.PendingUpgradeByUnit)
|
||
.FirstOrDefaultAsync(p => p.Id == personnelId);
|
||
|
||
if (personnel == null)
|
||
throw new ArgumentException("人员记录不存在");
|
||
|
||
if (!personnel.PendingUpgradeByUnitId.HasValue)
|
||
throw new ArgumentException("该人员没有待处理的向上申报请求");
|
||
|
||
var approvedByUnit = await _context.OrganizationalUnits.FindAsync(approvedByUnitId);
|
||
if (approvedByUnit == null)
|
||
throw new ArgumentException("审批单位不存在");
|
||
|
||
_logger.LogInformation("ApproveUpgrade: personnelId={PersonnelId}, approvedByUnitId={ApprovedByUnitId}, pendingUpgradeByUnitId={PendingUpgradeByUnitId}, approvedByUnitLevel={Level}, personnelApprovedLevel={PersonnelLevel}",
|
||
personnelId, approvedByUnitId, personnel.PendingUpgradeByUnitId.Value, approvedByUnit.Level, personnel.ApprovedLevel);
|
||
|
||
var previousLevel = personnel.ApprovedLevel;
|
||
PersonnelLevel newLevel;
|
||
|
||
// 特殊情况:师部直接升级团级人才为师级
|
||
var isDivisionUnit = approvedByUnit.Level == OrganizationalLevel.Division;
|
||
var isRegimentPersonnel = personnel.ApprovedLevel == PersonnelLevel.Regiment;
|
||
|
||
_logger.LogInformation("检查条件: isDivisionUnit={IsDivision}, isRegimentPersonnel={IsRegiment}",
|
||
isDivisionUnit, isRegimentPersonnel);
|
||
|
||
if (isDivisionUnit && isRegimentPersonnel)
|
||
{
|
||
// 师部直接升级团级人才为师级
|
||
newLevel = PersonnelLevel.Division;
|
||
_logger.LogInformation("师部直接升级团级人才为师级");
|
||
}
|
||
else
|
||
{
|
||
// 验证审批单位必须是申报单位的上级
|
||
var isParent = await _organizationService.IsParentUnitAsync(approvedByUnitId, personnel.PendingUpgradeByUnitId.Value);
|
||
_logger.LogInformation("检查上级关系: isParent={IsParent}", isParent);
|
||
if (!isParent)
|
||
throw new ArgumentException("审批单位必须是申报单位的上级");
|
||
|
||
// 人才等级升级为申报单位的等级
|
||
newLevel = (PersonnelLevel)(int)personnel.PendingUpgradeByUnit!.Level;
|
||
}
|
||
|
||
personnel.ApprovedLevel = newLevel;
|
||
personnel.ApprovedByUnitId = approvedByUnitId;
|
||
personnel.ApprovedAt = DateTime.UtcNow;
|
||
personnel.PendingUpgradeByUnitId = null; // 清除待申报标记
|
||
|
||
await _context.SaveChangesAsync();
|
||
|
||
// 记录审批历史
|
||
var userId = await GetUserIdByUnitAsync(approvedByUnitId);
|
||
await RecordApprovalHistoryAsync(personnelId, PersonnelApprovalAction.LevelUpgraded,
|
||
PersonnelStatus.Approved, PersonnelStatus.Approved, previousLevel, newLevel,
|
||
userId, approvedByUnitId, $"向上申报通过,等级从{GetLevelName(previousLevel)}升级为{GetLevelName(newLevel)}");
|
||
|
||
_logger.LogInformation("人员 {PersonnelId} 向上申报已被单位 {UnitId} 审批通过,等级升级为 {Level}",
|
||
personnelId, approvedByUnitId, newLevel);
|
||
|
||
return personnel;
|
||
}
|
||
}
|