diff --git a/src/MilitaryTrainingManagement/Controllers/AllocationsController.cs b/src/MilitaryTrainingManagement/Controllers/AllocationsController.cs index 4b0bbb6..9044ea1 100644 --- a/src/MilitaryTrainingManagement/Controllers/AllocationsController.cs +++ b/src/MilitaryTrainingManagement/Controllers/AllocationsController.cs @@ -28,7 +28,9 @@ public class AllocationsController : BaseApiController /// 获取当前用户可见的所有物资配额 /// [HttpGet] - public async Task GetAll() + public async Task GetAll( + [FromQuery] int pageNumber = 1, + [FromQuery] int pageSize = 10) { var unitId = GetCurrentUnitId(); var unitLevel = GetCurrentUnitLevel(); @@ -37,18 +39,31 @@ public class AllocationsController : BaseApiController return Unauthorized(new { message = "无法获取用户组织信息" }); // 师团级可以看到所有配额,其他级别只能看到分配给自己及下级的配额 - IEnumerable allocations; + IEnumerable allAllocations; if (unitLevel == OrganizationalLevel.Division) { - allocations = await _allocationService.GetAllAsync(); + allAllocations = await _allocationService.GetAllAsync(); } else { - allocations = await _allocationService.GetVisibleToUnitAsync(unitId.Value); + allAllocations = await _allocationService.GetVisibleToUnitAsync(unitId.Value); } - var response = allocations.Select(MapToResponse); - return Ok(response); + var totalCount = allAllocations.Count(); + var items = allAllocations + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .Select(MapToResponse) + .ToList(); + + return Ok(new + { + items, + totalCount, + pageNumber, + pageSize, + totalPages = (int)Math.Ceiling(totalCount / (double)pageSize) + }); } /// @@ -132,7 +147,7 @@ public class AllocationsController : BaseApiController request.Unit, request.TotalQuota, unitId.Value, - request.Distributions); + request.GetDistributionsDictionary()); return CreatedAtAction(nameof(GetById), new { id = allocation.Id }, MapToResponse(allocation)); } @@ -251,12 +266,14 @@ public class AllocationsController : BaseApiController [HttpPost("validate")] public async Task ValidateDistributions([FromBody] CreateAllocationRequest request) { + var distributions = request.GetDistributionsDictionary(); + var isValid = await _allocationService.ValidateDistributionQuotasAsync( request.TotalQuota, - request.Distributions); + distributions); var targetUnitsExist = await _allocationService.ValidateTargetUnitsExistAsync( - request.Distributions.Keys); + distributions.Keys); return Ok(new { diff --git a/src/MilitaryTrainingManagement/Controllers/FilesController.cs b/src/MilitaryTrainingManagement/Controllers/FilesController.cs new file mode 100644 index 0000000..1981683 --- /dev/null +++ b/src/MilitaryTrainingManagement/Controllers/FilesController.cs @@ -0,0 +1,56 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.StaticFiles; + +namespace MilitaryTrainingManagement.Controllers; + +/// +/// 文件服务控制器 +/// +[ApiController] +[Route("api/files")] +public class FilesController : ControllerBase +{ + private readonly IWebHostEnvironment _environment; + private readonly FileExtensionContentTypeProvider _contentTypeProvider; + + public FilesController(IWebHostEnvironment environment) + { + _environment = environment; + _contentTypeProvider = new FileExtensionContentTypeProvider(); + } + + /// + /// 获取上传的文件 + /// + [HttpGet("{*filePath}")] + [AllowAnonymous] + public IActionResult GetFile(string filePath) + { + if (string.IsNullOrEmpty(filePath)) + return BadRequest(new { message = "文件路径不能为空" }); + + // 安全检查:防止路径遍历攻击 + if (filePath.Contains("..") || filePath.Contains("~")) + return BadRequest(new { message = "无效的文件路径" }); + + var uploadsPath = Path.Combine(_environment.ContentRootPath, "uploads"); + var fullPath = Path.Combine(uploadsPath, filePath); + + // 确保文件在uploads目录内 + if (!fullPath.StartsWith(uploadsPath)) + return BadRequest(new { message = "无效的文件路径" }); + + if (!System.IO.File.Exists(fullPath)) + return NotFound(new { message = "文件不存在" }); + + // 获取文件的MIME类型 + if (!_contentTypeProvider.TryGetContentType(fullPath, out var contentType)) + { + contentType = "application/octet-stream"; + } + + var fileStream = System.IO.File.OpenRead(fullPath); + return File(fileStream, contentType); + } +} diff --git a/src/MilitaryTrainingManagement/Controllers/OrganizationsController.cs b/src/MilitaryTrainingManagement/Controllers/OrganizationsController.cs index 479b4aa..f792cfd 100644 --- a/src/MilitaryTrainingManagement/Controllers/OrganizationsController.cs +++ b/src/MilitaryTrainingManagement/Controllers/OrganizationsController.cs @@ -1,7 +1,10 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; +using MilitaryTrainingManagement.Data; using MilitaryTrainingManagement.Models.DTOs; +using MilitaryTrainingManagement.Models.Entities; using MilitaryTrainingManagement.Services.Interfaces; +using Microsoft.EntityFrameworkCore; namespace MilitaryTrainingManagement.Controllers; @@ -12,10 +15,17 @@ namespace MilitaryTrainingManagement.Controllers; public class OrganizationsController : BaseApiController { private readonly IOrganizationService _organizationService; + private readonly IAuthenticationService _authService; + private readonly ApplicationDbContext _context; - public OrganizationsController(IOrganizationService organizationService) + public OrganizationsController( + IOrganizationService organizationService, + IAuthenticationService authService, + ApplicationDbContext context) { _organizationService = organizationService; + _authService = authService; + _context = context; } [HttpGet] @@ -63,4 +73,39 @@ public class OrganizationsController : BaseApiController await _organizationService.DeleteAsync(id); return NoContent(); } + + /// + /// 为组织创建账户 + /// + [HttpPost("{id}/accounts")] + public async Task CreateAccount(int id, [FromBody] CreateAccountRequest request) + { + var unit = await _organizationService.GetByIdAsync(id); + if (unit == null) + { + return NotFound(new { message = "组织不存在" }); + } + + // 检查用户名是否已存在 + var existingUser = await _context.UserAccounts + .FirstOrDefaultAsync(u => u.Username == request.Username); + if (existingUser != null) + { + return BadRequest(new { message = "用户名已存在" }); + } + + var account = new UserAccount + { + Username = request.Username, + PasswordHash = _authService.HashPassword(request.Password), + DisplayName = unit.Name + "管理员", + OrganizationalUnitId = id, + CreatedAt = DateTime.UtcNow + }; + + _context.UserAccounts.Add(account); + await _context.SaveChangesAsync(); + + return Ok(new { message = "账户创建成功", username = account.Username }); + } } diff --git a/src/MilitaryTrainingManagement/Controllers/PersonnelController.cs b/src/MilitaryTrainingManagement/Controllers/PersonnelController.cs index 3a03094..5aa4275 100644 --- a/src/MilitaryTrainingManagement/Controllers/PersonnelController.cs +++ b/src/MilitaryTrainingManagement/Controllers/PersonnelController.cs @@ -32,7 +32,10 @@ public class PersonnelController : BaseApiController /// 获取人员列表(基于层级权限控制) /// [HttpGet] - public async Task GetPersonnel([FromQuery] bool includeSubordinates = false) + public async Task GetPersonnel( + [FromQuery] int pageNumber = 1, + [FromQuery] int pageSize = 10, + [FromQuery] string? status = null) { var unitId = GetCurrentUnitId(); var userLevel = GetCurrentUnitLevel(); @@ -42,8 +45,28 @@ public class PersonnelController : BaseApiController return Unauthorized(); } - var personnel = await _personnelService.GetVisiblePersonnelAsync(unitId.Value, userLevel.Value); - return Ok(personnel); + var allPersonnel = await _personnelService.GetVisiblePersonnelAsync(unitId.Value, userLevel.Value); + + // 状态筛选 + if (!string.IsNullOrEmpty(status) && Enum.TryParse(status, out var statusFilter)) + { + allPersonnel = allPersonnel.Where(p => p.Status == statusFilter); + } + + var totalCount = allPersonnel.Count(); + var items = allPersonnel + .Skip((pageNumber - 1) * pageSize) + .Take(pageSize) + .ToList(); + + return Ok(new + { + items, + totalCount, + pageNumber, + pageSize, + totalPages = (int)Math.Ceiling(totalCount / (double)pageSize) + }); } /// @@ -136,8 +159,21 @@ public class PersonnelController : BaseApiController } [HttpPost] - public async Task Create([FromBody] CreatePersonnelRequest request) + public async Task Create([FromForm] CreatePersonnelRequest request, + IFormFile? photo, IFormFile? supportingDocuments) { + // 返回详细的验证错误 + if (!ModelState.IsValid) + { + var errors = ModelState + .Where(x => x.Value?.Errors.Count > 0) + .ToDictionary( + kvp => kvp.Key, + kvp => kvp.Value?.Errors.Select(e => e.ErrorMessage).ToArray() + ); + return BadRequest(new { message = "验证失败", errors }); + } + var unitId = GetCurrentUnitId(); if (unitId == null) { @@ -162,15 +198,27 @@ public class PersonnelController : BaseApiController SubmittedByUnitId = unitId.Value }; - var created = await _personnelService.CreateAsync(personnel); - return CreatedAtAction(nameof(GetById), new { id = created.Id }, created); + try + { + var created = await _personnelService.SubmitPersonnelAsync(personnel, photo, supportingDocuments); + return CreatedAtAction(nameof(GetById), new { id = created.Id }, created); + } + catch (ArgumentException ex) + { + return BadRequest(new { message = ex.Message }); + } + catch (InvalidOperationException ex) + { + return BadRequest(new { message = ex.Message }); + } } /// /// 更新人员信息(仅限未审批的人员) /// [HttpPut("{id}")] - public async Task Update(int id, [FromBody] UpdatePersonnelRequest request) + public async Task Update(int id, [FromForm] UpdatePersonnelRequest request, + IFormFile? photo, IFormFile? supportingDocuments) { var personnel = await _personnelService.GetByIdAsync(id); if (personnel == null) @@ -206,6 +254,30 @@ public class PersonnelController : BaseApiController personnel.TrainingParticipation = request.TrainingParticipation; personnel.Achievements = request.Achievements; + // 处理照片上传 + if (photo != null) + { + if (!string.IsNullOrEmpty(personnel.PhotoPath)) + { + await _fileUploadService.DeleteFileAsync(personnel.PhotoPath); + } + personnel.PhotoPath = await _fileUploadService.UploadPhotoAsync(photo); + } + + // 处理文档上传 + if (supportingDocuments != null) + { + var documentPath = await _fileUploadService.UploadDocumentAsync(supportingDocuments); + if (string.IsNullOrEmpty(personnel.SupportingDocuments)) + { + personnel.SupportingDocuments = documentPath; + } + else + { + personnel.SupportingDocuments += ";" + documentPath; + } + } + var updated = await _personnelService.UpdateAsync(personnel); return Ok(updated); } @@ -356,7 +428,7 @@ public class PersonnelController : BaseApiController var canApprove = await _personnelService.CanApprovePersonnelAsync(userId.Value, id); if (!canApprove) { - return Forbid("您没有权限审批此人员"); + return StatusCode(403, new { message = "您没有权限审批此人员" }); } try @@ -366,7 +438,7 @@ public class PersonnelController : BaseApiController } catch (ArgumentException ex) { - return BadRequest(ex.Message); + return BadRequest(new { message = ex.Message }); } } @@ -387,7 +459,7 @@ public class PersonnelController : BaseApiController var canApprove = await _personnelService.CanApprovePersonnelAsync(userId.Value, id); if (!canApprove) { - return Forbid("您没有权限拒绝此人员"); + return StatusCode(403, new { message = "您没有权限拒绝此人员" }); } var personnel = await _personnelService.RejectAsync(id, userId.Value); diff --git a/src/MilitaryTrainingManagement/Models/DTOs/AllocationDTOs.cs b/src/MilitaryTrainingManagement/Models/DTOs/AllocationDTOs.cs index bc1c1db..7e52ddb 100644 --- a/src/MilitaryTrainingManagement/Models/DTOs/AllocationDTOs.cs +++ b/src/MilitaryTrainingManagement/Models/DTOs/AllocationDTOs.cs @@ -2,6 +2,15 @@ using System.ComponentModel.DataAnnotations; namespace MilitaryTrainingManagement.Models.DTOs; +/// +/// 分配请求项 +/// +public class DistributionRequestItem +{ + public int TargetUnitId { get; set; } + public decimal UnitQuota { get; set; } +} + /// /// 创建物资配额请求 /// @@ -35,10 +44,19 @@ public class CreateAllocationRequest public decimal TotalQuota { get; set; } /// - /// 分配记录 (目标单位ID -> 配额) + /// 分配记录 /// - [Required(ErrorMessage = "分配记录为必填项")] - public Dictionary Distributions { get; set; } = new(); + public List Distributions { get; set; } = new(); + + /// + /// 转换为字典格式 + /// + public Dictionary GetDistributionsDictionary() + { + return Distributions + .Where(d => d.TargetUnitId > 0) + .ToDictionary(d => d.TargetUnitId, d => d.UnitQuota); + } } /// diff --git a/src/MilitaryTrainingManagement/Models/DTOs/OrganizationDTOs.cs b/src/MilitaryTrainingManagement/Models/DTOs/OrganizationDTOs.cs index 415b6ac..c78f956 100644 --- a/src/MilitaryTrainingManagement/Models/DTOs/OrganizationDTOs.cs +++ b/src/MilitaryTrainingManagement/Models/DTOs/OrganizationDTOs.cs @@ -13,3 +13,9 @@ public class UpdateOrganizationRequest { public string Name { get; set; } = string.Empty; } + +public class CreateAccountRequest +{ + public string Username { get; set; } = string.Empty; + public string Password { get; set; } = string.Empty; +} diff --git a/src/MilitaryTrainingManagement/Program.cs b/src/MilitaryTrainingManagement/Program.cs index 4cc48e7..104edc2 100644 --- a/src/MilitaryTrainingManagement/Program.cs +++ b/src/MilitaryTrainingManagement/Program.cs @@ -88,6 +88,8 @@ builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles; + // 将枚举序列化为字符串 + options.JsonSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter()); }); builder.Services.AddEndpointsApiExplorer(); diff --git a/src/MilitaryTrainingManagement/Services/Implementations/FileUploadService.cs b/src/MilitaryTrainingManagement/Services/Implementations/FileUploadService.cs index 302b7ef..670f4a7 100644 --- a/src/MilitaryTrainingManagement/Services/Implementations/FileUploadService.cs +++ b/src/MilitaryTrainingManagement/Services/Implementations/FileUploadService.cs @@ -11,13 +11,12 @@ public class FileUploadService : IFileUploadService private readonly IWebHostEnvironment _environment; private readonly ILogger _logger; - // 2寸照片标准尺寸(像素):35mm x 49mm,300dpi约为413x579像素 - // 允许一定的误差范围 - private const int MinPhotoWidth = 300; - private const int MaxPhotoWidth = 600; - private const int MinPhotoHeight = 400; - private const int MaxPhotoHeight = 800; - private const long MaxPhotoSize = 5 * 1024 * 1024; // 5MB + // 照片尺寸限制(放宽限制以适应各种照片) + private const int MinPhotoWidth = 100; + private const int MaxPhotoWidth = 4000; + private const int MinPhotoHeight = 100; + private const int MaxPhotoHeight = 4000; + private const long MaxPhotoSize = 10 * 1024 * 1024; // 10MB private const long MaxDocumentSize = 20 * 1024 * 1024; // 20MB private static readonly string[] AllowedPhotoExtensions = { ".jpg", ".jpeg", ".png" }; @@ -73,7 +72,7 @@ public class FileUploadService : IFileUploadService var width = image.Width; var height = image.Height; - // 验证2寸照片尺寸要求 + // 验证照片尺寸(放宽限制) if (width < MinPhotoWidth || width > MaxPhotoWidth) { return PhotoValidationResult.Failure($"照片宽度应在{MinPhotoWidth}-{MaxPhotoWidth}像素之间,当前为{width}像素"); @@ -84,13 +83,7 @@ public class FileUploadService : IFileUploadService return PhotoValidationResult.Failure($"照片高度应在{MinPhotoHeight}-{MaxPhotoHeight}像素之间,当前为{height}像素"); } - // 验证宽高比(2寸照片约为35:49 ≈ 0.71) - var aspectRatio = (double)width / height; - if (aspectRatio < 0.5 || aspectRatio > 0.9) - { - return PhotoValidationResult.Failure("照片宽高比不符合2寸照片要求(约35:49)"); - } - + // 不再严格验证宽高比,允许各种照片 return PhotoValidationResult.Success(width, height); } catch (Exception ex) diff --git a/src/MilitaryTrainingManagement/Services/Implementations/PersonnelService.cs b/src/MilitaryTrainingManagement/Services/Implementations/PersonnelService.cs index 219a5a2..463e729 100644 --- a/src/MilitaryTrainingManagement/Services/Implementations/PersonnelService.cs +++ b/src/MilitaryTrainingManagement/Services/Implementations/PersonnelService.cs @@ -91,10 +91,11 @@ public class PersonnelService : IPersonnelService if (approvedByUnit == null) throw new ArgumentException("审批单位不存在"); - // 验证人员等级与审批单位层级一致 - var expectedLevel = (PersonnelLevel)(int)approvedByUnit.Level; - if (level != expectedLevel) - 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; @@ -528,6 +529,10 @@ public class PersonnelService : IPersonnelService 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); diff --git a/src/MilitaryTrainingManagement/bin/Debug/net8.0/MilitaryTrainingManagement.dll b/src/MilitaryTrainingManagement/bin/Debug/net8.0/MilitaryTrainingManagement.dll index 8658df2..e3c73e3 100644 Binary files a/src/MilitaryTrainingManagement/bin/Debug/net8.0/MilitaryTrainingManagement.dll and b/src/MilitaryTrainingManagement/bin/Debug/net8.0/MilitaryTrainingManagement.dll differ diff --git a/src/MilitaryTrainingManagement/bin/Debug/net8.0/MilitaryTrainingManagement.exe b/src/MilitaryTrainingManagement/bin/Debug/net8.0/MilitaryTrainingManagement.exe index 19b37b9..f293879 100644 Binary files a/src/MilitaryTrainingManagement/bin/Debug/net8.0/MilitaryTrainingManagement.exe and b/src/MilitaryTrainingManagement/bin/Debug/net8.0/MilitaryTrainingManagement.exe differ diff --git a/src/MilitaryTrainingManagement/bin/Debug/net8.0/MilitaryTrainingManagement.pdb b/src/MilitaryTrainingManagement/bin/Debug/net8.0/MilitaryTrainingManagement.pdb index 3c83b97..4d0ac2d 100644 Binary files a/src/MilitaryTrainingManagement/bin/Debug/net8.0/MilitaryTrainingManagement.pdb and b/src/MilitaryTrainingManagement/bin/Debug/net8.0/MilitaryTrainingManagement.pdb differ diff --git a/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.AssemblyInfo.cs b/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.AssemblyInfo.cs index 42b0d69..ad889f2 100644 --- a/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.AssemblyInfo.cs +++ b/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.AssemblyInfo.cs @@ -13,7 +13,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("MilitaryTrainingManagement")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+dea416c1206a35a2f77e33f37406891d5377f1a6")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+8cada25804844f7e625d0f213b568324580ab12f")] [assembly: System.Reflection.AssemblyProductAttribute("MilitaryTrainingManagement")] [assembly: System.Reflection.AssemblyTitleAttribute("MilitaryTrainingManagement")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.AssemblyInfoInputs.cache b/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.AssemblyInfoInputs.cache index af04cf4..a49bc0f 100644 --- a/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.AssemblyInfoInputs.cache +++ b/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.AssemblyInfoInputs.cache @@ -1 +1 @@ -71761eb666e1de962287ab2d3efffb4d59f9a1ceb181540c49222eadb77fb760 +947693409f11b6d9a783dd5578eff75f0295de8dfc4524de39ae86b037f3a76a diff --git a/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.csproj.CoreCompileInputs.cache b/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.csproj.CoreCompileInputs.cache index a074306..66b6394 100644 --- a/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.csproj.CoreCompileInputs.cache +++ b/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.csproj.CoreCompileInputs.cache @@ -1 +1 @@ -9aa7a804a7b716c6d51cb391fd2292638fb67f08685ae7d440982f042b40fd5e +33d3e5abc8ec2790adb522e907e856fe53e06ac3a2e49a9f8cb326340b3b3177 diff --git a/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.dll b/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.dll index 8658df2..e3c73e3 100644 Binary files a/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.dll and b/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.dll differ diff --git a/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.pdb b/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.pdb index 3c83b97..4d0ac2d 100644 Binary files a/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.pdb and b/src/MilitaryTrainingManagement/obj/Debug/net8.0/MilitaryTrainingManagement.pdb differ diff --git a/src/MilitaryTrainingManagement/obj/Debug/net8.0/apphost.exe b/src/MilitaryTrainingManagement/obj/Debug/net8.0/apphost.exe index 19b37b9..f293879 100644 Binary files a/src/MilitaryTrainingManagement/obj/Debug/net8.0/apphost.exe and b/src/MilitaryTrainingManagement/obj/Debug/net8.0/apphost.exe differ diff --git a/src/MilitaryTrainingManagement/obj/Debug/net8.0/ref/MilitaryTrainingManagement.dll b/src/MilitaryTrainingManagement/obj/Debug/net8.0/ref/MilitaryTrainingManagement.dll index e16aafd..dc7eb07 100644 Binary files a/src/MilitaryTrainingManagement/obj/Debug/net8.0/ref/MilitaryTrainingManagement.dll and b/src/MilitaryTrainingManagement/obj/Debug/net8.0/ref/MilitaryTrainingManagement.dll differ diff --git a/src/MilitaryTrainingManagement/obj/Debug/net8.0/refint/MilitaryTrainingManagement.dll b/src/MilitaryTrainingManagement/obj/Debug/net8.0/refint/MilitaryTrainingManagement.dll index e16aafd..dc7eb07 100644 Binary files a/src/MilitaryTrainingManagement/obj/Debug/net8.0/refint/MilitaryTrainingManagement.dll and b/src/MilitaryTrainingManagement/obj/Debug/net8.0/refint/MilitaryTrainingManagement.dll differ diff --git a/src/MilitaryTrainingManagement/uploads/photos/cecfd4b9-34f6-4aff-a8b3-bd10ca34652c.jpg b/src/MilitaryTrainingManagement/uploads/photos/cecfd4b9-34f6-4aff-a8b3-bd10ca34652c.jpg new file mode 100644 index 0000000..eb12a19 Binary files /dev/null and b/src/MilitaryTrainingManagement/uploads/photos/cecfd4b9-34f6-4aff-a8b3-bd10ca34652c.jpg differ diff --git a/src/frontend/src/api/personnel.ts b/src/frontend/src/api/personnel.ts index ada15c9..5098959 100644 --- a/src/frontend/src/api/personnel.ts +++ b/src/frontend/src/api/personnel.ts @@ -41,8 +41,7 @@ export const personnelApi = { async approve(data: PersonnelApprovalRequest): Promise { const response = await apiClient.post(`/personnel/${data.personnelId}/approve`, { - approved: data.approved, - comments: data.comments + level: data.level }) return response.data }, diff --git a/src/frontend/src/stores/auth.ts b/src/frontend/src/stores/auth.ts index 36330d6..65838b2 100644 --- a/src/frontend/src/stores/auth.ts +++ b/src/frontend/src/stores/auth.ts @@ -2,6 +2,7 @@ import { defineStore } from 'pinia' import { ref, computed } from 'vue' import { authApi } from '@/api/auth' import type { User, LoginRequest } from '@/types' +import { OrganizationalLevel, OrganizationalLevelValue } from '@/types' export const useAuthStore = defineStore('auth', () => { const user = ref(null) @@ -11,13 +12,17 @@ export const useAuthStore = defineStore('auth', () => { // 只检查token是否存在 const isAuthenticated = computed(() => !!token.value) - const organizationalLevel = computed(() => user.value?.organizationalLevel ?? 4) + // 获取组织级别的数值(用于权限比较) + const organizationalLevelNum = computed(() => { + if (!user.value?.organizationalLevel) return 4 + return OrganizationalLevelValue[user.value.organizationalLevel] ?? 4 + }) - const canManageSubordinates = computed(() => organizationalLevel.value < 4) + const canManageSubordinates = computed(() => organizationalLevelNum.value < 4) - const canCreateAllocations = computed(() => organizationalLevel.value === 1) + const canCreateAllocations = computed(() => organizationalLevelNum.value === 1) - const canApprove = computed(() => organizationalLevel.value < 4) + const canApprove = computed(() => organizationalLevelNum.value < 4) async function login(credentials: LoginRequest): Promise { try { @@ -80,7 +85,7 @@ export const useAuthStore = defineStore('auth', () => { } function hasPermission(requiredLevel: number): boolean { - return organizationalLevel.value <= requiredLevel + return organizationalLevelNum.value <= requiredLevel } return { @@ -88,7 +93,8 @@ export const useAuthStore = defineStore('auth', () => { token, initialized, isAuthenticated, - organizationalLevel, + organizationalLevel: computed(() => user.value?.organizationalLevel), + organizationalLevelNum, canManageSubordinates, canCreateAllocations, canApprove, diff --git a/src/frontend/src/types/index.ts b/src/frontend/src/types/index.ts index 0042df2..aef0fde 100644 --- a/src/frontend/src/types/index.ts +++ b/src/frontend/src/types/index.ts @@ -1,16 +1,24 @@ // Enums export enum OrganizationalLevel { - Division = 1, // 师团 - Regiment = 2, // 团 - Battalion = 3, // 营 - Company = 4 // 连 + Division = 'Division', // 师团 + Regiment = 'Regiment', // 团 + Battalion = 'Battalion', // 营 + Company = 'Company' // 连 +} + +// 用于权限判断的级别数值映射 +export const OrganizationalLevelValue: Record = { + [OrganizationalLevel.Division]: 1, + [OrganizationalLevel.Regiment]: 2, + [OrganizationalLevel.Battalion]: 3, + [OrganizationalLevel.Company]: 4 } export enum PersonnelLevel { - Division = 1, // 师级人才 - Regiment = 2, // 团级人才 - Battalion = 3, // 营级人才 - Company = 4 // 连级人才 + Division = 'Division', // 师级人才 + Regiment = 'Regiment', // 团级人才 + Battalion = 'Battalion', // 营级人才 + Company = 'Company' // 连级人才 } export enum PersonnelStatus { @@ -192,6 +200,7 @@ export interface PersonnelApprovalRequest { personnelId: number approved: boolean comments?: string + level?: string } // Approval types diff --git a/src/frontend/src/views/allocations/AllocationList.vue b/src/frontend/src/views/allocations/AllocationList.vue index 5431999..29e3701 100644 --- a/src/frontend/src/views/allocations/AllocationList.vue +++ b/src/frontend/src/views/allocations/AllocationList.vue @@ -1,66 +1,202 @@ diff --git a/src/frontend/src/views/personnel/PersonnelList.vue b/src/frontend/src/views/personnel/PersonnelList.vue index ebe3b99..2ca27c6 100644 --- a/src/frontend/src/views/personnel/PersonnelList.vue +++ b/src/frontend/src/views/personnel/PersonnelList.vue @@ -1,14 +1,35 @@