Appearance
Configurando o Loop de Polling de Comandos
O loop de polling de comandos é o coração da integração. A cada ciclo, o ERP pergunta à Maggu: "há algum comando novo para mim?" — e executa o que receber.
Endpoint
http
GET /v3/comandos/pendentes
Authorization: Bearer <token_da_loja>Response 200:
json
[
{
"id": 5001,
"tipo": "ENVIAR_TODOS_USUARIOS",
"argumentos": {},
"criadoEm": "2026-05-13T10:00:00Z"
},
{
"id": 5002,
"tipo": "ENVIAR_VENDAS_INTERVALO",
"argumentos": {
"de": "2026-05-01T00:00:00Z",
"ate": "2026-05-13T23:59:59Z"
},
"criadoEm": "2026-05-13T10:05:00Z"
}
]Retorna [] se não houver comandos pendentes para a loja.
Loop principal
csharp
public async Task IniciarAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
await ExecutarAsync(ct);
await Task.Delay(Config.IntervalMs, ct);
}
}
private async Task ExecutarAsync(CancellationToken ct)
{
var requisicao = new RestRequest("api/v3/comandos/pendentes", Method.Get);
var resposta = await _provedor.Client.GetAsync(requisicao, ct);
var comandos = JsonSerializer.Deserialize<List<Comando>>(resposta.Content!, jsonOptions);
if (comandos is null) return;
// Processar do mais antigo para o mais recente
foreach (var comando in comandos.OrderBy(c => c.CriadoEm))
await _orquestrador.ProcessarAsync(comando, ct);
}Notificando o resultado
Após executar cada comando, informe a Maggu:
csharp
// Sucesso
var req = new RestRequest("api/v3/comandos/completou", Method.Post);
req.AddJsonBody(new { id = command.Id });
await _provedor.Client.PostAsync(req, ct);
// Falha
var req = new RestRequest("api/v3/comandos/falhou", Method.Post);
req.AddJsonBody(new { id = command.Id, motivo = "Descrição do erro" });
await _provedor.Client.PostAsync(req, ct);Endpoint opcional:
POST /v3/comandos/inicioupode ser chamado logo antes de executar o comando para registrar que o ERP começou o processamento. Útil para diagnóstico de comandos travados.
Prevenindo execução duplicada
O mesmo comando pode aparecer em ciclos consecutivos se a confirmação ainda não chegou à Maggu. Use um repositório local em memória para rastrear o estado:
csharp
public class RepositorioComandos
{
private readonly ConcurrentDictionary<int, StatusComando> _estado = new();
public bool TentarObter(int id, out StatusComando status)
=> _estado.TryGetValue(id, out status);
public void Salvar(int id, StatusComando status)
=> _estado[id] = status;
}Antes de executar, verifique:
csharp
if (_repositorio.TentarObter(comando.Id, out var status))
{
if (status == StatusComando.Pendente) return; // já em andamento
if (status == StatusComando.Concluido) { _notificador.Concluido(comando); return; }
if (status == StatusComando.Falhou) { _notificador.Falhou(comando, motivo); return; }
}
_repositorio.Salvar(comando.Id, StatusComando.Pendente);Resumo das respostas
| Endpoint | Sucesso | Erro |
|---|---|---|
GET /v3/comandos/pendentes | 200 OK + lista | 403 não autorizado |
POST /v3/comandos/completou | 204 No Content | 404 comando não encontrado |
POST /v3/comandos/falhou | 204 No Content | 404 comando não encontrado |
Implementação completa do ProcessarAsync
Junta o controle de duplicatas, execução e notificação em um único método:
csharp
public async Task ProcessarAsync(Comando comando, CancellationToken ct)
{
// 1. Evitar execução duplicada
if (_repositorio.TentarObter(comando.Id, out var status))
{
if (status == StatusComando.Pendente) return;
if (status == StatusComando.Concluido) { await NotificarConcluidoAsync(comando, ct); return; }
if (status == StatusComando.Falhou) { await NotificarFalhaAsync(comando, _repositorio.ObterMotivo(comando.Id), ct); return; }
}
_repositorio.Salvar(comando.Id, StatusComando.Pendente);
// 2. Sinalizar início (opcional — útil para diagnóstico)
var iniciou = new RestRequest("api/v3/comandos/iniciou", Method.Post);
iniciou.AddJsonBody(new { id = comando.Id, total = totalRegistros }); // total recomendado
await _provedor.Client.PostAsync(iniciou, ct);
// 3. Executar com retry local
try
{
await comando.ExecutarAsync(ct);
_repositorio.Salvar(comando.Id, StatusComando.Concluido);
await NotificarConcluidoAsync(comando, ct);
}
catch (Exception ex) when (ex is not OperationCanceledException)
{
_repositorio.Salvar(comando.Id, StatusComando.Falhou, ex.Message);
await NotificarFalhaAsync(comando, ex.Message, ct);
}
}
private async Task NotificarConcluidoAsync(Comando comando, CancellationToken ct)
{
var req = new RestRequest("api/v3/comandos/completou", Method.Post);
req.AddJsonBody(new { id = comando.Id });
await _provedor.Client.PostAsync(req, ct);
}
private async Task NotificarFalhaAsync(Comando comando, string motivo, CancellationToken ct)
{
var req = new RestRequest("api/v3/comandos/falhou", Method.Post);
req.AddJsonBody(new { id = comando.Id, motivo });
await _provedor.Client.PostAsync(req, ct);
}