V13-FE-011: finalize search list layout slice
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
|
||||
using Dapper;
|
||||
using FastEndpoints;
|
||||
using Npgsql;
|
||||
namespace KBX.Modules.OMS.Claims.Search;
|
||||
public sealed record Request(DateOnly? From, DateOnly? To, string? Type, string? Status, string? Keyword, int Page = 1, int PageSize = 100);
|
||||
public sealed record Row(Guid Id,string ClaimNo,string OrderNo,string ChannelName,string Type,string Reason,decimal RequestedQty,string Status,string? OwnerName,DateTimeOffset RequestedAt);
|
||||
public sealed record Response(IReadOnlyList<Row> Items,long TotalCount);
|
||||
public sealed class Endpoint(NpgsqlDataSource db) : Endpoint<Request,Response>
|
||||
{
|
||||
public override void Configure(){Get("/api/oms/claims");Permissions("oms.claim.read");}
|
||||
public override async Task HandleAsync(Request req,CancellationToken ct){await using var c=await db.OpenConnectionAsync(ct); const string where=""" from oms.claim_search_projection where (@From is null or requested_at>=@From) and (@To is null or requested_at<@To+1) and (@Type is null or type=@Type) and (@Status is null or status=@Status) and (@Keyword is null or claim_no ilike '%'||@Keyword||'%' or order_no ilike '%'||@Keyword||'%') """; var items=(await c.QueryAsync<Row>(new CommandDefinition("select id,claim_no ClaimNo,order_no OrderNo,channel_name ChannelName,type,reason,requested_qty RequestedQty,status,owner_name OwnerName,requested_at RequestedAt"+where+" order by requested_at desc limit @PageSize offset @Offset",new {req.From,req.To,req.Type,req.Status,req.Keyword,req.PageSize,Offset=(req.Page-1)*req.PageSize},cancellationToken:ct))).AsList(); var total=await c.ExecuteScalarAsync<long>(new CommandDefinition("select count(*)"+where,req,cancellationToken:ct)); await SendOkAsync(new(items,total),ct); }
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using Dapper;
|
||||
using FastEndpoints;
|
||||
using Npgsql;
|
||||
using Shared.Problems;
|
||||
|
||||
namespace Modules.OMS.Claims.Workflow;
|
||||
|
||||
public sealed record ClaimTransitionRequest(Guid Id);
|
||||
public sealed record ClaimTransitionResponse(Guid Id, string Status, long Version);
|
||||
|
||||
public sealed class ClaimTransitionHandler(NpgsqlDataSource dataSource)
|
||||
{
|
||||
public async Task<ClaimTransitionResponse?> HandleAsync(
|
||||
Guid id,
|
||||
string fromStatus,
|
||||
string toStatus,
|
||||
CancellationToken ct)
|
||||
{
|
||||
await using var connection = await dataSource.OpenConnectionAsync(ct);
|
||||
return await connection.QuerySingleOrDefaultAsync<ClaimTransitionResponse>(new CommandDefinition("""
|
||||
update oms.claims
|
||||
set status=@ToStatus, version=version+1
|
||||
where id=@Id and status=@FromStatus
|
||||
returning id, status, version;
|
||||
""", new { Id = id, FromStatus = fromStatus, ToStatus = toStatus }, cancellationToken: ct));
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ApproveEndpoint(ClaimTransitionHandler handler)
|
||||
: Endpoint<ClaimTransitionRequest, ClaimTransitionResponse>
|
||||
{
|
||||
public override void Configure() { Post("/api/oms/claims/{id:guid}/approve"); Permissions("oms.claim.approve"); }
|
||||
public override async Task HandleAsync(ClaimTransitionRequest req, CancellationToken ct)
|
||||
=> await RespondAsync(await handler.HandleAsync(req.Id, "REQUESTED", "APPROVED", ct), "approve", "REQUESTED", ct);
|
||||
|
||||
private async Task RespondAsync(ClaimTransitionResponse? result, string transition, string required, CancellationToken ct)
|
||||
{
|
||||
if (result is not null) { await Send.OkAsync(result, ct); return; }
|
||||
await Send.ResponseAsync(KbxBusinessProblem.Create("CLAIM_TRANSITION_NOT_ALLOWED", "현재 상태에서는 승인할 수 없습니다.", $"Transition '{transition}' requires state '{required}'."), 409, cancellation: ct);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class HoldEndpoint(ClaimTransitionHandler handler)
|
||||
: Endpoint<ClaimTransitionRequest, ClaimTransitionResponse>
|
||||
{
|
||||
public override void Configure() { Post("/api/oms/claims/{id:guid}/hold"); Permissions("oms.claim.hold"); }
|
||||
public override async Task HandleAsync(ClaimTransitionRequest req, CancellationToken ct)
|
||||
{
|
||||
var result = await handler.HandleAsync(req.Id, "REQUESTED", "HOLD", ct);
|
||||
if (result is not null) { await Send.OkAsync(result, ct); return; }
|
||||
await Send.ResponseAsync(KbxBusinessProblem.Create("CLAIM_TRANSITION_NOT_ALLOWED", "현재 상태에서는 보류할 수 없습니다."), 409, cancellation: ct);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class StartEndpoint(ClaimTransitionHandler handler)
|
||||
: Endpoint<ClaimTransitionRequest, ClaimTransitionResponse>
|
||||
{
|
||||
public override void Configure() { Post("/api/oms/claims/{id:guid}/start"); Permissions("oms.claim.process"); }
|
||||
public override async Task HandleAsync(ClaimTransitionRequest req, CancellationToken ct)
|
||||
{
|
||||
var result = await handler.HandleAsync(req.Id, "APPROVED", "IN_PROGRESS", ct);
|
||||
if (result is not null) { await Send.OkAsync(result, ct); return; }
|
||||
await Send.ResponseAsync(KbxBusinessProblem.Create("CLAIM_TRANSITION_NOT_ALLOWED", "승인된 클레임만 처리를 시작할 수 있습니다."), 409, cancellation: ct);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class CompleteEndpoint(ClaimTransitionHandler handler)
|
||||
: Endpoint<ClaimTransitionRequest, ClaimTransitionResponse>
|
||||
{
|
||||
public override void Configure() { Post("/api/oms/claims/{id:guid}/complete"); Permissions("oms.claim.process"); }
|
||||
public override async Task HandleAsync(ClaimTransitionRequest req, CancellationToken ct)
|
||||
{
|
||||
var result = await handler.HandleAsync(req.Id, "IN_PROGRESS", "COMPLETED", ct);
|
||||
if (result is not null) { await Send.OkAsync(result, ct); return; }
|
||||
await Send.ResponseAsync(KbxBusinessProblem.Create("CLAIM_TRANSITION_NOT_ALLOWED", "처리 중인 클레임만 완료할 수 있습니다."), 409, cancellation: ct);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user