using Dapper; using Npgsql; using Shared.Operations; namespace Kbx.Shared.Integrations; public sealed class KbxIntegrationRetryActionHandler(NpgsqlDataSource dataSource) : IWorkItemActionHandler { public string Key => "integration.retry"; public async Task ExecuteAsync(string tenantId, string actorId, Guid workItemId, CancellationToken ct) { await using var connection = await dataSource.OpenConnectionAsync(ct); await using var tx = await connection.BeginTransactionAsync(ct); var messageId = await connection.QuerySingleOrDefaultAsync(new CommandDefinition(""" select source_id::uuid from kbx.work_items where id=@WorkItemId and tenant_id=@TenantId and retry_action_key='integration.retry' for update; """, new { WorkItemId = workItemId, TenantId = tenantId }, tx, cancellationToken: ct)); if (messageId is null) { await tx.RollbackAsync(ct); return; } var changed = await connection.ExecuteAsync(new CommandDefinition(""" update kbx.integration_attempts set state='retrying', next_retry_at=now() where id = ( select id from kbx.integration_attempts where message_id=@MessageId and state='failed' order by attempt_no desc limit 1 ); """, new { MessageId = messageId.Value }, tx, cancellationToken: ct)); if (changed == 1) { await connection.ExecuteAsync(new CommandDefinition(""" insert into kbx.work_item_audit(id,work_item_id,tenant_id,action,actor_id,actor_name,reason,before_status,after_status) values(gen_random_uuid(),@WorkItemId,@TenantId,'integration-retry',@ActorId,@ActorId,'안전한 연계 재처리 예약','open','open'); """, new { WorkItemId = workItemId, TenantId = tenantId, ActorId = actorId }, tx, cancellationToken: ct)); } await tx.CommitAsync(ct); } }