bug修改

This commit is contained in:
18631081161 2026-01-17 15:39:37 +08:00
parent 425ac307bb
commit 355087e0f0
16 changed files with 635 additions and 35 deletions

View File

@ -15,13 +15,21 @@ public class AllocationsController : BaseApiController
{
private readonly IAllocationService _allocationService;
private readonly IOrganizationalAuthorizationService _authorizationService;
private readonly IAuditService _auditService;
public AllocationsController(
IAllocationService allocationService,
IOrganizationalAuthorizationService authorizationService)
IOrganizationalAuthorizationService authorizationService,
IAuditService auditService)
{
_allocationService = allocationService;
_authorizationService = authorizationService;
_auditService = auditService;
}
private string? GetClientIpAddress()
{
return HttpContext.Connection.RemoteIpAddress?.ToString();
}
/// <summary>
@ -202,6 +210,7 @@ public class AllocationsController : BaseApiController
public async Task<IActionResult> Create([FromBody] CreateAllocationRequest request)
{
var unitId = GetCurrentUnitId();
var userId = GetCurrentUserId();
if (unitId == null)
return Unauthorized(new { message = "无法获取用户组织信息" });
@ -219,6 +228,16 @@ public class AllocationsController : BaseApiController
unitId.Value,
request.GetDistributionsDictionary());
// 记录审计日志
await _auditService.LogApprovalAsync(
"MaterialAllocation",
allocation.Id,
"Create",
$"创建物资配额:{request.Category} - {request.MaterialName},总配额:{request.TotalQuota}{request.Unit}",
userId,
unitId,
GetClientIpAddress());
return CreatedAtAction(nameof(GetById), new { id = allocation.Id }, MapToResponse(allocation));
}
catch (ArgumentException ex)
@ -235,6 +254,7 @@ public class AllocationsController : BaseApiController
public async Task<IActionResult> Update(int id, [FromBody] UpdateAllocationRequest request)
{
var unitId = GetCurrentUnitId();
var userId = GetCurrentUserId();
if (unitId == null)
return Unauthorized(new { message = "无法获取用户组织信息" });
@ -260,6 +280,16 @@ public class AllocationsController : BaseApiController
request.Unit,
request.TotalQuota);
// 记录审计日志
await _auditService.LogApprovalAsync(
"MaterialAllocation",
id,
"Update",
$"更新物资配额:{request.Category} - {request.MaterialName},总配额:{request.TotalQuota}{request.Unit}",
userId,
unitId,
GetClientIpAddress());
return Ok(MapToResponse(await _allocationService.GetByIdAsync(id) ?? allocation));
}
catch (ArgumentException ex)
@ -276,6 +306,7 @@ public class AllocationsController : BaseApiController
public async Task<IActionResult> UpdateDistributions(int id, [FromBody] Dictionary<int, decimal> distributions)
{
var unitId = GetCurrentUnitId();
var userId = GetCurrentUserId();
if (unitId == null)
return Unauthorized(new { message = "无法获取用户组织信息" });
@ -291,6 +322,17 @@ public class AllocationsController : BaseApiController
try
{
var allocation = await _allocationService.UpdateDistributionsAsync(id, distributions);
// 记录审计日志
await _auditService.LogApprovalAsync(
"AllocationDistribution",
id,
"Update",
$"更新配额分配:{existingAllocation.MaterialName},分配给 {distributions.Count} 个单位",
userId,
unitId,
GetClientIpAddress());
return Ok(MapToResponse(allocation));
}
catch (ArgumentException ex)
@ -321,6 +363,17 @@ public class AllocationsController : BaseApiController
request.ActualCompletion,
request.Remarks);
// 记录审计日志
var materialName = distribution.Allocation?.MaterialName ?? "未知物资";
await _auditService.LogApprovalAsync(
"ConsumptionReport",
distributionId,
"Create",
$"上报消耗:{materialName},数量:{request.ActualCompletion},备注:{request.Remarks ?? ""}",
userId,
unitId,
GetClientIpAddress());
return Ok(MapDistributionToResponse(distribution));
}
catch (UnauthorizedAccessException ex)
@ -341,6 +394,7 @@ public class AllocationsController : BaseApiController
public async Task<IActionResult> Delete(int id)
{
var unitId = GetCurrentUnitId();
var userId = GetCurrentUserId();
if (unitId == null)
return Unauthorized(new { message = "无法获取用户组织信息" });
@ -355,7 +409,21 @@ public class AllocationsController : BaseApiController
try
{
var materialName = existingAllocation.MaterialName;
var category = existingAllocation.Category;
await _allocationService.DeleteAsync(id);
// 记录审计日志
await _auditService.LogApprovalAsync(
"MaterialAllocation",
id,
"Delete",
$"删除物资配额:{category} - {materialName}",
userId,
unitId,
GetClientIpAddress());
return NoContent();
}
catch (ArgumentException ex)

View File

@ -15,13 +15,21 @@ public class ApprovalsController : BaseApiController
{
private readonly IWorkflowService _workflowService;
private readonly IAllocationService _allocationService;
private readonly IAuditService _auditService;
public ApprovalsController(
IWorkflowService workflowService,
IAllocationService allocationService)
IAllocationService allocationService,
IAuditService auditService)
{
_workflowService = workflowService;
_allocationService = allocationService;
_auditService = auditService;
}
private string? GetClientIpAddress()
{
return HttpContext.Connection.RemoteIpAddress?.ToString();
}
/// <summary>
@ -198,12 +206,24 @@ public class ApprovalsController : BaseApiController
public async Task<IActionResult> Approve(int id, [FromBody] ProcessApprovalRequest request)
{
var userId = GetCurrentUserId();
var unitId = GetCurrentUnitId();
if (userId == null)
return Unauthorized(new { message = "无法获取用户信息" });
try
{
var approvalRequest = await _workflowService.ApproveAsync(id, userId.Value, request.Comments);
// 记录审计日志
await _auditService.LogApprovalAsync(
"ApprovalRequest",
id,
"Approve",
$"审批通过修改请求:{GetTypeName(approvalRequest.Type)},处理意见:{request.Comments ?? ""}",
userId,
unitId,
GetClientIpAddress());
return Ok(MapToResponse(approvalRequest));
}
catch (ArgumentException ex)
@ -229,12 +249,24 @@ public class ApprovalsController : BaseApiController
public async Task<IActionResult> Reject(int id, [FromBody] ProcessApprovalRequest request)
{
var userId = GetCurrentUserId();
var unitId = GetCurrentUnitId();
if (userId == null)
return Unauthorized(new { message = "无法获取用户信息" });
try
{
var approvalRequest = await _workflowService.RejectAsync(id, userId.Value, request.Comments);
// 记录审计日志
await _auditService.LogApprovalAsync(
"ApprovalRequest",
id,
"Reject",
$"拒绝修改请求:{GetTypeName(approvalRequest.Type)},处理意见:{request.Comments ?? ""}",
userId,
unitId,
GetClientIpAddress());
return Ok(MapToResponse(approvalRequest));
}
catch (ArgumentException ex)

View File

@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore;
using MilitaryTrainingManagement.Data;
using MilitaryTrainingManagement.Models.Entities;
using MilitaryTrainingManagement.Models.Enums;
using MilitaryTrainingManagement.Services.Interfaces;
namespace MilitaryTrainingManagement.Controllers;
@ -15,13 +16,21 @@ public class ConsumptionChangeRequestsController : BaseApiController
{
private readonly ApplicationDbContext _context;
private readonly ILogger<ConsumptionChangeRequestsController> _logger;
private readonly IAuditService _auditService;
public ConsumptionChangeRequestsController(
ApplicationDbContext context,
ILogger<ConsumptionChangeRequestsController> logger)
ILogger<ConsumptionChangeRequestsController> logger,
IAuditService auditService)
{
_context = context;
_logger = logger;
_auditService = auditService;
}
private string? GetClientIpAddress()
{
return HttpContext.Connection.RemoteIpAddress?.ToString();
}
/// <summary>
@ -38,6 +47,8 @@ public class ConsumptionChangeRequestsController : BaseApiController
// 验证消耗记录存在且属于当前单位
var report = await _context.ConsumptionReports
.Include(r => r.ReportedByUnit)
.Include(r => r.AllocationDistribution)
.ThenInclude(d => d.Allocation)
.FirstOrDefaultAsync(r => r.Id == request.ConsumptionReportId);
if (report == null)
@ -71,6 +82,19 @@ public class ConsumptionChangeRequestsController : BaseApiController
_logger.LogInformation("单位 {UnitId} 创建了消耗记录 {ReportId} 的{Type}申请",
unitId, request.ConsumptionReportId, request.RequestType);
// 记录审计日志
var requestTypeName = request.RequestType == ChangeRequestType.Delete ? "删除" : "修改";
var materialName = report.AllocationDistribution?.Allocation?.MaterialName ?? "未知物资";
await _auditService.LogApprovalAsync(
"ConsumptionReportChangeRequest",
changeRequest.Id,
"Create",
$"提交消耗记录{requestTypeName}申请,物资:{materialName},申请原因:{request.Reason}",
userId,
unitId,
GetClientIpAddress());
return Ok(new { message = "申请已提交", id = changeRequest.Id });
}
@ -167,6 +191,7 @@ public class ConsumptionChangeRequestsController : BaseApiController
var changeRequest = await _context.ConsumptionReportChangeRequests
.Include(r => r.ConsumptionReport)
.ThenInclude(cr => cr.AllocationDistribution)
.ThenInclude(d => d.Allocation)
.Include(r => r.RequestedByUnit)
.FirstOrDefaultAsync(r => r.Id == id);
@ -211,6 +236,20 @@ public class ConsumptionChangeRequestsController : BaseApiController
_logger.LogInformation("单位 {UnitId} {Action}了消耗记录删改申请 {RequestId}",
unitId, action, id);
// 记录审计日志
var requestTypeName = changeRequest.RequestType == ChangeRequestType.Delete ? "删除" : "修改";
var materialName = changeRequest.ConsumptionReport?.AllocationDistribution?.Allocation?.MaterialName ?? "未知物资";
var reportedByUnitName = changeRequest.RequestedByUnit?.Name ?? "未知单位";
await _auditService.LogApprovalAsync(
"ConsumptionReportChangeRequest",
id,
request.Approved ? "Approve" : "Reject",
$"{action}「{reportedByUnitName}」的消耗记录{requestTypeName}申请,物资:{materialName},原因:{changeRequest.Reason},处理意见:{request.Comments ?? ""}",
userId,
unitId,
GetClientIpAddress());
return Ok(new { message = $"已{action}该申请" });
}
@ -227,6 +266,7 @@ public class ConsumptionChangeRequestsController : BaseApiController
var changeRequest = await _context.ConsumptionReportChangeRequests
.Include(r => r.ConsumptionReport)
.ThenInclude(cr => cr.AllocationDistribution)
.ThenInclude(d => d.Allocation)
.FirstOrDefaultAsync(r => r.Id == id);
if (changeRequest == null)
@ -258,6 +298,19 @@ public class ConsumptionChangeRequestsController : BaseApiController
_logger.LogInformation("消耗记录 {ReportId} 已被修改,数量从 {OldAmount} 改为 {NewAmount}",
report.Id, oldAmount, request.NewAmount);
// 记录审计日志
var userId = GetCurrentUserId();
var materialName = distribution.Allocation?.MaterialName ?? "未知物资";
await _auditService.LogApprovalAsync(
"ConsumptionReport",
report.Id,
"Update",
$"修改消耗记录,物资:{materialName},数量从 {oldAmount} 改为 {request.NewAmount}",
userId,
unitId,
GetClientIpAddress());
return Ok(new { message = "修改成功" });
}

View File

@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore;
using MilitaryTrainingManagement.Data;
using MilitaryTrainingManagement.Models.DTOs;
using MilitaryTrainingManagement.Models.Entities;
using MilitaryTrainingManagement.Services.Interfaces;
namespace MilitaryTrainingManagement.Controllers;
@ -14,10 +15,17 @@ namespace MilitaryTrainingManagement.Controllers;
public class MaterialCategoriesController : BaseApiController
{
private readonly ApplicationDbContext _context;
private readonly IAuditService _auditService;
public MaterialCategoriesController(ApplicationDbContext context)
public MaterialCategoriesController(ApplicationDbContext context, IAuditService auditService)
{
_context = context;
_auditService = auditService;
}
private string? GetClientIpAddress()
{
return HttpContext.Connection.RemoteIpAddress?.ToString();
}
/// <summary>
@ -61,6 +69,9 @@ public class MaterialCategoriesController : BaseApiController
[HttpPost]
public async Task<IActionResult> Create([FromBody] CreateMaterialCategoryRequest request)
{
var unitId = GetCurrentUnitId();
var userId = GetCurrentUserId();
// 检查名称是否已存在
var exists = await _context.MaterialCategories
.AnyAsync(c => c.Name == request.Name);
@ -81,6 +92,16 @@ public class MaterialCategoriesController : BaseApiController
_context.MaterialCategories.Add(category);
await _context.SaveChangesAsync();
// 记录审计日志
await _auditService.LogApprovalAsync(
"MaterialCategory",
category.Id,
"Create",
$"创建物资类别:{request.Name}",
userId,
unitId,
GetClientIpAddress());
return CreatedAtAction(nameof(GetById), new { id = category.Id }, category);
}
@ -90,6 +111,9 @@ public class MaterialCategoriesController : BaseApiController
[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, [FromBody] UpdateMaterialCategoryRequest request)
{
var unitId = GetCurrentUnitId();
var userId = GetCurrentUserId();
var category = await _context.MaterialCategories.FindAsync(id);
if (category == null)
{
@ -111,6 +135,16 @@ public class MaterialCategoriesController : BaseApiController
await _context.SaveChangesAsync();
// 记录审计日志
await _auditService.LogApprovalAsync(
"MaterialCategory",
id,
"Update",
$"更新物资类别:{request.Name}",
userId,
unitId,
GetClientIpAddress());
return Ok(category);
}
@ -120,6 +154,9 @@ public class MaterialCategoriesController : BaseApiController
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
var unitId = GetCurrentUnitId();
var userId = GetCurrentUserId();
var category = await _context.MaterialCategories.FindAsync(id);
if (category == null)
{
@ -134,9 +171,18 @@ public class MaterialCategoriesController : BaseApiController
return BadRequest(new { message = "该类别已被使用,无法删除" });
}
var categoryName = category.Name;
_context.MaterialCategories.Remove(category);
await _context.SaveChangesAsync();
return Ok(new { message = "删除成功" });
// 记录审计日志
await _auditService.LogApprovalAsync(
"MaterialCategory",
id,
"Delete",
$"删除物资类别:{categoryName}",
userId,
unitId,
GetClientIpAddress()); return Ok(new { message = "删除成功" });
}
}

View File

@ -17,15 +17,23 @@ public class OrganizationsController : BaseApiController
private readonly IOrganizationService _organizationService;
private readonly IAuthenticationService _authService;
private readonly ApplicationDbContext _context;
private readonly IAuditService _auditService;
public OrganizationsController(
IOrganizationService organizationService,
IAuthenticationService authService,
ApplicationDbContext context)
ApplicationDbContext context,
IAuditService auditService)
{
_organizationService = organizationService;
_authService = authService;
_context = context;
_auditService = auditService;
}
private string? GetClientIpAddress()
{
return HttpContext.Connection.RemoteIpAddress?.ToString();
}
[HttpGet]
@ -87,23 +95,69 @@ public class OrganizationsController : BaseApiController
[HttpPost]
public async Task<IActionResult> Create([FromBody] CreateOrganizationRequest request)
{
var unitId = GetCurrentUnitId();
var userId = GetCurrentUserId();
var unit = await _organizationService.CreateAsync(request.Name, request.Level, request.ParentId);
// 记录审计日志
await _auditService.LogApprovalAsync(
"OrganizationalUnit",
unit.Id,
"Create",
$"创建组织单位:{request.Name},级别:{request.Level}",
userId,
unitId,
GetClientIpAddress());
return CreatedAtAction(nameof(GetById), new { id = unit.Id }, unit);
}
[HttpPut("{id}")]
public async Task<IActionResult> Update(int id, [FromBody] UpdateOrganizationRequest request)
{
var unitId = GetCurrentUnitId();
var userId = GetCurrentUserId();
var unit = await _organizationService.UpdateAsync(id, request.Name);
// 记录审计日志
await _auditService.LogApprovalAsync(
"OrganizationalUnit",
id,
"Update",
$"更新组织单位名称为:{request.Name}",
userId,
unitId,
GetClientIpAddress());
return Ok(unit);
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(int id)
{
var unitId = GetCurrentUnitId();
var userId = GetCurrentUserId();
// 获取组织信息用于日志
var orgUnit = await _organizationService.GetByIdAsync(id);
var orgName = orgUnit?.Name ?? "未知";
try
{
await _organizationService.DeleteAsync(id);
// 记录审计日志
await _auditService.LogApprovalAsync(
"OrganizationalUnit",
id,
"Delete",
$"删除组织单位:{orgName}",
userId,
unitId,
GetClientIpAddress());
return NoContent();
}
catch (ArgumentException ex)
@ -178,6 +232,18 @@ public class OrganizationsController : BaseApiController
_context.UserAccounts.Add(account);
await _context.SaveChangesAsync();
// 记录审计日志
var currentUnitId = GetCurrentUnitId();
var currentUserId = GetCurrentUserId();
await _auditService.LogApprovalAsync(
"UserAccount",
account.Id,
"Create",
$"创建账户:{request.Username},所属组织:{unit.Name}",
currentUserId,
currentUnitId,
GetClientIpAddress());
return Ok(new { message = "账户创建成功", username = account.Username });
}
@ -219,9 +285,22 @@ public class OrganizationsController : BaseApiController
return BadRequest(new { message = "该账户有审批记录,无法删除" });
}
var username = account.Username;
_context.UserAccounts.Remove(account);
await _context.SaveChangesAsync();
// 记录审计日志
var currentUnitId = GetCurrentUnitId();
var currentUserId = GetCurrentUserId();
await _auditService.LogApprovalAsync(
"UserAccount",
accountId,
"Delete",
$"删除账户:{username}",
currentUserId,
currentUnitId,
GetClientIpAddress());
return Ok(new { message = "账户删除成功" });
}
}

View File

@ -17,15 +17,18 @@ public class PersonnelController : BaseApiController
private readonly IPersonnelService _personnelService;
private readonly IFileUploadService _fileUploadService;
private readonly IOrganizationalAuthorizationService _authorizationService;
private readonly IAuditService _auditService;
public PersonnelController(
IPersonnelService personnelService,
IFileUploadService fileUploadService,
IOrganizationalAuthorizationService authorizationService)
IOrganizationalAuthorizationService authorizationService,
IAuditService auditService)
{
_personnelService = personnelService;
_fileUploadService = fileUploadService;
_authorizationService = authorizationService;
_auditService = auditService;
}
/// <summary>
@ -533,8 +536,22 @@ public class PersonnelController : BaseApiController
try
{
// 获取人员信息用于审计日志
var personnelBefore = await _personnelService.GetByIdAsync(id);
// 不传递level参数让服务层根据人员所在单位自动确定等级
var personnel = await _personnelService.ApproveAsync(id, unitId.Value);
// 记录审计日志
await _auditService.LogApprovalAsync(
"Personnel",
id,
"Approve",
$"审批通过人才「{personnel.Name}」,等级:{GetLevelDisplayName(personnel.ApprovedLevel)}",
userId,
unitId,
GetClientIpAddress());
return Ok(MapToResponse(personnel));
}
catch (ArgumentException ex)
@ -542,6 +559,23 @@ public class PersonnelController : BaseApiController
return BadRequest(new { message = ex.Message });
}
}
private string GetLevelDisplayName(PersonnelLevel? level)
{
return level switch
{
PersonnelLevel.Division => "师级",
PersonnelLevel.Regiment => "团级",
PersonnelLevel.Battalion => "营级",
PersonnelLevel.Company => "连级",
_ => "未定级"
};
}
private string? GetClientIpAddress()
{
return HttpContext.Connection.RemoteIpAddress?.ToString();
}
/// <summary>
/// 拒绝人员
@ -564,7 +598,21 @@ public class PersonnelController : BaseApiController
return StatusCode(403, new { message = "您没有权限拒绝此人员" });
}
// 获取人员信息用于审计日志
var personnelBefore = await _personnelService.GetByIdAsync(id);
var personnel = await _personnelService.RejectAsync(id, userId.Value, unitId.Value, request.Reason);
// 记录审计日志
await _auditService.LogApprovalAsync(
"Personnel",
id,
"Reject",
$"拒绝人才「{personnel.Name}」的审批申请,原因:{request.Reason ?? ""}",
userId,
unitId,
GetClientIpAddress());
return Ok(MapToResponse(personnel));
}
@ -777,7 +825,8 @@ public class PersonnelController : BaseApiController
public async Task<IActionResult> RequestUpgrade(int id)
{
var unitId = GetCurrentUnitId();
if (unitId == null)
var userId = GetCurrentUserId();
if (unitId == null || userId == null)
{
return Unauthorized();
}
@ -785,6 +834,17 @@ public class PersonnelController : BaseApiController
try
{
var personnel = await _personnelService.RequestUpgradeAsync(id, unitId.Value);
// 记录审计日志
await _auditService.LogApprovalAsync(
"Personnel",
id,
"RequestUpgrade",
$"发起向上申报:人才「{personnel.Name}」({GetLevelDisplayName(personnel.ApprovedLevel)})申请升级",
userId,
unitId,
GetClientIpAddress());
return Ok(MapToResponse(personnel));
}
catch (ArgumentException ex)
@ -801,14 +861,30 @@ public class PersonnelController : BaseApiController
public async Task<IActionResult> ApproveUpgrade(int id)
{
var unitId = GetCurrentUnitId();
if (unitId == null)
var userId = GetCurrentUserId();
if (unitId == null || userId == null)
{
return Unauthorized();
}
try
{
// 获取人员信息用于审计日志
var personnelBefore = await _personnelService.GetByIdAsync(id);
var previousLevel = personnelBefore?.ApprovedLevel;
var personnel = await _personnelService.ApproveUpgradeAsync(id, unitId.Value);
// 记录审计日志
await _auditService.LogApprovalAsync(
"Personnel",
id,
"ApproveUpgrade",
$"批准人才「{personnel.Name}」的向上申报,等级从{GetLevelDisplayName(previousLevel)}升级为{GetLevelDisplayName(personnel.ApprovedLevel)}",
userId,
unitId,
GetClientIpAddress());
return Ok(MapToResponse(personnel));
}
catch (ArgumentException ex)
@ -825,14 +901,29 @@ public class PersonnelController : BaseApiController
public async Task<IActionResult> RejectUpgrade(int id, [FromBody] RejectUpgradeRequest request)
{
var unitId = GetCurrentUnitId();
if (unitId == null)
var userId = GetCurrentUserId();
if (unitId == null || userId == null)
{
return Unauthorized();
}
try
{
// 获取人员信息用于审计日志
var personnelBefore = await _personnelService.GetByIdAsync(id);
var personnel = await _personnelService.RejectUpgradeAsync(id, unitId.Value, request.Comments);
// 记录审计日志
await _auditService.LogApprovalAsync(
"Personnel",
id,
"RejectUpgrade",
$"拒绝人才「{personnel.Name}」的向上申报,原因:{request.Comments}",
userId,
unitId,
GetClientIpAddress());
return Ok(MapToResponse(personnel));
}
catch (ArgumentException ex)
@ -849,7 +940,8 @@ public class PersonnelController : BaseApiController
public async Task<IActionResult> DirectUpgrade(int id)
{
var unitId = GetCurrentUnitId();
if (unitId == null)
var userId = GetCurrentUserId();
if (unitId == null || userId == null)
{
return Unauthorized();
}
@ -857,6 +949,17 @@ public class PersonnelController : BaseApiController
try
{
var personnel = await _personnelService.DirectUpgradeAsync(id, unitId.Value);
// 记录审计日志
await _auditService.LogApprovalAsync(
"Personnel",
id,
"DirectUpgrade",
$"师部直接升级人才「{personnel.Name}」为师级人才",
userId,
unitId,
GetClientIpAddress());
return Ok(MapToResponse(personnel));
}
catch (ArgumentException ex)
@ -865,4 +968,50 @@ public class PersonnelController : BaseApiController
}
}
/// <summary>
/// 重新提交被拒绝的人才审批
/// </summary>
[HttpPost("{id}/resubmit")]
public async Task<IActionResult> ResubmitPersonnel(int id)
{
var unitId = GetCurrentUnitId();
var userId = GetCurrentUserId();
if (unitId == null || userId == null)
{
return Unauthorized();
}
try
{
var personnel = await _personnelService.GetByIdAsync(id);
if (personnel == null)
{
return NotFound(new { message = "人员记录不存在" });
}
// 检查权限:只有提交单位可以重新提交
if (personnel.SubmittedByUnitId != unitId.Value)
{
return Forbid();
}
var resubmitted = await _personnelService.ResubmitPersonnelAsync(id);
// 记录审计日志
await _auditService.LogApprovalAsync(
"Personnel",
id,
"Resubmit",
$"重新提交审批:人才「{resubmitted.Name}」重新提交审批申请",
userId,
unitId,
GetClientIpAddress());
return Ok(MapToResponse(resubmitted));
}
catch (ArgumentException ex)
{
return BadRequest(new { message = ex.Message });
}
}
}

View File

@ -353,8 +353,8 @@ public class PersonnelService : IPersonnelService
if (personnel == null)
return false;
// 只有待审批状态的人员可以直接修改
if (personnel.Status == PersonnelStatus.Pending)
// 待审批状态和已拒绝状态的人员可以直接修改
if (personnel.Status == PersonnelStatus.Pending || personnel.Status == PersonnelStatus.Rejected)
return true;
// 已审批的人员需要通过工作流修改
@ -860,4 +860,39 @@ public class PersonnelService : IPersonnelService
return personnel;
}
/// <summary>
/// 重新提交被拒绝的人才审批
/// </summary>
public async Task<Personnel> ResubmitPersonnelAsync(int personnelId)
{
var personnel = await _context.Personnel
.Include(p => p.SubmittedByUnit)
.FirstOrDefaultAsync(p => p.Id == personnelId);
if (personnel == null)
throw new ArgumentException("人员记录不存在");
if (personnel.Status != PersonnelStatus.Rejected)
throw new ArgumentException("只有已拒绝的人员才能重新提交");
var previousStatus = personnel.Status;
// 重新提交后,状态变为待审批
personnel.Status = PersonnelStatus.Pending;
personnel.SubmittedAt = DateTime.UtcNow; // 更新提交时间
await _context.SaveChangesAsync();
// 记录审批历史
var userId = await GetUserIdByUnitAsync(personnel.SubmittedByUnitId);
await RecordApprovalHistoryAsync(personnelId, PersonnelApprovalAction.Approved,
previousStatus, PersonnelStatus.Pending, null, null,
userId, personnel.SubmittedByUnitId, "重新提交审批");
_logger.LogInformation("人员 {PersonnelId} 已由单位 {UnitId} 重新提交审批",
personnelId, personnel.SubmittedByUnitId);
return personnel;
}
}

View File

@ -95,6 +95,11 @@ public interface IPersonnelService
/// 师部直接升级团级人才为师级
/// </summary>
Task<Personnel> DirectUpgradeAsync(int personnelId, int divisionUnitId);
/// <summary>
/// 重新提交被拒绝的人才审批
/// </summary>
Task<Personnel> ResubmitPersonnelAsync(int personnelId);
}
/// <summary>

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -83,6 +83,12 @@ export const personnelApi = {
return response.data
},
// 重新提交被拒绝的人才审批
async resubmit(personnelId: number): Promise<Personnel> {
const response = await apiClient.post<Personnel>(`/personnel/${personnelId}/resubmit`)
return response.data
},
// 获取审批历史
async getApprovalHistory(personnelId: number): Promise<PersonnelApprovalHistory[]> {
const response = await apiClient.get<PersonnelApprovalHistory[]>(`/personnel/${personnelId}/approval-history`)

View File

@ -14,14 +14,21 @@
<el-option label="创建" value="Create" />
<el-option label="更新" value="Update" />
<el-option label="删除" value="Delete" />
<el-option label="审批" value="Approve" />
<el-option label="拒绝" value="Reject" />
<el-option label="审批通过" value="Approve" />
<el-option label="审批拒绝" value="Reject" />
<el-option label="向上申报" value="RequestUpgrade" />
<el-option label="批准升级" value="ApproveUpgrade" />
<el-option label="拒绝升级" value="RejectUpgrade" />
<el-option label="直接升级" value="DirectUpgrade" />
<el-option label="重新提交" value="Resubmit" />
</el-select>
</el-form-item>
<el-form-item label="数据类型">
<el-select v-model="filters.entityType" placeholder="全部" clearable style="width: 150px">
<el-select v-model="filters.entityType" placeholder="全部" clearable style="width: 180px">
<el-option label="物资配额" value="MaterialAllocation" />
<el-option label="配额分配" value="AllocationDistribution" />
<el-option label="消耗记录" value="ConsumptionReport" />
<el-option label="消耗删改申请" value="ConsumptionReportChangeRequest" />
<el-option label="用户账户" value="UserAccount" />
<el-option label="组织单位" value="OrganizationalUnit" />
<el-option label="人员信息" value="Personnel" />
@ -158,7 +165,29 @@ const pagination = reactive({
})
function formatDate(dateStr: string): string {
return new Date(dateStr).toLocaleString('zh-CN')
// UTC
const date = new Date(dateStr)
// UTC
if (!dateStr.includes('Z') && !dateStr.includes('+') && !dateStr.includes('-', 10)) {
// Z UTC
const utcDate = new Date(dateStr + 'Z')
return utcDate.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
}
return date.toLocaleString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
})
}
function getActionName(action: string): string {
@ -166,10 +195,15 @@ function getActionName(action: string): string {
'Create': '创建',
'Update': '更新',
'Delete': '删除',
'Approve': '审批',
'Reject': '拒绝',
'Approve': '审批通过',
'Reject': '审批拒绝',
'RequestUpgrade': '向上申报',
'ApproveUpgrade': '批准升级',
'RejectUpgrade': '拒绝升级',
'DirectUpgrade': '直接升级',
'Submit': '提交',
'Review': '审核'
'Review': '审核',
'Resubmit': '重新提交'
}
return actionMap[action] || action
}
@ -181,8 +215,13 @@ function getActionTagType(action: string): string {
'Delete': 'danger',
'Approve': 'success',
'Reject': 'danger',
'RequestUpgrade': 'primary',
'ApproveUpgrade': 'success',
'RejectUpgrade': 'danger',
'DirectUpgrade': 'success',
'Submit': 'primary',
'Review': 'info'
'Review': 'info',
'Resubmit': 'primary'
}
return typeMap[action] || 'info'
}
@ -191,6 +230,8 @@ function getEntityTypeName(entityType: string): string {
const nameMap: Record<string, string> = {
'MaterialAllocation': '物资配额',
'AllocationDistribution': '配额分配',
'ConsumptionReport': '消耗记录',
'ConsumptionReportChangeRequest': '消耗删改申请',
'UserAccount': '用户账户',
'OrganizationalUnit': '组织单位',
'Personnel': '人员信息',
@ -224,7 +265,8 @@ async function loadLogs() {
}
const result = await auditLogsApi.getAll(params)
logs.value = result.items
//
logs.value = result.items.filter(log => log.userId != null)
pagination.totalCount = result.totalCount
} catch {
ElMessage.error('加载操作记录失败')

View File

@ -300,6 +300,8 @@ async function loadPersonnel() {
form.unit = person.unit || ''
form.position = person.position
form.rank = person.rank
form.gender = person.gender || '男'
form.age = person.age || 25
form.idNumber = person.idNumber || ''
form.professionalTitle = person.professionalTitle || ''
form.politicalStatus = person.politicalStatus || ''
@ -351,6 +353,8 @@ async function handleSubmit() {
formData.append('name', form.name)
formData.append('position', form.position)
formData.append('rank', form.rank)
formData.append('gender', form.gender)
formData.append('age', form.age.toString())
// -
if (form.unit) formData.append('unit', form.unit)

View File

@ -30,9 +30,6 @@
<el-option label="已批准" value="Approved">
<el-tag type="success" size="small">已批准</el-tag>
</el-option>
<el-option label="已拒绝" value="Rejected">
<el-tag type="danger" size="small">已拒绝</el-tag>
</el-option>
</el-select>
<el-button type="primary" @click="$router.push('/personnel/create')">
<el-icon><Plus /></el-icon>
@ -68,10 +65,7 @@
<el-table-column prop="submittedByUnitName" label="所属单位" min-width="120" show-overflow-tooltip />
<el-table-column prop="status" label="状态" width="120" align="center">
<template #default="{ row }">
<el-tag v-if="row.pendingUpgradeByUnitId" :type="canApproveUpgrade(row) ? 'warning' : 'primary'" effect="dark" size="small">
{{ canApproveUpgrade(row) ? '待审批' : '待上级审批' }}
</el-tag>
<el-tag v-else :type="getDisplayStatusType(row)" effect="dark" size="small">
<el-tag :type="getDisplayStatusType(row)" effect="dark" size="small">
{{ getDisplayStatus(row) }}
</el-tag>
</template>
@ -117,11 +111,16 @@
<el-icon><Check /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip v-if="row.status === 'Pending'" content="编辑" placement="top">
<el-tooltip v-if="row.status === 'Pending' || (row.status === 'Rejected' && canResubmit(row))" content="编辑" placement="top">
<el-button type="warning" link size="small" @click="handleEdit(row)">
<el-icon><Edit /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip v-if="row.status === 'Rejected' && canResubmit(row)" content="重新提交审批" placement="top">
<el-button type="primary" link size="small" @click="handleResubmit(row)">
<el-icon><Upload /></el-icon>
</el-button>
</el-tooltip>
<el-tooltip v-if="canDelete(row)" content="删除" placement="top">
<el-button type="danger" link size="small" @click="handleDelete(row)">
<el-icon><Delete /></el-icon>
@ -206,7 +205,7 @@
import { ref, reactive, watch, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus'
import { Plus, Search, View, Edit, Delete, Check, Close, User, Top } from '@element-plus/icons-vue'
import { Plus, Search, View, Edit, Delete, Check, Close, User, Top, Upload } from '@element-plus/icons-vue'
import { useAuthStore } from '@/stores/auth'
import { personnelApi } from '@/api'
import type { Personnel } from '@/types'
@ -252,27 +251,68 @@ function getStatusName(status: PersonnelStatus): string {
//
function getDisplayStatus(person: Personnel): string {
//
if (person.pendingUpgradeByUnitId) {
if (canApproveUpgrade(person)) {
return '待审批'
} else {
return '待上级审批'
}
}
//
if (person.status === PersonnelStatus.Rejected) {
return '已拒绝'
}
//
if (person.status === PersonnelStatus.Approved) {
return '已批准'
}
// Pending
if (person.status === PersonnelStatus.Pending) {
//
if (canApprovePersonnel(person)) {
return '待审批'
} else {
return '待上级审批'
}
}
//
return getStatusName(person.status)
}
//
function getDisplayStatusType(person: Personnel): string {
//
if (person.pendingUpgradeByUnitId) {
if (canApproveUpgrade(person)) {
return 'warning' // -
} else {
return 'primary' // -
}
}
// -
if (person.status === PersonnelStatus.Rejected) {
return 'danger'
}
//
if (person.status === PersonnelStatus.Approved) {
return 'success' // - 绿
}
// Pending
if (person.status === PersonnelStatus.Pending) {
//
if (canApprovePersonnel(person)) {
return 'warning' // -
} else {
return 'primary' // -
}
}
return getStatusTagType(person.status)
}
@ -422,7 +462,7 @@ function canApprovePersonnel(person: Personnel): boolean {
if (person.submittedByUnitId === authStore.user.organizationalUnitId) return false
//
//
return true
return authStore.organizationalLevelNum < 4 //
}
//
@ -436,7 +476,17 @@ function canDelete(person: Personnel): boolean {
return true
}
//
//
if (person.status === PersonnelStatus.Rejected) {
return person.submittedByUnitId === authStore.user.organizationalUnitId
}
//
if (person.status === PersonnelStatus.Approved && !person.approvedLevel) {
return true
}
//
if (person.status === PersonnelStatus.Approved && person.approvedLevel) {
const personnelLevelNum = {
'Division': 1,
@ -451,6 +501,37 @@ function canDelete(person: Personnel): boolean {
return false
}
//
function canResubmit(person: Personnel): boolean {
if (!authStore.user) return false
//
return person.status === PersonnelStatus.Rejected &&
person.submittedByUnitId === authStore.user.organizationalUnitId
}
//
async function handleResubmit(person: Personnel) {
try {
await ElMessageBox.confirm(
`确定要重新提交 "${person.name}" 的审批申请吗?\n重新提交后该人才将进入待审批状态。`,
'确认重新提交',
{
type: 'warning',
confirmButtonText: '确认提交',
cancelButtonText: '取消'
}
)
await personnelApi.resubmit(person.id)
ElMessage.success('已重新提交审批,等待上级审批')
await loadPersonnel()
} catch (error: any) {
if (error !== 'cancel') {
ElMessage.error(error.response?.data?.message || '重新提交失败')
}
}
}
function handleApprove(person: Personnel) {
selectedPerson.value = person
approvalForm.comments = ''