464 lines
16 KiB
C#
464 lines
16 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// 物资配额控制器
|
|
/// </summary>
|
|
[Authorize]
|
|
public class AllocationsController : BaseApiController
|
|
{
|
|
private readonly IAllocationService _allocationService;
|
|
private readonly IOrganizationalAuthorizationService _authorizationService;
|
|
|
|
public AllocationsController(
|
|
IAllocationService allocationService,
|
|
IOrganizationalAuthorizationService authorizationService)
|
|
{
|
|
_allocationService = allocationService;
|
|
_authorizationService = authorizationService;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取当前用户可见的所有物资配额
|
|
/// </summary>
|
|
[HttpGet]
|
|
public async Task<IActionResult> 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<Models.Entities.MaterialAllocation> 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)
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取当前单位创建的物资配额
|
|
/// </summary>
|
|
[HttpGet("my")]
|
|
public async Task<IActionResult> 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取分配给当前单位的配额分配记录
|
|
/// </summary>
|
|
[HttpGet("distributions")]
|
|
public async Task<IActionResult> 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 根据ID获取物资配额
|
|
/// 需求1.1, 1.2, 1.3:营部及以下级别用户只能查看本单位及直接下级的配额
|
|
/// </summary>
|
|
[HttpGet("{id}")]
|
|
public async Task<IActionResult> 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));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 过滤配额的分配记录,只保留用户可见范围内的记录
|
|
/// </summary>
|
|
private async Task<Models.Entities.MaterialAllocation> FilterAllocationDistributions(
|
|
Models.Entities.MaterialAllocation allocation,
|
|
int userUnitId,
|
|
OrganizationalLevel userLevel)
|
|
{
|
|
var visibleDistributions = new List<Models.Entities.AllocationDistribution>();
|
|
|
|
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
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 创建物资配额
|
|
/// 需求2.1:师团管理员可以创建物资类别
|
|
/// 需求2.2:创建配额需要物资名称、单位、总配额和目标单位
|
|
/// 需求2.4:创建后自动分发给目标单位
|
|
/// </summary>
|
|
[HttpPost]
|
|
[Authorize(Policy = "DivisionLevel")]
|
|
public async Task<IActionResult> 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 });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 更新物资配额基本信息
|
|
/// </summary>
|
|
[HttpPut("{id}")]
|
|
[Authorize(Policy = "DivisionLevel")]
|
|
public async Task<IActionResult> 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 });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 更新配额分配记录
|
|
/// </summary>
|
|
[HttpPut("{id}/distributions")]
|
|
[Authorize(Policy = "DivisionLevel")]
|
|
public async Task<IActionResult> UpdateDistributions(int id, [FromBody] Dictionary<int, decimal> 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 });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 上报消耗 - 更新单个分配记录的实际完成数量
|
|
/// 团部及以下单位可以上报自己的消耗数据
|
|
/// </summary>
|
|
[HttpPut("distributions/{distributionId}")]
|
|
public async Task<IActionResult> 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 });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 删除物资配额
|
|
/// </summary>
|
|
[HttpDelete("{id}")]
|
|
[Authorize(Policy = "DivisionLevel")]
|
|
public async Task<IActionResult> 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 });
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 验证配额分配是否有效
|
|
/// </summary>
|
|
[HttpPost("validate")]
|
|
public async Task<IActionResult> 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 ? "目标单位不存在" : "验证通过"
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取配额分配的上报历史记录
|
|
/// 需求1.1, 1.2, 1.3:营部及以下级别用户只能查看本单位及直接下级的记录
|
|
/// </summary>
|
|
[HttpGet("distributions/{distributionId}/reports")]
|
|
public async Task<IActionResult> 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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 映射实体到响应DTO
|
|
/// </summary>
|
|
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()
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// 映射分配记录到响应DTO
|
|
/// </summary>
|
|
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
|
|
};
|
|
}
|
|
}
|