diff --git a/src/MilitaryTrainingManagement/Controllers/PersonnelController.cs b/src/MilitaryTrainingManagement/Controllers/PersonnelController.cs index dea19f6..be69084 100644 --- a/src/MilitaryTrainingManagement/Controllers/PersonnelController.cs +++ b/src/MilitaryTrainingManagement/Controllers/PersonnelController.cs @@ -704,7 +704,8 @@ public class PersonnelController : BaseApiController // 检查权限:只有提交单位或上级单位可以删除 var unitId = GetCurrentUnitId(); - if (unitId == null) + var userLevel = GetCurrentUnitLevel(); + if (unitId == null || userLevel == null) { return Unauthorized(); } @@ -722,10 +723,19 @@ public class PersonnelController : BaseApiController } } - // 只能删除待审批状态的人员 - if (personnel.Status != PersonnelStatus.Pending) + // 待审批状态的人员可以直接删除 + // 已批准状态的人员,只有审批单位或更高级别单位可以删除 + if (personnel.Status == PersonnelStatus.Approved) { - return BadRequest(new { message = "只能删除待审批状态的人员记录" }); + // 检查当前用户单位层级是否足够高(数值越小层级越高) + var personnelLevelValue = personnel.ApprovedLevel.HasValue ? (int)personnel.ApprovedLevel.Value : 4; + var userLevelValue = (int)userLevel.Value; + + // 用户层级必须高于或等于人员等级才能删除 + if (userLevelValue > personnelLevelValue) + { + return BadRequest(new { message = "您的权限不足以删除该等级的人员" }); + } } try @@ -787,4 +797,52 @@ public class PersonnelController : BaseApiController } } + /// + /// 拒绝向上申报请求 + /// + [HttpPost("{id}/reject-upgrade")] + [Authorize(Policy = "BattalionLevel")] // 营级及以上权限 + public async Task RejectUpgrade(int id, [FromBody] RejectUpgradeRequest request) + { + var unitId = GetCurrentUnitId(); + if (unitId == null) + { + return Unauthorized(); + } + + try + { + var personnel = await _personnelService.RejectUpgradeAsync(id, unitId.Value, request.Comments); + return Ok(MapToResponse(personnel)); + } + catch (ArgumentException ex) + { + return BadRequest(new { message = ex.Message }); + } + } + + /// + /// 师部直接升级团级人才为师级 + /// + [HttpPost("{id}/direct-upgrade")] + [Authorize(Policy = "DivisionLevel")] // 师级权限 + public async Task DirectUpgrade(int id) + { + var unitId = GetCurrentUnitId(); + if (unitId == null) + { + return Unauthorized(); + } + + try + { + var personnel = await _personnelService.DirectUpgradeAsync(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 e056bd6..1e09331 100644 --- a/src/MilitaryTrainingManagement/Models/DTOs/PersonnelDTOs.cs +++ b/src/MilitaryTrainingManagement/Models/DTOs/PersonnelDTOs.cs @@ -122,6 +122,16 @@ public class RejectPersonnelRequest public string? Reason { get; set; } } +/// +/// 拒绝向上申报请求DTO +/// +public class RejectUpgradeRequest +{ + [Required(ErrorMessage = "拒绝原因不能为空")] + [StringLength(500, ErrorMessage = "拒绝原因长度不能超过500个字符")] + public string Comments { get; set; } = string.Empty; +} + /// /// 人员响应DTO /// diff --git a/src/MilitaryTrainingManagement/Services/Implementations/PersonnelService.cs b/src/MilitaryTrainingManagement/Services/Implementations/PersonnelService.cs index 3148fd2..8779777 100644 --- a/src/MilitaryTrainingManagement/Services/Implementations/PersonnelService.cs +++ b/src/MilitaryTrainingManagement/Services/Implementations/PersonnelService.cs @@ -778,4 +778,91 @@ public class PersonnelService : IPersonnelService return personnel; } + + /// + /// 拒绝向上申报请求 + /// + public async Task RejectUpgradeAsync(int personnelId, int rejectedByUnitId, string comments) + { + 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 rejectedByUnit = await _context.OrganizationalUnits.FindAsync(rejectedByUnitId); + if (rejectedByUnit == null) + throw new ArgumentException("审批单位不存在"); + + var previousLevel = personnel.ApprovedLevel; + + // 清除待申报标记,保持原有等级不变 + personnel.PendingUpgradeByUnitId = null; + + await _context.SaveChangesAsync(); + + // 记录审批历史 + var userId = await GetUserIdByUnitAsync(rejectedByUnitId); + await RecordApprovalHistoryAsync(personnelId, PersonnelApprovalAction.Rejected, + PersonnelStatus.Approved, PersonnelStatus.Approved, previousLevel, previousLevel, + userId, rejectedByUnitId, $"向上申报被拒绝:{comments}"); + + _logger.LogInformation("人员 {PersonnelId} 向上申报已被单位 {UnitId} 拒绝,原因:{Comments}", + personnelId, rejectedByUnitId, comments); + + return personnel; + } + + /// + /// 师部直接升级团级人才为师级 + /// + public async Task DirectUpgradeAsync(int personnelId, int divisionUnitId) + { + 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.ApprovedLevel != PersonnelLevel.Regiment) + throw new ArgumentException("只能升级团级人才"); + + var divisionUnit = await _context.OrganizationalUnits.FindAsync(divisionUnitId); + if (divisionUnit == null) + throw new ArgumentException("单位不存在"); + + if (divisionUnit.Level != OrganizationalLevel.Division) + throw new ArgumentException("只有师部可以直接升级团级人才"); + + var previousLevel = personnel.ApprovedLevel; + + // 直接升级为师级 + personnel.ApprovedLevel = PersonnelLevel.Division; + personnel.ApprovedByUnitId = divisionUnitId; + personnel.ApprovedAt = DateTime.UtcNow; + + await _context.SaveChangesAsync(); + + // 记录审批历史 + var userId = await GetUserIdByUnitAsync(divisionUnitId); + await RecordApprovalHistoryAsync(personnelId, PersonnelApprovalAction.LevelUpgraded, + PersonnelStatus.Approved, PersonnelStatus.Approved, previousLevel, PersonnelLevel.Division, + userId, divisionUnitId, "师部直接升级团级人才为师级"); + + _logger.LogInformation("人员 {PersonnelId} 已被师部 {UnitId} 直接升级为师级", + personnelId, divisionUnitId); + + return personnel; + } } diff --git a/src/MilitaryTrainingManagement/Services/Interfaces/IPersonnelService.cs b/src/MilitaryTrainingManagement/Services/Interfaces/IPersonnelService.cs index a21b71e..3fd5dab 100644 --- a/src/MilitaryTrainingManagement/Services/Interfaces/IPersonnelService.cs +++ b/src/MilitaryTrainingManagement/Services/Interfaces/IPersonnelService.cs @@ -85,6 +85,16 @@ public interface IPersonnelService /// 审批向上申报请求 /// Task ApproveUpgradeAsync(int personnelId, int approvedByUnitId); + + /// + /// 拒绝向上申报请求 + /// + Task RejectUpgradeAsync(int personnelId, int rejectedByUnitId, string comments); + + /// + /// 师部直接升级团级人才为师级 + /// + Task DirectUpgradeAsync(int personnelId, int divisionUnitId); } /// diff --git a/src/frontend/src/api/personnel.ts b/src/frontend/src/api/personnel.ts index c290713..11e8f59 100644 --- a/src/frontend/src/api/personnel.ts +++ b/src/frontend/src/api/personnel.ts @@ -67,5 +67,19 @@ export const personnelApi = { async approveUpgrade(personnelId: number): Promise { const response = await apiClient.post(`/personnel/${personnelId}/approve-upgrade`) return response.data + }, + + // 拒绝向上申报请求 + async rejectUpgrade(personnelId: number, comments: string): Promise { + const response = await apiClient.post(`/personnel/${personnelId}/reject-upgrade`, { + comments + }) + return response.data + }, + + // 师部直接升级团级人才为师级 + async directUpgrade(personnelId: number): Promise { + const response = await apiClient.post(`/personnel/${personnelId}/direct-upgrade`) + return response.data } } diff --git a/src/frontend/src/views/personnel/PersonnelList.vue b/src/frontend/src/views/personnel/PersonnelList.vue index 6d00575..5706a0c 100644 --- a/src/frontend/src/views/personnel/PersonnelList.vue +++ b/src/frontend/src/views/personnel/PersonnelList.vue @@ -65,10 +65,10 @@ @@ -104,6 +104,11 @@ + + + + + @@ -114,7 +119,7 @@ - + @@ -161,6 +166,36 @@ + + + + + + {{ selectedPerson?.name }} + {{ selectedPerson?.position }} + {{ upgradeInfo.currentLevel }} + {{ upgradeInfo.newLevel }} + + + + + + + + @@ -182,8 +217,11 @@ const loading = ref(false) const statusFilter = ref('') const searchKeyword = ref('') const showApprovalDialog = ref(false) +const showUpgradeApprovalDialog = ref(false) const approving = ref(false) +const approvingUpgrade = ref(false) const selectedPerson = ref(null) +const upgradeInfo = ref<{ currentLevel: string; newLevel: string }>({ currentLevel: '', newLevel: '' }) const pagination = reactive({ pageNumber: 1, @@ -196,6 +234,10 @@ const approvalForm = reactive({ level: 'Company' }) +const upgradeApprovalForm = reactive({ + comments: '' +}) + function getStatusName(status: PersonnelStatus): string { switch (status) { case PersonnelStatus.Pending: return '待审批' @@ -218,6 +260,19 @@ function getDisplayStatus(person: Personnel): string { return getStatusName(person.status) } +// 根据当前用户判断显示的状态标签类型 +function getDisplayStatusType(person: Personnel): string { + if (person.status === PersonnelStatus.Pending) { + // 待审批状态:判断当前用户是否能审批 + if (canApprovePersonnel(person)) { + return 'warning' // 待审批 - 黄色 + } else { + return 'primary' // 待上级审批 - 蓝色 + } + } + return getStatusTagType(person.status) +} + function getStatusTagType(status: PersonnelStatus): string { switch (status) { case PersonnelStatus.Pending: return 'warning' @@ -296,20 +351,27 @@ function canUpgrade(person: Personnel): boolean { // 2. 不是师级人才(师级不能再向上) // 3. 没有待处理的向上申报请求 // 4. 当前用户是审批单位(可以发起向上申报) - // 或者:师部可以直接升级团级人才 + // 注意:师部对团级人才使用"直接升级",不显示"向上申报" if (!authStore.user || !person.approvedLevel || !person.approvedByUnitId) return false if (person.approvedLevel === PersonnelLevel.Division) return false if (person.pendingUpgradeByUnitId) return false // 已有待处理的向上申报 - // 当前用户单位是审批单位,可以发起向上申报 - if (person.approvedByUnitId === authStore.user.organizationalUnitId) return true - - // 特殊情况:师部可以直接升级团级人才为师级 + // 师部对团级人才使用直接升级,不显示向上申报 if (authStore.organizationalLevelNum === 1 && person.approvedLevel === PersonnelLevel.Regiment) { - return true + return false } - return false + // 当前用户单位是审批单位,可以发起向上申报 + return person.approvedByUnitId === authStore.user.organizationalUnitId +} + +// 判断是否可以直接升级(师部直接升级团级人才为师级) +function canDirectUpgrade(person: Personnel): boolean { + if (!authStore.user || !person.approvedLevel) return false + if (person.pendingUpgradeByUnitId) return false // 已有待处理的向上申报 + + // 只有师部可以直接升级团级人才为师级 + return authStore.organizationalLevelNum === 1 && person.approvedLevel === PersonnelLevel.Regiment } // 判断是否可以审批向上申报请求 @@ -349,6 +411,32 @@ function canApprovePersonnel(person: Personnel): boolean { return true } +// 判断是否可以删除该人员 +function canDelete(person: Personnel): boolean { + if (!authStore.user) return false + + const userLevelNum = authStore.organizationalLevelNum + + // 待审批状态:提交单位或上级单位可以删除 + if (person.status === PersonnelStatus.Pending) { + return true + } + + // 已批准状态:用户层级必须高于或等于人员等级才能删除 + if (person.status === PersonnelStatus.Approved && person.approvedLevel) { + const personnelLevelNum = { + 'Division': 1, + 'Regiment': 2, + 'Battalion': 3, + 'Company': 4 + }[person.approvedLevel] || 4 + + return userLevelNum <= personnelLevelNum + } + + return false +} + function handleApprove(person: Personnel) { selectedPerson.value = person approvalForm.comments = '' @@ -380,35 +468,71 @@ async function handleRequestUpgrade(person: Personnel) { } } -// 审批向上申报请求 -async function handleApproveUpgrade(person: Personnel) { +// 师部直接升级团级人才为师级 +async function handleDirectUpgrade(person: Personnel) { try { - const currentLevel = getLevelName(person.approvedLevel!) - // 计算升级后的等级 - const levelOrder = ['Company', 'Battalion', 'Regiment', 'Division'] - const currentIndex = levelOrder.indexOf(person.approvedLevel || 'Company') - const newLevel = currentIndex < levelOrder.length - 1 ? getLevelName(levelOrder[currentIndex + 1] as PersonnelLevel) : currentLevel - await ElMessageBox.confirm( - `确定要批准 "${person.name}" 的向上申报请求吗?\n批准后,该人才将从${currentLevel}升级为${newLevel}。`, - '确认审批向上申报', + `确定要将 "${person.name}"(团级人才)直接升级为师级人才吗?`, + '确认直接升级', { type: 'warning', - confirmButtonText: '批准', + confirmButtonText: '确认升级', cancelButtonText: '取消' } ) - await personnelApi.approveUpgrade(person.id) - ElMessage.success('向上申报已批准,人才等级已升级') + await personnelApi.directUpgrade(person.id) + ElMessage.success('已成功升级为师级人才') await loadPersonnel() } catch (error: any) { if (error !== 'cancel') { - ElMessage.error(error.response?.data?.message || '审批失败') + ElMessage.error(error.response?.data?.message || '升级失败') } } } +// 审批向上申报请求 +async function handleApproveUpgrade(person: Personnel) { + const currentLevel = getLevelName(person.approvedLevel!) + // 计算升级后的等级 + const levelOrder = ['Company', 'Battalion', 'Regiment', 'Division'] + const currentIndex = levelOrder.indexOf(person.approvedLevel || 'Company') + const newLevel = currentIndex < levelOrder.length - 1 ? getLevelName(levelOrder[currentIndex + 1] as PersonnelLevel) : currentLevel + + selectedPerson.value = person + upgradeInfo.value = { currentLevel, newLevel } + upgradeApprovalForm.comments = '' + showUpgradeApprovalDialog.value = true +} + +// 提交向上申报审批 +async function submitUpgradeApproval(approved: boolean) { + if (!selectedPerson.value) return + + // 拒绝时必须填写原因 + if (!approved && !upgradeApprovalForm.comments.trim()) { + ElMessage.warning('请填写拒绝原因') + return + } + + approvingUpgrade.value = true + try { + if (approved) { + await personnelApi.approveUpgrade(selectedPerson.value.id) + ElMessage.success('向上申报已批准,人才等级已升级') + } else { + await personnelApi.rejectUpgrade(selectedPerson.value.id, upgradeApprovalForm.comments) + ElMessage.success('向上申报已拒绝') + } + showUpgradeApprovalDialog.value = false + await loadPersonnel() + } catch (error: any) { + ElMessage.error(error.response?.data?.message || '审批失败') + } finally { + approvingUpgrade.value = false + } +} + async function submitApproval(approved: boolean) { if (!selectedPerson.value) return