using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using MiGu.Server.Wms; namespace MiGu.Server.Controllers; [ApiController] [Authorize] [TypeFilter(typeof(WmsExceptionFilter))] [Route("api/wms")] public sealed class WmsController : ControllerBase { private readonly WmsService _service; private readonly WmsTransportRuleService _transportRules; private readonly WmsTransportTaskService _transportTasks; public WmsController(WmsService service, WmsTransportRuleService transportRules, WmsTransportTaskService transportTasks) { _service = service; _transportRules = transportRules; _transportTasks = transportTasks; } [HttpGet("areas")] public Task> Areas([FromQuery] string? q) => _service.Areas(q); [HttpPost("areas")] public async Task SaveArea([FromBody] MasterDataRequest req) => Ok(await _service.SaveArea(req, User.ActorName())); [HttpPut("areas/{id:guid}")] public async Task UpdateArea(Guid id, [FromBody] MasterDataRequest req) => Ok(await _service.SaveArea(req with { Id = id }, User.ActorName())); [HttpDelete("areas/{id:guid}")] public async Task DeleteArea(Guid id, [FromQuery] long? version) { await _service.DeleteEntity(id, version, User.ActorName()); return NoContent(); } [HttpGet("storages")] public Task> Storages([FromQuery] string? q) => _service.Storages(q); [HttpPost("storages")] public async Task SaveStorage([FromBody] StorageRequest req) => Ok(await _service.SaveStorage(req, User.ActorName())); [HttpPut("storages/{id:guid}")] public async Task UpdateStorage(Guid id, [FromBody] StorageRequest req) => Ok(await _service.SaveStorage(req with { Id = id }, User.ActorName())); [HttpDelete("storages/{id:guid}")] public async Task DeleteStorage(Guid id, [FromQuery] long? version) { await _service.DeleteEntity(id, version, User.ActorName()); return NoContent(); } [HttpGet("containers")] public Task> Containers([FromQuery] string? q) => _service.Containers(q); [HttpPost("containers")] public async Task SaveContainer([FromBody] MasterDataRequest req) => Ok(await _service.SaveContainer(req, User.ActorName())); [HttpPut("containers/{id:guid}")] public async Task UpdateContainer(Guid id, [FromBody] MasterDataRequest req) => Ok(await _service.SaveContainer(req with { Id = id }, User.ActorName())); [HttpDelete("containers/{id:guid}")] public async Task DeleteContainer(Guid id, [FromQuery] long? version) { await _service.DeleteEntity(id, version, User.ActorName()); return NoContent(); } [HttpGet("materials")] public Task> Materials([FromQuery] string? q) => _service.Materials(q); [HttpPost("materials")] public async Task SaveMaterial([FromBody] MaterialRequest req) => Ok(await _service.SaveMaterial(req, User.ActorName())); [HttpPut("materials/{id:guid}")] public async Task UpdateMaterial(Guid id, [FromBody] MaterialRequest req) => Ok(await _service.SaveMaterial(req with { Id = id }, User.ActorName())); [HttpDelete("materials/{id:guid}")] public async Task DeleteMaterial(Guid id, [FromQuery] long? version) { await _service.DeleteEntity(id, version, User.ActorName()); return NoContent(); } [HttpGet("container-locations")] public Task> ContainerLocations([FromQuery] string? locationType, [FromQuery] string? q) => _service.ContainerLocations(locationType, q); [HttpPost("container-locations")] public async Task SaveContainerLocation([FromBody] ContainerLocationRequest req) => Ok(await _service.BindOrTransferLocation(req, User.ActorName())); [HttpPost("container-locations/transfer")] public async Task TransferContainerLocation([FromBody] ContainerLocationRequest req) => Ok(await _service.BindOrTransferLocation(req, User.ActorName())); [HttpDelete("container-locations/{containerId:guid}")] public async Task UnbindContainerLocation(Guid containerId, [FromQuery] string? reason) { await _service.UnbindLocation(containerId, User.ActorName(), reason ?? ""); return NoContent(); } [HttpGet("container-materials")] public Task> ContainerMaterials([FromQuery] string? q) => _service.ContainerMaterials(q); [HttpPost("container-materials")] public async Task SaveContainerMaterial([FromBody] ContainerMaterialRequest req) => Ok(await _service.SaveContainerMaterial(req, User.ActorName())); [HttpPut("container-materials/{id:guid}")] public async Task UpdateContainerMaterial(Guid id, [FromBody] ContainerMaterialRequest req) => Ok(await _service.SaveContainerMaterial(req with { Id = id }, User.ActorName())); [HttpDelete("container-materials/{id:guid}")] public async Task UnloadContainerMaterial(Guid id, [FromQuery] string? reason) { await _service.UnloadMaterial(id, User.ActorName(), reason ?? ""); return NoContent(); } [HttpGet("container-location-history")] public Task> ContainerLocationHistory([FromQuery] Guid? containerId) => _service.LocationHistory(containerId); [HttpGet("container-material-history")] public Task> ContainerMaterialHistory([FromQuery] Guid? containerId, [FromQuery] Guid? materialId) => _service.MaterialHistory(containerId, materialId); [HttpGet("transport-rules")] public Task> TransportRules([FromQuery] string? triggerType, [FromQuery] string? q) => _transportRules.ListAsync(triggerType, q); [HttpPost("transport-rules")] public async Task SaveTransportRule([FromBody] TransportRuleRequest req) => Ok(await _transportRules.SaveAsync(req, User.ActorName())); [HttpPut("transport-rules/{id:guid}")] public async Task UpdateTransportRule(Guid id, [FromBody] TransportRuleRequest req) => Ok(await _transportRules.SaveAsync(req with { Id = id }, User.ActorName())); [HttpDelete("transport-rules/{id:guid}")] public async Task DeleteTransportRule(Guid id, [FromQuery] long? version) { await _transportRules.DeleteAsync(id, version, User.ActorName()); return NoContent(); } [HttpGet("transport-candidates")] public async Task TransportCandidates([FromQuery] TransportRequestQuery q) => Ok(await _transportTasks.PreviewCandidatesAsync(q.ToRequest())); [HttpPost("transport-tasks/generate")] public async Task GenerateTransportTask([FromBody] TransportRequestQuery q) => Ok(await _transportTasks.GenerateAsync(q.ToRequest(), User.ActorName())); [HttpGet("transport-tasks")] public Task> TransportTasks([FromQuery] string? status, [FromQuery] string? triggerType) => _transportTasks.ListAsync(status, triggerType); [HttpPost("transport-tasks/{id:guid}/reserve")] public async Task ReserveTransportTask(Guid id, [FromQuery] string? reason) => Ok(await _transportTasks.ReserveAsync(id, User.ActorName(), reason ?? "")); [HttpPost("transport-tasks/{id:guid}/cancel")] public async Task CancelTransportTask(Guid id, [FromQuery] string? reason) => Ok(await _transportTasks.CancelAsync(id, User.ActorName(), reason ?? "")); [HttpPost("transport-tasks/{id:guid}/complete")] public async Task CompleteTransportTask(Guid id, [FromQuery] string? reason) => Ok(await _transportTasks.CompleteAsync(id, User.ActorName(), reason ?? "")); [HttpPost("transport-tasks/{id:guid}/dispatch")] public async Task DispatchTransportTask(Guid id) => Ok(await _transportTasks.DispatchAsync(id, User.ActorName())); } public sealed class WmsExceptionFilter : IExceptionFilter { public void OnException(ExceptionContext context) { if (context.Exception is not InvalidOperationException ex) return; context.Result = new BadRequestObjectResult(new { message = ex.Message }); context.ExceptionHandled = true; } } public sealed class TransportRequestQuery { public string TriggerType { get; set; } = WmsTransportTriggerTypes.MaterialCall; public Guid? RuleId { get; set; } public string? RequestSiteId { get; set; } public Guid? MaterialId { get; set; } public decimal? Quantity { get; set; } public Guid? ContainerId { get; set; } public Guid? SourceStorageId { get; set; } public Guid? TargetStorageId { get; set; } public string? BatchNo { get; set; } public int Priority { get; set; } public string Reason { get; set; } = ""; public WmsTransportRequest ToRequest() => new( TriggerType, RuleId, RequestSiteId, MaterialId, Quantity, ContainerId, SourceStorageId, TargetStorageId, BatchNo, Priority, Reason, null); }