using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using MilitaryTrainingManagement.Authorization; using MilitaryTrainingManagement.Models.DTOs; using MilitaryTrainingManagement.Models.Enums; using MilitaryTrainingManagement.Services.Interfaces; namespace MilitaryTrainingManagement.Controllers; /// /// 物资配额控制器 /// [Authorize] public class AllocationsController : BaseApiController { private readonly IAllocationService _allocationService; private readonly IOrganizationalAuthorizationService _authorizationService; public AllocationsController( IAllocationService allocationService, IOrganizationalAuthorizationService authorizationService) { _allocationService = allocationService; _authorizationService = authorizationService; } /// /// 获取当前用户可见的所有物资配额 /// [HttpGet] public async Task GetAll( [FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 10) { var unitId = GetCurrentUnitId(); var unitLevel = GetCurrentUnitLevel(); if (unitId == null || unitLevel == null) return Unauthorized(new { message = "无法获取用户组织信息" }); // 师团级可以看到所有配额,其他级别只能看到分配给自己及下级的配额 IEnumerable allAllocations; if (unitLevel == OrganizationalLevel.Division) { allAllocations = await _allocationService.GetAllAsync(); } else { allAllocations = await _allocationService.GetVisibleToUnitAsync(unitId.Value); } 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) }); } /// /// 获取当前单位创建的物资配额 /// [HttpGet("my")] public async Task GetMyAllocations() { var unitId = GetCurrentUnitId(); if (unitId == null) return Unauthorized(new { message = "无法获取用户组织信息" }); var allocations = await _allocationService.GetByUnitAsync(unitId.Value); var response = allocations.Select(MapToResponse); return Ok(response); } /// /// 获取分配给当前单位的配额分配记录 /// [HttpGet("distributions")] public async Task GetMyDistributions() { var unitId = GetCurrentUnitId(); if (unitId == null) return Unauthorized(new { message = "无法获取用户组织信息" }); var distributions = await _allocationService.GetDistributionsForUnitAsync(unitId.Value); var response = distributions.Select(MapDistributionToResponse); return Ok(response); } /// /// 根据ID获取物资配额 /// 需求1.1, 1.2, 1.3:营部及以下级别用户只能查看本单位及直接下级的配额 /// [HttpGet("{id}")] public async Task GetById(int id) { var unitId = GetCurrentUnitId(); var unitLevel = GetCurrentUnitLevel(); if (unitId == null || unitLevel == null) return Unauthorized(new { message = "无法获取用户组织信息" }); var allocation = await _allocationService.GetByIdAsync(id); if (allocation == null) return NotFound(new { message = "配额不存在" }); // 检查访问权限: // 1. 可以查看自己创建的配额 // 2. 使用可见性过滤检查是否可以访问配额中的任何一个分配记录 var canAccess = allocation.CreatedByUnitId == unitId.Value; if (!canAccess) { // 检查是否有任何分配记录在用户的可见范围内 foreach (var dist in allocation.Distributions) { if (await _allocationService.CanViewDistributionAsync(unitId.Value, unitLevel.Value, dist.TargetUnitId)) { canAccess = true; break; } } } if (!canAccess) return Forbid(); // 过滤分配记录,只返回用户可见范围内的记录 var filteredAllocation = await FilterAllocationDistributions(allocation, unitId.Value, unitLevel.Value); return Ok(MapToResponse(filteredAllocation)); } /// /// 过滤配额的分配记录,只保留用户可见范围内的记录 /// private async Task FilterAllocationDistributions( Models.Entities.MaterialAllocation allocation, int userUnitId, OrganizationalLevel userLevel) { var visibleDistributions = new List(); foreach (var dist in allocation.Distributions) { if (await _allocationService.CanViewDistributionAsync(userUnitId, userLevel, dist.TargetUnitId)) { visibleDistributions.Add(dist); } } // 创建一个新的配额对象,只包含可见的分配记录 return new Models.Entities.MaterialAllocation { Id = allocation.Id, Category = allocation.Category, MaterialName = allocation.MaterialName, Unit = allocation.Unit, TotalQuota = allocation.TotalQuota, CreatedByUnitId = allocation.CreatedByUnitId, CreatedByUnit = allocation.CreatedByUnit, CreatedAt = allocation.CreatedAt, Distributions = visibleDistributions }; } /// /// 创建物资配额 /// 需求2.1:师团管理员可以创建物资类别 /// 需求2.2:创建配额需要物资名称、单位、总配额和目标单位 /// 需求2.4:创建后自动分发给目标单位 /// [HttpPost] [Authorize(Policy = "DivisionLevel")] public async Task Create([FromBody] CreateAllocationRequest request) { var unitId = GetCurrentUnitId(); if (unitId == null) return Unauthorized(new { message = "无法获取用户组织信息" }); // 验证必填字段 if (!ModelState.IsValid) return BadRequest(ModelState); try { var allocation = await _allocationService.CreateAsync( request.Category, request.MaterialName, request.Unit, request.TotalQuota, unitId.Value, request.GetDistributionsDictionary()); return CreatedAtAction(nameof(GetById), new { id = allocation.Id }, MapToResponse(allocation)); } catch (ArgumentException ex) { return BadRequest(new { message = ex.Message }); } } /// /// 更新物资配额基本信息 /// [HttpPut("{id}")] [Authorize(Policy = "DivisionLevel")] public async Task Update(int id, [FromBody] UpdateAllocationRequest request) { var unitId = GetCurrentUnitId(); if (unitId == null) return Unauthorized(new { message = "无法获取用户组织信息" }); // 验证必填字段 if (!ModelState.IsValid) return BadRequest(ModelState); // 检查配额是否存在 var existingAllocation = await _allocationService.GetByIdAsync(id); if (existingAllocation == null) return NotFound(new { message = "配额不存在" }); // 检查是否有权限修改(只能修改自己创建的配额) if (existingAllocation.CreatedByUnitId != unitId.Value) return Forbid(); try { var allocation = await _allocationService.UpdateAsync( id, request.Category, request.MaterialName, request.Unit, request.TotalQuota); return Ok(MapToResponse(await _allocationService.GetByIdAsync(id) ?? allocation)); } catch (ArgumentException ex) { return BadRequest(new { message = ex.Message }); } } /// /// 更新配额分配记录 /// [HttpPut("{id}/distributions")] [Authorize(Policy = "DivisionLevel")] public async Task UpdateDistributions(int id, [FromBody] Dictionary distributions) { var unitId = GetCurrentUnitId(); if (unitId == null) return Unauthorized(new { message = "无法获取用户组织信息" }); // 检查配额是否存在 var existingAllocation = await _allocationService.GetByIdAsync(id); if (existingAllocation == null) return NotFound(new { message = "配额不存在" }); // 检查是否有权限修改 if (existingAllocation.CreatedByUnitId != unitId.Value) return Forbid(); try { var allocation = await _allocationService.UpdateDistributionsAsync(id, distributions); return Ok(MapToResponse(allocation)); } catch (ArgumentException ex) { return BadRequest(new { message = ex.Message }); } } /// /// 上报消耗 - 更新单个分配记录的实际完成数量 /// 团部及以下单位可以上报自己的消耗数据 /// [HttpPut("distributions/{distributionId}")] public async Task UpdateDistribution(int distributionId, [FromBody] UpdateDistributionRequest request) { var unitId = GetCurrentUnitId(); var userId = GetCurrentUserId(); if (unitId == null || userId == null) return Unauthorized(new { message = "无法获取用户信息" }); try { var distribution = await _allocationService.UpdateDistributionCompletionAsync( distributionId, unitId.Value, userId.Value, request.ActualCompletion); return Ok(MapDistributionToResponse(distribution)); } catch (UnauthorizedAccessException ex) { return Forbid(); } catch (ArgumentException ex) { return BadRequest(new { message = ex.Message }); } } /// /// 删除物资配额 /// [HttpDelete("{id}")] [Authorize(Policy = "DivisionLevel")] public async Task Delete(int id) { var unitId = GetCurrentUnitId(); if (unitId == null) return Unauthorized(new { message = "无法获取用户组织信息" }); // 检查配额是否存在 var existingAllocation = await _allocationService.GetByIdAsync(id); if (existingAllocation == null) return NotFound(new { message = "配额不存在" }); // 检查是否有权限删除 if (existingAllocation.CreatedByUnitId != unitId.Value) return Forbid(); try { await _allocationService.DeleteAsync(id); return NoContent(); } catch (ArgumentException ex) { return BadRequest(new { message = ex.Message }); } } /// /// 验证配额分配是否有效 /// [HttpPost("validate")] public async Task ValidateDistributions([FromBody] CreateAllocationRequest request) { var distributions = request.GetDistributionsDictionary(); var isValid = await _allocationService.ValidateDistributionQuotasAsync( request.TotalQuota, distributions); var targetUnitsExist = await _allocationService.ValidateTargetUnitsExistAsync( distributions.Keys); return Ok(new { quotaValid = isValid, targetUnitsExist = targetUnitsExist, isValid = isValid && targetUnitsExist, message = !isValid ? "有配额未分配,无法提交" : !targetUnitsExist ? "目标单位不存在" : "验证通过" }); } /// /// 获取配额分配的上报历史记录 /// 需求1.1, 1.2, 1.3:营部及以下级别用户只能查看本单位及直接下级的记录 /// [HttpGet("distributions/{distributionId}/reports")] public async Task GetConsumptionReports(int distributionId) { var unitId = GetCurrentUnitId(); var unitLevel = GetCurrentUnitLevel(); if (unitId == null || unitLevel == null) return Unauthorized(new { message = "无法获取用户组织信息" }); // 检查分配记录是否存在 var distribution = await _allocationService.GetDistributionByIdAsync(distributionId); if (distribution == null) return NotFound(new { message = "配额分配记录不存在" }); // 使用可见性过滤检查访问权限 // 营部及以下级别:只能查看本单位及直接下级的记录 // 师团/团级:可以查看所有下级的记录 var canAccess = await _allocationService.CanViewDistributionAsync( unitId.Value, unitLevel.Value, distribution.TargetUnitId); if (!canAccess) return Forbid(); // 获取上报历史记录(带可见性过滤) // 只返回用户可见范围内的单位的上报记录 var reports = await _allocationService.GetConsumptionReportsAsync(distributionId, unitId.Value, unitLevel.Value); var response = reports.Select(r => new { id = r.Id, reportedAmount = r.ReportedAmount, cumulativeAmount = r.CumulativeAmount, remarks = r.Remarks, reportedByUserName = r.ReportedByUser?.DisplayName, reportedAt = r.ReportedAt }); return Ok(response); } /// /// 映射实体到响应DTO /// private static AllocationResponse MapToResponse(Models.Entities.MaterialAllocation allocation) { return new AllocationResponse { Id = allocation.Id, Category = allocation.Category, MaterialName = allocation.MaterialName, Unit = allocation.Unit, TotalQuota = allocation.TotalQuota, CreatedByUnitId = allocation.CreatedByUnitId, CreatedByUnitName = allocation.CreatedByUnit?.Name ?? string.Empty, CreatedAt = allocation.CreatedAt, Distributions = allocation.Distributions.Select(d => new AllocationDistributionResponse { Id = d.Id, TargetUnitId = d.TargetUnitId, TargetUnitName = d.TargetUnit?.Name ?? string.Empty, UnitQuota = d.UnitQuota, ActualCompletion = d.ActualCompletion, CompletionRate = d.CompletionRate, ReportedAt = d.ReportedAt, ReportedByUserName = d.ReportedByUser?.DisplayName }).ToList() }; } /// /// 映射分配记录到响应DTO /// private static AllocationDistributionResponse MapDistributionToResponse(Models.Entities.AllocationDistribution distribution) { return new AllocationDistributionResponse { Id = distribution.Id, TargetUnitId = distribution.TargetUnitId, TargetUnitName = distribution.TargetUnit?.Name ?? string.Empty, UnitQuota = distribution.UnitQuota, ActualCompletion = distribution.ActualCompletion, CompletionRate = distribution.CompletionRate, ReportedAt = distribution.ReportedAt, ReportedByUserName = distribution.ReportedByUser?.DisplayName }; } }