Seguidores

sexta-feira, 29 de maio de 2026

Exemplo 5 de uso do MASP com IA em TI - Bug de Duplicação de Mensagens

   O exemplo 5 de uso do MASP (Método de Análise e Solução de Problemas), com IA (Inteligência Artificial) em TI (Tecnologia da Informação), apresenta uma continuação do Exemplo 4, situação de teste incompleto que causou uma falha num aplicativo. Desta vez, foi encontrado outro bug: duplicação de mensagens.

Exemplo de Prompt completo

estruturado com MASP

para a IA Gemini


1CONTEXTO

1.1 Correção de bug no app VoteNet, no ambiente de desenvolvimento

1.1.1 Descrição do bug:
Durante os testes as mensagens que recebi de comunicado geral foram duplicadas. 

1.1.2 Frontend: 

1.1.2.1 ReactJS v15.6.1

1.1.2.2 Componente: SectionInvites.jsx

1.1.2.2 Node v6.17.1 

1.1.2.3 NPM v3.10.10

1.1.3 Axios v.0.26.0: comunicação via API, chamando funções do arquivo ajax_utils.js.

1.3.1.1 Função: ?

1.1.4 Backend: Django v4.2.3
1.1.4.1 Aplicação: ?

1.1.4.2 model.py: ?

2IDENTIFICAÇÃO DO PROBLEMA

Um problema é caracterizado pela diferença entre o que é e o que deveria ser. As perguntas abaixo (5W1H) ajudarão a caracterizar o problema, com detalhes do cenário.

[What?] O que está acontecendo?

Está acontecendo um bug no aplicativo VoteNet, no ambiente de desenvolvimento, durante os testes de correção de um bug na homologação.

[Who?] Quem está envolvido?

Quem está envolvido é o desenvolvedor que está fazendo testes.

[When?] Quando ocorreu?

Ocorreu quando o desenvolvedor clicou na opção Mensagens do menu principal e viu a mesma mensagem repetida 9 vezes.

 

[Where?] Onde aconteceu?

Aconteceu no componente SectionInvites.jsx, especificamente, na funcionalidade implementada no botão COMUNICADO GERAL.

[Why?] Por que isso é um problema?

Porque cada usuário deveria receber apenas uma mensagem, mas recebeu 9 mensagens iguais, com mesma data e hora, além do texto igual. É importante informar que a rede usada neste teste, tem, exatamente, 9 convidados. 

[How?] Como surgiu?:

1)após o login;

2)um usuário quer enviar um comunicado geral para os seus 9 convidados, clica no botão COMUNICADO GERAL e uma janela modal é aberta;

3)o usuário escreve a mensagem;

4)o usuário clica no botão DISPARAR AGORA e a mensagem deveria ir para os 9 convidados;

5)um dos usuários faz login, observa no badge que há mensagens não lidas e clica no menu “Mensagens”;

6)aparecem na página 9 mensagens, a sua e as mensagens repetidas dos demais convidados da rede, todas iguais, quando deveria aparecer apenas uma mensagem.

3Observação:

Investigue características específicas do problema usando o Diagrama de Ishikawa (Causa e Efeito). Registre “SIM”, “NÃO” ou “NÃO SEI”, para fatores como:

3.1. Método (A Lógica)

  • Regras de Negócio: O algoritmo está aplicando a regra correta? NÃO

  • Fluxo do Processo: Existe algum "nó" na sequência da automação (ex: um passo que tenta rodar antes do anterior terminar)? NÃO SEI

  • Tratamento de Erros: O código sabe o que fazer quando recebe uma resposta inesperada? NÃO SEI

3.2. Máquina (Infraestrutura/Software)

  • Ambiente: O erro ocorre em produção, mas não em homologação? NÃO (Os testes estão sendo realizados em desenvolvimento)

  • Recursos: Há falta de memória, timeout de servidor ou falha na conexão com o banco de dados? NÃO SEI

  • Versões: Bibliotecas ou frameworks desatualizados estão gerando conflitos? NÃO SEI, mas esta é uma hipótese a ser analisada.

3.3. Mão de Obra (Intervenção Humana)

  • Desenvolvimento: Houve falha na escrita do código ou falta de revisão (code review)? SIM 

  • Testes: O cenário que gerou o bug foi previsto no plano de testes? NÃO

  • Operação: Alguém alterou uma configuração manualmente que quebrou a automação? NÃO SEI

3.4. Medida (Dados e Monitoramento)

  • Input de Dados: O bug é causado por um caractere especial ou campo vazio que a automação não esperava? NÃO

  • Logs: O sistema está gerando registros suficientes para rastrear onde o erro começa? NÃO SEI

  • Métricas: O erro acontece após um número X de execuções (vazamento de memória)? NÃO

3.5. Material (Integrações/APIs)

  • Dependências Externas: Uma API de terceiros mudou o formato do JSON ou está fora do ar? NÃO SEI

  • Qualidade dos Dados: A base de dados que alimenta a automação contém informações corrompidas? NÃO (é a mesma que foi usada no teste do desenvolvedor)

3.6. Meio Ambiente (Contexto de Execução)

  1. Conectividade: Instabilidade na rede afetou a troca de pacotes? NÃO SEI

  2. Simultaneidade: Outro processo automatizado está tentando acessar o mesmo recurso ao mesmo tempo? NÃO SEI

4Análise:

Para cada fator identificado na etapa anterior, utilize os “5 Porquês” para encontrar a causa raiz.

4.1 Método (A Lógica)

Por quê? NÃO SEI

4.2 Máquina (Infraestrutura/Software)

Por quê? NÃO SEI

4.3 Mão de Obra (Intervenção Humana)

Por quê? Porque na função /votenet/emails/views.py/send_broadcast_message

foi copiada a lógica da classe /votenet/persons/views.py/RedeDetailView, que acessa os dados da rede, sem ter sido feita a mudança necessária para esta funcionalidade.

Por quê? Porque a mesma mensagem deveria ter sido enviada para os N convidados da rede e não repetida N vezes para cada um dos N convidados da rede, tal como está implementada.

4.4 Medida (Dados e Monitoramento)

Por quê? NÃO SEI

4.5 Material (Integrações/APIs)

Por quê? NÃO SEI

4.6 Meio Ambiente (Contexto de Execução)

Por quê? NÃO SEI

5. Comando: 

Atue como um Especialista em Qualidade de Software e Metodologia MASP (Método de Análise e Solução de Problemas). 

Estou analisando um bug em um processo automatizado dentro do aplicativo VoteNet, implementado no componente SectionInvites.jsx, em anexo. 

Com base nas 3 primeiras etapas do MASP especificadas acima, elabore a etapa 4, "Plano de Ação" (formato 5W2H), focando no bloqueio e correção definitiva das causas do bug. 

O comportamento ao clicar no botão COMUNICADO GERAL do componente SectionInvites.jsx deve ser o envio da mensagem escrita pelo usuário, para todos os convidados da sua rede,  mas sendo uma, e somente uma mensagem para cada convidado. 

Adicione ao “Plano de Ação” etapas bem definidas para execução, monitoramento e controle dos testes:

  • EXECUÇÃO DE TESTE - para especificar resultados esperados nos testes e 

  • CONTROLE DE TESTE - para comparar o resultado esperado com o resultado de cada teste.

Resposta completa

do Prompt estruturado com MASP

Olá, Gustavo. Excelente detalhamento na aplicação da metodologia MASP. A quebra do problema através dos 5 Porquês foi cirúrgica para isolar a causa raiz no backend. O erro clássico de produto cartesiano em queries do ORM, gerando múltiplas instâncias na iteração de criação de mensagens, é o que vamos resolver agora.

Abaixo está o detalhamento da Etapa 4, focado em isolar e bloquear definitivamente o bug, utilizando a abordagem Design by Contract para garantir a integridade dos dados na entrada e na saída da função.

4. Plano de Ação (5W2H)

Com o objetivo de bloquear e corrigir o envio duplicado de mensagens no recurso de "Comunicado Geral", o seguinte plano será executado:

  • What (O que será feito): Refatorar a view send_broadcast_message para extrair um conjunto único de destinatários (Users) antes de montar os objetos de Message. Implementar validações de contrato (pré e pós-condições) para garantir que não haja duplicação no envio.

  • Why (Por que será feito): A ausência de filtragem por registros únicos (uso de .distinct() ou dicionários de controle) em relacionamentos complexos no Django causou um produto cartesiano na query, multiplicando as mensagens inseridas no banco para cada usuário da rede.

  • Where (Onde será feito): No backend Django, especificamente na função send_broadcast_message.

  • When (Quando será feito): Imediatamente, no ambiente de desenvolvimento, para validação antes de subir para homologação.

  • Who (Quem fará): Gustavo Carvalho (Desenvolvedor Full-Stack).

  • How (Como será feito): 1. Ajustando a query active_invites para incluir .distinct(). 2. Filtrando os resultados através de um dictionary comprehension indexado pelo user.id para eliminar qualquer possibilidade lógica de destinatários repetidos na memória antes do bulk_create. 3. Aplicando assertions para garantir as invariantes do sistema.

  • How much (Quanto custará): Esforço interno de desenvolvimento e testes (já provisionado).

Execução e Controle de Teste

Para validar a correção seguindo o ciclo PDCA, utilizaremos os seguintes parâmetros:

EXECUÇÃO DE TESTE

  1. Fazer login no ambiente de desenvolvimento com o usuário padrinho que possui exatamente 9 convidados aprovados.

  2. Clicar em "COMUNICADO GERAL" no componente SectionInvites.jsx, digitar o texto "Teste MASP de Correção" e disparar.

  3. Fazer logout e realizar o login com a conta de um dos 9 convidados da rede.

  4. Acessar a caixa de mensagens.

  5. Acessar o Django Admin (ou inspecionar o banco) e contar o total de mensagens criadas no último disparo.

CONTROLE DE TESTE

  • Resultado Esperado (Front-end): O convidado acessa a caixa de mensagens e encontra 1 (uma), e somente uma, mensagem não lida com o título/corpo "Teste MASP de Correção".

  • Resultado Esperado (Back-end): A inserção no banco de dados (Message.objects.bulk_create) deve registrar exatamente 9 novas linhas, correspondendo a uma mensagem única para cada um dos 9 convidados.

  • Resultado Real: (A ser preenchido após a execução - deve ser igual ao esperado para finalizar o ticket).

Correção do Código Fonte (Backend)

Aqui está a implementação corrigida para o arquivo emails/views.py. O código foi projetado estritamente para compatibilidade com Python 3.11.3 e Django 4.2.3, aplicando os princípios contratuais de pré e pós-condições para evitar regressões:




Python

from django.db import transaction
from django.contrib.auth.models import User
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

# Importe o modelo Message de acordo com o seu app
# from votenet.emails.models import Message

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def send_broadcast_message(request):
    body = request.data.get('body')
   
    # --- PRÉ-CONDIÇÃO: O corpo da mensagem não pode ser nulo ou vazio ---
    if not body:
        return Response({"status": 0, "error": "Mensagem vazia."}, status=400)

    try:
        user_padrinho = request.user

        # Busca convites aprovados, garantindo a seleção de entidades distintas
        # O uso do .distinct() previne a duplicação na query SQL base
        active_invites = user_padrinho.elector.invitation.filter(
            invited__isnull=False
            invited__user__is_active=True
            approved=True 
        ).select_related('invited__user').distinct()

        # --- INVARIANTE LÓGICA: Garantir 100% de unicidade em memória ---
        # Cria um dicionário onde a chave é o ID do usuário, garantindo que mesmo
        # se a query falhar no distinct por causa de joins complexos, o Python filtra.
        unique_recipients = {
            inv.invited.user.id: inv.invited.user
            for inv in active_invites
            if inv.invited and getattr(inv.invited, 'user', None)
        }

        if not unique_recipients:
            return Response({"status": 0, "error": "Nenhum eleitor aprovado encontrado na sua rede."}, status=200)

        # Monta a lista de objetos para inserção em lote baseada apenas nos usuários únicos
        messages_to_create = [
            Message(
                sender=user_padrinho,
                recipient=user,
                body=body,
                is_broadcast=True
            ) for user in unique_recipients.values()
        ]

        # --- PÓS-CONDIÇÃO: A quantidade de mensagens a serem criadas DEVE ser
        # exatamente igual ao número de convidados únicos identificados ---
        assert len(messages_to_create) == len(unique_recipients), "Inconsistência: quantidade de mensagens difere do número de destinatários únicos."

        # Executa a operação atômica de escrita no banco
        with transaction.atomic():
            Message.objects.bulk_create(messages_to_create)

        return Response({
            "status": 1,
            "message": f"Comunicado enviado para {len(messages_to_create)} eleitores da sua rede."
        })

    except AssertionError as contract_error:
        # Falha na garantia do contrato (Pós-condição)
        return Response({"status": 0, "error": f"Erro de validação lógica: {str(contract_error)}"}, status=500)
    except Exception as e:
        return Response({"status": 0, "error": f"Erro interno: {str(e)}"}, status=500)

Resultado do Teste

Olá! Resultado real do teste.


Resultado Real (Front-end) está diferente do esperado: O convidado acessa a caixa de mensagens e encontra 9 mensagens, tal como antes da correção. 


Resultado Real(Back-end) está igual ao esperado: A inserção no banco de dados (Message.objects.bulk_create) deve registrar exatamente 9 novas linhas, correspondendo a uma mensagem única para cada um dos 9 convidados.


Informações adicionais:


Front-end: O badge funcionou corretamente, porque apareceu apenas 1 mensagem e quando ela foi lida, o badge mostrou a diferença corretamente.


Email do convidado: O email do meu convidado, que usei para testar, é diferente do meu email apenas por um ponto (.) após a primeira letra. Porque fiz isso? Por sugestão de um colega, para agilizar a criação de um convidado para teste, já que o Gmail não considera o ponto após a primeira letra como um diferencial. Será que isso está causando o problema?


Marcar como lida: Outra observação é que como convidado, só consigo ler 1 mensagem, porque quando clico nas outras, elas não mudam para cor branca, para marcar como lida. Isto tem coerência com o comportamento no badge.




Com estas informações adicionais você consegue deduzir a causa raiz? Precisa de algum código fonte? 


Resposta do Gemini

Olá! Excelente trabalho na execução e no controle do teste. Os detalhes que você mapeou de forma tão analítica — o comportamento do badge, o bloqueio na alteração de status de leitura e a estratégia do e-mail — foram fundamentais para isolarmos o problema rapidamente, bem ao estilo de um bom ciclo PDCA.

Posso confirmar que já temos a dedução clara da causa raiz. Vamos desdobrar as suas observações:

1. O Truque do E-mail (A Pista Falsa)

O uso do ponto no Gmail (g.ustavo@... vs gustavo@...) é uma excelente estratégia para agilizar a criação de massas de teste. Para o banco de dados e para o ORM do Django, essas são strings de caracteres distintas. Como a inserção no banco gerou exatamente 9 linhas (comprovando que o backend identificou 9 usuários únicos), podemos descartar o e-mail como a causa do problema.

2. A Causa Raiz: O Vazamento na Listagem

Como o Message.objects.bulk_create registrou as 9 linhas corretamente (uma para cada convidado), o contrato de criação que estabelecemos na send_broadcast_message está sendo cumprido perfeitamente. O bug não está mais no envio, ele está na view de leitura/listagem da caixa de mensagens do convidado.

Aqui está a anatomia do que está acontecendo:

  • Por que aparecem 9 mensagens? A view (API) que o React consome para montar a lista de mensagens na tela muito provavelmente possui uma query permissiva demais. Ela deve estar buscando algo como Mensagens onde o destinatário sou eu OU (is_broadcast=True). Como o padrinho acabou de disparar 9 mensagens de broadcast (uma para você e 8 para os outros), a query traz todas as 9 para a sua tela, ignorando o fato de que 8 delas pertencem a outros IDs de usuário.

  • Por que o Badge mostra apenas 1? A query que alimenta o contador do badge (provavelmente em outro endpoint ou função) está correta e mais rigorosa. Ela deve estar buscando estritamente Mensagens onde o destinatário sou eu E lida=False. Como só 1 das 9 mensagens realmente pertence ao seu usuário de teste no banco de dados, o badge mostra "1".

  • Por que não consigo marcar as outras 8 como lidas? Quando você clica em uma das 8 mensagens "intrusas", o React dispara um PUT/PATCH para o backend pedindo para marcá-la como lida. O backend (felizmente) parece ter uma trava de segurança que verifica: se request.user != mensagem.recipient: negar acesso. A requisição falha silenciosamente ou é ignorada, e o React não atualiza a cor da mensagem para branco porque a ação não foi concluída no servidor.

Próximo Passo

O problema agora é estritamente de filtro na recuperação dos dados. Para aplicarmos a correção definitiva:

Você poderia compartilhar o código fonte da view (no Django) responsável por listar as mensagens da caixa de entrada do usuário, para que possamos ajustar o filtro do ORM e fechar essa brecha?


Correção do código da função get_inbox

Olá! Excelente trabalho. O código fonte enviado confirmou a nossa dedução com precisão cirúrgica.

O "vazamento" de dados estava de fato acontecendo na query de listagem. Vamos analisar o trecho exato do arquivo views.py e aplicar a correção definitiva para fecharmos este ciclo.

A Causa Raiz Revelada

Ao inspecionar a função get_inbox, encontramos a seguinte instrução:

Python

messages = Message.objects.filter(

    Q(recipient=request.user) | Q(is_broadcast=True)

).order_by('-created_at')


O que o código estava dizendo ao banco de dados:

"Traga todas as mensagens onde o destinatário sou eu OU (|) qualquer mensagem do sistema que tenha a marcação de comunicado geral (is_broadcast=True)."

Como o sistema foi arquitetado para gerar uma linha de mensagem física e individual no banco para cada convidado durante o disparo do broadcast, essa condição "OU" acabou puxando as 8 mensagens dos seus colegas de rede para a sua tela, ignorando a trava de destinatário.

Correção do Código Fonte (Backend)

Uma vez que o disparo do comunicado (send_broadcast_message) já se responsabiliza por criar uma cópia exata da mensagem para o usuário alvo, a view de leitura não precisa de uma lógica global. Ela deve ser estrita: o usuário só vê o que é explicitamente dele. A flag is_broadcast continuará existindo, mas servirá apenas para o frontend (React) saber se deve exibir um ícone diferente ou tratar o layout da mensagem de forma especial.

Substitua a função get_inbox atual no seu views.py por esta versão corrigida:

Python

from django.db.models import Q

from rest_framework.decorators import api_view, permission_classes

from rest_framework.permissions import IsAuthenticated

from rest_framework.response import Response

from .serializers import MessageSerializer

from .models import Message


@api_view(['GET'])

@permission_classes([IsAuthenticated])

def get_inbox(request):

    """

    Lista estritamente as mensagens destinadas ao usuário logado, 

    incluindo mensagens diretas e comunicados gerais.

    """

    # A restrição é absoluta: recipient deve ser o request.user.

    messages = Message.objects.filter(

        recipient=request.user

    ).order_by('-created_at')


    serializer = MessageSerializer(messages, many=True)

    return Response({"status": 1, "messages": serializer.data})


Fechamento do Ciclo MASP (Controle)

Após aplicar esta alteração, o comportamento esperado ao refazer o teste com o seu usuário convidado de teste (g.ustavo@...) será o seguinte:

  1. Front-end (Lista de Mensagens): Apenas 1 (uma) mensagem será renderizada na tela ("Teste MASP de Correção").

  2. Badge (Contador): Permanecerá mostrando 1, pois já estava com a lógica correta.

  3. Ação de Leitura: Ao clicar na mensagem, a requisição enviada para a view mark_as_read passará na validação Message.objects.get(id=message_id, recipient=request.user), atualizando o status de leitura no banco e alterando a cor no React corretamente.

Com a aplicação das pré/pós-condições na escrita e o bloqueio estrito na leitura, o bug está tecnicamente bloqueado e a integridade dos dados garantida em ambas as pontas.


Teste de limpeza das mensagem lidas

Ola! Testei e está funcionando. Muito obrigado!

Agora, quero saber como forçar a execução do job para remover as mensagens já lidas, pois configurei para zero dias, a fim de testar se vai remover tudo.



Resposta do Gemini

Que excelente notícia, Gustavo! Fico feliz que a correção tenha funcionado perfeitamente e que tenhamos fechado essa etapa com sucesso e integridade nos dados.

Sobre o seu novo teste para a rotina de exclusão de mensagens lidas (configurada para zero dias), a forma de forçar a execução depende de como você arquitetou esse job no Django. Abaixo estão as três formas mais comuns para forçar o disparo no seu ambiente de desenvolvimento local:

1. Via Django Shell (Método mais direto e universal)

Se o seu job de limpeza está encapsulado em uma função Python, a maneira mais rápida de testar é rodar a função manualmente no console interativo do projeto.

Abra o seu terminal na pasta raiz do projeto e execute:

Bash

python manage.py shell


Dentro do shell do Python, importe a sua função e execute-a:

Python

# Substitua pelo caminho real do seu arquivo e função

from votenet.emails.tasks import limpar_mensagens_lidas 


# Dispare a função

limpar_mensagens_lidas()


2. Via Management Command (Para Cron/Agendadores do SO)

É muito comum em projetos Django que jobs de limpeza de banco sejam construídos como comandos personalizados (arquivos dentro de management/commands/). Se você seguiu essa arquitetura, basta rodar o comando diretamente no terminal:

Bash

python manage.py nome_do_seu_arquivo_de_comando


3. Via Celery (Para filas assíncronas)

Caso você esteja utilizando o Celery para gerenciar tarefas em segundo plano (com o decorador @shared_task), você também pode forçar a execução via Django Shell.

Bash

python manage.py shell


Python

from votenet.emails.tasks import limpar_mensagens_lidas


# Rodar de forma totalmente síncrona, executando no próprio console sem enviar para a fila do worker:

limpar_mensagens_lidas()


# OU, se quiser forçar o envio para o worker do Celery processar na hora:

limpar_mensagens_lidas.apply_async()


⚠️ Observação Analítica: A Lógica de "Zero Dias"

Sua estratégia de reduzir a configuração para 0 dias é ótima para validar a query de remoção. Apenas tenha em mente como a matemática de datas do Python (datetime.timedelta) interpreta isso.

Se a sua query de exclusão estiver estruturada desta forma:

Python

limite_tempo = timezone.now() - timedelta(days=0)

Message.objects.filter(lida=True, data_leitura__lte=limite_tempo).delete()


Na prática, timedelta(days=0) retorna o tempo atual exato. O ORM vai buscar "tudo que foi lido antes ou exatamente agora". Isso cumprirá o seu objetivo de limpar 100% da base de mensagens lidas para validar se o delete() está operando corretamente e liberando o espaço em disco no backend.


Explicações em texto, vídeo e áudio pela IA NotebookLM

Manual de Padronização: Resolução de Falhas Críticas via Metodologia MASP

1. Introdução e Contextualização Estratégica

A padronização no tratamento de falhas de software é o alicerce fundamental para a escalabilidade e a confiabilidade de sistemas de alta criticidade. A aplicação rigorosa da metodologia MASP (Método de Análise e Solução de Problemas) permite que a engenharia de software transcenda a mera correção reativa de bugs, transformando incidentes técnicos em ativos de aprendizado organizacional e robustez arquitetural. Ao estruturar a resolução de problemas em etapas lógicas e baseadas em dados, garantimos que cada correção atue não apenas no sintoma, mas no bloqueio definitivo da causa raiz, preservando os recursos de engenharia para a inovação.Para este manual, utilizaremos o cenário crítico detectado no aplicativo  VoteNet , onde uma falha de redundância de dados comprometeu a integridade das comunicações. As especificações técnicas do ambiente afetado são:

  • Frontend:  ReactJS v15.6.1

  • Ambiente de Execução:  Node v6.17.1 / NPM v3.10.10

  • Backend:  Django v4.2.3

  • Comunicação API:  Axios v0.26.0A complexidade desta infraestrutura exige uma identificação milimétrica do problema para que a intervenção técnica seja cirúrgica e livre de efeitos colaterais.

2. Etapa 1: Identificação Detalhada do Problema (5W1H)

A definição precisa da "lacuna" — a distância entre o comportamento esperado e o observado — é o passo mais crítico para evitar o desperdício de esforço intelectual. Sem um diagnóstico claro do "que é" versus "o que deveria ser", a investigação torna-se errática.A falha no VoteNet foi mapeada sob a ótica do framework 5W1H conforme a tabela abaixo:


Dimensão

Descrição Detalhada

What (O que?)

Duplicação de mensagens de comunicado geral recebidas pelos usuários da rede.

Who (Quem?)

Desenvolvedor em ambiente de desenvolvimento e usuários convidados.

When (Quando?)

Ao acessar a opção "Mensagens" do menu principal após um disparo de comunicado.

Where (Onde?)

Componente SectionInvites.jsx (Frontend) e View de envio/leitura (Backend).

Why (Por que?)

Cada usuário deveria receber uma única mensagem, mas visualiza 9 cópias idênticas (mesma data/hora/texto).

How (Como?)

Após abrir a janela modal , escrever o texto e clicar em "DISPARAR AGORA" , o convidado logado visualiza sua mensagem e as mensagens repetidas de todos os outros 8 convidados da rede.



Análise de Impacto:  A falha resulta na redundância física de dados, gerando um Produto Cartesiano na interface. Em uma rede de votação, onde a precisão é o valor máximo, a exibição de 9 mensagens duplicadas mina a credibilidade da plataforma e sobrecarrega o banco de dados desnecessariamente, exigindo um mapeamento rigoroso das variáveis sistêmicas.

3. Etapa 2: Observação e Mapeamento de Variáveis (Ishikawa)

Para evitar a "visão de túnel", aplicamos o Diagrama de Ishikawa para garantir que infraestrutura, método e erro humano sejam avaliados de forma holística.Status das variáveis conforme o contexto do VoteNet:

  • Método (Lógica):  O algoritmo está aplicando a regra correta?  NÃO  (Confirmada anomalia na lógica de filtragem).

  • Máquina (Infraestrutura):  Erro ocorre apenas em produção? NÃO (Ocorre em ambiente de desenvolvimento).

  • Mão de Obra (Intervenção Humana):   Houve falha na escrita ou falta de revisão? SIM  (Código replicado sem adaptação). Cenário previsto em testes?  NÃO .

  • Medida (Dados):  Bug causado por caracteres especiais? NÃO. Erro após X execuções (vazamento)? NÃO.

  • Material (Integrações):  Base de dados corrompida? NÃO (Justificativa: É a mesma base íntegra utilizada nos testes de desenvolvimento).

  • Meio Ambiente (Contexto):  Instabilidade de rede ou simultaneidade? NÃO SEI (Hipótese secundária).Foco Investigativo:  A investigação concentrou-se nos pilares de  Mão de Obra  e  Método , onde a falha foi confirmada. A replicação de lógica sem a devida contextualização para a funcionalidade de broadcast foi identificada como o ponto de origem da anomalia.

4. Etapa 3: Análise e Descoberta da Causa Raiz (5 Porquês)

Utilizamos a técnica dos "5 Porquês" para atravessar os sintomas da interface e atingir a falha lógica original.Progressão Lógica:

  1. Por que o usuário visualiza 9 mensagens?  Porque o backend está retornando registros redundantes na consulta da caixa de entrada.

  2. Por que a consulta retorna registros de outros usuários?  Porque a função de listagem (get_inbox) possui uma falha de filtro de segurança.

  3. Por que as mensagens foram criadas em massa?  Porque a função send_broadcast_message utilizou uma query de destinatários que gerou um Produto Cartesiano.

  4. Qual a origem dessa lógica de envio?  A lógica foi copiada da classe RedeDetailView (projetada para listar dados da rede) para a função de envio de e-mails sem os ajustes de unicidade necessários.

  5. Qual o erro técnico fundamental?  A ausência de .distinct() na query ORM do Django e a falta de vinculação estrita entre recipient e request.user na query de leitura.Análise "So What?":  Tecnicamente, o reuso de código sem adaptação gerou um "Produto Cartesiano" onde cada mensagem foi multiplicada pelo número de convites ativos. A falta de um filtro de unicidade no banco de dados permitiu a persistência dessa redundância física, comprometendo a integridade lógica do sistema.

5. Etapa 4: Plano de Ação e Bloqueio (5W2H)

O Plano de Ação estabelece o protocolo de bloqueio para garantir que o erro não reincida, utilizando o rigor do  Design by Contract .

  • What:  Refatorar a view send_broadcast_message e a query get_inbox.

  • Why:  Bloquear a criação e a visualização de mensagens duplicadas/indevidas.

  • Where:  Backend Django (votenet/emails/views.py).

  • When:  Imediatamente (Ambiente de Desenvolvimento).

  • Who:  Desenvolvedor Full-Stack (Gustavo Carvalho).

  • How:  Implementar .distinct() e  dictionary comprehension  por user.id no envio; restringir o filtro de leitura ao request.user.

  • How Much:  Esforço de desenvolvimento já provisionado.Princípios de Design by Contract (Mandatórios):

  1. Pré-condição:  Validação de corpo de mensagem não nulo antes do processamento.

  2. Invariante:  Uso de dicionário indexado para garantir destinatários únicos em memória antes do bulk_create.

  3. Pós-condição:  O sistema deve disparar um  AssertionError  caso a quantidade de mensagens criadas divirja do número de destinatários únicos identificados, bloqueando a persistência de dados inconsistentes.

6. Verificação e Controle de Resultados (Ciclo PDCA)

6.1 Execução de Teste

Para validar a correção, foi utilizada a estratégia de "Gmail Dots" (ex: g.ustavo@...), garantindo que o sistema trate os e-mails como usuários únicos no banco de dados, validando se o problema era lógico e não de entrada de dados.

6.2 Quadro de Controle e Análise de Desvio

Teste,Resultado Esperado,Resultado Real

Back-end (Envio),Inserção de exatamente 9 linhas (1 por convidado).,Sucesso (Igual ao esperado)

Front-end (Lista),1 única mensagem visível para o convidado.,9 mensagens (Falha detectada)

Análise de Desvio e Segunda Causa Raiz:  Um insight vital de QA foi observado: o  Badge (contador de mensagens)  mostrava corretamente "1", enquanto a  Lista  exibia "9". Além disso, o usuário só conseguia marcar como lida a sua própria mensagem (as outras 8 permaneciam inalteradas).Isso isolou o problema na query get_inbox. A lógica original utilizava o operador Q(recipient=request.user) | Q(is_broadcast=True). O uso do "OU" (|) causava um "vazamento" de dados, permitindo que um usuário visse mensagens de broadcast destinadas a terceiros. A correção definitiva exige a remoção da condição global, tornando o filtro estrito ao recipient=request.user.

7. Padronização e Manutenção Preventiva

A conclusão do ciclo MASP exige a preservação da performance sistêmica através de rotinas automáticas.Protocolo Mandatório de Segurança de Dados:  Fica terminantemente proibida a utilização de flags globais (como is_broadcast) como critério de listagem sem a vinculação mandatória ao ID do usuário logado. Toda query de recuperação de mensagens deve seguir o padrão: Message.objects.filter(recipient=request.user).Rotina de Manutenção Preventiva:  Deve-se executar periodicamente o Job de limpeza de mensagens lidas para evitar o inchaço do banco de dados.

  • Configuração Analítica:  Em ambiente de validação, utilizar timedelta(days=0) para garantir que o comando delete() limpe 100% das mensagens marcadas como lidas, validando a eficácia da query.

  • Métodos de Execução:  O disparo deve ser realizado via Django Shell (python manage.py shell), Management Command ou através de tarefas agendadas via Celery (apply_async).





Nenhum comentário:

Postar um comentário

Entendendo a Metáfora "Home Office é Moto Boy"

Exemplo 5 de uso do MASP com IA em TI - Bug de Duplicação de Mensagens

   O exemplo 5 de uso do MASP (Método de Análise e Solução de Problemas), com IA (Inteligência Artificial) em TI (Tecnologia da Informação),...