Consolida os dois controladores legados que decidiam status/fechamento de forma concorrente (processGlpiClosureController + processStatusController) em um único processTicketLifecycleController, eliminando a dependência do campo source_last (mantido só como rastro de auditoria) e a flag ENABLE_STATUS_SYNC (removida). Isso corrige o bug de reabertura: chamados reabertos no SN voltavam a ser fechados no GLPI porque a regra de precedência de finalização do fluxo legado forçava o status do GLPI de volta pro SN. Correções de bugs encontrados em teste (Frente 1): - Constraint UNIQUE em ticket_sync.sn_ticket_id + ON CONFLICT DO NOTHING na criação do registro, prevenindo duplicação de tickets. - Fechamento no SN agora é confirmado por leitura ao vivo do status antes de marcar o ticket como solved/closed, em vez de confiar só no HTTP 200 (a API do SN pode retornar sucesso e silenciosamente ignorar um valor de estado inválido — descoberto e corrigido também no mandante). - Nota de encerramento do SN pro GLPI agora é idempotente e criada uma única vez por ticket. - Erros de sincronização passam a ter um limite de tentativas (MAX_ERROR_RETRIES) antes de marcar o ticket como error_permanent, exigindo intervenção manual em vez de tentar para sempre. - Chamados criados no GLPI passam a gravar o número do ticket do SN no campo externalid. Nova funcionalidade (Frente 2): quando o SN resolve/encerra um chamado por conta própria antes do GLPI, além da nota informativa no GLPI (agora com o nome de quem encerrou, buscado ao vivo via resolved_by/closed_by/ sys_updated_by), o próprio SN recebe um aviso de que a Sothis não vai mais atender aquele chamado e ele é transferido pra fila de TI da CAOA. Nova funcionalidade (Mandante, regras 19-22): espelhamento opcional e configurável de status intermediário (Aguardando Atendimento/Em Atendimento/Em Espera) entre os dois sistemas, controlado pela env MANDANTE=GLPI|SNOW. Sem essa env configurada, mantém o comportamento atual (nenhum espelhamento de status intermediário). Nunca afeta os status finais (resolvido/encerrado/fora de escopo), que continuam regidos pelas regras fixas de fechamento. Corrigido também o workflow de deploy (.gitea/workflows/deploy-production.yml), que ainda validava os controladores antigos (já removidos) e exigia ENABLE_STATUS_SYNC=true, o que quebraria o próximo deploy em produção. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4.1 KiB
Fluxo Operacional
1. Fluxo do ciclo (main)
processErrorControllerprocessTicketsControllerprocessTicketLifecycleController(seENABLE_GLPI_CLOSE_CRON=true) — motor de regras unico pra status/fechamento/reabertura, ver secao 4.processCommentsController
2. Fluxo SN -> GLPI
- Coleta incidentes/requisicoes no SN por watermark.
- UPSERT em
tickets_sn. - Cria/atualiza
ticket_synccom estados iniciais. - Cria ticket no GLPI quando
pending_check. - Grava
glpi_ticket_idemticket_sync. - Atualiza campo "Ticket Externo" no SN com ID GLPI.
- Se falhar atualizar "Ticket Externo", agenda retry em
ticket_updates.
3. Fluxo de comentarios/tasks
3.1 SN -> GLPI
- Busca
commentsework_notesno SN. - Persiste em
ticket_updates(idempotente porsource_id). - Envia:
comment-> followup GLPItask-> task GLPI
3.2 GLPI -> SN
- Busca followups GLPI.
- Sanitiza conteudo HTML/imagens.
- Envia comentario para SN evitando duplicidade.
- Atualiza
destiny_idpara rastrear espelhamento.
4. Motor de regras unico (processTicketLifecycleController)
Substitui os antigos processGlpiClosureController + processStatusController. Nao ha mais dois
controladores decidindo o mesmo ticket, e nenhuma decisao depende de source_last (campo mantido
so como rastro de auditoria). Roda pra todo ticket com glpi_ticket_id que nao esteja
closed/ignored/error_permanent:
- GLPI status
5(Solucionado):- se o ticket ja estava
solvedlocalmente, so verifica se o SN foi reaberto (ver secao 5) e para por ai; - senao: verifica fora de escopo; se nao for, busca nota de solucao, se SN estiver em
espera/aguardando move pra "Em Atendimento", resolve o SN confirmando via leitura ao vivo
antes de marcar
solved/solved(nunca marca so por causa de um HTTP 200).
- se o ticket ja estava
- GLPI status
6(Fechado definitivamente):- adiciona comentario idempotente no SN: "Chamado encerrado definitivamente. Reabertura deste chamado nao sera atendida. Caso necessario, abra um novo chamado."
- nao altera status do chamado no SN
- marca
ticket_synccomoclosed/closedpara retirar do monitoramento
- GLPI ainda aberto (
< 5) e SN resolvido/encerrado por conta propria:- adiciona nota idempotente no GLPI avisando o encerramento vindo do SN, sem fechar o chamado
(inclui o nome de quem encerrou no SN, buscado ao vivo:
resolved_by->closed_by->sys_updated_by, o que vier preenchido primeiro) - (primeira vez apenas) avisa no proprio SN que a Sothis nao vai mais atender esse chamado e transfere pra fila CAOA
- marca
ticket_synccomosolved/solvedouclosed/closed
- adiciona nota idempotente no GLPI avisando o encerramento vindo do SN, sem fechar o chamado
(inclui o nome de quem encerrou no SN, buscado ao vivo:
- GLPI ainda aberto (
< 5) e SN tambem em status intermediario (nenhum dos dois em Resolvido/Encerrado): aplica o mandante, se configurado — ver secao 6.
5. Reabertura
- Reabertura automatica permitida apenas para tickets em GLPI
5— verificado a cada ciclo pelo mesmoprocessTicketLifecycleController, sem depender de nenhuma flag. - Se GLPI
6, reabertura automatica bloqueada (a regra nem entra nesse caminho).
6. Mandante (regras 19-22)
Espelhamento opcional de status intermediario (Aguardando Atendimento/Em Atendimento/
Em Espera no SN, equivalentes ao GLPI 1/2/4 — GLPI 3 tambem mapeia para
Em Atendimento). Nunca toca em status finais (5, 6, fora de escopo) — essas continuam sendo as
regras fixas das secoes 4.1-4.3, sempre ativas.
Controlado pelo env MANDANTE:
MANDANTE=GLPI: quando o status intermediario do GLPI muda, o SN e atualizado pra acompanhar. Mudancas de status intermediario feitas diretamente no SN sao ignoradas.MANDANTE=SNOW: o oposto — o SN manda, o GLPI acompanha.MANDANTEvazio/ausente/invalido: nenhum espelhamento (comportamento padrao, igual a antes do mandante existir).
Em qualquer configuracao, so escreve quando o valor desejado difere do atual (evita chamada de API desnecessaria) e nunca le o status do lado seguidor pra decidir nada — o mandante so manda, nunca escuta, entao nao ha risco de loop entre os dois sistemas.