V13-FE-011: finalize search list layout slice
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
using Dapper;
|
||||
using FastEndpoints;
|
||||
using Modules.WMS.Picking.Shared;
|
||||
using Npgsql;
|
||||
using Shared.Problems;
|
||||
|
||||
namespace Modules.WMS.Picking.Exceptions;
|
||||
|
||||
public sealed record Request(
|
||||
Guid? LineId,
|
||||
string Type,
|
||||
string? Memo,
|
||||
string IdempotencyKey,
|
||||
long ExpectedVersion);
|
||||
|
||||
public sealed class Endpoint(NpgsqlDataSource dataSource)
|
||||
: Endpoint<Request, PickingTaskDto>
|
||||
{
|
||||
public override void Configure()
|
||||
{
|
||||
Post("/api/wms/picking/tasks/{taskId:guid}/exceptions");
|
||||
Permissions("wms.picking.execute");
|
||||
}
|
||||
|
||||
public override async Task HandleAsync(Request req, CancellationToken ct)
|
||||
{
|
||||
var taskId = Route<Guid>("taskId");
|
||||
var allowed = new[] { "no-stock", "short-quantity", "wrong-location", "damaged-item", "barcode-issue", "other" };
|
||||
if (!allowed.Contains(req.Type))
|
||||
{
|
||||
await Send.ResponseAsync(KbxValidationProblem.Create(
|
||||
new KbxValidationError("type", null, "EXCEPTION_TYPE_INVALID", "문제 유형을 확인하세요.")), 400, cancellation: ct);
|
||||
return;
|
||||
}
|
||||
|
||||
await using var connection = await dataSource.OpenConnectionAsync(ct);
|
||||
await using var tx = await connection.BeginTransactionAsync(ct);
|
||||
|
||||
var currentVersion = await connection.QuerySingleOrDefaultAsync<long?>(new CommandDefinition(
|
||||
"select version from wms.picking_tasks where id=@TaskId for update",
|
||||
new { TaskId = taskId }, tx, cancellationToken: ct));
|
||||
|
||||
if (currentVersion is null)
|
||||
{
|
||||
await tx.RollbackAsync(ct);
|
||||
await SendNotFoundAsync(ct);
|
||||
return;
|
||||
}
|
||||
|
||||
var duplicate = await connection.ExecuteScalarAsync<bool>(new CommandDefinition(
|
||||
"select exists(select 1 from wms.picking_exceptions where task_id=@TaskId and idempotency_key=@Key)",
|
||||
new { TaskId = taskId, Key = req.IdempotencyKey }, tx, cancellationToken: ct));
|
||||
if (duplicate)
|
||||
{
|
||||
await tx.RollbackAsync(ct);
|
||||
var existing = await PickingTaskQueries.GetAsync(connection, null, taskId, ct);
|
||||
await Send.OkAsync(existing!, ct);
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentVersion != req.ExpectedVersion)
|
||||
{
|
||||
await tx.RollbackAsync(ct);
|
||||
await Send.ResponseAsync(KbxConflictProblem.Version(currentVersion), 409, cancellation: ct);
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.ExecuteAsync(new CommandDefinition("""
|
||||
insert into wms.picking_exceptions(
|
||||
id, task_id, line_id, exception_type, memo, status, reported_at, reported_by, idempotency_key)
|
||||
values (@Id, @TaskId, @LineId, @Type, @Memo, 'OPEN', now(), @Actor, @Key)
|
||||
on conflict (task_id, idempotency_key) do nothing;
|
||||
|
||||
update wms.picking_tasks
|
||||
set status='BLOCKED', version=version+1, updated_at=now()
|
||||
where id=@TaskId;
|
||||
|
||||
insert into audit.entries(id, aggregate_type, aggregate_id, action, actor, occurred_at, data)
|
||||
values (@AuditId, 'WmsPickingTask', @TaskId, 'PickingExceptionReported', @Actor, now(),
|
||||
jsonb_build_object('type', @Type, 'lineId', @LineId, 'memo', @Memo));
|
||||
|
||||
insert into integration.outbox(id,event_type,aggregate_id,payload,occurred_at,status)
|
||||
values (@EventId,'WmsPickingExceptionReported',@TaskId,
|
||||
jsonb_build_object('taskId',@TaskId,'lineId',@LineId,'type',@Type),now(),'PENDING');
|
||||
""", new {
|
||||
Id = Guid.NewGuid(),
|
||||
AuditId = Guid.NewGuid(),
|
||||
EventId = Guid.NewGuid(),
|
||||
TaskId = taskId,
|
||||
req.LineId,
|
||||
req.Type,
|
||||
req.Memo,
|
||||
Key = req.IdempotencyKey,
|
||||
Actor = User.Identity?.Name ?? "unknown",
|
||||
}, tx, cancellationToken: ct));
|
||||
|
||||
await tx.CommitAsync(ct);
|
||||
var task = await PickingTaskQueries.GetAsync(connection, null, taskId, ct);
|
||||
await SendOkAsync(task! with { Message = "문제를 등록했습니다. 관리자 확인이 필요합니다." }, ct);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user