diff --git a/src/MilitaryTrainingManagement/Controllers/PersonnelController.cs b/src/MilitaryTrainingManagement/Controllers/PersonnelController.cs index 6c50110..396af82 100644 --- a/src/MilitaryTrainingManagement/Controllers/PersonnelController.cs +++ b/src/MilitaryTrainingManagement/Controllers/PersonnelController.cs @@ -139,7 +139,8 @@ public class PersonnelController : BaseApiController ApprovedByUnitName = personnel.ApprovedByUnit?.Name, Status = personnel.Status, SubmittedAt = personnel.SubmittedAt, - ApprovedAt = personnel.ApprovedAt + ApprovedAt = personnel.ApprovedAt, + PendingUpgradeByUnitId = personnel.PendingUpgradeByUnitId }; } @@ -738,4 +739,52 @@ public class PersonnelController : BaseApiController } } + /// + /// 发起向上申报请求 + /// + [HttpPost("{id}/request-upgrade")] + [Authorize(Policy = "RegimentLevel")] // 团级及以上权限 + public async Task RequestUpgrade(int id) + { + var unitId = GetCurrentUnitId(); + if (unitId == null) + { + return Unauthorized(); + } + + try + { + var personnel = await _personnelService.RequestUpgradeAsync(id, unitId.Value); + return Ok(MapToResponse(personnel)); + } + catch (ArgumentException ex) + { + return BadRequest(new { message = ex.Message }); + } + } + + /// + /// 审批向上申报请求 + /// + [HttpPost("{id}/approve-upgrade")] + [Authorize(Policy = "RegimentLevel")] // 团级及以上权限 + public async Task ApproveUpgrade(int id) + { + var unitId = GetCurrentUnitId(); + if (unitId == null) + { + return Unauthorized(); + } + + try + { + var personnel = await _personnelService.ApproveUpgradeAsync(id, unitId.Value); + return Ok(MapToResponse(personnel)); + } + catch (ArgumentException ex) + { + return BadRequest(new { message = ex.Message }); + } + } + } diff --git a/src/MilitaryTrainingManagement/Models/DTOs/PersonnelDTOs.cs b/src/MilitaryTrainingManagement/Models/DTOs/PersonnelDTOs.cs index b951588..e056bd6 100644 --- a/src/MilitaryTrainingManagement/Models/DTOs/PersonnelDTOs.cs +++ b/src/MilitaryTrainingManagement/Models/DTOs/PersonnelDTOs.cs @@ -159,6 +159,7 @@ public class PersonnelResponse public PersonnelStatus Status { get; set; } public DateTime SubmittedAt { get; set; } public DateTime? ApprovedAt { get; set; } + public int? PendingUpgradeByUnitId { get; set; } // 待向上申报的申报单位ID } /// diff --git a/src/MilitaryTrainingManagement/Models/Entities/Personnel.cs b/src/MilitaryTrainingManagement/Models/Entities/Personnel.cs index e384099..6315f44 100644 --- a/src/MilitaryTrainingManagement/Models/Entities/Personnel.cs +++ b/src/MilitaryTrainingManagement/Models/Entities/Personnel.cs @@ -40,4 +40,8 @@ public class Personnel public PersonnelStatus Status { get; set; } = PersonnelStatus.Pending; public DateTime SubmittedAt { get; set; } = DateTime.UtcNow; public DateTime? ApprovedAt { get; set; } + + // 向上申报相关字段 + public int? PendingUpgradeByUnitId { get; set; } // 发起向上申报的单位ID + public OrganizationalUnit? PendingUpgradeByUnit { get; set; } } diff --git a/src/MilitaryTrainingManagement/Program.cs b/src/MilitaryTrainingManagement/Program.cs index 1bb45bc..781d59d 100644 --- a/src/MilitaryTrainingManagement/Program.cs +++ b/src/MilitaryTrainingManagement/Program.cs @@ -235,6 +235,9 @@ using (var scope = app.Services.CreateScope()) IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('Personnel') AND name = 'Specialty') ALTER TABLE Personnel ADD Specialty nvarchar(200) NULL; + + IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('Personnel') AND name = 'PendingUpgradeByUnitId') + ALTER TABLE Personnel ADD PendingUpgradeByUnitId INT NULL; "); Console.WriteLine("Personnel 表列检查完成"); } diff --git a/src/MilitaryTrainingManagement/Services/Implementations/PersonnelService.cs b/src/MilitaryTrainingManagement/Services/Implementations/PersonnelService.cs index 6dccf49..67559f2 100644 --- a/src/MilitaryTrainingManagement/Services/Implementations/PersonnelService.cs +++ b/src/MilitaryTrainingManagement/Services/Implementations/PersonnelService.cs @@ -85,6 +85,7 @@ public class PersonnelService : IPersonnelService { var personnel = await _context.Personnel .Include(p => p.SubmittedByUnit) + .Include(p => p.ApprovedByUnit) .FirstOrDefaultAsync(p => p.Id == personnelId); if (personnel == null) throw new ArgumentException("人员记录不存在"); @@ -93,7 +94,9 @@ public class PersonnelService : IPersonnelService if (approvedByUnit == null) throw new ArgumentException("审批单位不存在"); - // 人员等级变更为审批单位的等级 + var previousStatus = personnel.Status; + var previousLevel = personnel.ApprovedLevel; + PersonnelLevel actualLevel; if (level.HasValue) { @@ -101,19 +104,40 @@ public class PersonnelService : IPersonnelService } else { - // 根据审批单位的层级确定人员等级 - actualLevel = (PersonnelLevel)(int)approvedByUnit.Level; + // 区分首次审批和向上申报两种场景 + 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("该人员状态不允许审批"); + } } - // 验证审批单位层级必须高于或等于人员等级(数值越小层级越高) - var unitLevelValue = (int)approvedByUnit.Level; - var personnelLevelValue = (int)actualLevel; - if (unitLevelValue > personnelLevelValue) - throw new ArgumentException("审批单位层级不足以审批该等级人才"); - - var previousStatus = personnel.Status; - var previousLevel = personnel.ApprovedLevel; - personnel.Status = PersonnelStatus.Approved; personnel.ApprovedByUnitId = approvedByUnitId; personnel.ApprovedLevel = actualLevel; @@ -569,15 +593,20 @@ public class PersonnelService : IPersonnelService 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; @@ -619,4 +648,96 @@ public class PersonnelService : IPersonnelService return user?.Id ?? 0; // 如果找不到用户,返回0作为系统操作 } + + /// + /// 发起向上申报请求 + /// + public async Task 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.ApprovedByUnitId != requestByUnitId) + throw new ArgumentException("只有当前审批单位才能发起向上申报"); + + // 师级人才不能再向上申报 + if (personnel.ApprovedLevel == PersonnelLevel.Division) + throw new ArgumentException("师级人才不能再向上申报"); + + // 标记为待向上申报 + personnel.PendingUpgradeByUnitId = requestByUnitId; + + await _context.SaveChangesAsync(); + + _logger.LogInformation("人员 {PersonnelId} 已由单位 {UnitId} 发起向上申报请求", + personnelId, requestByUnitId); + + return personnel; + } + + /// + /// 审批向上申报请求 + /// + public async Task 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("审批单位不存在"); + + // 验证审批单位必须是申报单位的上级 + var isParent = await _organizationService.IsParentUnitAsync(approvedByUnitId, personnel.PendingUpgradeByUnitId.Value); + if (!isParent) + throw new ArgumentException("审批单位必须是申报单位的上级"); + + var previousLevel = personnel.ApprovedLevel; + + // 人才等级升级为申报单位的等级 + var 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; + } } diff --git a/src/MilitaryTrainingManagement/Services/Interfaces/IPersonnelService.cs b/src/MilitaryTrainingManagement/Services/Interfaces/IPersonnelService.cs index 7a6e855..a21b71e 100644 --- a/src/MilitaryTrainingManagement/Services/Interfaces/IPersonnelService.cs +++ b/src/MilitaryTrainingManagement/Services/Interfaces/IPersonnelService.cs @@ -75,6 +75,16 @@ public interface IPersonnelService /// 验证审批权限 /// Task CanApprovePersonnelAsync(int userId, int personnelId); + + /// + /// 发起向上申报请求 + /// + Task RequestUpgradeAsync(int personnelId, int requestByUnitId); + + /// + /// 审批向上申报请求 + /// + Task ApproveUpgradeAsync(int personnelId, int approvedByUnitId); } /// diff --git a/src/MilitaryTrainingManagement/wwwroot/uploads/documents/fe649db1-6110-4015-8acd-1c4ebf844ed0.jpg b/src/MilitaryTrainingManagement/wwwroot/uploads/documents/fe649db1-6110-4015-8acd-1c4ebf844ed0.jpg new file mode 100644 index 0000000..f7a5b18 Binary files /dev/null and b/src/MilitaryTrainingManagement/wwwroot/uploads/documents/fe649db1-6110-4015-8acd-1c4ebf844ed0.jpg differ diff --git a/src/MilitaryTrainingManagement/wwwroot/uploads/photos/af379bda-6c88-4d5e-96e1-5ae10c18be82.jpg b/src/MilitaryTrainingManagement/wwwroot/uploads/photos/af379bda-6c88-4d5e-96e1-5ae10c18be82.jpg new file mode 100644 index 0000000..3802c60 Binary files /dev/null and b/src/MilitaryTrainingManagement/wwwroot/uploads/photos/af379bda-6c88-4d5e-96e1-5ae10c18be82.jpg differ diff --git a/src/MilitaryTrainingManagement/wwwroot/uploads/photos/ba845008-3425-47d3-9536-262a38a5141c.jpg b/src/MilitaryTrainingManagement/wwwroot/uploads/photos/ba845008-3425-47d3-9536-262a38a5141c.jpg new file mode 100644 index 0000000..3802c60 Binary files /dev/null and b/src/MilitaryTrainingManagement/wwwroot/uploads/photos/ba845008-3425-47d3-9536-262a38a5141c.jpg differ diff --git a/src/MilitaryTrainingManagement/wwwroot/uploads/photos/e2a93ae7-5e95-4ac7-be94-595cb93582f9.jpg b/src/MilitaryTrainingManagement/wwwroot/uploads/photos/e2a93ae7-5e95-4ac7-be94-595cb93582f9.jpg new file mode 100644 index 0000000..3802c60 Binary files /dev/null and b/src/MilitaryTrainingManagement/wwwroot/uploads/photos/e2a93ae7-5e95-4ac7-be94-595cb93582f9.jpg differ diff --git a/src/frontend/src/api/personnel.ts b/src/frontend/src/api/personnel.ts index b451364..c290713 100644 --- a/src/frontend/src/api/personnel.ts +++ b/src/frontend/src/api/personnel.ts @@ -55,5 +55,17 @@ export const personnelApi = { async getPendingApprovals(): Promise { const response = await apiClient.get('/personnel/pending') return response.data + }, + + // 发起向上申报请求 + async requestUpgrade(personnelId: number): Promise { + const response = await apiClient.post(`/personnel/${personnelId}/request-upgrade`) + return response.data + }, + + // 审批向上申报请求 + async approveUpgrade(personnelId: number): Promise { + const response = await apiClient.post(`/personnel/${personnelId}/approve-upgrade`) + return response.data } } diff --git a/src/frontend/src/layouts/MainLayout.vue b/src/frontend/src/layouts/MainLayout.vue index f9f8fe8..1367444 100644 --- a/src/frontend/src/layouts/MainLayout.vue +++ b/src/frontend/src/layouts/MainLayout.vue @@ -31,22 +31,6 @@ 创建配额 - - - - 上报列表 - 数据汇总 - - - - - - 数据汇总 - -