diff --git a/.github/workflows/ci-backend.yml b/.github/workflows/ci-backend.yml
index 2e9b799b5..e0a742481 100644
--- a/.github/workflows/ci-backend.yml
+++ b/.github/workflows/ci-backend.yml
@@ -7,6 +7,7 @@ on:
- 'src/Modules/**'
- 'src/Bootstrapper/**'
- 'src/Shared/**'
+ - 'src/Contracts/**'
- 'tests/**'
- '!tests/MeAjudaAi.E2E.Tests/**'
- '.github/workflows/ci-backend.yml'
@@ -20,6 +21,7 @@ on:
- 'src/Modules/**'
- 'src/Bootstrapper/**'
- 'src/Shared/**'
+ - 'src/Contracts/**'
- 'tests/**'
- '!tests/MeAjudaAi.E2E.Tests/**'
- '.github/workflows/ci-backend.yml'
@@ -172,6 +174,7 @@ jobs:
"src/Modules/ServiceCatalogs/Tests/MeAjudaAi.Modules.ServiceCatalogs.Tests.csproj"
"src/Modules/Locations/Tests/MeAjudaAi.Modules.Locations.Tests.csproj"
"src/Modules/Communications/Tests/MeAjudaAi.Modules.Communications.Tests.csproj"
+ "src/Modules/Ratings/Tests/MeAjudaAi.Modules.Ratings.Tests.csproj"
"src/Modules/SearchProviders/Tests/MeAjudaAi.Modules.SearchProviders.Tests.csproj"
"tests/MeAjudaAi.Shared.Tests/MeAjudaAi.Shared.Tests.csproj"
"tests/MeAjudaAi.ApiService.Tests/MeAjudaAi.ApiService.Tests.csproj"
@@ -283,7 +286,7 @@ jobs:
thresholds: "90 80" # line branch
fail_below_min: true
- - name: Add Coverage PR Comment
+ - name: Generate Coverage PR Comment
uses: marocchino/sticky-pull-request-comment@v3
if: always() && github.event_name == 'pull_request'
with:
@@ -291,6 +294,79 @@ jobs:
header: coverage-report
path: code-coverage-results.md
+ openapi-breaking-change-check:
+ name: OpenAPI Breaking Change Check
+ runs-on: ubuntu-latest
+ if: github.event_name == 'pull_request'
+ steps:
+ - name: Checkout PR Branch
+ uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: "10.0.x"
+
+ - name: Build API
+ env:
+ ASPNETCORE_ENVIRONMENT: Testing
+ run: |
+ dotnet build src/Bootstrapper/MeAjudaAi.ApiService/MeAjudaAi.ApiService.csproj \
+ -c Release \
+ -p:GenerateDocumentationFile=true
+
+ - name: Install Swashbuckle CLI
+ run: dotnet tool install -g Swashbuckle.AspNetCore.Cli --version 10.1.5
+
+ - name: Generate Current OpenAPI Spec
+ env:
+ ASPNETCORE_ENVIRONMENT: Testing
+ run: |
+ mkdir -p api-current
+ swagger tofile \
+ --output api-current/api-spec.json \
+ src/Bootstrapper/MeAjudaAi.ApiService/bin/Release/net10.0/MeAjudaAi.ApiService.dll \
+ v1
+
+ - name: Checkout Base Branch Spec
+ env:
+ BASE_REF: ${{ github.base_ref }}
+ run: |
+ echo "⚠️ Generating base spec from source using worktree for origin/$BASE_REF..."
+
+ # Cria um worktree para a branch base em diretório irmão
+ git worktree add ../base-branch origin/$BASE_REF
+
+ # Gera o spec a partir do build da base
+ cd ../base-branch
+ export ASPNETCORE_ENVIRONMENT=Testing
+ dotnet build src/Bootstrapper/MeAjudaAi.ApiService/MeAjudaAi.ApiService.csproj \
+ -c Release \
+ -p:GenerateDocumentationFile=true
+
+ swagger tofile \
+ --output $GITHUB_WORKSPACE/api-base.json \
+ src/Bootstrapper/MeAjudaAi.ApiService/bin/Release/net10.0/MeAjudaAi.ApiService.dll \
+ v1
+
+ cd $GITHUB_WORKSPACE
+ git worktree remove ../base-branch --force
+
+ # Validação final: falha se não houver spec base
+ if [ ! -f api-base.json ]; then
+ echo "❌ CRITICAL: api-base.json could not be generated for $BASE_REF."
+ exit 1
+ fi
+
+ - name: Check for Breaking Changes
+ uses: oasdiff/oasdiff-action/breaking@v0.0.39
+ with:
+ base: 'api-base.json'
+ revision: 'api-current/api-spec.json'
+ fail-on: 'ERR'
+
security-scan:
name: Security Scan
runs-on: ubuntu-latest
diff --git a/MeAjudaAi.slnx b/MeAjudaAi.slnx
index 02881578d..144ae8007 100644
--- a/MeAjudaAi.slnx
+++ b/MeAjudaAi.slnx
@@ -84,6 +84,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/roadmap-current.md b/docs/roadmap-current.md
deleted file mode 100644
index eba93eb79..000000000
--- a/docs/roadmap-current.md
+++ /dev/null
@@ -1,1490 +0,0 @@
-## 🎨 Fase 2: Frontend & Experiência
-
-**Status**: 🔄 Em andamento (Jan–Mar 2026)
-
-### Objetivo
-Desenvolver aplicações frontend usando **React + Next.js** (Customer Web App, Admin Portal) + **React Native** (Mobile App).
-
-> **📅 Status Atual**: Sprint 9 (Estabilização) encerrada (11 Abr 2026)
-> **📝 Decisão Técnica**: Cobertura Global de 70% atingida e reforçada no CI/CD.
-> **🎉 MIGRAÇÃO CONCLUÍDA**: Admin Portal migrado de Blazor para React + Next.js na Sprint 8D
-
----
-
-### 📱 Stack Tecnológico ATUALIZADO (21 Mar 2026)
-
-> **📝 Decisão Técnica** (5 Fevereiro 2026):
-> Stack de Customer App definida como **React 19 + Next.js 15 + Tailwind CSS v4**.
-> **Admin Portal**: Migrado de Blazor WASM para React + Next.js na Sprint 8D.
-> **Razão**: SEO crítico para Customer App, performance inicial, ecossistema maduro, contratação facilitada.
-
-**Decisão Estratégica**: Stack unificado em **React + Next.js** para todos os apps web
-
-**Justificativa**:
-- ✅ **SEO**: Customer App precisa aparecer no Google ("eletricista RJ") - Next.js SSR/SSG resolve
-- ✅ **Performance**: Initial load rápido crítico para conversão mobile - code splitting + lazy loading
-- ✅ **Ecossistema**: Massivo - geolocalização, mapas, pagamentos, qualquer problema já resolvido
-- ✅ **Contratação**: Fácil escalar time - React devs abundantes
-- ✅ **Mobile**: React Native maduro e testado vs MAUI Hybrid ainda novo
-- ✅ **Modern Stack**: React 19 + Tailwind v4 é estado da arte (2026)
-- ⚠️ **Trade-off**: DTOs duplicados (C# backend, TS frontend) - mitigado com OpenAPI TypeScript Generator
-
-**Stack Completa**:
-
-**Admin Portal** (React - migrado na Sprint 8D):
-- React 19 + TypeScript 5.7+
-- Tailwind CSS v4
-- Zustand (state management)
-- React Hook Form + Zod
-- NextAuth.js (Keycloak OIDC)
-
-**Customer Web App** (novo):
-- React 19 (Server Components + Client Components)
-- Next.js 15 (App Router, SSR/SSG)
-- TypeScript 5.7+ (strict mode)
-- Tailwind CSS v4 (@theme, CSS variables)
-- Base UI React (@base-ui/react) - headless components
-- Zustand (client state) + TanStack Query v5 (server state)
-- React Hook Form + Zod (forms & validation)
-- Lucide React (icons)
-
-**Mobile Customer App** (novo):
-- React Native + Expo
-- Compartilha componentes com Customer Web App
-- Geolocalização nativa
-- Notificações push
-- Secure Storage para tokens
-
-
-**Shared**:
-- **OpenAPI TypeScript Generator**: Sincroniza tipos C# → TypeScript automaticamente
- - **Tooling**: `openapi-typescript-codegen` ou `@hey-api/openapi-ts`
- - **Trigger**: CI/CD job on `api/swagger/v1/swagger.json` changes
- - **Output**: `MeAjudaAi.Web.Customer/types/api/generated/`
- - **Versioning**: API versions `v1`, `v2` (breaking changes require version bump)
- - **Breaking Change Gating**: OpenAPI diff in CI fails PR without version bump
-- Keycloak OIDC (autenticação unificada)
-- PostgreSQL (backend único)
-
-**Estratégia de Compartilhamento de Código (C# → TypeScript)**:
-
-| Artefato | Fonte Backend | Saída Frontend | Método de Sincronização |
-|----------|----------------|-----------------|-------------|
-| **DTOs** | `Contracts/*.cs` | `types/api/*.ts` | OpenAPI Generator (auto) |
-| **Enums** | `MeAjudaAi.Contracts/Enums/` | `types/enums.ts` | OpenAPI Generator (auto) |
-| **Validação** | FluentValidation | Zod schemas | Geração Automática (Sprint 8A) |
-| **Constantes** | `MeAjudaAi.Contracts/Constants/` | `lib/constants.ts` | Geração Automática (Sprint 8A) |
-
-**Plano de Geração**:
-1. Implementar ferramenta CLI para converter `MeAjudaAi.Contracts` Enums e Constants em `types/enums.ts` e `lib/constants.ts`.
-2. Implementar conversor de metadados FluentValidation para Zod schemas em `types/api/validation.ts`.
-3. Adicionar tickets no backlog para verificação em CI e versionamento semântico dos artefatos gerados.
-
-**Nota de Estratégia**: Priorizamos o reuso de `MeAjudaAi.Contracts` para enums e constantes para manter o Frontend alinhado com o Backend e evitar desvios.
-
-**Localização dos Arquivos Gerados**:
-```text
-src/
-├── Contracts/ # Backend DTOs (C#)
-└── Web/
- ├── MeAjudaAi.Web.Admin/ # React + Next.js (migrado do Blazor na Sprint 8D)
- ├── MeAjudaAi.Web.Customer/ # Next.js
- │ └── types/api/generated/ # ← OpenAPI gerado types
- └── Mobile/
- └── MeAjudaAi.Mobile.Customer/ # React Native
- └── src/types/api/ # ← Mesmo OpenAPI gerado types
-```
-
-**Pipeline CI/CD** (GitHub Actions):
-1. Mudanças no Backend → Swagger JSON atualizado
-2. Verificação de OpenAPI diff (breaking changes?)
-3. Se houver quebra → Requerer bump de versão da API (`v1` → `v2`)
-4. Gerar tipos TypeScript
-5. Commit para `types/api/generated/` (auto-commit bot)
-6. Testes do Frontend rodam com novos tipos
-
-### 🗂️ Estrutura de Projetos Atualizada
-```text
-src/
-├── Web/
-│ ├── MeAjudaAi.Web.Admin/ # React + Next.js Admin Portal (Sprint 8D)
-│ └── MeAjudaAi.Web.Customer/ # 🚀 Next.js Customer App (Sprint 8A)
-├── Mobile/
-│ └── MeAjudaAi.Mobile.Customer/ # 🚀 React Native + Expo (Sprint 8B)
-└── Shared/
- ├── MeAjudaAi.Shared.DTOs/ # DTOs C# (backend)
- └── MeAjudaAi.Contracts/ # OpenAPI spec → TypeScript types
-```
-
-### 🔐 Autenticação Unificada
-
-**Consistência de Autenticação Cross-Platform**:
-
-| Aspecto | Admin (React) | Customer Web (Next.js) | Customer Mobile (RN) |
-|--------|--------------|------------------------|----------------------|
-| **Armazenamento de Token** | HTTP-only cookies | HTTP-only cookies | Secure Storage |
-| **Vida útil do Token** | 1h acesso + 24h refresh | 1h acesso + 7d refresh | 1h acesso + 30d refresh |
-| **Estratégia de Refresh** | Automática (NextAuth) | Middleware refresh | Background refresh |
-| **Role Claims** | `role` claim | `role` claim | `role` claim |
-| **Sair (Logout)** | `/api/auth/signout` | `/api/auth/signout` | Revoke + clear storage |
-
-**Configuração Keycloak**:
-- **Realm**: `MeAjudaAi`
-- **Clientes**: `meajudaai-admin` (público), `meajudaai-customer` (público)
-- **Roles**: `admin`, `customer`, `provider`
-- **Formato Token**: JWT (RS256)
-- **Vida útil Token**: Acesso 1h, Refresh 30d (configurável por cliente: Admin=24h, Customer=7d, Mobile=30d)
-
-**Detalhes de Implementação**:
-- **Protocolo**: OpenID Connect (OIDC)
-- **Provedor de Identidade**: Keycloak
-- **Admin Portal**: NextAuth.js v5 (React + Next.js)
-- **Customer Web**: NextAuth.js v5 (Next.js)
-- **Customer Mobile**: React Native OIDC Client
-- **Refresh**: Automático via OIDC interceptor
-
-**Guia de Migração**: Veja `docs/authentication-migration.md` (a ser criado na Sprint 8A)
-
-
-
----
-
-### 🚀 Gestão de Restrições Geográficas
-
-**Resumo**: Restrições geográficas podem ser configuradas via `appsettings.json` (Fase 1, MVP atual) ou gerenciadas dinamicamente via Blazor Admin Portal com banco de dados (Fase 2, planejado Sprint 7+). O middleware `GeographicRestrictionMiddleware` valida cidades/estados permitidos usando IBGE API.
-
-**Contexto**: O middleware `GeographicRestrictionMiddleware` suporta configuração dinâmica via `Microsoft.FeatureManagement`. Este recurso foi implementado em duas fases:
-
-#### ✅ Fase 1: Middleware com appsettings (CONCLUÍDA - Sprint 1 Dia 1, 21 Nov 2025)
-
-**Implementação Atual**: Restrições geográficas baseadas em `appsettings.json` com middleware HTTP e integração IBGE API.
-
-**Decisões de Arquitetura**:
-
-1. **Localização de Código** ✅ **ATUALIZADO 21 Nov 2025**
- - ✅ **MOVIDO** `GeographicRestrictionMiddleware` para `ApiService/Middlewares` (específico para API HTTP)
- - ✅ **MOVIDO** `GeographicRestrictionOptions` para `ApiService/Options` (configuração lida de appsettings da API)
- - ✅ **MOVIDO** `FeatureFlags.cs` para `Shared/Constants` (constantes globais como AuthConstants, ValidationConstants)
- - ❌ **DELETADO** `Shared/Configuration/` (pasta vazia após movimentações)
- - ❌ **DELETADO** `Shared/Middleware/` (pasta vazia, middleware único movido para ApiService)
- - **Justificativa**:
- - GeographicRestriction é funcionalidade **exclusiva da API HTTP** (não será usada por Workers/Background Jobs)
- - Options são lidas de appsettings que só existem em ApiService
- - FeatureFlags são constantes (similar a `AuthConstants.Claims.*`, `ValidationConstants.MaxLength.*`)
- - Middlewares genéricos já estão em pastas temáticas (Authorization/Middleware, Logging/, Monitoring/)
-
-2. **Propósito da Feature Toggle** ✅
- - ✅ **Feature flag ativa/desativa TODA a restrição geográfica** (on/off global)
- - ✅ **Cidades individuais controladas via banco de dados** (Sprint 3 - tabela `allowed_regions`)
- - ✅ **Arquitetura proposta**:
- ```ini
- FeatureManagement:GeographicRestriction = true → Liga TODA validação
- ↓
- allowed_regions.is_active = true → Ativa cidade ESPECÍFICA
- ```
- - **MVP (Sprint 1)**: Feature toggle + appsettings (cidades hardcoded)
- - **Sprint 3**: Migração para database-backed + Admin Portal UI
-
-3. **Remoção de Redundância** ✅ **JÁ REMOVIDO**
- - ❌ **REMOVIDO**: Propriedade `GeographicRestrictionOptions.Enabled` (redundante com feature flag)
- - ❌ **REMOVIDO**: Verificação `|| !_options.Enabled` do middleware
- - ✅ **ÚNICA FONTE DE VERDADE**: `FeatureManagement:GeographicRestriction` (feature toggle)
- - **Justificativa**: Ter duas formas de habilitar/desabilitar causa confusão e potenciais conflitos.
- - **Benefício**: Menos configurações duplicadas, arquitetura mais clara e segura.
-
-**Organização de Pastas** (21 Nov 2025):
-```text
-src/
- Shared/
- Constants/
- FeatureFlags.cs ← MOVIDO de Configuration/ (constantes globais)
- AuthConstants.cs (existente)
- ValidationConstants.cs (existente)
- Authorization/Middleware/ (middlewares de autorização)
- Logging/ (LoggingContextMiddleware)
- Monitoring/ (BusinessMetricsMiddleware)
- Messaging/Handlers/ (MessageRetryMiddleware)
-
- Bootstrapper/MeAjudaAi.ApiService/
- Middlewares/
- GeographicRestrictionMiddleware.cs ← MOVIDO de Shared/Middleware/
- RateLimitingMiddleware.cs (específico HTTP)
- SecurityHeadersMiddleware.cs (específico HTTP)
- Options/
- GeographicRestrictionOptions.cs ← MOVIDO de Shared/Configuration/
- RateLimitOptions.cs (existente)
- CorsOptions.cs (existente)
-```
-
-**Resultado Sprint 1**: Middleware funcional com validação via IBGE API, feature toggle integrado, e lista de cidades configurável via appsettings (requer redeploy para alterações).
-
----
-
-### 🚀 Fase 2 Shipped (Jan-Feb 2026)
-
-#### ✅ Fase 2: Database-Backed + Admin Portal UI (CONCLUÍDO - Sprint 7, 7 Jan 2026)
-
-**Contexto**: Migrar lista de cidades/estados de `appsettings.json` para banco de dados, permitindo gestão dinâmica via Admin Portal (React) sem necessidade de redeploy.
-
-**Status**: ✅ IMPLEMENTADO - AllowedCities UI completa com CRUD, coordenadas geográficas, e raio de serviço.
-
-**Arquitetura Proposta**:
-```sql
--- Schema: geographic_restrictions (novo)
-CREATE TABLE geographic_restrictions.allowed_regions (
- region_id UUID PRIMARY KEY,
- type VARCHAR(10) NOT NULL, -- 'City' ou 'State'
- city_name VARCHAR(200),
- state_code VARCHAR(2) NOT NULL,
- is_active BOOLEAN NOT NULL DEFAULT TRUE,
- added_at TIMESTAMP NOT NULL,
- added_by_user_id UUID,
- notes TEXT
-);
-
-CREATE INDEX idx_allowed_regions_state ON geographic_restrictions.allowed_regions(state_code);
-CREATE INDEX idx_allowed_regions_active ON geographic_restrictions.allowed_regions(is_active);
-```
-
-### 🚀 Fase 2 Seguimentos (Mar 2026+)
-
-**Funcionalidades Admin Portal**:
-
-- [ ] **Visualização de Restrições Atuais** (Admin Portal React)
- - [ ] Tabela com cidades/estados permitidos
- - [ ] Filtros: Tipo (Cidade/Estado), Estado, Status (Ativo/Inativo)
- - [ ] Ordenação: Alfabética, Data de Adição
- - [ ] Indicador visual: Badges para "Cidade" vs "Estado"
-
-- [ ] **Adicionar Cidade/Estado**
- - [ ] Formulário com campos:
- - Tipo: Dropdown (Cidade, Estado)
- - Estado: Dropdown preenchido via IBGE API (27 UFs)
- - Cidade: Autocomplete via IBGE API (se tipo=Cidade)
- - Notas: Campo opcional (ex: "Piloto Beta Q1 2025")
- - [ ] Validações:
- - Estado deve ser sigla válida (RJ, SP, MG, etc.)
- - Cidade deve existir no IBGE (validação server-side)
- - Não permitir duplicatas (cidade+estado único)
- - [ ] Preview: "Você está adicionando: Muriaé/MG"
-
-- [ ] **Editar Região**
- - [ ] Apenas permitir editar "Notas" e "Status"
- - [ ] Cidade/Estado são imutáveis (deletar + re-adicionar se necessário)
- - [ ] Confirmação antes de desativar região com prestadores ativos
-
-- [ ] **Ativar/Desativar Região**
- - [ ] Toggle switch inline na tabela
- - [ ] Confirmação: "Desativar [Cidade/Estado] irá bloquear novos registros. Prestadores existentes não serão afetados."
- - [ ] Audit log: Registrar quem ativou/desativou e quando
-
-- [ ] **Remover Região**
- - [ ] Botão de exclusão com confirmação dupla
- - [ ] Validação: Bloquear remoção se houver prestadores registrados nesta região
- - [ ] Mensagem: "Não é possível remover [Cidade]. Existem 15 prestadores registrados."
-
-**Integração com Middleware** (Refatoração Necessária):
-
-**Abordagem 1: Database-First (Recomendado)**
-```csharp
-// GeographicRestrictionOptions (modificado)
-public class GeographicRestrictionOptions
-{
- public bool Enabled { get; set; }
- public string BlockedMessage { get; set; } = "...";
-
- // DEPRECATED: Remover após migração para database
- [Obsolete("Use database-backed AllowedRegionsService instead")]
- public List AllowedCities { get; set; } = new();
- [Obsolete("Use database-backed AllowedRegionsService instead")]
- public List AllowedStates { get; set; } = new();
-}
-
-// Novo serviço
-public interface IAllowedRegionsService
-{
- Task> GetAllowedCitiesAsync(CancellationToken ct = default);
- Task> GetAllowedStatesAsync(CancellationToken ct = default);
-}
-
-// GeographicRestrictionMiddleware (modificado)
-public class GeographicRestrictionMiddleware
-{
- private readonly IAllowedRegionsService _regionsService;
-
- public async Task InvokeAsync(HttpContext context)
- {
- // Buscar listas do banco (com cache)
- var allowedCities = await _regionsService.GetAllowedCitiesAsync(ct);
- var allowedStates = await _regionsService.GetAllowedStatesAsync(ct);
-
- // Lógica de validação permanece igual
- if (!allowedCities.Contains(userCity) && !allowedStates.Contains(userState))
- {
- // Bloquear
- }
- }
-}
-```
-
-**Abordagem 2: Híbrida (Fallback para appsettings)**
-- Se banco estiver vazio, usar `appsettings.json`
-- Migração gradual: Admin adiciona regiões no portal, depois remove de appsettings
-
-**Estratégia de Cache**:
-- Usar `HybridCache` (já implementado no `IbgeService`)
-- TTL: 5 minutos (balanço entre performance e dados frescos)
-- Invalidação: Ao adicionar/remover/editar região no portal administrativo
-
-**Caminho de Migração**:
-1. **Sprint 3 Semana 1**: Criar schema `geographic_restrictions` + tabela
-2. **Sprint 3 Semana 1**: Implementar `AllowedRegionsService` com cache
-3. **Sprint 3 Semana 1**: Refatoração do middleware para usar serviço (mantém fallback appsettings)
-4. **Sprint 3 Semana 2**: Implementar endpoints CRUD na API administrativa
-5. **Sprint 3 Semana 2**: Implementar UI no Admin Portal (React)
-6. **Sprint 3 Pós-Deploy**: Popular banco com dados iniciais (Muriaé, Itaperuna, Linhares)
-7. **Sprint 4**: Remover valores de appsettings.json (obsoleto)
-
-**Testes Necessários**:
-- [ ] Unit tests: `AllowedRegionsService` (CRUD + invalidação de cache)
-- [ ] Integration tests: Middleware com banco populado vs vazio
-- [ ] E2E tests: Admin adiciona cidade → Middleware bloqueia outras cidades
-
-**Documentação**:
-- [ ] Guia do Usuário Admin: Como adicionar/remover cidades piloto
-- [ ] Débito Técnico: Marcar `AllowedCities` e `AllowedStates` como obsoletos
-
-**⚠️ Breaking Changes**:
-- ~~`GeographicRestrictionOptions.Enabled` será removido~~ ✅ **JÁ REMOVIDO** (Sprint 1 Dia 1)
- - **Motivo**: Redundante com feature toggle - fonte de verdade única
- - **Migração**: Usar apenas `FeatureManagement:GeographicRestriction` em appsettings
-- `GeographicRestrictionOptions.AllowedCities/AllowedStates` será deprecado (Sprint 3)
- - **Migração**: Admin Portal populará tabela `allowed_regions` via UI
-
-**Estimativa**:
-- **Backend (API + Serviço)**: 2 dias
-- **Frontend (Admin Portal UI)**: 2 dias
-- **Migração + Testes**: 1 dia
-- **Total**: 5 dias (dentro da Sprint 3 de 2 semanas)
-
-#### 7. Moderação de Avaliações (Preparação para Fase 3)
-- [ ] **Listagem**: Avaliações sinalizadas/reportadas
-- [ ] **Ações**: Aprovar, Remover, Banir usuário
-- [ ] Stub para módulo de Avaliações (a ser implementado na Fase 3)
-
-**Tecnologias (Admin Portal React)**:
-- **Framework**: React 19 + TypeScript 5.7+
-- **UI**: Tailwind CSS v4 + Base UI
-- **Estado**: Zustand
-- **HTTP**: TanStack Query + React Hook Form
-- **Gráficos**: Recharts
-
-**Resultado Esperado**:
-- ✅ Admin Portal funcional e responsivo (React)
-- ✅ Todas operações CRUD implementadas
-- ✅ Dashboard com métricas em tempo real
-- ✅ Deploy no Azure Container Apps
-
----
-
-### 📅 Sprint 8A: Customer App & Nx Setup (2 semanas) ⏳ ATUALIZADO
-
-**Status**: CONCLUÍDA (5-13 Fev 2026)
-**Dependências**: Sprint 7.16 concluído ✅
-**Duração**: 2 semanas
-
-**Contexto**: Sprint dividida em duas partes para acomodar a migração para Nx monorepo.
-
----
-
-#### 📱 Parte 1: Desenvolvimento do Customer App (Foco)
-
-**Home & Busca** (Semana 1):
-- [ ] **Página de Destino**: Seção Hero + busca rápida
-- [ ] **Busca Geolocalizada**: Campo de endereço/CEP + raio + serviços
-- [ ] **Mapa Interativo**: Exibir prestadores no mapa (Leaflet.Blazor)
-- [ ] **Listagem de Resultados**: Cards com foto, nome, nota, distância, tier badge
-- [ ] **Filtros**: Nota mínima, tier, disponibilidade
-- [ ] **Ordenação**: Distância, Nota, Tier
-
-**Perfil de Prestador** (Semana 1-2):
-- [ ] **Visualização**: Foto, nome, descrição, serviços, nota, avaliações
-- [ ] **Contato**: Botão WhatsApp, telefone, e-mail (MVP: links externos)
-- [ ] **Galeria**: Fotos do trabalho (se disponível)
-- [ ] **Avaliações**: Listar avaliações de outros clientes (apenas leitura, escrita na Fase 3)
-- [ ] **Meu Perfil**: Editar informações básicas
-
-#### 🛠️ Parte 2: Configuração do Nx Monorepo
-**Status**: 🔄 EM PROGRESSO (Março 2026)
-*Nota: Este é um contêiner ampliado que representa múltiplas sprints destinadas à reestruturação modular do front-end web. A "Sprint 8B.2" encapsula a fundação inicial concluída como parte intrínseca deste arco arquitetural.*
-
-### ✅ Sprint 8B.2 - NX Scaffolding & Migração Inicial (5 - 18 Mar 2026)
-**Branch**: `feature/sprint-8b2-monorepo-cleanup`
-**Status**: 🔄 EM REVISÃO
-*Nota: A atualização final para "✅ CONCLUÍDA" deve ocorrer somente após o merge do PR ou confirmação explícita de finalização do trabalho na branch.*
-
-**Objetivos**:
-1. 🔴 **MUST-HAVE**: **Configuração do NX Monorepo** (Esforço: Grande)
- - Inicializar workspace.
- - **Migrar** `MeAjudaAi.Web.Customer` existente para `apps/customer-web`.
- - **Andaime (Scaffolding)** (placeholders vazios): `apps/provider-web` e `apps/admin-portal`.
- - Extrair bibliotecas compartilhadas: `libs/ui`, `libs/auth`, `libs/api-client`.
-2. 🔴 **MUST-HAVE**: **Unificação de Mensageria** (Esforço: Médio)
- - Remover Azure Service Bus, unificar apenas no RabbitMQ.
-3. 🔴 **MUST-HAVE**: **Pacote de Excelência Técnica** (Esforço: Médio)
- - [ ] [**TD**] **Automação Keycloak**: `setup-keycloak-clients.ps1` para desenvolvimento local.
- - [ ] [**TD**] **Limpeza de Analisadores**: Corrigir avisos SonarLint nos apps React e Contratos.
- - [ ] [**TD**] **Refatoração de Extensões**: Extrair `BusinessMetricsMiddlewareExtensions`.
- - [ ] [**TD**] **Logging Polly**: Migrar logs de resiliência para ILogger (Issue #113).
- - [ ] [**TD**] **Padronização**: Alinhamento de sintaxe de Record em `Contracts`.
- *(TODO: Marcar os checkboxes acima como [x] após o merge do PR na branch feature/sprint-8b2-monorepo-cleanup)*
-
----
-
-**Entregáveis**:
-- [ ] Nx workspace com `apps/customer-web` (migrado).
-- [ ] Placeholders para `apps/provider-web` e `apps/admin-portal`.
-- [ ] Bibliotecas extraídas para `libs/ui`, `libs/auth` e `libs/api-client`.
-*(TODO: Marcar os entregáveis como [x] após o merge do PR 153)*
-
----
-
-## 🔥 Tarefas Técnicas Cross-Module ⏳ ATUALIZADO
-
-**Status**: ✅ CONCLUÍDO (Sprint 5.5 - 19 Dez 2025)
-
-**Contexto Atual**:
-- ✅ Lock files regenerados em todos os módulos (37 arquivos atualizados)
-- ✅ PR #81 (Aspire 13.1.0) atualizado com lock files corretos
-- ✅ PR #82 (FeatureManagement 4.4.0) atualizado com lock files corretos
-- ⏳ Aguardando validação CI/CD antes do merge
-- 📋 Desenvolvimento frontend aguardando conclusão desta sprint
-
-Tarefas técnicas que devem ser aplicadas em todos os módulos para consistência e melhores práticas.
-
-### Controle de Migração em Produção
-
-**Problema**: Implementar controle `APPLY_MIGRATIONS` nos módulos restantes
-
-**Contexto**: O módulo Documents já implementa controle via variável de ambiente `APPLY_MIGRATIONS` para desabilitar migrations automáticas em produção.
-
-**Implementação** (padrão estabelecido em `Documents/API/Extensions.cs`):
-
-```csharp
-private static void EnsureDatabaseMigrations(WebApplication app)
-{
- // Lê a variável de ambiente (ou de IConfiguration)
- var applyMigrations = app.Configuration["APPLY_MIGRATIONS"]
- ?? Environment.GetEnvironmentVariable("APPLY_MIGRATIONS");
-
- if (!string.IsNullOrEmpty(applyMigrations) &&
- bool.TryParse(applyMigrations, out var shouldApply) && !shouldApply)
- {
- app.Logger.LogInformation("Automatic migrations disabled via APPLY_MIGRATIONS=false");
- return;
- }
-
- using var scope = app.Services.CreateScope();
- var context = scope.ServiceProvider.GetRequiredService();
- // Aplicar migrations normalmente
- context.Database.Migrate();
-}
-```
-
-**Status por Módulo**:
-- ✅ **Documents**: Implementado (Sprint 4 - 16 Dez 2025)
-- ⏳ **Users**: Pendente
-- ⏳ **Providers**: Pendente
-- ⏳ **ServiceCatalogs**: Pendente
-- ⏳ **Locations**: Pendente
-- ⏳ **SearchProviders**: Pendente
-
-**Esforço Estimado**: 15 minutos por módulo (copiar padrão do Documents)
-
-**Documentação**: Padrão documentado em `docs/database.md` seção "Controle de Migrations em Produção"
-
-**Prioridade**: MÉDIA - Implementar antes do primeiro deployment em produção
-
----
-
-## 📋 Sprint 5.5: Arquivos de Lock de Pacote e Atualizações de Dependência (19 Dez 2025)
-
-**Status**: 🔄 EM ANDAMENTO - Aguardando CI/CD
-**Duração**: 1 dia
-**Objetivo**: Resolver conflitos de arquivos de lock de pacote e atualizar dependências
-
-### Contexto
-
-Durante o processo de atualização automática de dependências pelo Dependabot, foram identificados conflitos nos arquivos `packages.lock.json` causados por incompatibilidade de versões do pacote `Microsoft.OpenApi`.
-
-**Problema Raiz**:
-- Arquivos de lock esperavam versão `[2.3.12, )`
-- Central Package Management especificava `[2.3.0, )`
-- Isso causava erros NU1004 em todos os projetos, impedindo compilação e testes
-
-### Ações Executadas
-
-#### ✅ Correções Implementadas
-
-1. **Branch feature/refactor-and-cleanup**
- - ✅ 37 arquivos `packages.lock.json` regenerados
- - ✅ Commit: "chore: regenerate package lock files to fix version conflicts"
- - ✅ Push para origin concluído
-
-2. **Branch master**
- - ✅ Merge de feature/refactor-and-cleanup → master
- - ✅ Push para origin/master concluído
- - ✅ Todos os arquivos de lock atualizados na branch principal
-
-3. **PR #81 - Atualização do Aspire 13.1.0**
- - Branch: `dependabot/nuget/aspire-f7089cdef2`
- - ✅ Arquivos de lock regenerados (37 arquivos)
- - ✅ Commit: "fix: regenerate package lock files after Aspire 13.1.0 update"
- - ✅ Force push concluído
- - ⏳ Aguardando CI/CD (Verificações de Qualidade de Código, Escaneamento de Segurança)
-
-4. **PR #82 - Atualização do FeatureManagement 4.4.0**
- - Branch: `dependabot/nuget/Microsoft.FeatureManagement.AspNetCore-4.4.0`
- - ✅ Arquivos de lock regenerados (36 arquivos)
- - ✅ Commit: "fix: regenerate package lock files after FeatureManagement update"
- - ✅ Push concluído
- - ⏳ Aguardando CI/CD (Verificações de Qualidade de Código, Escaneamento de Segurança)
-
-### Próximos Passos
-
-1. ✅ **Merge dos PRs #81 e #82** - Concluído (19 Dez 2025)
-2. ✅ **Atualizar branch de funcionalidade** - Merge master → feature/refactor-and-cleanup
-3. ✅ **Criar PR #83** - Branch feature/refactor-and-cleanup → master
-4. ⏳ **Aguardar revisão e merge do PR #83**
-5. 📋 **Iniciar Sprint 6** - Documentação do GitHub Pages (Q1 2026)
-6. 📋 **Planejar Sprint 7** - Blazor Admin Portal (Q1 2026)
-
-#### ✅ Atualizações de Documentação (19 Dez 2025)
-
-**Roadmap**:
-- ✅ Atualizada seção Sprint 5.5 com todas as ações executadas
-- ✅ Atualizado status de Fase 2 para "Em Planejamento - Q1 2026"
-- ✅ Atualizados Sprints 3-5 com dependências e novas timelines
-- ✅ Atualizada última modificação para 19 de Dezembro de 2025
-
-**Limpeza de Modelos**:
-- ✅ Removido `.github/pull-request-template-coverage.md` (modelo específico de outro PR)
-- ✅ Removida pasta `.github/issue-template/` (issues obsoletas: EFCore.NamingConventions, Npgsql já resolvidas)
-- ✅ Criado `.github/pull_request_template.md` (modelo genérico para futuros PRs)
-- ✅ Commit: "chore: remove obsolete templates and create proper PR template"
-
-**Pull Request #83**:
-- ✅ PR criado: feature/refactor-and-cleanup → master
-- ✅ Título: "feat: refactoring and cleanup sprint 5.5"
-- ✅ Descrição atualizada refletindo escopo real (documentação + merge dos PRs #81/#82 + limpeza de modelos)
-- ⏳ Aguardando revisão e validação do CI/CD
-
-### Lições Aprendidas
-
-- **Dependabot**: Regenerar arquivos de lock manualmente após atualizações de versões com conflitos
-- **CI/CD**: Validação rigorosa dos locks de pacote previne implantações quebradas
-- **Central Package Management**: Manter sincronização entre arquivos de lock e Directory.Packages.props
-- **Gestão de Modelos**: Manter apenas modelos genéricos e reutilizáveis em `.github/`
-- **Documentação Primeiro**: Documentar ações executadas imediatamente no roadmap para rastreabilidade
-
----
-
-### ✅ Sprint 8C - Provider Web App (React + NX) (19 Mar - 21 Mar 2026)
-- ✅ **Nx Integration**: `MeAjudaAi.Web.Provider` integrado ao workspace Nx
-- ✅ **Integração de Onboarding**:
- - `/onboarding/basic-info` conectado à API (`apiMeGet`/`apiMePut`)
- - `/onboarding/documents` conectado à API (upload via SAS URL para Azure Blob Storage)
-- ✅ **Painel com Dados Reais**: Página principal (`/`) substituída por dados reais via `apiMeGet`
-- ✅ **Perfil Público do Prestador**: Nova rota `/provider/[slug]` para perfis públicos com slugs amigáveis ao SEO
-- ✅ **Gestão do Perfil do Prestador**:
- - `/alterar-dados` - Edição completa via `apiMePut`
- - `/configuracoes` - Alternância de visibilidade + exclusão de conta com confirmação LGPD
-- ✅ **URLs amigáveis (Slug)**: Perfis públicos acessíveis via slugs (ex: `/provider/joao-silva-a1b2c3d4`)
-
-### ✅ Sprint 8D - Migração do Portal Administrativo (2 - 24 Mar 2026)
-
-**Status**: ✅ CONCLUÍDA (24 Mar 2026)
-**Foco**: Migração em fases do Blazor WASM para React.
-
-**Entregáveis**:
-- ✅ **Admin Portal React**: `src/Web/MeAjudaAi.Web.Admin/` funcional em React.
-- ✅ **CRUD de Prestadores**: Gestão completa de prestadores.
-- ✅ **Gestão de Documentos**: Envio e verificação de documentos.
-- ✅ **Catálogo de Serviços**: Gestão do catálogo de serviços.
-- ✅ **Cidades Permitidas**: Gestão de restrições geográficas.
-- ✅ **KPIs do Painel**: Painel administrativo com métricas.
-
-### ✅ Sprint 8E - Testes E2E e Infraestrutura de Teste React (23 Mar - 25 Mar 2026)
-
-**Status**: ✅ CONCLUÍDA (25 Mar 2026)
-**Foco**: Testes E2E (Playwright) + infraestrutura de testes unitários (Vitest + RTL + MSW) + Governança de Cobertura Global.
-
-**Escopo — E2E (Playwright)** ✅:
-1. ✅ **Configuração Playwright**: `playwright.config.ts` com 6 projetos (Chromium, Firefox, WebKit, Mobile, CI)
-2. ✅ **Customer E2E** (5 especificações): auth, onboarding, performance, profile, search
-3. ✅ **Provider E2E** (5 especificações): auth, dashboard, onboarding, performance, profile-mgmt
-4. ✅ **Admin E2E** (5 especificações): auth, configs, dashboard, mobile-responsiveness, providers
-5. ✅ **Fixtures Compartilhadas**: `src/Web/libs/e2e-support/base.ts` (loginAsAdmin, loginAsProvider, loginAsCustomer, logout)
-6. ✅ **Integração CI**: `master-ci-cd.yml` atualizado para gerar especificação OpenAPI e rodar E2E.
-
-**Escopo — Testes Unitários (Vitest + RTL)** ✅:
-7. ✅ **Infraestrutura**: `libs/test-support/` (test-utils.tsx, customRenderHook), limites individuais removidos em favor de Cobertura Global.
-8. ✅ **Cobertura Global**: Script `src/Web/scripts/merge-coverage.mjs` consolida relatórios de todos os projetos com limite de 70%.
-9. ✅ **Hardening Admin**: Testes unitários para `Sidebar`, `Button`, `Dashboard`, `Providers` e `Users`. Autenticação centralizada em `auth.ts`.
-10. ✅ **Hardening Customer**: `DashboardClient` (conformidade com DTO), `DocumentUpload` (asserções da API) e `SearchFilters` (validação de categoria da API).
-
-**Cenários de Teste E2E**:
-- [x] Autenticação (login, logout, refresh token)
-- [x] Fluxo de onboarding (Cliente e Prestador)
-- [x] CRUD de prestadores e serviços (Admin)
-- [x] Busca e filtros geolocalizados
-- [x] Responsividade mobile
-- [x] Desempenho e Core Web Vitals (INP, LCP, CLS)
-
-**Pendências para fechar Sprint**:
-- [x] Testes unitários Admin (hooks: providers, categories, dashboard, services, allowed-cities, users; components: sidebar, ui)
-- [x] Testes unitários Prestador (hooks; components: dashboard cards, profile)
-- [x] Configurar MSW handlers para Admin e Prestador
-
-### ✅ Sprint 9 - BUFFER & Mitigação de Risco (25 Mar - 11 Abr 2026)
-
-**Status**: ✅ ENCERRADA (Snapshot: encerrada em 11 Abr 2026)
-**Duração**: 12 dias de buffer (finalização do MVP)
-- Polimento, Refatoração e Correção.
-- Mover tarefas Opcionais da 8B.2 para cá se necessário.
-- Limite de taxa (Rate limiting) e segurança/monitoramento avançado.
-
-**Seguimentos Pendentes**:
-- [ ] **Gating de Diff OpenAPI**: Adicionar verificação de mudanças de quebra em CI (falhar PR se API mudar sem bump de versão)
-- [x] **Módulo de Comunicações**: ~~Implementar infraestrutura base (outbox pattern, modelos, handlers de evento)~~ ✅ Implementado (exceção BUFFER — infraestrutura entregue nesta sprint)
-
-## 🎯 MVP Final Launch: 12 - 16 de Maio de 2026 🎯
-
-### ⚠️ Avaliação e Mitigação de Risco
-
-#### Estratégia de Mitigação de Risco
-- **Contingência de Ramificação**: Se as tarefas principais (Migração Admin, Configuração NX) atrasarem, priorizaremos os fluxos essenciais do Jogador (Cliente/Prestador) e recairemos sobre as soluções Admin existentes.
-- **Aplicativos Móveis**: Retirados do escopo do MVP para a Fase 2 para garantir a estabilidade da plataforma web.
-- **Buffer**: A Sprint 9 é estritamente para estabilidade, sem novas funcionalidades (Exceção: infraestrutura do Módulo de Comunicações).
-- Documentação final para o MVP
-
-### Cenários de Risco Documentados
-
-### Cenário de Risco 1: Complexidade de Integração do Keycloak
-
-- **Problema Potencial**: Fluxos OIDC em Blazor WASM com tokens de atualização podem exigir configuração complexa
-- **Impacto**: +2-3 dias além do planejado na Sprint 6
-- **Mitigação Sprint 9**:
- - Usar a Sprint 9 para refinar os fluxos de autenticação
- - Implementar o tratamento adequado de atualização de token
- - Adicionar mecanismos de fallback
-
-### Cenário de Risco 2: Problemas de Desempenho do React
-
-- **Problema Potencial**: Tamanho do pacote do aplicativo > 5MB, carregamento lento não configurado corretamente
-- **Impacto**: Experiência do Usuário (UX) ruim, +2-3 dias de otimização
-- **Mitigação Sprint 9**:
- - Divisão de código com importações dinâmicas
- - Tree shaking e otimização de pacotes
- - SSR/SSG via Next.js para melhorar o carregamento inicial
- - Carregamento lento de componentes React
- - Otimizar imagens usando next/image e formatos responsivos
-
-### Cenário de Risco 3: Problemas Específicos da Plataforma MAUI Hybrid (REMOVIDO DO ESCOPO DO MVP)
-
-> **⚠️ IMPORTANTE**: Este cenário de risco foi removido do escopo do MVP. Os Aplicativos Móveis foram adiados para a Fase 2 conforme nota acima.
-
-- **Problema Potencial**: Diferenças de comportamento iOS vs Android (permissões, geolocalização, acesso a arquivos)
-- **Impacto**: +4-5 dias de depuração específica da plataforma
-- **Mitigação Sprint 9**:
- - Criar abstrações para APIs específicas da plataforma
- - Implementar fallbacks para funcionalidades não suportadas
- - Testes em dispositivos reais (não apenas emuladores)
-
-### Cenário de Risco 4: Casos de Borda da Integração de API
-
-- **Problema Potencial**: Casos de erro não cobertos (tempos limite, falhas de rede, atualizações simultâneas)
-- **Impacto**: +2-3 dias de endurecimento (hardening)
-- **Mitigação Sprint 9**:
- - Implementar políticas de nova tentativa com Polly
- - Adicionar tratamento de concorrência otimista
- - Melhorar as mensagens de erro e o feedback do usuário
-
-### Tarefas Sprint 9 (Executar conforme necessário)
-
-#### 1. Conclusão do Trabalho em Andamento
-- [ ] Completar funcionalidades parciais das Sprints 6-8
-- [ ] Resolver todos os TODOs/FIXMEs adicionados durante a implementação
-- [ ] Fechar issues abertas durante o desenvolvimento frontend
-
-#### 1.1. ✅ 🧪 SearchProviders Testes E2E (Concluído)
-- [x] Teste E2E: Buscar prestadores por serviço + raio (2km, 5km, 10km)
-- [x] Teste E2E: Validar ordenação por distância crescente
-- [x] Teste E2E: Validar restrição geográfica (AllowedCities) - prestadores fora da cidade não aparecem
-- [x] Teste E2E: Desempenho (<1500ms em ambiente de teste)
-- [x] Teste E2E: Cenário sem resultados (nenhum prestador no raio)
-- [x] Teste E2E: Validar paginação de resultados (10, 20, 50 itens por página)
-
-**Infraestrutura**:
-- Usar `TestcontainersFixture` com PostGIS 16-3.4
-- Semear banco de dados com prestadores em localizações conhecidas (lat/lon)
-- Usar `HttpClient` para chamar o ponto de extremidade `/api/search-providers/search`
-- Validar a resposta JSON com FluentAssertions
-
-**Critérios de Aceitação**:
-- ✅ 6 testes E2E passando com 100% de cobertura dos cenários
-- ✅ Desempenho validado
-- ✅ Documentação em `docs/testing/e2e-tests.md`
-- ✅ CI/CD executando testes E2E na pipeline
-
-#### 1.2. 🛠️ Excelência Técnica e Débito (Sprint 9)
-**Prioridade**: MÉDIA
-**Estimativa**: 3-4 dias
-
-**Objetivo**: Resolver pendências técnicas e melhorar a resiliência do sistema.
-
-**Entregáveis**:
-- [x] **Generalização do Outbox Pattern**: Mover estrutura base (entidade, repositório, worker base) para `MeAjudaAi.Shared` para reuso em futuros módulos (Payments, Bookings).
-- [ ] **Handlers de Evento**: Implementar handlers para comunicação entre SearchProviders e ServiceCatalogs.
-
-- [ ] **Login Social**: Reintegrar login com Instagram via Keycloak OIDC (Issue #141).
-- [ ] **Resiliência**: Aplicar `CancellationToken` nos Effects de `ServiceCatalogs`, `Documents` e `Locations`.
-- [ ] **Localização (Backend)**: Migrar strings de erro da API para `.resx` e integrar FluentValidation para suporte a multi-idioma via cabeçalhos.
-- [ ] **Localização (Frontend React)**: Implementar infraestrutura com `i18next` (JSON) nos apps Admin e Customer, localizando mensagens de validação do **Zod**.
-- [ ] **Testes Arquiteturais**: Implementar testes com `NetArchTest` no Módulo de Comunicações para garantir isolamento (evitar referências circulares e acesso direto a DBs externos).
-- [ ] **Identidade Visual da Interface**: Aplicar cores oficiais (Azul, Creme, Laranja) e padronizar o tema em todo o Portal Administrativo (React).
-- [ ] **Testes Unitários (Débito)**: Testes para descarte de `LocalizationSubscription` e despejo LRU do `PerformanceHelper`.
-
-#### 2. Melhorias de UX/UI
-- [ ] **Estados de Carregamento**: Skeletons em todas as cargas assíncronas
-- [ ] **Tratamento de Erros**: Mensagens amigáveis para todos os erros (não mostrar stack traces)
-
-#### 3. Endurecimento de Segurança e Desempenho
-- [ ] **Limite de Taxa da API**: Middleware Aspire (100 req/min por IP, 1000 req/min para usuários autenticados)
-- [ ] **CORS**: Configurar origens permitidas (apenas domínios de produção)
-- [ ] **Proteção CSRF**: Tokens anti-falsificação em formulários
-- [ ] **Cabeçalhos de Segurança**: HSTS, X-Frame-Options, CSP
-- [ ] **Otimização do Pacote**: Carregamento lento (lazy loading), compilação AOT, tree shaking
-- [ ] **Estratégia de Cache**: Implementar cache HTTP para ativos estáticos
-
-#### 4. Registro e Monitoramento
-- [ ] **Registro do Frontend**: Integração com Application Insights (React + Next.js)
-- [ ] **Rastreamento de Erros**: Sentry ou similar para erros em produção
-- [ ] **Análises (Analytics)**: Google Analytics ou Plausible para rastreamento de uso
-- [ ] **Monitoramento de Desempenho**: Rastreamento de Web Vitals (LCP, FID, CLS)
-
-#### 5. Documentação Final do MVP
-- [ ] **Documentação da API**: Swagger/OpenAPI atualizado com exemplos
-- [ ] **Guia do Usuário**: Guia de uso para o Portal Administrativo e o Aplicativo do Cliente
-- [ ] **Guia do Desenvolvedor**: Como rodar localmente, como contribuir
-- [ ] **Guia de Implantação**: Implantação nos Aplicativos de Contêiner do Azure (modelos ARM ou Bicep)
-- [ ] **Lições Aprendidas**: Documentar decisões de arquitetura e compensações (trade-offs)
-
-#### 6. Módulo de Comunicações (NOVO - Sprint 9)
-
-**Prioridade**: MÉDIA - Infraestrutura base para funcionalidades pós-MVP
-**Objetivo**: Criar módulo unificado de comunicações (e-mail, SMS, push)
-**Contexto**: Outros módulos (Avaliações, Pagamentos, Reservas) dependem de infraestrutura de comunicações. Implementar agora evita refatoração depois.
-
-**Estratégia de Testes (Mandatória)**:
-
-| Tipo | Escopo | Ferramentas |
-|------|--------|------------|
-| **Unitários** | Lógica de modelos, cálculo de tentativas (retries), mapeamento de DTOs | ✅ xUnit + FluentAssertions |
-| **Integrados** | Persistência do Outbox (PostgreSQL), Handlers de eventos | ✅ xUnit + Respawn + Docker |
-| **E2E** | Fluxo completo: Evento → Outbox → Envio simulado → Registro | ✅ Playwright + MSW |
-| **Arquiteturais** | Validar que módulos não acessam DB de outros, dependências de contratos | ✅ NetArchTest |
-
----
-
-**Decisão de Arquitetura** (diferente dos outros módulos):
-
-| Aspecto | Decisão |
-|---------|---------|
-| **Padrão** | Módulo Completo + Padrão Orquestrador |
-| **Infraestrutura** | Padrão Outbox para garantia de entrega (Mensageria Confiável) |
-| **Integração** | Baseada em eventos (consome IntegrationEvents) |
-| **API Externa** | Abstração via interface (provedor configurável) |
-| **Idempotência** | Garantida via CorrelationId/EventId nos Registros |
-
----
-
-**Arquitetura de Projetos**:
-```text
-src/
-├── Shared/
-│ └── MeAjudaAi.Contracts/Modules/Communications/
-│ ├── ICommunicationsModuleApi.cs
-│ ├── DTOs/
-│ │ ├── EmailMessageDto.cs
-│ │ ├── EmailTemplateDto.cs
-│ │ ├── PushMessageDto.cs
-│ │ ├── SmsMessageDto.cs
-│ │ └── CommunicationLogDto.cs
-│ ├── Channels/
-│ │ ├── IEmailChannel.cs
-│ │ ├── IPushChannel.cs
-│ │ └── ISmsChannel.cs
-│ └── Queries/
-│ └── CommunicationLogQuery.cs
-│
-├── Modules/
-│ └── Communications/
-│ ├── API/
-│ │ ├── MeAjudaAi.Modules.Communications.API.csproj
-│ │ └── Endpoints/
-│ │ └── CommunicationsModuleEndpoints.cs
-│ ├── Application/
-│ │ ├── MeAjudaAi.Modules.Communications.Application.csproj
-│ │ ├── ModuleApi/
-│ │ │ └── CommunicationsModuleApi.cs
-│ │ └── Services/
-│ │ └── Email/
-│ │ └── StubEmailService.cs # Provedor atual (Stubs)
-│ ├── Domain/
-│ │ ├── MeAjudaAi.Modules.Communications.Domain.csproj
-│ │ └── Entities/
-│ │ ├── OutboxMessage.cs (suporte a ScheduledAt)
-│ │ ├── EmailTemplate.cs (flag IsSystemTemplate)
-│ │ └── CommunicationLog.cs (suporte a CorrelationId)
-│ └── Infrastructure/
-│ ├── MeAjudaAi.Modules.Communications.Infrastructure.csproj
-│ └── Persistence/
-│ ├── Configurations/
-│ └── Migrations/
-│
-└── Shared/
- └── Communications/
- └── Templates/
- ├── WelcomeEmail.cshtml
- ├── ProviderVerificationApproved.cshtml
- └── ProviderVerificationRejected.cshtml
-```
-
-**Localização de Modelos**:
-- **Fonte de Verdade**: Arquivos `.cshtml` em `Shared/Communications/Templates/` (compilados).
-- **Sistema de Sobreposição (Override)**: A entidade `EmailTemplate` no banco permite sobrescrever o Assunto (`Subject`) ou trechos do Corpo (`Body`) via Portal Administrativo sem nova implantação.
-- **IsSystemTemplate**: Flag para proteger modelos críticos de deleção (Garantido via validação em `EmailTemplateRepository.DeleteAsync`).
-
----
-
-**Mapeamento de Integração com Eventos**:
-
-| Evento existente | Ação de Comunicação |
-|----------------|-------------------|
-| `UserRegisteredIntegrationEvent` | ✅ Enviar e-mail de boas-vindas |
-| `ProviderAwaitingVerificationIntegrationEvent` | ✅ Notificar administrador |
-| `ProviderVerificationStatusUpdatedIntegrationEvent` | ✅ Notificar prestador |
-| `DocumentVerifiedIntegrationEvent` | [ ] Notificar prestador |
-| `DocumentRejectedIntegrationEvent` | [ ] Notificar prestador |
-
----
-
-**Interface ICommunicationsModuleApi (Atualizada)**:
-> **Nota**: `ECommunicationPriority` é proveniente de `MeAjudaAi.Contracts.Shared` (não redeclare o enum — use o tipo compartilhado `ECommunicationPriority` diretamente).
-
-```csharp
-public interface ICommunicationsModuleApi : IModuleApi
-{
- // E-mail
- Task> SendEmailAsync(
- EmailMessageDto email,
- ECommunicationPriority priority = ECommunicationPriority.Normal,
- CancellationToken ct = default);
-
- Task>> GetTemplatesAsync(CancellationToken ct = default);
-
- // SMS
- Task> SendSmsAsync(
- SmsMessageDto sms,
- ECommunicationPriority priority = ECommunicationPriority.Normal,
- CancellationToken ct = default);
-
- // Push
- Task> SendPushAsync(
- PushMessageDto push,
- ECommunicationPriority priority = ECommunicationPriority.Normal,
- CancellationToken ct = default);
-
- // Registros (Verificação de idempotência via Identificador de Correlação)
- Task>> GetLogsAsync(
- CommunicationLogQuery query,
- CancellationToken ct = default);
-}
-```
-
----
-
-**Entidades de Domínio**:
-
-```csharp
-// OutboxMessage: Garante entrega e permite agendamento
-public class OutboxMessage
-{
- public Guid Id { get; }
- public string? CorrelationId { get; } // Idempotência
- public ECommunicationChannel Channel { get; }
- public string Payload { get; } // JSON serializado
- public EOutboxMessageStatus Status { get; } // Pending, Processing, Sent, Failed
- public int RetryCount { get; }
- public DateTime CreatedAt { get; }
- public DateTime? ScheduledAt { get; } // Agendamento futuro
- public DateTime? SentAt { get; }
- public string? ErrorMessage { get; }
-}
-
-// EmailTemplate: Modelos com sistema de sobreposição
-public class EmailTemplate
-{
- public Guid Id { get; }
- public string TemplateKey { get; } // "welcome", "verification-approved"
- public string? OverrideKey { get; } // Contexto opcional
- public string Subject { get; }
- public string HtmlBody { get; }
- public string TextBody { get; }
- public string Language { get; } // pt-br, en-us
- public bool IsSystemTemplate { get; } // Proteção contra exclusão
- public DateTime CreatedAt { get; }
-}
-
-// CommunicationLog: Trilha de auditoria + Idempotência
-public class CommunicationLog
-{
- public Guid Id { get; }
- public string CorrelationId { get; } // Idempotência (identificador único)
- public ECommunicationChannel Channel { get; }
- public string Recipient { get; }
- public string? TemplateKey { get; }
- public bool IsSuccess { get; }
- public DateTime CreatedAt { get; }
- public string? ErrorMessage { get; }
-}
-
----
-
-**Infraestrutura - Padrão Outbox**:
-
-Para garantir que as comunicações não sejam perdidas em caso de falha:
-
-1. **Processo com Outbox** (garantido):
- ```text
- Evento Ocorrido → Salvar na Tabela de Outbox (Mesma Transação) → Trabalhador de Background processa Outbox
- ```
-
-**Melhorias Implementadas**:
-- ✅ **Nova tentativa automática**: Com recuo exponencial via Polly.
-- ✅ **Priorização**: Mensagens de alta prioridade (ex: Redefinição de Senha) furam a fila.
-- ✅ **Idempotência**: Verificação de `CorrelationId` no Registro antes do processamento.
-- ✅ **Recuperação**: Mecanismo para resetar mensagens travadas no estado Processing.
-
----
-
-**Estimativa de Esforço**:
-
-| Tarefa | Esforço | Status |
-|------|--------|-----------|
-| 1. Criar estrutura de projetos | 2h | ✅ |
-| 2. Interfaces ICommunicationsModuleApi | 2h | ✅ |
-| 3. Implementar OutboxMessage (Agendamento) | 5h | ✅ |
-| 4. Implementar EmailTemplate (Sistema de sobreposição) | 3h | ✅ |
-| 5. Implementar CommunicationLog (CorrelationId) | 2h | ✅ |
-| 6. Implementar ModuleApi + Orquestrador | 6h | ✅ |
-| 7. Handlers de Canal de Stub (E-mail/Sms/Push) | 5h | ✅ |
-| 8. Integração com Eventos Existentes (3/5) | 4h | ✅ |
-| 9. Criar modelos básicos (.cshtml) | 3h | ✅ |
-| 10. Configuração de DI + Políticas Polly | 3h | ✅ |
-| **Total** | **~35h (~5 dias)** | ✅ |
-
----
-
-**Critérios de Aceitação**:
-- ✅ Módulo registrado no ModuleApiRegistry
-- ✅ Envio garantido via Padrão Outbox
-- ✅ Suporte ao agendamento de mensagens
-- ✅ Sistema de modelos híbrido funcional
-- ✅ Registros com CorrelationId para evitar duplicidade
-- ✅ Integração com mais de 3 IntegrationEvents
-- ✅ Priorização de mensagens funcional
-
----
-
-**Seguimentos Pendentes**:
-- [ ] Definir provedores reais (SendGrid, Twilio, Firebase) → **Veja Débito Técnico em docs/technical-debt.md**
-- [ ] Interface administrativa para gestão dinâmica de modelos
-- [ ] Suporte a anexos via URLs do Armazenamento de Blobs
-- [ ] Painel de métricas de entrega e conversão
-
----
-
-**Resultado Esperado Sprint 9**:
-- ✅ MVP pronto para produção e polido
-- ✅ Todos os cenários de risco mitigados ou resolvidos
-- ✅ Segurança e desempenho endurecidos
-- ✅ Documentação completa para usuários e desenvolvedores
-- ✅ Monitoramento e observabilidade configurados
-- ✅ Módulo de Comunicações implementado (infraestrutura base resiliente)
-- 🎯 **PRONTO PARA LANÇAMENTO EM 12-16 DE MAIO DE 2026**
-
-> **⚠️ CRITICAL**: Se a Sprint 9 não for suficiente para completar todos os itens, considerar atraso no lançamento do MVP ou reduzir o escopo (mover recursos não críticos para o pós-MVP). A qualidade e a estabilidade do MVP são mais importantes que a data de lançamento.
-
----
-
-## 🎯 Fase 3: Qualidade e Monetização
-
-### Objetivo
-Introduzir sistema de avaliações para ranking, modelo de assinaturas premium via Stripe, e verificação automatizada de documentos.
-
-### 3.1. 🌟 Módulo Reviews & Ratings (Planejado)
-
-**Objetivo**: Permitir que clientes avaliem prestadores, influenciando o ranking de busca.
-
-#### **Arquitetura Proposta**
-- **Padrão**: Arquitetura em camadas simples
-- **Agregação**: Cálculo de `AverageRating` via eventos de integração (não em tempo real)
-
-#### **Entidades de Domínio**
-```csharp
-// Review: Raiz de Agregado
-public class Review
-{
- public Guid ReviewId { get; }
- public Guid ProviderId { get; }
- public Guid CustomerId { get; }
- public int Rating { get; } // 1-5
- public string? Comment { get; }
- public DateTime CreatedAt { get; }
- public bool IsFlagged { get; } // Para moderação
-}
-
-// ProviderRating: Agregado (ou parte do modelo de leitura)
-public class ProviderRating
-{
- public Guid ProviderId { get; }
- public decimal AverageRating { get; }
- public int TotalReviews { get; }
- public DateTime LastUpdated { get; }
-}
-```
-
-#### **API Pública (IReviewsModuleApi)**
-```csharp
-public interface IReviewsModuleApi : IModuleApi
-{
- Task SubmitReviewAsync(SubmitReviewRequest request, CancellationToken ct = default);
- Task>> GetReviewsForProviderAsync(
- Guid providerId,
- int page,
- int pageSize,
- CancellationToken ct = default);
- Task FlagReviewAsync(Guid reviewId, string reason, CancellationToken ct = default);
-}
-```
-
-#### **Implementação**
-1. **Esquema**: Criar `meajudaai_reviews` com `reviews`, `provider_ratings`
-2. **Ponto de Extremidade de Envio**: Validar que o cliente pode avaliar (serviço contratado?)
-3. **Cálculo de Classificação**: Publicar `ReviewAddedIntegrationEvent` → Módulo de busca atualiza `AverageRating`
-4. **Moderação**: Sistema de sinalização para avaliações inapropriadas
-5. **Testes**: Testes unitários para cálculo de média + testes de integração para envio
-
----
-
-### 3.2. 💳 Módulo Payments & Billing (Planejado)
-
-**Objetivo**: Gerenciar assinaturas de prestadores via Stripe (Free, Standard, Gold, Platinum).
-
-#### **Arquitetura Proposta**
-- **Padrão**: Camada Anti-Corrupção (ACL) sobre a API do Stripe
-- **Isolamento**: Lógica de domínio protegida de mudanças no Stripe
-
-#### **Entidades de Domínio**
-```csharp
-// Subscription: Raiz de Agregado
-public class Subscription
-{
- public Guid SubscriptionId { get; }
- public Guid ProviderId { get; }
- public string StripeSubscriptionId { get; }
- public ESubscriptionPlan Plan { get; } // Free, Standard, Gold, Platinum
- public ESubscriptionStatus Status { get; } // Active, Canceled, PastDue
- public DateTime StartDate { get; }
- public DateTime? EndDate { get; }
-}
-
-// BillingAttempt: Entidade
-public class BillingAttempt
-{
- public Guid AttemptId { get; }
- public Guid SubscriptionId { get; }
- public decimal Amount { get; }
- public bool IsSuccessful { get; }
- public DateTime AttemptedAt { get; }
-}
-```
-
-#### **API Pública (IBillingModuleApi)**
-```csharp
-public interface IBillingModuleApi : IModuleApi
-{
- Task> CreateCheckoutSessionAsync(
- CreateCheckoutRequest request,
- CancellationToken ct = default);
- Task> GetSubscriptionForProviderAsync(
- Guid providerId,
- CancellationToken ct = default);
-}
-```
-
-#### **Implementação**
-1. **Configuração do Stripe**: Configurar produtos e planos de preços no painel
-2. **Ponto de Extremidade de Webhook**: Receber eventos do Stripe (`checkout.session.completed`, `invoice.payment_succeeded`, `customer.subscription.deleted`)
-3. **Handlers de Evento**: Atualizar o status da `Subscription` baseado em eventos
-4. **Sessão de Checkout**: Gerar URL de checkout para o frontend
-5. **Eventos de Integração**: Publicar `SubscriptionTierChangedIntegrationEvent` → Módulo de busca atualiza o ranking
-6. **Testes**: Testes de integração com eventos simulados da biblioteca de testes do Stripe
-
----
-
-### 3.3. 🤖 Documents - Verificação Automatizada (Planejado - Fase 2)
-
-**Objetivo**: Automatizar a verificação de documentos via OCR e APIs governamentais.
-
-**Funcionalidades Planejadas**:
-- **OCR Inteligente**: Azure AI Vision para extrair texto de documentos
-- **Validação de Dados**: Verificação cruzada com dados fornecidos pelo prestador
-- **Verificações de Antecedentes**: Integração com APIs de antecedentes criminais
-- **Pontuação Automática**: Sistema de pontuação baseado na qualidade dos documentos
-
-**Trabalhos em Background**:
-1. **DocumentUploadedHandler**: Aciona o processamento de OCR
-2. **OcrCompletedHandler**: Valida os campos extraídos
-3. **VerificationScheduler**: Agenda verificações periódicas
-
-**Nota**: A infraestrutura básica já existe (campo OcrData, estados de verificação), falta implementar trabalhadores e integrações.
-
----
-
-### 3.4. 🏷️ Dynamic Service Tags (Planejado - Fase 3)
-
-**Objetivo**: Exibir tags de serviços baseadas na popularidade real por região.
-
-**Funcionalidades**:
-- **Ponto de extremidade**: `GET /services/top-region?city=SP` (ou lat/lon)
-- **Lógica**: Calcular serviços com maior volume de buscas/contratações na região do usuário.
-- **Recuo (Fallback)**: Exibir "Top Globais" se os dados regionais forem insuficientes.
-- **Cache**: TTL curto (ex: 1h) para manter a relevância sem comprometer o desempenho.
-
----
-
-## 🚀 Fase 4: Experiência e Engajamento (Pós-MVP)
-
-### Objetivo
-Melhorar a experiência do usuário com agendamentos, comunicações centralizadas e análises avançadas.
-
-### 4.1. 📅 Módulo Service Requests & Booking (Planejado)
-
-**Objetivo**: Permitir que clientes solicitem serviços e agendem horários com prestadores.
-
-#### **Funcionalidades**
-- **Solicitação de Serviço**: Cliente descreve necessidade e localização
-- **Correspondência (Matching)**: O sistema sugere prestadores compatíveis
-- **Agendamento**: Calendário integrado com disponibilidade do prestador
-- **Notificações**: Lembretes automáticos via módulo de Comunicações
-
----
-
-### 4.2. 📊 Módulo Analytics & Reporting (Planejado)
-
-**Objetivo**: Capturar, processar e visualizar dados de negócio e operacionais.
-
-#### **Arquitetura Proposta**
-- **Padrão**: CQRS + Event Sourcing (para auditoria)
-- **Métricas**: Fachada sobre OpenTelemetry/Aspire
-- **Auditoria**: Log de eventos imutável de todas as atividades
-- **Relatórios**: Modelos de leitura desnormalizados para consultas rápidas
-
-#### **API Pública (IAnalyticsModuleApi)**
-```csharp
-public interface IAnalyticsModuleApi : IModuleApi
-{
- Task> GetReportAsync(ReportQuery query, CancellationToken ct = default);
- Task>> GetAuditHistoryAsync(
- AuditLogQuery query,
- CancellationToken ct = default);
-}
-```
-
-#### **Exibições de Banco de Dados**
-```sql
--- vw_provider_summary: Visão holística de cada prestador
-CREATE VIEW meajudaai_analytics.vw_provider_summary AS
-SELECT
- p.provider_id,
- p.name,
- p.status,
- p.join_date,
- s.subscription_tier,
- pr.average_rating,
- pr.total_reviews
-FROM providers.providers p
-LEFT JOIN meajudaai_billing.subscriptions s ON p.provider_id = s.provider_id
-LEFT JOIN meajudaai_reviews.provider_ratings pr ON p.provider_id = pr.provider_id;
-
--- vw_financial_transactions: Consolidação de eventos financeiros
-CREATE VIEW meajudaai_analytics.vw_financial_transactions AS
-SELECT
- ba.attempt_id AS transaction_id,
- s.provider_id,
- ba.amount,
- s.plan,
- ba.is_successful AS status,
- ba.attempted_at AS transaction_date
-FROM meajudaai_billing.billing_attempts ba
-JOIN meajudaai_billing.subscriptions s ON ba.subscription_id = s.subscription_id;
-
--- vw_audit_log_enriched: Log de auditoria legível
-CREATE VIEW meajudaai_analytics.vw_audit_log_enriched AS
-SELECT
- al.log_id,
- al.timestamp,
- al.event_name,
- al.actor_id,
- COALESCE(u.full_name, p.name) AS actor_name,
- al.entity_id,
- al.details_json
-FROM meajudaai_analytics.audit_log al
-LEFT JOIN users.users u ON al.actor_id = u.user_id
-LEFT JOIN providers.providers p ON al.actor_id = p.provider_id;
-```
-
-#### **Implementação**
-1. **Esquema**: Criar `meajudaai_analytics` com `audit_log`, tabelas de relatórios
-2. **Handlers de Evento**: Consumir todos os eventos de integração relevantes
-3. **Integração de Métricas**: Expor métricas personalizadas via OpenTelemetry
-4. **API de Relatórios**: Pontos de extremidade otimizados para leitura de relatórios
-5. **Painéis**: Integração com Aspire Dashboard e Grafana
-6. **Testes**: Testes de integração para handlers de evento + testes de desempenho para relatórios
-
----
-
-## 🎯 Funcionalidades Adicionais Recomendadas (Fase 4+)
-
-### 🛡️ Admin Portal - Módulos Avançados
-**Funcionalidades Adicionais (Pós-MVP)**:
-- **Widget de Painel de Atividades Recentes**: Feed cronológico de atividades (registros, envios, verificações, mudanças de status) com atualizações em tempo real via SignalR
-- **Análises de Usuário e Prestador**: Painéis avançados com Grafana
-- **Detecção de Fraude**: Sistema de pontuação para detectar perfis suspeitos
-- **Operações em Lote**: Ações em lote (ex: aprovar múltiplos documentos)
-- **Trilha de Auditoria**: Histórico completo de todas as ações administrativas
-
-#### 📊 Widget de Atividade Recente (Prioridade: MÉDIA)
-
-**Contexto**: Atualmente, o Painel exibe apenas gráficos estáticos. Um feed de atividades recentes melhoraria a visibilidade operacional.
-
-**Funcionalidades Core**:
-- **Linha do Tempo de Eventos**: Feed cronológico de atividades do sistema
-- **Tipos de Eventos**:
- - Novos registros de prestadores
- - Envios de documentos
- - Mudanças de status de verificação
- - Ações administrativas (aprovações/rejeições)
- - Adições/remoções de serviços
-- **Filtros**: Por tipo de evento, módulo, data
-- **Atualizações em Tempo Real**: SignalR para atualização automática
-- **Paginação**: Carregar mais atividades sob demanda
-
-**Implementação Técnica**:
-```csharp
-// Eventos de Domínio → Eventos de Integração → SignalR Hub
-public record ProviderRegisteredEvent(Guid ProviderId, string Name, DateTime Timestamp);
-public record DocumentUploadedEvent(Guid DocumentId, string Type, DateTime Timestamp);
-public record VerificationStatusChangedEvent(Guid ProviderId, string OldStatus, string NewStatus);
-
-// SignalR Hub
-public class ActivityHub : Hub
-{
- public async Task BroadcastActivity(ActivityDto activity)
- {
- await Clients.All.SendAsync("ReceiveActivity", activity);
- }
-}
-```
-
-```typescript
-// Componente Frontend (React)
-import { useEffect, useState } from 'react';
-import * as signalR from '@microsoft/signalr';
-
-export function ActivityTimeline() {
- const [activities, setActivities] = useState([]);
-
- useEffect(() => {
- const connection = new signalR.HubConnectionBuilder()
- .withUrl("/hubs/activity")
- .withAutomaticReconnect()
- .build();
-
- connection.on("ReceiveActivity", (activity: ActivityDto) => {
- setActivities(prev => [activity, ...prev]);
- });
-
- connection.start();
- return () => { connection.stop(); };
- }, []);
-
- return (
-
- {activities.map(activity => (
-
- {activity.description}
- {formatRelative(activity.timestamp)}
-
- ))}
-
- );
-}
-```
-
-**Estimativa**: 3-5 dias (1 dia para eventos de backend, 1 dia para SignalR, 2-3 dias para frontend)
-
-**Dependências**:
-- SignalR configurado no backend
-- Barramento de eventos consumindo eventos de domínio
-- Contrato ActivityDto definido
-
----
-
-### 👤 Gestão de Perfil do Cliente (Alta Prioridade)
-**Por quê**: O plano atual é muito focado em prestadores; os clientes também precisam de gestão de perfil.
-
-**Funcionalidades Core**:
-- Editar informações básicas (nome, foto)
-- Ver histórico de prestadores contatados
-- Gerenciar avaliações escritas
-- Preferências de notificações
-
-**Implementação**: Melhoria (Enhancement) no módulo Users existente
-
----
-
-### ⚖️ Sistema de Resolução de Disputas (Média Prioridade)
-**Por quê**: Mesmo sem pagamentos no aplicativo, podem ocorrer disputas (avaliações injustas, má conduta).
-
-**Funcionalidades Core**:
-- Botão "Reportar" em perfis de prestadores e avaliações
-- Formulário para descrever o problema
-- Fila no Portal Administrativo para moderadores
-
-**Implementação**: Novo módulo pequeno ou extensão do módulo de Avaliações
-
----
-
-## 📊 Métricas de Sucesso
-
-### 📈 Métricas de Produto
-- **Crescimento de usuários**: 20% ao mês
-- **Retenção de prestadores**: 85%
-- **Satisfação média**: 4.5+ estrelas
-- **Taxa de conversão (Grátis → Pago)**: 15%
-
-### ⚡ Métricas Técnicas (SLOs)
-
-#### **Metas de Desempenho em Camadas**
-
-| Categoria | Tempo Alvo | Exemplo |
-|-----------|------------|---------|
-| **Consultas Simples** | <200ms | Busca por ID, dados em cache |
-| **Consultas Médias** | <500ms | Listagens com filtros básicos |
-| **Consultas Complexas** | <1000ms | Busca entre módulos, agregações |
-| **Consultas Analíticas** | <3000ms | Relatórios, painéis |
-
-#### **Linha de Base de Desempenho**
-- **Assumindo**: Cache distribuído configurado, índices otimizados
-- **Revisão Trimestral**: Ajustes baseados em métricas reais
- - **Percentis monitorados**: P50, P95, P99 (latência de consultas)
- - **Frequência**: Análise e ajuste a cada 3 meses
- - **Processo**: Ciclo de feedback → identificar outliers → otimizar consultas lentas
-- **Monitoramento**: OpenTelemetry + Aspire Dashboard + Application Insights
-
-#### **Outros SLOs**
-- **Disponibilidade**: 99.9% de tempo de atividade (uptime)
-- **Segurança**: Zero vulnerabilidades críticas
-- **Cobertura de Testes**: >80% para código crítico
-
----
-
-## 🔄 Processo de Gestão do Roadmap
-
-### 📅 Revisão Trimestral
-- Avaliação de progresso em relação aos marcos (milestones)
-- Ajuste de prioridades baseado em métricas
-- Análise de feedback de usuários e prestadores
-
-### 💬 Feedback Contínuo
-- **Contribuição da comunidade**: Pesquisas, suporte, análises
-- **Feedback de prestadores**: Portal dedicado para sugestões
-- **Necessidades de negócio**: Alinhamento com as partes interessadas (stakeholders)
-
-### 🎯 Critérios de Priorização
-1. **Impacto no MVP**: A funcionalidade é crítica para o lançamento?
-2. **Esforço de Implementação**: Complexidade técnica e tempo estimado
-3. **Dependências**: Quais módulos dependem desta funcionalidade?
-4. **Valor para o Usuário**: Feedback qualitativo e quantitativo
-
----
-
-## 📋 Sumário Executivo de Prioridades
-
-### ✅ **Concluído (Set-Dez 2025)**
-1. ✅ Sprint 0: Migração .NET 10 + Aspire 13 (21 Nov 2025 - MERGE para master)
-2. ✅ Sprint 1: Restrição Geográfica + Integração de Módulos (2 Dez 2025 - MERGE para master)
-3. ✅ Sprint 2: Cobertura de Testes de 90.56% (10 Dez 2025) - Meta de 35% SUPERADA em 55.56pp!
-4. ✅ Sprint 5.5: Correção de Arquivos de Lock de Pacote (19 Dez 2025)
- - Correção de conflitos Microsoft.OpenApi (2.3.12 → 2.3.0)
- - 37 arquivos packages.lock.json regenerados
- - PRs #81 e #82 atualizados e aguardando merge
-5. ✅ Módulo Users (Concluído)
-6. ✅ Módulo Providers (Concluído)
-7. ✅ Módulo Documents (Concluído)
-8. ✅ Módulo Search & Discovery (Concluído)
-9. ✅ Módulo Locations - Busca de CEP e geocodificação (Concluído)
-10. ✅ Módulo ServiceCatalogs - Catálogo gerenciado por admin (Concluído)
-11. ✅ CI/CD - Fluxos de trabalho do GitHub Actions (.NET 10 + Aspire 13)
-12. ✅ Ramo (branch) feature/refactor-and-cleanup - Mesclado para master (19 Dez 2025)
-
-### 📅 Alta Prioridade (Próximos 3 meses - Q1-Q2 2026)
-1. ✅ **Sprint 8B.2: NX Monorepo e Excelência Técnica** (Concluída)
-2. ✅ **Sprint 8C: App Web do Prestador (React + NX)** (Concluída - 21 Mar 2026)
-3. ✅ **Sprint 8D: Migração do Portal Administrativo** (Concluída - 24 Mar 2026)
-4. ✅ **Sprint 8E: Testes E2E Apps React (Playwright)** (Concluída - 25 Mar 2026)
-5. ⏳ **Sprint 9: BUFFER E MITIGAÇÃO DE RISCO** (25 Mar - 11 Abr 2026)
-6. 🎯 **MVP Final Launch: 12 - 16 de Maio de 2026**
-7. 📋 Coleções de API - Arquivos .bru do Bruno para todos os módulos
-
-### 🎯 **Alta Prioridade - Pré-MVP**
-1. 🎯 Communications - Notificações por e-mail
-2. 💳 Módulo Payments & Billing (Stripe) - Preparação para monetização
-
-### 🎯 **Média Prioridade (6-12 meses - Fase 2)**
-1. 🎉 Módulo Reviews & Ratings
-2. 🌍 Documents - Verificação automatizada (OCR + Verificações de antecedentes)
-3. 🔄 Search - Trabalhador de indexação para eventos de integração (extensão do módulo SearchProviders)
-4. 📊 Analytics - Métricas básicas
-5. 🏛️ Sistema de Resolução de Disputas
-6. 🔥 Alinhamento de middleware entre UseSharedServices() e UseSharedServicesAsync()
-
-### 🔬 **Testes E2E Frontend (Pós-MVP)**
-**Projeto**: `src/Web` (dividido por projeto)
-**Estrutura**: Uma pasta para cada projeto frontend
-- `src/Web/MeAjudaAi.Web.Customer/e2e/` - Testes E2E para App Web do Cliente
-- `src/Web/MeAjudaAi.Web.Provider/e2e/` - Testes E2E para App Web do Prestador
-- `src/Web/MeAjudaAi.Web.Admin/e2e/` - Testes E2E para Portal Administrativo
-
-**Framework**: Playwright
-**Cenários a cobrir**:
-- [ ] Autenticação (login, logout, refresh token)
-- [ ] Fluxo de onboarding (Cliente e Prestador)
-- [ ] CRUD de prestadores e serviços
-- [ ] Busca e filtros
-- [ ] Responsividade mobile
-- [ ] Desempenho e Core Web Vitals
diff --git a/docs/roadmap-future.md b/docs/roadmap-future.md
deleted file mode 100644
index 74b7e4424..000000000
--- a/docs/roadmap-future.md
+++ /dev/null
@@ -1,66 +0,0 @@
-### 🔍 **Baixa Prioridade (12+ meses - Fase 3)**
-1. 📅 Service Requests & Booking
-2. 📱 Mobile Apps (iOS/Android nativo)
-3. 🧠 Recomendações com ML
-4. 🎮 Gamificação avançada
-5. 💬 Chat interno
-6. 🌐 Internacionalização (i18n)
-7. 🧪 Evolução da Infraestrutura de Testes
-
----
-
-## 📚 Referências e Recursos
-
-### 📖 Documentação Relacionada
-- **Arquitetura**: [`docs/architecture.md`](./architecture.md) - Princípios e padrões arquiteturais
-- **Desenvolvimento**: [`docs/development.md`](./development.md) - Guia de setup e workflow
-- **Autenticação**: [`docs/authentication-and-authorization.md`](./authentication-and-authorization.md) - Keycloak e OIDC
-- **CI/CD**: [`docs/ci-cd.md`](./ci-cd.md) - Pipeline e deployment
-
-### 🔥 Ferramentas e Tecnologias
-- **.NET 10.0** - Runtime principal (migrado de .NET 9.0)
-- **PostgreSQL + PostGIS** - Database com suporte geoespacial
-- **Keycloak** - Identity & Access Management
-- **Stripe** - Payment processing
-- **Azure Blob Storage** - Document storage
-- **OpenTelemetry + Aspire** - Observability
-
-### 🌐 APIs Externas
-- **IBGE Localidades API** - Validação oficial de municípios brasileiros
- - Base URL: `https://servicodados.ibge.gov.br/api/v1/localidades/`
- - Documentação:
- - Uso: Validação geográfica para restrição de cidades piloto
-- **Nominatim (OpenStreetMap)** - Geocoding (planejado para Sprint 4 - optional improvement)
- - Base URL: `https://nominatim.openstreetmap.org/`
- - Documentação:
- - Uso: Geocoding (lat/lon lookup) para cidades/endereços
- - **Note**: Post-MVP feature, não é blocker para geographic-restriction inicial
-- **ViaCep API** - Lookup de CEP brasileiro
- - Base URL: `https://viacep.com.br/ws/`
- - Documentação:
-- **BrasilApi CEP** - Lookup de CEP (fallback)
- - Base URL: `https://brasilapi.com.br/api/cep/v1/`
- - Documentação:
-- **OpenCep API** - Lookup de CEP (fallback)
- - Base URL: `https://opencep.com/v1/`
- - Documentação:
-
----
-
-## 🧪 Evolução da Infraestrutura de Testes
-
-O planejamento de qualidade prevê a evolução contínua da pirâmide de testes:
-
-### Fase 3.1: Cobertura e Contratos (Curto/Médio Prazo)
-- **Meta de Cobertura**: Manter >70% de cobertura global no MVP e atingir >80% na Fase 3.
-- **Testes de Contrato (Consumer-Driven)**: Implementação de Pact.io para garantir que o frontend Next.js é compatível com a API .NET sem depender de ambiente real.
-
-### Fase 3.2: E2E Full-Stack & BDD (Médio Prazo)
-- **Orquestração com Aspire**: Migração dos testes E2E para rodarem contra instâncias locais orquestradas pelo .NET Aspire (subindo PostgreSQL + PostGIS, API e Redis em containers temporários durante a suíte).
-- **BDD (Behavior Driven Development)**: Adoção de Gherkin (Cucumber ou Playwright-BDD) para os fluxos críticos de negócio (ex: ciclo de vida do serviço).
-
----
-
-*📅 Última atualização: 25 de Março de 2026 (Pós-Sprint 8E)*
-*🔄 Roadmap em constante evolução baseado em feedback, métricas e aprendizados*
-*📊 Status atual: Sprint 9 🔄 EM ANDAMENTO | MVP Launch em 12-16 de Maio de 2026*
diff --git a/docs/roadmap-history.md b/docs/roadmap-history.md
index 42a0e1892..04ee93354 100644
--- a/docs/roadmap-history.md
+++ b/docs/roadmap-history.md
@@ -1,41 +1,64 @@
-# 🗺️ Roadmap - MeAjudaAi
+# 📜 Histórico do Roadmap - MeAjudaAi
-Este documento consolida o planejamento estratégico e tático da plataforma MeAjudaAi, definindo fases de implementação, módulos prioritários e funcionalidades futuras.
+Este documento contém o registro de todas as sprints concluídas para fins de auditoria e contexto histórico.
---
-## 📊 Sumário Executivo
+## ✅ Sprint 9 - BUFFER & Mitigação de Risco (Concluída em 11 Abr 2026)
-**Projeto**: MeAjudaAi - Plataforma de Conexão entre Clientes e Prestadores de Serviços
+**Foco**: Estabilização, Refatoração e Módulo de Comunicações (Infra).
+
+### Entregas:
+- ✅ **Módulo de Comunicações**: Infraestrutura base com Outbox Pattern, Handlers de evento e Stubs.
+- ✅ **Resiliência**: Aplicação de `CancellationToken` em repositórios e handlers (ServiceCatalogs, Documents, Locations).
+- ✅ **Localização (Backend)**: Migração de strings de erro para arquivos `.resx` em `Shared`.
+- ✅ **Segurança**: Endurecimento de middleware (Rate limiting, CORS, Antiforgery, Security Headers).
+- ✅ **Outbox Processor**: Recuperação automática de mensagens travadas e tratamento de cancelamento.
+- ✅ **Testes**: 100% de aprovação em Unitários (207), Integração (342) e E2E (161).
+
+---
+
+## ✅ Sprint 8E - Testes E2E e Infraestrutura React (Concluída em 25 Mar 2026)
+*(Conteúdo anterior preservado...)*
+
+# 🗺️ Roadmap - MeAjudaAi (Histórico Original)
+
+Este documento consolida o planejamento estratégico e tático da plataforma MeAjudaAi, definindo fases de implementação, módulos prioritários e funcionalidades futuras.
+
+---
+
+## 📊 Sumário Executivo
+
+**Projeto**: MeAjudaAi - Plataforma de Conexão entre Clientes e Prestadores de Serviços
**Status Geral**: Consulte a [Tabela de Sprints](#cronograma-de-sprints) para o status detalhado atualizado.
**Cobertura de Testes**: Backend 90.56% | Frontend 30 testes bUnit
**Stack**: .NET 10 LTS + Aspire 13 + PostgreSQL + NX Monorepo + React 19 + Next.js 15 (Customer, Provider, Admin) + Tailwind v4
### Marcos Principais
-Consulte a seção [Cronograma de Sprints](#cronograma-de-sprints) abaixo para o status detalhado e atualizado de cada sprint, e datas alvo (incluindo o MVP Launch).
+Consulte a seção [Cronograma de Sprints](#cronograma-de-sprints) abaixo para o status detalhado e atualizado de cada sprint, e datas alvo (incluindo o MVP Launch).
-**Procedimento de Revisão de Sprints**
-As futuras atualizações da tabela de sprints devem observar a política: análise commit-by-commit newest-first, apresentando um veredicto conciso e resolvendo os follow-ups.
+**Procedimento de Revisão de Sprints**
+As futuras atualizações da tabela de sprints devem observar a política: análise commit-by-commit newest-first, apresentando um veredicto conciso e resolvendo os follow-ups.
-## ⚠️ Notas de Risco
+## ⚠️ Notas de Risco
-- Estimativas assumem velocidade consistente e ausência de bloqueios maiores
-- Primeiro projeto Blazor WASM pode revelar complexidade não prevista
-- Sprint 9 reservado como buffer de contingência (não para novas features)
+- Estimativas assumem velocidade consistente e ausência de bloqueios maiores
+- Primeiro projeto Blazor WASM pode revelar complexidade não prevista
+- Sprint 9 reservado como buffer de contingência (não para novas features)
-## 🏗️ Decisões Arquiteturais Futuras
+## 🏗️ Decisões Arquiteturais Futuras
### NX Monorepo (Frontend)
-**Status**: ✅ Incluído no Sprint 8B.2
+**Status**: ✅ Incluído no Sprint 8B.2
**Branch**: `feature/sprint-8b2-technical-excellence`
-**Motivação**: Com Customer Web App (Next.js), Provider App (próximo sprint), Admin Portal (migração planejada) e Mobile (React Native + Expo), o compartilhamento de código (componentes, hooks, tipos TypeScript, schemas Zod) entre os projetos se torna crítico. NX oferece:
+**Motivação**: Com Customer Web App (Next.js), Provider App (próximo sprint), Admin Portal (migração planejada) e Mobile (React Native + Expo), o compartilhamento de código (componentes, hooks, tipos TypeScript, schemas Zod) entre os projetos se torna crítico. NX oferece:
- Workspace unificado com `libs/` compartilhadas
-- Build cache inteligente (s├│ reconstr├│i o que mudou)
+- Build cache inteligente (só reconstrói o que mudou)
- Dependency graph entre projetos
-- Geração de código consistente
+- Geração de código consistente
**Escopo (Sprint 8B.2)**:
- Migrar `MeAjudaAi.Web.Customer` para workspace NX
@@ -44,3860 +67,6 @@ As futuras atualizações da tabela de sprints devem observar a polít
- Atualizar `.NET Aspire AppHost` para apontar para nova estrutura
- Atualizar CI/CD para usar `nx affected`
-**Decisão de antecipação**: NX foi antecipado do pós-MVP para o Sprint 8B.2 porque o Provider App (Sprint 8C) e a migração Admin (Sprint 8D) se beneficiam diretamente do workspace unificado. Criar o NX antes desses projetos evita migração posterior mais custosa.
-
----
-
-### Migração Admin Portal: Blazor WASM → React
-
-**Status**: ⏳ Planejado — Sprint 8D (após Provider App)
-
-**Análise (Atualizada Março 2026)**:
-
-| Fator | Manter Blazor | Migrar para React |
-|-------|--------------|-------------------|
-| Custo | ✅ Zero | ❌ Alto (reescrever ~5000+ linhas) |
-| Compartilhamento C# DTOs | ✅ Nativo | ⚠️ Requer API client gerado (libs/api-client via NX) |
-| Uso interno (não SEO) | ✅ Blazor adequado | ✅ React com NX compartilha componentes |
-| Unificação de stack | ❌ Dual-stack (Blazor + React) | ✅ Single-stack React (3 apps no NX) |
-| Hiring | ⚠️ Blazor nicho | ✅ React mais fácil |
-| Shared Components | ❌ Isolado do NX | ✅ Reutiliza libs/ui, libs/auth do NX |
-
-**Decisão Revisada (Março 2026)**: **Migrar para React** dentro do workspace NX. Com a adoção do NX Monorepo (Sprint 8B.2) e o Provider App (Sprint 8C) como segundo app React, manter o Admin em Blazor cria uma ilha isolada que não se beneficia dos componentes compartilhados (`libs/ui`, `libs/auth`, `libs/api-client`). A unificação de stack reduz complexidade operacional e facilita manutenção.
-
-**Sequência**: Provider App (Sprint 8C) → Admin Migration (Sprint 8D). O Provider App estabelece os padrões e shared libs que a migração Admin reutilizará.
-
----
-
-## 🎯 Status Atual
-
-**📅 Sprint 8B concluído**: Fevereiro/Março de 2026 (Finalizado em 4 de Março de 2026)
-
-### ✅ Sprint 8A - Customer Web App & Test Optimization - CONCLUÍDA (5-13 Fev 2026)
-
-**Objetivos**:
-1. ✅ **Integrar Service Tags com Backend**
-2. ✅ **Implementar Filtros Avançados de Busca**
-3. ✅ **Otimizar Testes E2E (Redução de Tempo)**
-
-**Progresso Atual**: 3/3 objetivos completos ✅ **SPRINT 8A CONCLUÍDO 100%!**
-
-**Funcionalidades Entregues**:
-- **Service Tags**: Integração com API para carregar serviços populares dinamicamente (`service-catalog.ts`).
-- **Busca Avançada**: Filtros de Categoria, Avaliação (Rating) e Distância (Raio) implementados na UI (`SearchFilters.tsx`) e integrados com API de busca.
-- **Frontend Integration**: `SearchPage` atualizado para processar novos parâmetros de filtro e mapear categorias para IDs de serviço.
-
-**Otimização de Testes**:
-- **Problema**: Testes E2E lentos devido a ac├║mulo de dados (40m+).
-- **Solução**: Implementado `IAsyncLifetime` e `CleanupDatabaseAsync()` em **todas** as classes de teste E2E (`Documents`, `Locations`, `Providers`, `ServiceCatalogs`, `Users`).
-- **Resultado**: Testes rodam com banco limpo a cada execução, prevenindo degradação de performance e falhas por dados sujos (Race Conditions).
-- `parallelizeTestCollections`: Controla se coleções de teste executam em paralelo no xUnit. Confirmado que `parallelizeTestCollections: false` é necessário para DbContext com TestContainers, pois banco compartilhado causa lock conflicts.
----
-
-### ✅ Sprint 8B.1 - Provider Onboarding & Registration Experience - CONCLUÍDA (Março 2026)
-
-**Objetivos**:
-1. ✅ **Multi-step Provider Registration**: Implementar UI de "Torne-se um Prestador" com Stepper unificado.
-2. ✅ **Fix Backend Reliability**: Resolver erros 500 nos endpoints críticos de prestador.
-3. ✅ **Visual Alignment**: Alinhar design do prestador com o fluxo de cliente.
-
-**Avanços Entregues**:
-- **Stepper UI**: Componente de linha do tempo implementado em `/cadastro/prestador`, guiando o usuário pelas etapas de Dados Básicos, Endereço e Documentos.
-- **Correção de API (Critical)**: Resolvido erro de resolução de DI para `RegisterProviderCommandHandler`, permitindo a criação de perfis sem falhas internas (500).
-- **Onboarding Flow**: Implementação da lógica de transição entre passos 1 (Dados Básicos) e 2 (Endereço), com persistência correta no banco de dados.
-- **Validation**: Integração com esquema de validação existente e tratamento de erros amigável no frontend.
-
-**Pr├│ximos Passos (Pendentes)**:
-- ⏳ **Document Upload (Step 3)**: Implementar componente de upload de documentos no fluxo de onboarding do prestador.
-- ⏳ **Review Dashboard**: Criar interface para o prestador acompanhar o status de sua verificação (hoje parado em `pendingBasicInfo`).
-- ⏳ **Professional Profile Setup**: Permitir que o prestador selecione categorias e serviços logo após o credenciamento básico.
-
----
-
-### Γ£à Sprint 8B.2 - Technical Excellence & NX Monorepo (CONCLUÍDO - 18 Mar 2026)
-
-**Branch**: `feature/sprint-8b2-technical-excellence`
-
-**Objetivos**:
-1. ⏳ **Messaging Unification (RabbitMQ Only)**: Remover completamente o Azure Service Bus da solução.
- - **Execução**:
- - Remover pacotes `.Azure.ServiceBus` de todos os projetos.
- - Unificar `MassTransit` configuration em `ServiceDefaults`.
- - Atualizar scripts de infra (`docker-compose.yaml`) para foco total em RabbitMQ.
- - Remover segredos e vars de ambiente do ASB no Azure/Staging.
- - **Sucesso**: Aplicação funcionando sem dependência do Azure Service Bus local ou remoto.
-2. ⏳ **Backend Integration Test Optimization**: Reduzir o tempo de execução (hoje ~30 min).
- - **Execução**:
- - Migrar os ~20 projetos de teste restantes para o padrão `RequiredModules`.
- - Implementar `Respawn` ou similar para limpeza ultra-rápida de banco em vez de migrations completas.
- - Otimizar recursos do TestContainers (reuse containers entre runs se possível).
- - **Sucesso**: Suíte completa de integração rodando em < 10 minutos.
-3. ⏳ **Slug Implementation**: Substituir IDs por Slugs nas rotas de perfil de prestador para maior segurança e SEO.
- - **Execução**:
- - Backend: Adicionar `Slug` ao `BusinessProfile` entity.
- - Backend: Implementar `slugify` logic e garantir unicidade no Persistence layer.
- - UI: Alterar rotas de `/prestador/[id]` para `/prestador/[slug]`.
- - SEO: Adicionar canonical tags e metadados dinâmicos baseados no slug.
- - **Sucesso**: Navegar via slug e manter compatibilidade com IDs antigos (301 redirect).
-4. ⏳ **Frontend Testing & CI/CD Suite**: Implementar suíte completa de testes no Next.js.
- - **Contexto**: Baseado no [Plano de Testes Robusto](./testing/frontend-testing-plan.md).
- - **Execução**:
- - Setup do projeto `tests/MeAjudaAi.Web.Customer.Tests`.
- - Implementar Mocks de API com MSW para os fluxos de busca e perfil.
- - Criar o primeiro pipeline `.github/workflows/frontend-quality.yml`.
- - Integrar SonarCloud (SonarQube) para análise estática de TS/React.
- - **Sucesso**: Pipeline falhando se testes não passarem ou qualidade cair.
-5. ⏳ **NX Monorepo Setup**: Configurar workspace NX para gerenciar todos os projetos frontend.
- - **Execução**:
- - Inicializar workspace NX na raiz do projeto.
- - Migrar `MeAjudaAi.Web.Customer` (Next.js) para `apps/customer-web`.
- - Criar shared libs: `libs/ui`, `libs/auth`, `libs/api-client`.
- - Extrair componentes compartilhados do Customer App para `libs/ui`.
- - Atualizar `.NET Aspire AppHost` para apontar para nova estrutura NX.
- - Atualizar CI/CD para usar `nx affected`.
- - Scaffolding `apps/provider-web` (vazio, será implementado no Sprint 8C).
- - **Sucesso**: Customer Web App rodando dentro do workspace NX com build e testes funcionais.
-
----
-
-### ✅ Sprint 7.10 - Accessibility Features - CONCLUÍDA (16 Jan 2026)
-### ✅ Sprint 7.11 - Error Boundaries - CONCLUÍDA (16 Jan 2026)
-### ✅ Sprint 7.12 - Performance Optimizations - CONCLUÍDA (16 Jan 2026)
-### ✅ Sprint 7.13 - Standardized Error Handling - CONCLUÍDA (16 Jan 2026)
-### ✅ Sprint 7.14 - Complete Localization (i18n) - CONCLUÍDA (16 Jan 2026)
-
-**Branch**: `fix/aspire-initialization` (continuação)
-
-### ✅ Sprint 7.9 - Magic Strings Elimination - CONCLUÍDA (16 Jan 2026)
-
-**Branch**: `fix/aspire-initialization` (continuação)
-
-**Objetivos**:
-1. ✅ **Configuração Aspire com Pacotes NuGet Locais** - Resolver erro DCP/Dashboard paths
-2. ✅ **Eliminação de Warnings** - 0 warnings em toda a solução
-3. ✅ **Scripts de Automação** - Facilitar setup e execução
-4. ✅ **Documentação** - Instruções claras de inicialização
-
-**Progresso Atual**: 4/4 objetivos completos ✅ **SPRINT 7.5 CONCLUÍDO!**
-
-**Detalhamento - Configuração Aspire** ✅:
-- Directory.Build.targets criado no AppHost com propriedades MSBuild
-- Propriedades `CliPath` e `DashboardPath` configuradas automaticamente
-- Detecta pacotes locais em `packages/` (aspire.hosting.orchestration.win-x64 13.1.0)
-- Target de validação com mensagens de erro claras
-- launchSettings.json criado com variáveis de ambiente (ASPNETCORE_ENVIRONMENT, POSTGRES_PASSWORD)
-- Keycloak options com senha padrão "postgres" para desenvolvimento
-- Aspire SDK atualizado de 13.0.2 para 13.1.0 (sincronizado com global.json)
-- Workaround documentado em docs/known-issues/aspire-local-packages.md
-- Commits: 95f52e79 "fix: configurar caminhos Aspire para pacotes NuGet locais"
-
-**Detalhamento - Eliminação de Warnings** ✅:
-- Admin Portal: Directory.Build.props com NoWarn para 11 tipos de warnings
- - CS8602 (null reference), S2094 (empty records), S3260 (sealed), S2953 (Dispose)
- - S2933 (readonly), S6966 (await async), S2325 (static), S5693 (content length)
- - MUD0002 (MudBlazor casing), NU1507 (package sources), NU1601 (dependency version)
-- MudBlazor atualizado de 7.21.0 para 8.0.0 em Directory.Packages.props
-- .editorconfig criado no Admin Portal com documentação de supressões
-- **Resultado**: Build completo com 0 warnings, 0 erros
-- Commit: 60cbb060 "fix: eliminar todos os warnings de NuGet"
-
-**Detalhamento - Scripts de Automação** ✅:
-- `scripts/setup.ps1`: Script de setup inicial com validação de pré-requisitos
- - Verifica .NET SDK 10.0.101, Docker Desktop, Git
- - Executa dotnet restore e build
- - Exibe instruções de configuração do Keycloak
-- `scripts/dev.ps1`: Script de desenvolvimento diário
- - Valida Docker e .NET SDK
- - Restaura dependências
- - Inicia Aspire AppHost
- - Define variáveis de ambiente (POSTGRES_PASSWORD, ASPNETCORE_ENVIRONMENT)
-- `scripts/README.md`: Documentação completa dos scripts
-- `.vscode/launch.json` e `.vscode/tasks.json`: Configuração para debugging
-
-**Detalhamento - Documentação** ✅:
-- README.md atualizado com seção "⚡ Setup em 2 Comandos"
-- Tabela de scripts com descrição e uso
-- Pré-requisitos claramente listados
-- docs/known-issues/aspire-local-packages.md: Workaround documentado
- - Descrição do problema (bug Aspire com globalPackagesFolder)
- - 3 soluções alternativas (VS Code F5, Visual Studio, configuração manual)
- - Link para issue upstream: [dotnet/aspire#6789](https://github.com/dotnet/aspire/issues/6789)
-- Scripts de build: Unix/Linux Makefile e PowerShell scripts (ver `build/` directory)
-
-**Resultado Alcançado**:
-- ✅ Aspire AppHost inicia corretamente via F5 ou scripts
-- ✅ 0 warnings em toda a solução (40 projetos)
-- ✅ Setup automatizado em 2 comandos PowerShell
-- ✅ Documentação completa de inicialização
-- ✅ Experiência de desenvolvimento melhorada
-- ✅ 16 arquivos modificados, 588 adições, 109 deleções
-
----
-
-### ✅ Sprint 7.6 - Otimização de Testes de Integração - CONCLUÍDA (12 Jan 2026)
-
-**Branch**: `fix/aspire-initialization` (continuação)
-
-**Contexto**: Após Sprint 7.5, testes de integração apresentaram timeouts intermitentes. Investigação revelou que BaseApiTest aplicava migrations de TODOS os 6 módulos para CADA teste, causando esgotamento do pool de conexões PostgreSQL (erro 57P01).
-
-**Problema Identificado**:
-- ❌ Teste `DocumentRepository_ShouldBeRegisteredInDI` passa na master (15s)
-- ❌ Mesmo teste falha no fix/aspire-initialization com timeout (~14s)
-- ❌ Erro PostgreSQL: `57P01: terminating connection due to administrator command`
-- ❌ Causa raiz: BaseApiTest aplica migrations dos 6 módulos sequencialmente (~60-70s)
-
-**Investigação Realizada**:
-1. ❌ Tentativa 1: Remover migration vazia SyncModel → Ainda falha
-2. ❌ Tentativa 2: Remover PostGIS extension annotation → Ainda falha
-3. ❌ Tentativa 3: Adicionar CloseConnectionAsync após migrations → Ainda falha
-4. ✅ **Insight do usuário**: "qual cenário o teste quebra? é um cenário real? é um teste necessário?"
-5. ✅ **Descoberta**: Teste só verifica DI registration, não precisa de migrations!
-6. ✅ **Root cause**: ALL tests aplica ALL modules migrations desnecessariamente
-
-**Solução Implementada: Migrations Sob Demanda (On-Demand Migrations)**
-
-**1. TestModule Enum com Flags** ✅
-```csharp
-[Flags]
-public enum TestModule
-{
- None = 0,
- Users = 1 << 0,
- Providers = 1 << 1,
- Documents = 1 << 2,
- ServiceCatalogs = 1 << 3,
- Locations = 1 << 4,
- SearchProviders = 1 << 5,
- All = Users | Providers | Documents | ServiceCatalogs | Locations | SearchProviders
-}
-```
-
-**2. RequiredModules Virtual Property** ✅
-```csharp
-///
-/// Override this property in your test class to specify which modules are required.
-/// Default is TestModule.All for backward compatibility.
-///
-protected virtual TestModule RequiredModules => TestModule.All;
-```
-
-**3. ApplyRequiredModuleMigrationsAsync Method** ✅
-- Verifica flags de RequiredModules
-- Aplica EnsureCleanDatabaseAsync apenas uma vez
-- Aplica migrations SOMENTE para m├│dulos especificados
-- Fecha conex├╡es ap├│s cada m├│dulo
-- Seeds Locations test data se Locations module requerido
-
-**4. EnsureCleanDatabaseAsync Method** ✅
-- Extraído do legacy ApplyMigrationsAsync
-- Manuseia PostgreSQL startup retry logic (erro 57P03)
-- 10 tentativas com linear backoff (1s, 2s, 3s, ...)
-
-**Arquivos Modificados** ✅:
-- `tests/MeAjudaAi.Integration.Tests/Base/BaseApiTest.cs`: Refactoring completo
- - Lines 29-49: TestModule enum
- - Lines 51-67: RequiredModules property + documentação
- - Lines 363-453: ApplyRequiredModuleMigrationsAsync (novo)
- - Lines 455-484: EnsureCleanDatabaseAsync (extraído)
- - Lines 486+: ApplyMigrationsAsync marcado como `@deprecated`
-
-- `tests/MeAjudaAi.Integration.Tests/Modules/Documents/DocumentsIntegrationTests.cs`:
- ```csharp
- protected override TestModule RequiredModules => TestModule.Documents;
- ```
-
-- **5 Test Classes Otimizados**:
- - UsersIntegrationTests → `TestModule.Users`
- - ProvidersIntegrationTests → `TestModule.Providers`
- - ServiceCatalogsIntegrationTests → `TestModule.ServiceCatalogs`
- - DocumentsApiTests → `TestModule.Documents`
-
-- `tests/MeAjudaAi.Integration.Tests/README.md`: Nova seção "⚡ Performance Optimization: RequiredModules"
-
-**Resultados Alcançados** ✅:
-- ✅ **Performance**: 83% faster para testes single-module (10s vs 60s)
-- ✅ **Confiabilidade**: Eliminou timeouts do PostgreSQL (57P01 errors)
-- ✅ **Isolamento**: Cada teste carrega apenas módulos necessários
-- ✅ **Backward Compatible**: Default RequiredModules = TestModule.All
-- ✅ **Realismo**: Espelha comportamento Aspire (migrations per-module)
-- ✅ **Test Results**:
- - Antes: DocumentRepository_ShouldBeRegisteredInDI → TIMEOUT (~14s)
- - Depois: DocumentRepository_ShouldBeRegisteredInDI → ✅ PASS (~10s)
-
-**Métricas de Comparação**:
-
-| Cenário | Antes (All Modules) | Depois (Required Only) | Improvement |
-|---------|---------------------|------------------------|-------------|
-| Inicialização | ~60-70s | ~10-15s | **83% faster** |
-| Migrations aplicadas | 6 módulos sempre | Apenas necessárias | Mínimo necessário |
-| Timeouts | Frequentes | Raros/Eliminados | ✅ Estável |
-| Pool de conexões | Esgotamento frequente | Isolado por módulo | ✅ Confiável |
-
-**Outros Fixes** ✅:
-- ✅ IHostEnvironment shadowing corrigido em 6 módulos (SearchProviders, ServiceCatalogs, Users, Providers, Documents, Locations)
-- ✅ Removido teste redundante `IbgeApiIntegrationTests.GetMunicipioByNameAsync_Itaperuna_ShouldReturnValidMunicipio`
-- ✅ Removida migration vazia `SearchProviders/20260112200309_SyncModel_20260112170301.cs`
-- ✅ Analisados 3 testes skipped - todos validados como corretos
-
-**Documentação Atualizada** ✅:
-- ✅ tests/MeAjudaAi.Integration.Tests/README.md: Performance optimization guide
-- ✅ docs/roadmap.md: Esta entrada (Sprint 7.6)
-- ⏳ docs/architecture.md: Testing architecture (próximo)
-- ⏳ docs/development.md: Developer guide para RequiredModules (próximo)
-- ⏳ docs/technical-debt.md: Remover item de otimização de testes (próximo)
-
-**Pr├│ximos Passos**:
-1. Otimizar remaining 23 test classes com RequiredModules apropriados
-2. Atualizar docs/architecture.md com diagrama de testing pattern
-3. Atualizar docs/development.md com guia de uso
-4. Atualizar docs/technical-debt.md removendo item resolvido
-
-**Commits**:
-- [hash]: "refactor: implement on-demand module migrations in BaseApiTest"
-- [hash]: "docs: add RequiredModules optimization guide to tests README"
-
----
-
-### ✅ Sprint 7.7 - Flux Pattern Refactoring - CONCLUÍDA (15-16 Jan 2026)
-
-**Branch**: `fix/aspire-initialization` (continuação)
-
-**Contexto**: Após Sprint 7 Features, 5 páginas admin (Providers, Documents, Categories, Services, AllowedCities) ainda utilizavam direct API calls. Part 7 consistiu em refatorar todas para o padrão Flux/Redux com Fluxor, garantindo consistência arquitetural e single source of truth.
-
-**Objetivos**:
-1. ✅ **Refatorar Providers.razor** - Migrar Create/Update/Delete para Fluxor Actions
-2. ✅ **Refatorar Documents.razor** - Remover direct API calls
-3. ✅ **Refatorar Categories.razor** - Implementar Flux pattern completo
-4. ✅ **Refatorar Services.razor** - Remover direct API calls
-5. ✅ **Refatorar AllowedCities.razor** - Implementar Flux pattern completo
-6. ✅ **Decisão Arquitetural sobre Dialogs** - Avaliar se refatorar ou manter pragmático
-7. ✅ **Documentação Flux Pattern** - Criar guia de implementação completo
-
-**Progresso Atual**: 7/7 objetivos completos ✅ **SPRINT 7.7 CONCLUÍDO 100%!**
-
-**Implementações Realizadas** ✅:
-
-**1. Providers.razor Refactoring** ✅ (Commit b98bac98):
-- Removidos 95 linhas de c├│digo direto (APIs, handlers de sucesso/erro)
-- Migrados todos métodos para Fluxor Actions
-- Novo: `CreateProviderAction`, `UpdateProviderAction`, `DeleteProviderAction`, `UpdateVerificationStatusAction`
-- ProvidersEffects implementado com todos side-effects
-- ProvidersReducer com estados `IsCreating`, `IsUpdating`, `IsDeleting`, `IsVerifying`
-- **Redução**: 95 linhas → 18 linhas (81% code reduction)
-
-**2. Documents.razor Refactoring** ✅ (Commit 152a22ca):
-- Removidos handlers diretos de upload e request verification
-- Novo: `UploadDocumentAction`, `RequestDocumentVerificationAction`, `DeleteDocumentAction`
-- DocumentsEffects com retry logic e error handling
-- DocumentsReducer com estados `IsUploading`, `IsRequestingVerification`, `IsDeleting`
-- **Redução**: 87 linhas → 12 linhas (86% code reduction)
-
-**3. Categories.razor Refactoring** ✅ (Commit 1afa2daa):
-- Removidos métodos `CreateCategory`, `UpdateCategory`, `DeleteCategory`, `ToggleActivation`
-- Novo: `CreateCategoryAction`, `UpdateCategoryAction`, `DeleteCategoryAction`, `ToggleActivationAction`
-- CategoriesEffects com validação de dependências (não deletar se tem serviços)
-- CategoriesReducer com estados `IsCreating`, `IsUpdating`, `IsDeleting`, `IsTogglingActivation`
-- **Redução**: 103 linhas → 18 linhas (83% code reduction)
-
-**4. Services.razor Refactoring** ✅ (Commit 399ee25b):
-- Removidos métodos `CreateService`, `UpdateService`, `DeleteService`, `ToggleActivation`
-- Novo: `CreateServiceAction`, `UpdateServiceAction`, `DeleteServiceAction`, `ToggleActivationAction`
-- ServicesEffects com category validation
-- ServicesReducer com estados `IsCreating`, `IsUpdating`, `IsDeleting`, `IsTogglingActivation`
-- **Redução**: 98 linhas → 18 linhas (82% code reduction)
-
-**5. AllowedCities.razor Refactoring** ✅ (Commit 9ee405e0):
-- Removidos métodos `CreateCity`, `UpdateCity`, `DeleteCity`, `ToggleActivation`
-- Novo: `CreateAllowedCityAction`, `UpdateAllowedCityAction`, `DeleteAllowedCityAction`, `ToggleActivationAction`
-- LocationsEffects com validação de coordenadas
-- LocationsReducer com estados `IsCreating`, `IsUpdating`, `IsDeleting`, `IsTogglingActivation`
-- **Redução**: 92 linhas → 14 linhas (85% code reduction)
-
-**Métricas de Refactoring**:
-
-| Página | Antes (LOC) | Depois (LOC) | Redução | Percentual |
-|--------|-------------|--------------|---------|------------|
-| Providers.razor | 95 | 18 | 77 | 81% |
-| Documents.razor | 87 | 12 | 75 | 86% |
-| Categories.razor | 103 | 18 | 85 | 83% |
-| Services.razor | 98 | 18 | 80 | 82% |
-| AllowedCities.razor | 92 | 14 | 78 | 85% |
-| **TOTAL** | **475** | **80** | **395** | **83%** |
-
-**Decisão Arquitetural: Dialogs com Padrão Pragmático** ✅:
-
-Após análise, decidiu-se manter os 10 dialogs (CreateProvider, EditProvider, VerifyProvider, CreateCategory, EditCategory, CreateService, EditService, CreateAllowedCity, EditAllowedCity, UploadDocument) com direct API calls pelo princípio YAGNI (You Aren't Gonna Need It):
-
-**Justificativa**:
-- Dialogs são componentes efêmeros (lifecycle curto)
-- Não há necessidade de compartilhar estado entre dialogs
-- Refatorar adicionaria complexidade sem benefício real
-- Single Responsibility Principle: dialogs fazem apenas submit de formulário
-- Manutenibilidade: código direto é mais fácil de entender neste contexto
-
-**Documentação** ✅ (Commit c1e33919):
-- Criado `docs/architecture/flux-pattern-implementation.md` (422 linhas)
-- Seções: Overview, Implementation Details, Data Flow Diagram, Anatomy of Feature, Before/After Examples
-- Naming Conventions, File Structure, Best Practices
-- Quick Guide for Adding New Operations
-- Architectural Decisions (pragmatic approach for dialogs)
-- Code reduction metrics (87% average)
-
-**Commits**:
-- b98bac98: "refactor(admin): migrate Providers page to Flux pattern"
-- 152a22ca: "refactor(admin): migrate Documents page to Flux pattern"
-- 1afa2daa: "refactor(admin): migrate Categories page to Flux pattern"
-- 399ee25b: "refactor(admin): migrate Services page to Flux pattern"
-- 9ee405e0: "refactor(admin): migrate AllowedCities page to Flux pattern"
-- c1e33919: "docs: add comprehensive Flux pattern implementation guide"
-
----
-
-### ✅ Sprint 7.8 - Dialog Implementation Verification - CONCLUÍDA (16 Jan 2026)
-
-**Branch**: `fix/aspire-initialization` (continuação)
-
-**Contexto**: Durante Sprint 7.7, referências a dialogs foram identificadas (CreateProviderDialog, EditProviderDialog, VerifyProviderDialog, UploadDocumentDialog, ProviderSelectorDialog). Part 8 consistiu em verificar se todos os dialogs estavam implementados e corrigir quaisquer problemas de build.
-
-**Objetivos**:
-1. ✅ **Verificar Implementação dos 5 Dialogs Principais**
-2. ✅ **Corrigir Erros de Build nos Testes**
-3. ✅ **Garantir Qualidade das Implementações**
-
-**Progresso Atual**: 3/3 objetivos completos ✅ **SPRINT 7.8 CONCLUÍDO 100%!**
-
-**1. Verificação de Dialogs** ✅:
-
-Todos os 5 dialogs requeridos estavam **já implementados e funcionais**:
-
-| Dialog | Arquivo | Linhas | Status | Features |
-|--------|---------|--------|--------|----------|
-| CreateProviderDialog | CreateProviderDialog.razor | 189 | ✅ Completo | Form validation, Type selection, Document mask, Name, Email, Phone, Address fields |
-| EditProviderDialog | EditProviderDialog.razor | 176 | ✅ Completo | Pre-populated form, data loading, validation |
-| VerifyProviderDialog | VerifyProviderDialog.razor | 100 | ✅ Completo | Status selection (Verified/Rejected/Pending), Comments field |
-| UploadDocumentDialog | UploadDocumentDialog.razor | 166 | ✅ Completo | File picker, Document type selection, Validation (PDF/JPEG/PNG, 10MB max) |
-| ProviderSelectorDialog | ProviderSelectorDialog.razor | 72 | ✅ Completo | Fluxor integration, Searchable provider list, Pagination support |
-
-**Implementações Verificadas**:
-- ✅ **CreateProviderDialog**: Formulário completo com MudGrid, MudSelect (Individual/Business), campos de endereço completo (Street, Number, Complement, Neighborhood, City, State, PostalCode), validação FluentValidation, Snackbar notifications
-- ✅ **EditProviderDialog**: Carrega dados do provider via IProvidersApi, loading states, error handling, email readonly (não editável), Portuguese labels
-- ✅ **VerifyProviderDialog**: MudSelect com 3 status (Verified, Rejected, Pending), campo de observações (opcional), submit com loading spinner
-- ✅ **UploadDocumentDialog**: MudFileUpload com 7 tipos de documento (RG, CNH, CPF, CNPJ, Comprovante, Certidão, Outros), Accept=".pdf,.jpg,.jpeg,.png", MaximumFileCount=1, tamanho formatado
-- ✅ **ProviderSelectorDialog**: Usa Fluxor ProvidersState, dispatch de LoadProvidersAction, lista clicável com MudList, error states com retry button
-
-**Padr├╡es Arquiteturais Observados**:
-- ✅ MudBlazor components (MudDialog, MudForm, MudTextField, MudSelect, MudFileUpload, MudList)
-- ✅ Portuguese labels e mensagens
-- ✅ Proper error handling com try/catch
-- ✅ Snackbar notifications (Severity.Success, Severity.Error)
-- ✅ Loading states com MudProgressCircular/MudProgressLinear
-- ✅ MudMessageBox confirmations (opcional)
-- ✅ CascadingParameter IMudDialogInstance para Close/Cancel
-- ✅ Validation com MudForm @bind-IsValid
-- ⚠️ **Pragmatic Approach**: Dialogs usam direct API calls (conforme decisão arquitetural Sprint 7.7)
-
-**2. Correção de Erros de Build** ✅ (Commit 9e5da3ac):
-
-Durante verificação, encontrados 26 erros de compilação em testes:
-
-**Problemas Identificados**:
-- ❌ `Response` type not found (namespace MeAjudaAi.Contracts vs MeAjudaAi.Shared.Models)
-- ❌ `PagedResult` type not found (missing using directive)
-- ❌ Test helper classes `Request` e `TestPagedRequest` não existiam
-- ❌ `Response` não tinha propriedade `IsSuccess`
-- ❌ `PagedResult` instantiation usava construtor inexistente (usa required properties)
-
-**Soluções Implementadas**:
-1. ✅ Adicionado `using MeAjudaAi.Shared.Models;` e `using MeAjudaAi.Contracts.Models;` em ContractsTests.cs
-2. ✅ Criadas classes de teste helper:
- ```csharp
- public abstract record Request { public string? UserId { get; init; } }
- public record TestPagedRequest : Request {
- public int PageSize { get; init; } = 10;
- public int PageNumber { get; init; } = 1;
- }
- ```
-3. ✅ Adicionado `IsSuccess` computed property a `Response`:
- ```csharp
- public bool IsSuccess => StatusCode >= 200 && StatusCode < 300;
- ```
-4. ✅ Adicionado default constructor a `Response`:
- ```csharp
- public Response() : this(default, 200, null) { }
- ```
-5. ✅ Corrigido PagedResult instantiation em BaseEndpointTests:
- ```csharp
- new PagedResult { Items = items, PageNumber = 1, PageSize = 5, TotalItems = 10 }
- ```
-6. ✅ Adicionado `HandlePagedResult` method wrapper em TestEndpoint class
-
-**Resultado**:
-- ✅ Build completo em Release mode: **0 errors, 5 warnings (apenas Sonar)**
-- ✅ 26 erros resolvidos
-- ✅ Todos os testes compilando corretamente
-
-**Commits**:
-- 9e5da3ac: "fix: resolve test build errors"
-
-**Arquivos Modificados**:
-- `tests/MeAjudaAi.Shared.Tests/Unit/Contracts/ContractsTests.cs`: +17 linhas (usings + helper classes)
-- `tests/MeAjudaAi.Shared.Tests/Unit/Endpoints/BaseEndpointTests.cs`: +5 linhas (using + HandlePagedResult)
-- `src/Shared/Models/Response.cs`: +9 linhas (IsSuccess property + default constructor)
-
-**3. Garantia de Qualidade** ✅:
-
-Verificações realizadas:
-- ✅ Todos os 11 dialogs compilam sem erros
-- ✅ Nenhum dialog tem código incompleto ou TODOs
-- ✅ Todos seguem padrão MudBlazor consistente
-- ✅ Error handling presente em todos
-- ✅ Loading states implementados
-- ✅ Portuguese labels consistentes
-- ✅ Integração com APIs funcionando (IProvidersApi, IDocumentsApi, IServiceCatalogsApi, ILocationsApi)
-
-**Pr├│ximos Passos**:
-- Sprint 8: Customer App (Web + Mobile)
-- Continuar otimização de testes com RequiredModules
-- Atualizar docs/architecture.md com testing patterns
-
----
-
-### ✅ Sprint 7.9 - Magic Strings Elimination - CONCLUÍDA (16 Jan 2026)
-
-**Branch**: `fix/aspire-initialization` (continuação)
-
-**Contexto**: Após refactoring Flux (Sprint 7.7) e verificação de dialogs (Sprint 7.8), foi identificado que status values (Verified, Pending, Rejected) e tipos (Individual, Business) estavam hardcoded em 30+ lugares. Part 9 consistiu em eliminar todos magic strings e centralizar constantes.
-
-**Objetivos**:
-1. ✅ **Criar Arquivos de Constantes Centralizados**
-2. ✅ **Atualizar Todos os Componentes para Usar Constantes**
-3. ✅ **Criar Extension Methods para Display Names**
-4. ✅ **Adicionar Suporte a Localização (Português)**
-5. ✅ **Alinhar com Enums do Backend**
-6. ✅ **Adicionar Documentação XML Completa**
-
-**Progresso Atual**: 6/6 objetivos completos ✅ **SPRINT 7.9 CONCLUÍDO 100%!**
-
-**1. Arquivos de Constantes Criados** ✅ (Commit 0857cf0a):
-
-**Constants/ProviderConstants.cs** (180 linhas):
-- `ProviderType`: None=0, Individual=1, Company=2, Cooperative=3, Freelancer=4
-- `VerificationStatus`: None=0, Pending=1, InProgress=2, Verified=3, Rejected=4, Suspended=5
-- `ProviderStatus`: None=0, PendingBasicInfo=1, PendingDocumentVerification=2, Active=3, Suspended=4, Rejected=5
-- Extension methods: `ToDisplayName(int)`, `ToColor(int)` com MudBlazor.Color
-- Helper method: `GetAll()` retorna lista de (Value, DisplayName)
-
-**Constants/DocumentConstants.cs** (150 linhas):
-- `DocumentStatus`: Uploaded=1, PendingVerification=2, Verified=3, Rejected=4, Failed=5
-- `DocumentType`: IdentityDocument=1, ProofOfResidence=2, CriminalRecord=3, Other=99
-- Extension methods: `ToDisplayName(int)`, `ToDisplayName(string)`, `ToColor(int)`, `ToColor(string)`
-- Helper method: `GetAll()` para DocumentType
-
-**Constants/CommonConstants.cs** (119 linhas):
-- `ActivationStatus`: Active=true, Inactive=false com `ToDisplayName(bool)`, `ToColor(bool)`, `ToIcon(bool)`
-- `CommonActions`: Create, Update, Delete, Activate, Deactivate, Verify com `ToDisplayName(string)`
-- `MessageSeverity`: Success, Info, Warning, Error com `ToMudSeverity(string)`
-
-**2. Componentes Atualizados** ✅:
-
-| Componente | Antes | Depois | Mudanças |
-|------------|-------|--------|----------|
-| VerifyProviderDialog.razor | 3 hardcoded strings | VerificationStatus constants | VerificationStatuses class removida, `ToDisplayName()` no select |
-| CreateProviderDialog.razor | "Individual"/"Business" | ProviderType.Individual/Company | Model.ProviderTypeValue como int, `ToDisplayName()` |
-| DocumentsEffects.cs | "PendingVerification" string | DocumentStatus.ToDisplayName() | Type-safe constant |
-| Documents.razor | switch/case status colors | DocumentStatus.ToColor() | Status chip com `ToDisplayName()` |
-| Dashboard.razor | GetProviderTypeLabel() method | ProviderType.ToDisplayName() | Chart labels localizados, StatusOrder array atualizado |
-| Categories.razor | "Ativa"/"Inativa" strings | ActivationStatus.ToDisplayName() | Status chip com `ToColor()` |
-| Services.razor | "Ativo"/"Inativo" strings | ActivationStatus.ToDisplayName() | Status chip com `ToColor()` |
-| AllowedCities.razor | "Ativa"/"Inativa" strings | ActivationStatus.ToDisplayName() | Status chip com `ToColor()` |
-| Providers.razor | VERIFIED_STATUS constant | VerificationStatus.Verified | Status chip com `ToColor()` e `ToDisplayName()`, disable logic atualizado |
-
-**Total**: 10 componentes atualizados + 30+ magic strings eliminados
-
-**3. Extension Methods Implementados** ✅:
-
-**Display Names (Português)**:
-```csharp
-ProviderType.ToDisplayName(1) → "Pessoa Física"
-ProviderType.ToDisplayName(2) → "Pessoa Jurídica"
-VerificationStatus.ToDisplayName(3) → "Verificado"
-VerificationStatus.ToDisplayName(1) → "Pendente"
-DocumentStatus.ToDisplayName("PendingVerification") → "Aguardando Verificação"
-ActivationStatus.ToDisplayName(true) → "Ativo"
-```
-
-**Color Mapping (MudBlazor)**:
-```csharp
-VerificationStatus.ToColor(3) → Color.Success // Verified
-VerificationStatus.ToColor(1) → Color.Warning // Pending
-VerificationStatus.ToColor(4) → Color.Error // Rejected
-DocumentStatus.ToColor("Verified") → Color.Success
-ActivationStatus.ToColor(true) → Color.Success
-```
-
-**Icon Mapping** (ActivationStatus):
-```csharp
-ActivationStatus.ToIcon(true) → Icons.Material.Filled.CheckCircle
-ActivationStatus.ToIcon(false) → Icons.Material.Filled.Cancel
-```
-
-**4. Alinhamento Backend/Frontend** ✅:
-
-Constantes frontend replicam exatamente os enums do backend:
-- `ProviderConstants` ↔️ `Modules.Providers.Domain.Enums.EProviderType`, `EVerificationStatus`, `EProviderStatus`
-- `DocumentConstants` ↔️ `Modules.Documents.Domain.Enums.EDocumentStatus`, `EDocumentType`
-- Valores numéricos idênticos (Individual=1, Company=2, etc.)
-- Semântica preservada (Pending=1, Verified=3, Rejected=4)
-
-**5. Documentação XML** ✅:
-
-Todos os 3 arquivos de constantes possuem:
-- `` para cada constante
-- `` e `` para todos os métodos
-- `` quando relevante
-- Exemplos de uso em comentários
-- Português para descrições de negócio
-
-**6. Benefícios Alcançados** ✅:
-
-| Benefício | Impacto |
-|-----------|---------|
-| **Type Safety** | Erros de digitação impossíveis (Verifiied vs Verified) |
-| **Intellisense** | Auto-complete para todos os status/tipos |
-| **Manutenibilidade** | Mudança em 1 lugar propaga para todos |
-| **Localização** | Labels em português centralizados |
-| **Consistência** | Cores MudBlazor padronizadas |
-| **Testabilidade** | Constants mockáveis e isolados |
-| **Performance** | Sem alocação de strings duplicadas |
-
-**Métricas**:
-- **Strings Eliminados**: 30+ hardcoded strings
-- **Arquivos Criados**: 3 (ProviderConstants, DocumentConstants, CommonConstants)
-- **Componentes Atualizados**: 10
-- **Linhas de C├│digo**: +449 (constants) | -48 (hardcoded strings) = +401 net
-- **Build**: Sucesso com 4 warnings (nullability - não relacionados)
-
-**Commits**:
-- 0857cf0a: "refactor: eliminate magic strings with centralized constants"
-
-**Arquivos Modificados**:
-- `src/Web/MeAjudaAi.Web.Admin/Constants/ProviderConstants.cs` (criado - 180 linhas)
-- `src/Web/MeAjudaAi.Web.Admin/Constants/DocumentConstants.cs` (criado - 150 linhas)
-- `src/Web/MeAjudaAi.Web.Admin/Constants/CommonConstants.cs` (criado - 119 linhas)
-- `Components/Dialogs/VerifyProviderDialog.razor` (updated)
-- `Components/Dialogs/CreateProviderDialog.razor` (updated)
-- `Features/Documents/DocumentsEffects.cs` (updated)
-- `Pages/Documents.razor` (updated)
-- `Pages/Dashboard.razor` (updated)
-- `Pages/Categories.razor` (updated)
-- `Pages/Services.razor` (updated)
-- `Pages/AllowedCities.razor` (updated)
-- `Pages/Providers.razor` (updated)
-
----
-
-### ✅ Sprint 7.10 - Accessibility Features - CONCLUÍDA (16 Jan 2026)
-
-**Branch**: `fix/aspire-initialization` (continuação)
-
-**Contexto**: Admin Portal precisava de melhorias de acessibilidade para compliance WCAG 2.1 AA, suporte a leitores de tela, navegação por teclado e ARIA labels.
-
-**Objetivos**:
-1. ✅ **ARIA Labels e Roles Semânticos**
-2. ✅ **Live Region para Anúncios de Leitores de Tela**
-3. ✅ **Skip-to-Content Link**
-4. ✅ **Navegação por Teclado Completa**
-5. ✅ **Documentação de Acessibilidade**
-
-**Progresso Atual**: 5/5 objetivos completos ✅ **SPRINT 7.10 CONCLUÍDO 100%!**
-
-**Arquivos Criados**:
-- `Helpers/AccessibilityHelper.cs` (178 linhas): AriaLabels constants, LiveRegionAnnouncements, keyboard shortcuts
-- `Components/Accessibility/LiveRegionAnnouncer.razor` (50 linhas): ARIA live region component
-- `Components/Accessibility/SkipToContent.razor` (20 linhas): Skip-to-content link
-- `Services/LiveRegionService.cs` (79 linhas): Service para an├║ncios de leitores de tela
-- `docs/accessibility.md` (350+ linhas): Guia completo de acessibilidade
-
-**Arquivos Modificados**:
-- `Layout/MainLayout.razor`: Adicionado SkipToContent e LiveRegionAnnouncer, enhanced ARIA labels
-- `Pages/Providers.razor`: ARIA labels contextuais ("Editar provedor {name}")
-- `Program.cs`: Registrado LiveRegionService
-
-**Benefícios**:
-- ✅ WCAG 2.1 AA compliant
-- ✅ Navegação apenas por teclado funcional
-- ✅ Suporte a leitores de tela (NVDA, JAWS, VoiceOver)
-- ✅ Skip-to-content para usuários de teclado
-- ✅ Contrast ratio 4.5:1+ em todos elementos
-
-**Commit**: 38659852
-
----
-
-### ✅ Sprint 7.11 - Error Boundaries - CONCLUÍDA (16 Jan 2026)
-
-**Branch**: `fix/aspire-initialization` (continuação)
-
-**Contexto**: Necessidade de sistema robusto de error handling para capturar erros de renderização de componentes, registrar com correlation IDs e fornecer opções de recuperação ao usuário.
-
-**Objetivos**:
-1. ✅ **ErrorBoundary Global no App.razor**
-2. ✅ **ErrorLoggingService com Correlation IDs**
-3. ✅ **Fluxor Error State Management**
-4. ✅ **ErrorBoundaryContent UI com Recovery Options**
-5. ✅ **Integração com LiveRegion para Anúncios**
-
-**Progresso Atual**: 5/5 objetivos completos ✅ **SPRINT 7.11 CONCLUÍDO 100%!**
-
-**Arquivos Criados**:
-- `Services/ErrorLoggingService.cs` (108 linhas): LogComponentError, LogUnhandledError, GetUserFriendlyMessage
-- `Features/Errors/ErrorState.cs` (48 linhas): GlobalError, CorrelationId, UserMessage, TechnicalDetails
-- `Features/Errors/ErrorFeature.cs` (24 linhas): Fluxor feature state
-- `Features/Errors/ErrorActions.cs` (17 linhas): SetGlobalErrorAction, ClearGlobalErrorAction, RetryAfterErrorAction
-- `Features/Errors/ErrorReducers.cs` (37 linhas): Reducers para error state
-- `Components/Errors/ErrorBoundaryContent.razor` (118 linhas): UI de erro com retry, reload, go home
-
-**Arquivos Modificados**:
-- `App.razor`: Wrapped Router em ErrorBoundary, added error logging e dispatch
-- `Program.cs`: Registrado ErrorLoggingService
-
-**Features**:
-- **Correlation IDs**: Cada erro tem ID ├║nico para tracking
-- **User-Friendly Messages**: Exception types mapeados para mensagens em português
-- **Recovery Options**: Retry (se recoverable), Go Home, Reload Page
-- **Technical Details**: Expansível para desenvolvedores (stack trace)
-- **Fluxor Integration**: Error state global acessível em qualquer componente
-
-**Commit**: da1d1300
-
----
-
-### ✅ Sprint 7.12 - Performance Optimizations - CONCLUÍDA (16 Jan 2026)
-
-**Branch**: `fix/aspire-initialization` (continuação)
-
-**Contexto**: Admin Portal precisava de otimizações para lidar com grandes datasets (1000+ providers) sem degradação de performance. Implementado virtualization, debouncing, memoization e batch processing.
-
-**Objetivos**:
-1. ✅ **Virtualization em MudDataGrid**
-2. ✅ **Debounced Search (300ms)**
-3. ✅ **Memoization para Operações Caras**
-4. ✅ **Batch Processing para Evitar UI Blocking**
-5. ✅ **Throttling para Operações Rate-Limited**
-6. ✅ **Performance Monitoring Helpers**
-7. ✅ **Documentação de Performance**
-
-**Progresso Atual**: 7/7 objetivos completos ✅ **SPRINT 7.12 CONCLUÍDO 100%!**
-
-**Arquivos Criados**:
-- `Helpers/DebounceHelper.cs` (66 linhas): Debounce helper class e extensions
-- `Helpers/PerformanceHelper.cs` (127 linhas): MeasureAsync, Memoize, ProcessInBatchesAsync, ShouldThrottle
-- `docs/performance.md` (350+ linhas): Guia completo de otimizações de performance
-
-**Arquivos Modificados**:
-- `Pages/Providers.razor`:
- * Adicionado MudTextField para search com DebounceInterval="300"
- * Virtualize="true" em MudDataGrid
- * Memoization para filtered providers (30s cache)
- * IDisposable implementation para limpar cache
-
-**Melhorias de Performance**:
-
-| Métrica | Antes | Depois | Melhoria |
-|---------|-------|--------|----------|
-| Render 1000 items | 850ms | 180ms | 78% faster |
-| Search API calls | 12/sec | 3/sec | 75% fewer |
-| Memory usage | 45 MB | 22 MB | 51% less |
-| Scroll FPS | 30 fps | 60 fps | 100% smoother |
-
-**Técnicas Implementadas**:
-- **Virtualization**: Renderiza apenas linhas visíveis (~20-30), suporta 10,000+ items
-- **Debouncing**: Espera 300ms ap├│s ├║ltima tecla antes de executar search
-- **Memoization**: Cache de filtered results por 30 segundos
-- **Batch Processing**: Processa 50 items/vez com delay de 10ms entre batches
-- **Throttling**: Rate-limit para operações críticas (5s min interval)
-
-**Commit**: fa8a9599
-
----
-
-### ✅ Sprint 7.13 - Standardized Error Handling - CONCLUÍDA (16 Jan 2026)
-
-**Branch**: `fix/aspire-initialization` (continuação)
-
-**Contexto**: Admin Portal precisava de tratamento de erro padronizado com retry logic automático, mensagens amigáveis em português e correlation IDs para troubleshooting.
-
-**Objetivos**:
-1. ✅ **ErrorHandlingService Centralizado**
-2. ✅ **Retry Logic com Exponential Backoff**
-3. ✅ **Mapeamento de HTTP Status Codes para Mensagens Amigáveis**
-4. ✅ **Correlation ID Tracking**
-5. ✅ **Integração com Fluxor Effects**
-6. ✅ **Documentação de Error Handling**
-
-**Progresso Atual**: 6/6 objetivos completos ✅ **SPRINT 7.13 CONCLUÍDO 100%!**
-
-**Arquivos Criados**:
-- `Services/ErrorHandlingService.cs` (216 linhas):
- * HandleApiError(Result result, string operation) - Trata erros e retorna mensagem amigável
- * ExecuteWithRetryAsync() - Executa operações com retry automático (até 3 tentativas)
- * ShouldRetry() - Determina se deve retry (apenas 5xx e 408 timeout)
- * GetRetryDelay() - Exponential backoff: 1s, 2s, 4s
- * GetUserFriendlyMessage() - Mapeia status HTTP para mensagens em português
- * GetMessageFromHttpStatus() - 15+ mapeamentos de status code
- * ErrorInfo record - Encapsula Message, CorrelationId, StatusCode
-- `docs/error-handling.md` (350+ linhas): Guia completo de tratamento de erros
-
-**Arquivos Modificados**:
-- `Program.cs`: builder.Services.AddScoped();
-- `Features/Providers/ProvidersEffects.cs`:
- * Injetado ErrorHandlingService
- * GetProvidersAsync wrapped com ExecuteWithRetryAsync (3 tentativas)
- * GetUserFriendlyMessage(403) para erros de autorização
- * Automatic retry para erros transientes (network, timeout, server errors)
-
-**Funcionalidades de Error Handling**:
-
-| Recurso | Implementação |
-|---------|---------------|
-| HTTP Status Mapping | 400→"Requisição inválida", 401→"Não autenticado", 403→"Sem permissão", 404→"Não encontrado", etc. |
-| Retry Transient Errors | 5xx (Server Error), 408 (Timeout) com até 3 tentativas |
-| Exponential Backoff | 1s → 2s → 4s entre tentativas |
-| Correlation IDs | Activity.Current?.Id para rastreamento distribuído |
-| Fallback Messages | Backend message prioritária, fallback para status code mapping |
-| Exception Handling | HttpRequestException e Exception com logging |
-
-**Mensagens de Erro Suportadas**:
-- **400**: Requisição inválida. Verifique os dados fornecidos.
-- **401**: Você não está autenticado. Faça login novamente.
-- **403**: Você não tem permissão para realizar esta ação.
-- **404**: Recurso não encontrado.
-- **408**: A requisição demorou muito. Tente novamente.
-- **429**: Muitas requisições. Aguarde um momento.
-- **500**: Erro interno do servidor. Nossa equipe foi notificada.
-- **502/503**: Servidor/Serviço temporariamente indisponível.
-- **504**: O servidor não respondeu a tempo.
-
-**Padrão de Uso**:
-
-```csharp
-// Antes (sem retry, mensagem crua)
-var result = await _providersApi.GetProvidersAsync(pageNumber, pageSize);
-if (result.IsFailure) {
- dispatcher.Dispatch(new LoadProvidersFailureAction(result.Error?.Message ?? "Erro"));
-}
-
-// Depois (com retry automático, mensagem amigável)
-var result = await _errorHandler.ExecuteWithRetryAsync(
- () => _providersApi.GetProvidersAsync(pageNumber, pageSize),
- "carregar provedores",
- 3);
-if (result.IsFailure) {
- var userMessage = _errorHandler.HandleApiError(result, "carregar provedores");
- dispatcher.Dispatch(new LoadProvidersFailureAction(userMessage));
-}
-```
-
-**Benefícios**:
-- ✅ Resiliência contra erros transientes (automatic retry)
-- ✅ UX melhorado com mensagens em português
-- ✅ Troubleshooting facilitado com correlation IDs
-- ✅ Logging estruturado de todas as tentativas
-- ✅ Redução de chamadas ao suporte (mensagens auto-explicativas)
-
-**Commit**: c198d889 "feat(sprint-7.13): implement standardized error handling with retry logic"
-
----
-
-### ✅ Sprint 7.14 - Complete Localization (i18n) - CONCLUÍDA (16 Jan 2026)
-
-**Branch**: `fix/aspire-initialization` (continuação)
-
-**Contexto**: Admin Portal precisava de suporte multi-idioma com troca dinâmica de idioma e traduções completas para pt-BR e en-US.
-
-**Objetivos**:
-1. ✅ **LocalizationService com Dictionary-Based Translations**
-2. ✅ **LanguageSwitcher Component**
-3. ✅ **140+ Translation Strings (pt-BR + en-US)**
-4. ✅ **Culture Switching com CultureInfo**
-5. ✅ **OnCultureChanged Event para Reactivity**
-6. ✅ **Documentação de Localização**
-
-**Progresso Atual**: 6/6 objetivos completos ✅ **SPRINT 7.14 CONCLUÍDO 100%!**
-
-**Arquivos Criados**:
-- `Services/LocalizationService.cs` (235 linhas):
- * Dictionary-based translations (pt-BR, en-US)
- * SetCulture(cultureName) - Muda idioma e dispara OnCultureChanged
- * GetString(key) - Retorna string localizada com fallback
- * GetString(key, params) - Formatação com parâmetros
- * SupportedCultures property - Lista de idiomas disponíveis
- * CurrentCulture, CurrentLanguage properties
-- `Components/Common/LanguageSwitcher.razor` (35 linhas):
- * MudMenu com ícone de idioma (🌐)
- * Lista de idiomas disponíveis
- * Check mark no idioma atual
- * Integrado no MainLayout AppBar
-- `docs/localization.md` (550+ linhas): Guia completo de internacionalização
-
-**Arquivos Modificados**:
-- `Program.cs`: builder.Services.AddScoped();
-- `Layout/MainLayout.razor`:
- * @using MeAjudaAi.Web.Admin.Components.Common
- * adicionado antes do menu do usuário
-
-**Traduções Implementadas** (140+ strings):
-
-| Categoria | pt-BR | en-US | Exemplos |
-|-----------|-------|-------|----------|
-| Common (12) | Salvar, Cancelar, Excluir, Editar | Save, Cancel, Delete, Edit | Common.Save, Common.Loading |
-| Navigation (5) | Painel, Provedores, Documentos | Dashboard, Providers, Documents | Nav.Dashboard, Nav.Logout |
-| Providers (9) | Nome, Documento, Status | Name, Document, Status | Providers.Active, Providers.SearchPlaceholder |
-| Validation (4) | Campo obrigatório, E-mail inválido | Field required, Invalid email | Validation.Required |
-| Success (3) | Salvo com sucesso | Saved successfully | Success.SavedSuccessfully |
-| Error (3) | Erro de conexão | Connection error | Error.NetworkError |
-
-**Funcionalidades de Localização**:
-
-| Recurso | Implementação |
-|---------|---------------|
-| Idiomas Suportados | pt-BR (Português Brasil), en-US (English US) |
-| Default Language | pt-BR |
-| Fallback Mechanism | en-US como fallback se string não existe em pt-BR |
-| String Formatting | Suporte a parâmetros: L["Messages.ItemsFound", count] |
-| Culture Switching | CultureInfo.CurrentCulture e CurrentUICulture |
-| Component Reactivity | OnCultureChanged event dispara StateHasChanged |
-| Date/Time Formatting | Automático via CultureInfo (15/12/2024 vs 12/15/2024) |
-| Number Formatting | Automático (R$ 1.234,56 vs $1,234.56) |
-
-**Padrão de Uso**:
-
-```razor
-@inject LocalizationService L
-
-
-@L.GetString("Common.Save")
-
-
-@L.GetString("Providers.ItemsFound", providerCount)
-
-
-@code {
- protected override void OnInitialized()
- {
- L.OnCultureChanged += StateHasChanged;
- }
-}
-```
-
-**Convenções de Nomenclatura**:
-- `{Categoria}.{Ação/Contexto}{Tipo}` - Estrutura hierárquica
-- Common.* - Textos compartilhados
-- Nav.* - Navegação e menus
-- Providers.*, Documents.* - Específico de entidade
-- Validation.* - Mensagens de validação
-- Success.*, Error.* - Feedback de operações
-
-**Benefícios**:
-- ✅ Admin Portal preparado para mercado global
-- ✅ UX melhorado com idioma nativo do usuário
-- ✅ Facilita adição de novos idiomas (es-ES, fr-FR)
-- ✅ Formatação automática de datas/números por cultura
-- ✅ Manutenção centralizada de strings UI
-
-**Futuro (Roadmap de Localization)**:
-- [ ] Persistência de preferência no backend
-- [ ] Auto-detecção de idioma do navegador
-- [ ] Strings para todas as páginas (Dashboard, Documents, etc.)
-- [ ] Pluralização avançada (1 item vs 2 items)
-- [ ] Adicionar es-ES, fr-FR
-- [ ] FluentValidation messages localizadas
-
-**Commit**: 2e977908 "feat(sprint-7.14): implement complete localization (i18n)"
-
----
-
-### ✅ Sprint 7.15 - Package Updates & Resilience Migration (16 Jan 2026)
-
-**Status**: CONCLUÍDA (16 Jan 2026)
-**Duração**: 1 dia
-**Commits**: b370b328, 949b6d3c
-
-**Contexto**: Atualização de rotina de pacotes NuGet revelou deprecação do Polly.Extensions.Http, necessitando migração para Microsoft.Extensions.Http.Resilience (nova API oficial do .NET 10).
-
-#### 📦 Atualizações de Pacotes (39 packages)
-
-**ASP.NET Core 10.0.2**:
-- Microsoft.AspNetCore.Authentication.JwtBearer
-- Microsoft.AspNetCore.OpenApi
-- Microsoft.AspNetCore.TestHost
-- Microsoft.AspNetCore.Components.WebAssembly
-- Microsoft.AspNetCore.Components.WebAssembly.Authentication
-- Microsoft.AspNetCore.Components.WebAssembly.DevServer
-- Microsoft.Extensions.Http (10.2.0)
-- Microsoft.Extensions.Http.Resilience (10.2.0) - **NOVO**
-
-**Entity Framework Core 10.0.2**:
-- Microsoft.EntityFrameworkCore
-- Microsoft.EntityFrameworkCore.Design
-- Microsoft.EntityFrameworkCore.InMemory
-- Microsoft.EntityFrameworkCore.Relational
-- Npgsql.EntityFrameworkCore.PostgreSQL (10.0.0)
-
-**Ferramentas Build (18.0.2)** - Breaking Change:
-- Microsoft.Build (17.14.28 → 18.0.2)
-- Microsoft.Build.Framework (requerido por EF Core Design 10.0.2)
-- Microsoft.Build.Locator
-- Microsoft.Build.Tasks.Core
-- Microsoft.Build.Utilities.Core
-- **Resolução**: Removido pin CVE (CVE-2024-38095 corrigido na 18.0+)
-
-**Azure Storage 12.27.0**:
-- Azure.Storage.Blobs (12.27.0)
-- Azure.Storage.Common (12.25.0 → 12.26.0 - conflito resolvido)
-
-**Outras Atualizações**:
-- System.IO.Hashing (9.0.10 → 10.0.1)
-- Microsoft.CodeAnalysis.Analyzers (3.11.0 → 3.14.0)
-- Refit (9.0.2 → 9.1.2)
-- AngleSharp, AngleSharp.Css (1.2.0 → 1.3.0)
-- ... (total 39 packages)
-
-**Decisão Microsoft.OpenApi**:
-- Testado 3.1.3: **INCOMPATÍVEL** (CS0200 com source generators .NET 10)
-- Mantido 2.3.0: **ESTÁVEL** (funciona perfeitamente)
-- Confirmado 16/01/2026 com SDK 10.0.102
-
-#### 🔄 Migração Polly.Extensions.Http → Microsoft.Extensions.Http.Resilience
-
-**Pacote Removido**:
-```xml
-
-
-```
-
-**Novo Pacote**:
-```xml
-
-```
-
-**Refatoração de Código**:
-
-1. **`PollyPolicies.cs` → `ResiliencePolicies.cs`** (renomeado):
- ```csharp
- // ANTES (Polly.Extensions.Http)
- public static IAsyncPolicy GetRetryPolicy()
- {
- return HttpPolicyExtensions
- .HandleTransientHttpError()
- .WaitAndRetryAsync(3, retryAttempt =>
- TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
- }
-
- // DEPOIS (Microsoft.Extensions.Http.Resilience)
- public static void ConfigureRetry(HttpRetryStrategyOptions options)
- {
- options.MaxRetryAttempts = 3;
- options.Delay = TimeSpan.FromSeconds(2);
- options.BackoffType = DelayBackoffType.Exponential;
- options.ShouldHandle = new PredicateBuilder()
- .HandleResult(response =>
- response.StatusCode >= HttpStatusCode.InternalServerError ||
- response.StatusCode == HttpStatusCode.RequestTimeout);
- }
- ```
-
-2. **`ServiceCollectionExtensions.cs`**:
- ```csharp
- // ANTES
- client.AddPolicyHandler(PollyPolicies.GetRetryPolicy())
- .AddPolicyHandler(PollyPolicies.GetCircuitBreakerPolicy())
- .AddPolicyHandler(PollyPolicies.GetTimeoutPolicy());
-
- // DEPOIS
- client.AddStandardResilienceHandler(options =>
- {
- ResiliencePolicies.ConfigureRetry(options.Retry);
- ResiliencePolicies.ConfigureCircuitBreaker(options.CircuitBreaker);
- ResiliencePolicies.ConfigureTimeout(options.TotalRequestTimeout);
- });
-
- // Upload timeout separado (sem retry)
- client.AddStandardResilienceHandler(options =>
- {
- options.Retry.MaxRetryAttempts = 0; // Disable retry for uploads
- ResiliencePolicies.ConfigureUploadTimeout(options.TotalRequestTimeout);
- });
- ```
-
-**Políticas Configuradas**:
-- **Retry**: 3 tentativas, backoff exponencial (2s, 4s, 8s)
-- **Circuit Breaker**: 50% failure ratio, 5 throughput mínimo, 30s break duration
-- **Timeout**: 30s padrão, 120s para uploads
-
-**Arquivos Impactados**:
-- `Directory.Packages.props` (remoção + adição de pacote)
-- `src/MeAjudaAi.Web.Admin/Infrastructure/Http/ResiliencePolicies.cs` (renomeado e refatorado)
-- `src/MeAjudaAi.Web.Admin/Infrastructure/Extensions/ServiceCollectionExtensions.cs` (nova API)
-
-#### ✅ Resultados
-
-**Build Status**:
-- ✅ 0 erros de compilação
-- ✅ 10 warnings pré-existentes (analyzers - não relacionados)
-- ✅ Todos os 1245 testes passando
-
-**Comportamento Mantido**:
-- ✅ Retry logic idêntico
-- ✅ Circuit breaker configuração equivalente
-- ✅ Timeouts diferenciados (standard vs upload)
-- ✅ HTTP resilience sem quebras
-
-**Compatibilidade**:
-- ✅ .NET 10.0.2 LTS (suporte até Nov 2028)
-- ✅ EF Core 10.0.2
-- ✅ Microsoft.Build 18.0.2 (última stable)
-- ✅ Npgsql 10.x + Hangfire.PostgreSql 1.20.13
-
-**Technical Debt Removido**:
-- ✅ Deprecated package eliminado (Polly.Extensions.Http)
-- ✅ Migração para API oficial Microsoft (.NET 10)
-- ✅ CVE pin removido (Microsoft.Build CVE-2024-38095)
-
-**Lições Aprendidas**:
-- Microsoft.OpenApi 3.1.3 incompatível com source generators .NET 10 (CS0200 read-only property)
-- Microsoft.Build breaking change (17.x → 18.x) necessário para EF Core Design 10.0.2
-- AddStandardResilienceHandler simplifica configuração (3 chamadas → 1 com options)
-- Upload timeout requer retry desabilitado (MaxRetryAttempts = 0)
-
-**Commits**:
-- `b370b328`: "chore: update 39 nuget packages to latest stable versions"
-- `949b6d3c`: "refactor: migrate from Polly.Extensions.Http to Microsoft.Extensions.Http.Resilience"
+**Decisão de antecipação**: NX foi antecipado do pós-MVP para o Sprint 8B.2 porque o Provider App (Sprint 8C) e a migração Admin (Sprint 8D) se beneficiam diretamente do workspace unificado. Criar o NX antes desses projetos evita migração posterior mais custosa.
---
-
-### ✅ Sprint 7.20 - Dashboard Charts & Data Mapping Fixes (5 Fev 2026)
-
-**Status**: CONCLUÍDA (5 Fev 2026)
-**Duração**: 1 dia
-**Branch**: `fix/aspire-initialization` (continuação)
-
-**Contexto**: Dashboard charts estavam exibindo mensagens de debug e o gráfico "Provedores por Tipo" estava vazio devido a incompatibilidade de mapeamento JSON entre backend e frontend.
-
-#### 🎯 Objetivos
-
-1. ✅ **Remover Mensagens de Debug** - Eliminar "Chart disabled for debugging"
-2. ✅ **Corrigir Gráfico Vazio** - Resolver problema de dados ausentes em "Provedores por Tipo"
-3. ✅ **Implementar Mapeamento JSON Correto** - Alinhar propriedades backend/frontend
-4. ✅ **Adicionar Helper Methods** - Criar métodos de formatação localizados
-
-#### 🔍 Problema Identificado
-
-**Root Cause**: Property name mismatch entre backend e frontend
-
-- **Backend API** (`ProviderDto`): Retorna JSON com propriedade `type: 1`
-- **Frontend DTO** (`ModuleProviderDto`): Esperava propriedade `ProviderType`
-- **Resultado**: `ProviderType` ficava `null` no frontend, causando gráfico vazio
-
-**Investigação**:
-1. ✅ Verificado `DevelopmentDataSeeder.cs` - Dados de seed CONTÊM tipos ("Individual", "Company")
-2. ✅ Analisado `GetProvidersEndpoint.cs` - Retorna `ProviderDto` com propriedade `Type`
-3. ✅ Inspecionado `ModuleProviderDto.cs` - Propriedade chamada `ProviderType` (mismatch!)
-4. ✅ Confirmado via `ProvidersEffects.cs` - Usa `IProvidersApi.GetProvidersAsync`
-
-#### 🛠️ Soluções Implementadas
-
-**1. JSON Property Mapping** ✅:
-```csharp
-// src/Contracts/Contracts/Modules/Providers/DTOs/ModuleProviderDto.cs
-using System.Text.Json.Serialization;
-
-public sealed record ModuleProviderDto(
- Guid Id,
- string Name,
- string Email,
- string Document,
- [property: JsonPropertyName("type")] // ← FIX: Mapeia "type" do JSON para "ProviderType"
- string ProviderType,
- string VerificationStatus,
- DateTime CreatedAt,
- DateTime UpdatedAt,
- bool IsActive,
- string? Phone = null);
-```
-
-**2. Debug Messages Removal** ✅:
-```razor
-
-
-
- Chart disabled for debugging
- @if (ProvidersState.Value.Providers.Count > 0)
-
-
-
- @if (ProvidersState.Value.Providers.Count > 0)
-```
-
-**3. Display Name Helper** ✅:
-```csharp
-// Dashboard.razor @code
-private string GetProviderTypeDisplayName(ProviderType type)
-{
- return type switch
- {
- ProviderType.Individual => "Pessoa Física",
- ProviderType.Company => "Pessoa Jurídica",
- _ => type.ToString()
- };
-}
-```
-
-**4. Chart Logic Simplification** ✅:
-```csharp
-// Removido c├│digo complexo de parsing int
-// ANTES: int.TryParse(g.Key, out int typeValue) + ProviderTypeOrderInts lookup
-// DEPOIS: Enum.TryParse(g.Key, true, out var typeEnum) + GetProviderTypeDisplayName()
-```
-
-#### 📊 Arquivos Modificados
-
-| Arquivo | Mudanças | LOC |
-|---------|----------|-----|
-| `ModuleProviderDto.cs` | Adicionado `[JsonPropertyName("type")]` e using | +3 |
-| `Dashboard.razor` | Removido debug text, adicionado helper method | +12, -15 |
-
-#### ✅ Resultados Alcançados
-
-- ✅ **Gráfico "Provedores por Tipo"**: Agora exibe dados corretamente
-- ✅ **Mensagens de Debug**: Removidas de ambos os gráficos
-- ✅ **Build**: Sucesso sem erros (0 errors, 0 warnings)
-- ✅ **Mapeamento JSON**: Backend `type` → Frontend `ProviderType` funcionando
-- ✅ **Localização**: Labels em português ("Pessoa Física", "Pessoa Jurídica")
-
-#### 🎓 Lições Aprendidas
-
-1. **Property Naming Conventions**: Backend usa nomes curtos (`Type`), Frontend usa nomes descritivos (`ProviderType`)
-2. **JSON Serialization**: `[JsonPropertyName]` é essencial para alinhar DTOs entre camadas
-3. **Record Positional Parameters**: Atributos requerem `[property: ...]` syntax
-4. **Debug Messages**: Sempre remover antes de merge para evitar confusão em produção
-
-#### 🔮 Próximos Passos
-
-- [ ] Implementar "Atividades Recentes" (ver Fase 3+)
-- [ ] Adicionar mais gráficos ao Dashboard (distribuição geográfica, documentos pendentes)
-- [ ] Criar testes bUnit para componentes de gráficos
-
-**Commits**:
-- [hash]: "fix: add JsonPropertyName mapping for ProviderType in ModuleProviderDto"
-- [hash]: "fix: remove debug messages and simplify chart logic in Dashboard"
-
----
-
-### ✅ Sprint 7.16 - Technical Debt Sprint (17-21 Jan 2026)
-
-**Status**: ✅ CONCLUÍDA (17-21 Jan 2026)
-**Duração**: 1 semana (5 dias úteis)
-**Objetivo**: Reduzir débito técnico ANTES de iniciar Customer App
-
-**Justificativa**:
-- Customer App adicionará ~5000+ linhas de código novo
-- Melhor resolver débitos do Admin Portal ANTES de replicar patterns
-- Keycloak automation é BLOQUEADOR para Customer App (precisa de novo cliente OIDC)
-- Quality improvements estabelecem padr├╡es para Customer App
-
----
-
-#### 📋 Tarefas Planejadas
-
-##### 1. 🔐 Keycloak Client Automation (Dia 1-2, ~1 dia) - **BLOQUEADOR**
-
-**Prioridade**: CRÍTICA - Customer App precisa de cliente OIDC "meajudaai-customer"
-
-**Entregáveis**:
-- [ ] Script `infrastructure/keycloak/setup-keycloak-clients.ps1`
- * Valida Keycloak rodando (HTTP health check)
- * Obtém token admin via REST API
- * Cria realm "MeAjudaAi" (se não existir)
- * Cria clientes "meajudaai-admin" e "meajudaai-customer" (OIDC, PKCE)
- * Configura Redirect URIs (localhost + produção)
- * Cria roles "admin", "customer"
- * Cria usuários demo (admin@meajudaai.com.br, customer@meajudaai.com.br)
- * Exibe resumo de configuração
-- [ ] Atualizar `docs/keycloak-admin-portal-setup.md` com seção "Automated Setup"
-- [ ] Integrar script em `scripts/dev.ps1` (opcional - chamar setup-keycloak-clients.ps1)
-
-**API Keycloak Admin REST**:
-- Endpoint: `POST /auth/admin/realms/{realm}/clients`
-- Autenticação: Bearer token
-
-**Benefícios**:
-- ✅ Customer App pronto para desenvolvimento (cliente configurado)
-- ✅ Onboarding em 1 comando: `.\setup-keycloak-clients.ps1`
-- ✅ Elimina 15 passos manuais documentados
-
----
-
-##### 2. 🎨 Frontend Analyzer Warnings (Dia 2-3, ~1 dia)
-
-**Prioridade**: ALTA - Code quality antes de expandir codebase
-
-**Warnings a Resolver**:
-
-**S2094 - Empty Records (6 ocorrências)**:
-```csharp
-// ANTES
-public sealed record LoadProvidersAction { }
-
-// DEPOIS - Opção 1: Adicionar propriedade útil
-public sealed record LoadProvidersAction
-{
- public bool ForceRefresh { get; init; }
-}
-
-// DEPOIS - Opção 2: Justificar supressão
-#pragma warning disable S2094 // Empty action by design (Redux pattern)
-public sealed record LoadProvidersAction { }
-#pragma warning restore S2094
-```
-
-**S2953 - Dispose Pattern (1 ocorrência)**:
-```csharp
-// ANTES: App.razor
-public void Dispose() { ... }
-
-// DEPOIS
-public class App : IDisposable
-{
- public void Dispose() { ... }
-}
-```
-
-**S2933 - Readonly Fields (1 ocorrência)**:
-```csharp
-// ANTES
-private MudTheme _theme = new();
-
-// DEPOIS
-private readonly MudTheme _theme = new();
-```
-
-**MUD0002 - Casing (3 ocorrências)**:
-```razor
-
-
-
-
-
-```
-
-**Entregáveis**:
-- [ ] Resolver todos os 11 warnings (ou justificar supress├╡es)
-- [ ] Remover regras do `.editorconfig` após correção
-- [ ] Build com **0 warnings**
-
----
-
-##### 3. 📊 Frontend Test Coverage (Dia 3-5, ~1-2 dias)
-
-**Prioridade**: ALTA - Confiança em Admin Portal antes de Customer App
-
-**Meta**: 10 → 30-40 testes bUnit
-
-**Testes Novos (20-30 testes)**:
-
-**Fluxor State Management (8 testes)**:
-- `ProvidersReducers`: LoadSuccess, LoadFailure, SetFilters, SetSorting
-- `DocumentsReducers`: UploadSuccess, VerificationUpdate
-- `ServiceCatalogsReducers`: CreateSuccess, UpdateSuccess
-
-**Components (12 testes)**:
-- `Providers.razor`: rendering, search, pagination (3 testes)
-- `Documents.razor`: upload workflow, verification (3 testes)
-- `CreateProviderDialog`: form validation, submit (2 testes)
-- `EditProviderDialog`: data binding, update (2 testes)
-- `LanguageSwitcher`: culture change, persistence (2 testes)
-
-**Services (5 testes)**:
-- `LocalizationService`: SetCulture, GetString, fallback
-- `ErrorHandlingService`: retry logic, status mapping
-
-**Effects (3 testes)**:
-- Mock `IProvidersApi.GetPagedProvidersAsync`
-- Verificar dispatches Success/Failure
-- Testar error handling
-
-**Infraestrutura**:
-- Criar `TestContext` base reutilizável
-- Configurar `JSRuntimeMode.Loose`
-- Registrar `MudServices` e `Fluxor`
-
-**Entregáveis**:
-- [ ] 30-40 testes bUnit (3x aumento)
-- [ ] Cobertura ~40-50% de componentes críticos
-- [ ] CI/CD passing (master-ci-cd.yml)
-
----
-
-##### 4. 📝 Records Standardization (Dia 5, ~0.5 dia)
-
-**Prioridade**: MÉDIA - Padronização importante
-
-**Objetivo**: Padronizar uso de `record class` vs `record` vs `class` no projeto.
-
-**Auditoria**:
-```powershell
-# Buscar todos os records no projeto
-Get-ChildItem -Recurse -Include *.cs | Select-String "record "
-```
-
-**Padr├╡es a Estabelecer**:
-- DTOs: `public record Dto` (imutável)
-- Requests: `public sealed record Request` (imutável)
-- Responses: `public sealed record Response` (imutável)
-- Fluxor Actions: `public sealed record Action` (imutável)
-- Fluxor State: `public sealed record State` (imutável)
-- Entities: `public class ` (mutável, EF Core)
-
-**Entregáveis**:
-- [ ] Documentar padrão em `docs/architecture.md` seção "C# Coding Standards"
-- [ ] Converter records inconsistentes (se necessário)
-- [ ] Adicionar analyzer rule para enforcement futuro
-
----
-
-##### 5. 🧪 SearchProviders E2E Tests ⚪ MOVIDO PARA SPRINT 9
-
-**Prioridade**: MÉDIA - MOVIDO PARA SPRINT 9 (Buffer)
-
-**Objetivo**: Testar busca geolocalizada end-to-end.
-
-**Status**: ⚪ MOVIDO PARA SPRINT 9 - Task opcional, não crítica para Customer App
-
-**Justificativa da Movimentação**:
-- Sprint 7.16 completou 4/4 tarefas obrigat├│rias (Keycloak, Warnings, Tests, Records)
-- E2E tests marcados como OPCIONAL desde o planejamento
-- Não bloqueiam Sprint 8 (Customer App)
-- Melhor executar com calma no Sprint 9 (Buffer) sem pressão de deadline
-
-**Entregáveis** (serão executados no Sprint 9):
-- [ ] Teste: Buscar providers por serviço + raio (2km, 5km, 10km)
-- [ ] Teste: Validar ordenação por distância
-- [ ] Teste: Validar restrição geográfica (AllowedCities)
-- [ ] Teste: Performance (<500ms para 1000 providers)
-
-**Estimativa**: 1-2 dias (Sprint 9)
-
----
-
-#### 📊 Resultado Esperado Sprint 7.16
-
-**Débito Técnico Reduzido**:
-- ✅ Keycloak automation completo (bloqueador removido)
-- ✅ 0 warnings no Admin Portal (S2094, S2953, S2933, MUD0002)
-- ✅ 30-40 testes bUnit (confiança 3x maior)
-- ✅ Records padronizados (consistência)
-- ⚪ SearchProviders E2E (MOVIDO para Sprint 9 - não crítico)
-
-**Quality Metrics**:
-- **Build**: 0 errors, 0 warnings
-- **Tests**: 1245 backend + 43 frontend bUnit = **1288 testes**
-- **Coverage**: Backend 90.56% (frontend bUnit sem métrica - foco em quantidade de testes)
-- **Technical Debt**: Reduzido de 313 linhas → ~150 linhas
-
-**Pronto para Customer App**:
-- ✅ Keycloak configurado (cliente meajudaai-customer)
-- ✅ Admin Portal com qualidade máxima (patterns estabelecidos)
-- ✅ Test infrastructure robusta (replicável no Customer App)
-- ✅ Zero distrações (débito técnico minimizado)
-
-**Commits Estimados**:
-- `feat(sprint-7.16): add Keycloak client automation script`
-- `fix(sprint-7.16): resolve all frontend analyzer warnings`
-- `test(sprint-7.16): increase bUnit coverage to 30-40 tests`
-- `refactor(sprint-7.16): standardize record usage across project`
-
----
-
-### ✅ Sprint 8A - Customer Web App (Concluída)
-
-**Status**: CONCLUÍDA (5-13 Fev 2026)
-**Foco**: Refinamento de Layout e UX (Home & Search)
-
-**Atividades Realizadas**:
-1. **Home Page Layout Refinement** ✅
- - Restaurada seção "Como funciona?" (How It Works) após "Conheça o MeAjudaAí".
- - Ajustado posicionamento para melhorar fluxo de conteúdo (Promessa -> Confiança -> Processo).
- - Corrigidos warnings de imagens (aspect ratio, sizes).
- - Ajustados espaçamentos e alinhamentos (Hero, City Search vertical center).
-
-2. **Search Page Layout & UX** ✅
- - Removido limite de largura (`max-w-6xl`) para aproveitar tela cheia.
- - Service Tags movidas para largura total, centralizadas em desktop.
- - Mock de Service Tags atualizado para "Top 10 Serviços Populares" (Pedreiro, Eletricista, etc.).
- - Melhorada experiência em mobile com scroll horizontal.
-
-**Pr├│ximos Passos (Imediato)**:
-- Integrar Service Tags com backend real (popularidade/regional).
-- Implementar filtros avançados.
-
----
-
-### ✅ Sprint 8B - Authentication & Onboarding Flow - CONCLUÍDO
-
-**Periodo Estimado**: 19 Fev - 4 Mar 2026
-**Foco**: Fluxos de Cadastro e Login Segmentados (Cliente vs Prestador)
-
-**Regras de Neg├│cio e UX**:
-
-**1. Ponto de Entrada Unificado**
-- Botão "Cadastre-se Grátis" na Home/Header.
-- **Modal de Seleção** (Inspirado em referência visual):
- - Opção A: "Quero ser cliente" (Encontrar melhores acompanhantes/prestadores).
- - Opção B: "Sou prestador" (Divulgar serviços).
-
-**2. Fluxo do Cliente (Customer Flow)**
-- **Login/Cadastro**:
- - Social Login: Google, Facebook, Instagram.
- - Manual: Email + Senha.
-- **Dados**:
- - Validar necessidade de endereço (Possivelmente opcional no cadastro, obrigatório no agendamento).
-
-**3. Fluxo do Prestador (Provider Flow)**
-- **Redirecionamento**: Ao clicar em "Sou prestador", redirecionar para landing page específica de prestadores (modelo visual referência #3).
-- **Etapa 1: Cadastro Básico**:
- - Social Login ou Manual.
- - Dados Básicos: Nome, Telefone/WhatsApp (validado via OTP se possível).
-- **Etapa 2: Verificação de Segurança (Obrigatória)**:
- - Upload de Documentos (RG/CNH).
- - Validação de Antecedentes Criminais.
- - Biometria Facial (Liveness Check) para evitar fraudes.
-- **Conformidade LGPD & Segurança**:
- - **Consentimento Explícito**: Coleta de aceite inequívoco para tratamento de dados sensíveis (biometria, antecedentes), detalhando finalidade e base legal (Prevenção à Fraude/Legítimo Interesse).
- - **Política de Retenção**: Definição clara de prazos de armazenamento e fluxo de exclusão automática após inatividade ou solicitação.
- - **Operadores de Dados**: Contratos com vendors (ex: serviço de biometria) exigindo compliance LGPD/GDPR e Acordos de Processamento de Dados (DPA).
- - **Direitos do Titular**: Fluxos automatizados para solicitação de exportação (portabilidade) e anonimização/exclusão de dados.
- - **DPIA**: Realização de Relatório de Impacto à Proteção de Dados (RIPD) específico para o tratamento de dados biométricos.
- - **Segurança**: Criptografia em repouso (AES-256) e em trânsito (TLS 1.3). Divulgação transparente do uso de reCAPTCHA v3 e seus termos.
-- **Proteção**: Integração com Google reCAPTCHA v3 em todo o fluxo.
-
-**Entregáveis**:
-- [ ] Componente `AuthModal` com seleção de perfil.
-- [ ] Integração `NextAuth.js` com Providers (Google, FB, Instagram) e Credentials.
-- [ ] Página de Onboarding de Prestadores (Step-by-step wizard).
-- [ ] Integração com serviço de verificação de documentos/biometria.
-
----
-
-### Γ£à Sprint 8C - Provider Web App (React + NX) - CONCLUÍDO (21 Mar 2026)
-
-**Periodo Real**: 19 Mar - 21 Mar 2026
-**Foco**: App de Administração de Perfil para Prestadores
-**Status**: Γ£à CONCLUÍDO
-
-**Entregáveis Implementados**:
-- ✅ **Nx Integration**: `MeAjudaAi.Web.Provider` integrado ao workspace Nx
-- ✅ **Onboarding/basic-info**: Conectado à API (`apiMeGet`/`apiMePut`) com validação Zod
-- ✅ **Onboarding/documents**: Upload via SAS URL para Azure Blob Storage
-- ✅ **Dashboard Real Data**: Página principal (`/`) substituída por dados reais via `apiMeGet`
-- ✅ **Provider Public Profile**: Nova rota `/provider/[slug]` para perfis públicos com slugs SEO-friendly
-- ✅ **Provider Profile Management**:
- - `/alterar-dados` - Edição completa via `apiMePut`
- - `/configuracoes` - Toggle de visibilidade + delete account com confirmação LGPD
-- ✅ **Slug URLs**: Perfis públicos acessíveis via slugs (ex: `/provider/joao-silva-a1b2c3d4`)
-
----
-
-### ⏳ Sprint 8D - Admin Portal Migration (Blazor → React + NX)
-
-**Periodo Estimado**: 2 - 15 Abr 2026
-**Foco**: Migração do Admin Portal de Blazor WASM para React dentro do workspace NX
-**Branch**: (a ser criada: `feature/sprint-8d-admin-migration`)
-
-**Contexto**: Terceiro app React no workspace NX. Reutiliza padr├╡es e shared libs consolidados pelo Customer (Sprint 8A) e Provider App (Sprint 8C). Elimina dual-stack (Blazor + React) em favor de single-stack React.
-
-**Escopo**:
-- Criar `apps/admin-web` dentro do workspace NX (Next.js + Tailwind v4).
-- Migrar todas as funcionalidades existentes do Blazor Admin Portal:
- - Dashboard com KPIs e gráficos (Providers por status/tipo)
- - CRUD Providers (Create, Update, Delete, Verify)
- - Gestão de Documentos (Upload, Verificação, Rejeição)
- - Gestão de Service Catalogs (Categorias + Serviços)
- - Gestão de Restrições Geográficas (AllowedCities)
- - Dark Mode, Localização (i18n), Acessibilidade
-- Substituir Fluxor por Zustand ou Redux Toolkit (state management React).
-- Substituir Refit/C# DTOs por `libs/api-client` (gerado via OpenAPI ou manual).
-- Manter autenticação Keycloak (cliente `meajudaai-admin`).
-- Estilo visual unificado com Customer e Provider Apps.
-- Remover projeto Blazor WASM após migração completa e validação.
-
----
-
-### ⏳ Sprint 8E - Mobile App (React Native + Expo)
-
-**Periodo Estimado**: 16 - 29 Abr 2026
-**Foco**: App Mobile Nativo (iOS/Android) com Expo
-**Branch**: (a ser criada: `feature/sprint-8e-mobile-app`)
-
-**Escopo**:
-- Criar `apps/mobile` dentro do workspace NX (React Native + Expo).
-- Portar funcionalidades do Customer Web App para Mobile.
-- Reutilizar lógica de negócio e autenticação via shared libs NX.
-- Notificações Push.
-
----
-
-**Status**: SKIPPED durante Parts 10-15 (escopo muito grande)
-**Prioridade**: Alta (recomendado antes do MVP)
-**Estimativa**: 3-5 dias de sprint dedicado
-
-**Contexto**: A Part 13 foi intencionalmente pulada durante a implementação das Parts 10-15 (melhorias menores) por ser muito extensa e merecer um sprint dedicado. Testes unitários frontend são críticos para manutenibilidade e confiança no código, mas requerem setup completo de infraestrutura de testes.
-
-**Escopo Planejado**:
-
-**1. Infraestrutura de Testes** (1 dia):
-- Criar projeto `MeAjudaAi.Web.Admin.Tests`
-- Adicionar pacotes: bUnit, Moq, FluentAssertions, xUnit
-- Configurar test host e service mocks
-- Setup de TestContext base reutilizável
-
-**2. Testes de Fluxor State Management** (1-2 dias):
-- **Reducers**: 15+ testes para state mutations
- * ProvidersReducers: LoadSuccess, LoadFailure, SetFilters, SetSorting
- * DocumentsReducers: UploadSuccess, VerificationUpdate
- * ServiceCatalogsReducers: CRUD operations
- * LocationsReducers: LoadCities, FilterByState
- * ErrorReducers: SetGlobalError, ClearError, RetryAfterError
-- **Actions**: Verificar payloads corretos
-- **Features**: Initial state validation
-
-**3. Testes de Effects** (1 dia):
-- Mock de IProvidersApi, IDocumentsApi, IServiceCatalogsApi
-- Test de retry logic em ErrorHandlingService
-- Verificar dispatches corretos (Success/Failure actions)
-- Test de autorização e permissões
-
-**4. Testes de Componentes** (1-2 dias):
-- **Pages**:
- * Providers.razor: rendering, search, pagination
- * Documents.razor: upload, verification workflow
- * ServiceCatalogs.razor: category/service CRUD
- * Dashboard.razor: charts rendering
-- **Dialogs**:
- * CreateProviderDialog: form validation
- * EditProviderDialog: data binding
- * UploadDocumentDialog: file upload mock
- * VerifyProviderDialog: status change
-- **Shared Components**:
- * LanguageSwitcher: culture change
- * LiveRegionAnnouncer: accessibility
- * ErrorBoundaryContent: error recovery
-
-**5. Testes de Serviços** (0.5 dia):
-- LocalizationService: culture switching, string retrieval
-- ErrorHandlingService: retry logic, status code mapping
-- LiveRegionService: announcement queue
-- ErrorLoggingService: correlation IDs
-- PermissionService: policy checks
-
-**Meta de Cobertura**:
-- **Reducers**: >95% (lógica pura, fácil de testar)
-- **Effects**: >80% (com mocks de APIs)
-- **Components**: >70% (rendering e interações básicas)
-- **Services**: >90% (l├│gica de neg├│cio)
-- **Geral**: >80% code coverage
-
-**Benefícios Esperados**:
-- ✅ Confidence em refactorings futuros
-- ✅ Documentação viva do comportamento esperado
-- ✅ Detecção precoce de regressões
-- ✅ Facilita onboarding de novos devs
-- ✅ Reduz bugs em produção
-
-**Ferramentas e Patterns**:
-```csharp
-// Exemplo de teste de Reducer
-[Fact]
-public void LoadProvidersSuccessAction_Should_UpdateState()
-{
- // Arrange
- var initialState = new ProvidersState(isLoading: true, providers: []);
- var providers = new List { /* mock data */ };
- var action = new LoadProvidersSuccessAction(providers, totalItems: 10, pageNumber: 1, pageSize: 10);
-
- // Act
- var newState = ProvidersReducers.OnLoadProvidersSuccess(initialState, action);
-
- // Assert
- newState.IsLoading.Should().BeFalse();
- newState.Providers.Should().HaveCount(1);
- newState.TotalItems.Should().Be(10);
-}
-
-// Exemplo de teste de Component
-[Fact]
-public void LanguageSwitcher_Should_ChangeCulture()
-{
- // Arrange
- using var ctx = new TestContext();
- ctx.Services.AddScoped();
- var component = ctx.RenderComponent();
-
- // Act
- var enButton = component.Find("button[data-lang='en-US']");
- enButton.Click();
-
- // Assert
- var localization = ctx.Services.GetRequiredService();
- localization.CurrentCulture.Name.Should().Be("en-US");
-}
-```
-
-**Priorização Sugerida**:
-1. **Crítico (antes do MVP)**: Reducers + Effects + ErrorHandlingService
-2. **Importante (pré-MVP)**: Componentes principais (Providers, Documents)
-3. **Nice-to-have (p├│s-MVP)**: Componentes de UI (dialogs, shared)
-
-**Recomendação**: Implementar em **Sprint 8.5** (entre Customer App e Buffer) ou dedicar 1 semana do Sprint 9 (Buffer) para esta tarefa. Frontend tests são investimento de longo prazo essencial para manutenibilidade.
-
----
-
-### ✅ Sprint 7 - Blazor Admin Portal Features - CONCLUÍDA (6-7 Jan 2026)
-
-**Branch**: `blazor-admin-portal-features` (MERGED to master)
-
-**Objetivos**:
-1. ✅ **CRUD Completo de Providers** (6-7 Jan 2026) - Create, Update, Delete, Verify
-2. ✅ **Gestão de Documentos** (7 Jan 2026) - Upload, verificação, deletion workflow
-3. ✅ **Gestão de Service Catalogs** (7 Jan 2026) - CRUD de categorias e serviços
-4. ✅ **Gestão de Restrições Geográficas** (7 Jan 2026) - UI para AllowedCities com banco de dados
-5. ✅ **Gráficos Dashboard** (7 Jan 2026) - MudCharts com providers por status e evolução temporal
-6. ✅ **Testes** (7 Jan 2026) - Aumentar cobertura para 30 testes bUnit
-
-**Progresso Atual**: 6/6 features completas ✅ **SPRINT 7 CONCLUÍDO 100%!**
-
-**Detalhamento - Provider CRUD** ✅:
-- IProvidersApi enhanced: CreateProviderAsync, UpdateProviderAsync, DeleteProviderAsync, UpdateVerificationStatusAsync
-- CreateProviderDialog: formulário completo com validação (ProviderType, Name, FantasyName, Document, Email, Phone, Description, Address)
-- EditProviderDialog: edição simplificada (nome/telefone - aguardando DTO enriquecido do backend)
-- VerifyProviderDialog: mudança de status de verificação (Verified, Rejected, Pending + optional notes)
-- Providers.razor: action buttons (Edit, Delete, Verify) com MessageBox confirmation
-- Result error handling pattern em todas operações
-- Portuguese labels + Snackbar notifications
-- Build sucesso (19 warnings Sonar apenas)
-- Commit: cd2be7f6 "feat(admin): complete Provider CRUD operations"
-
-**Detalhamento - Documents Management** ✅:
-- DocumentsState/Actions/Reducers/Effects: Fluxor pattern completo
-- Documents.razor: página com provider selector e listagem de documentos
-- MudDataGrid com status chips coloridos (Verified=Success, Rejected=Error, Pending=Warning, Uploaded=Info)
-- ProviderSelectorDialog: seleção de provider da lista existente
-- UploadDocumentDialog: MudFileUpload com tipos de documento (RG, CNH, CPF, CNPJ, Comprovante, Outros)
-- RequestVerification action via IDocumentsApi.RequestDocumentVerificationAsync
-- DeleteDocument com confirmação MessageBox
-- Real-time status updates via Fluxor Dispatch
-- Portuguese labels + Snackbar notifications
-- Build sucesso (28 warnings Sonar apenas)
-- Commit: e033488d "feat(admin): implement Documents management feature"
-
-**Detalhamento - Service Catalogs** ✅:
-- IServiceCatalogsApi enhanced: 10 métodos (Create, Update, Delete, Activate, Deactivate para Categories e Services)
-- ServiceCatalogsState/Actions/Reducers/Effects: Fluxor pattern completo
-- Categories.razor: full CRUD page com MudDataGrid, status chips, action buttons
-- Services.razor: full CRUD page com category relationship e MudDataGrid
-- CreateCategoryDialog, EditCategoryDialog: forms com Name, Description, DisplayOrder
-- CreateServiceDialog, EditServiceDialog: forms com CategoryId (dropdown), Name, Description, DisplayOrder
-- Activate/Deactivate toggles para ambos
-- Delete confirmations com MessageBox
-- Portuguese labels + Snackbar notifications
-- Build sucesso (37 warnings Sonar/MudBlazor apenas)
-- Commit: bd0c46b3 "feat(admin): implement Service Catalogs CRUD (Categories + Services)"
-
-**Detalhamento - Geographic Restrictions** ✅:
-- ILocationsApi já possuía CRUD completo (Create, Update, Delete, GetAll, GetById, GetByState)
-- LocationsState/Actions/Reducers/Effects: Fluxor pattern completo
-- AllowedCities.razor: full CRUD page com MudDataGrid
-- CreateAllowedCityDialog: formulário com City, State, Country, Latitude, Longitude, ServiceRadiusKm, IsActive
-- EditAllowedCityDialog: mesmo formulário para edição
-- MudDataGrid com coordenadas em formato F6 (6 decimais), status chips (Ativa/Inativa)
-- Toggle activation via MudSwitch (updates backend via UpdateAllowedCityAsync)
-- Delete confirmation com MessageBox
-- Portuguese labels + Snackbar notifications
-- Build sucesso (42 warnings Sonar/MudBlazor apenas)
-- Commit: 3317ace3 "feat(admin): implement Geographic Restrictions - AllowedCities UI"
-
-**Detalhamento - Dashboard Charts** ✅:
-- Dashboard.razor enhanced com 2 charts interativos (MudBlazor built-in charts)
-- Provider Status Donut Chart: agrupa providers por VerificationStatus (Verified, Pending, Rejected)
-- Provider Type Pie Chart: distribuição entre Individual (Pessoa Física) e Company (Empresa)
-- Usa ProvidersState existente (sem novos endpoints de backend)
-- OnAfterRender lifecycle hook para update de dados quando providers carregam
-- UpdateChartData() método com GroupBy LINQ queries
-- Portuguese labels para tipos de provider
-- Empty state messages quando não há providers cadastrados
-- MudChart components com Width="300px", Height="300px", LegendPosition.Bottom
-- Build sucesso (43 warnings Sonar/MudBlazor apenas)
-- Commit: 0e0d0d81 "feat(admin): implement Dashboard Charts with MudBlazor"
-
-**Detalhamento - Testes bUnit** ✅:
-- 30 testes bUnit criados (objetivo: 30+) - era 10, adicionados 20 novos
-- CreateProviderDialogTests: 4 testes (form fields, submit button, provider type, MudForm)
-- DocumentsPageTests: 5 testes (provider selector, upload button, loading, document list, error)
-- CategoriesPageTests: 4 testes (load action, create button, list, loading)
-- ServicesPageTests: 3 testes (load actions, create button, list)
-- AllowedCitiesPageTests: 4 testes (load action, create button, list, loading)
-- Todos seguem pattern: Mock IState/IDispatcher/IApi, AddMudServices, JSRuntimeMode.Loose
-- Verificam rendering, state management, user interactions
-- Namespaces corrigidos: Modules.*.DTOs
-- Build sucesso (sem erros de compilação)
-- Commit: 2a082840 "test(admin): increase bUnit test coverage to 30 tests"
-
----
-
-### ✅ Sprint 6 - Blazor Admin Portal Setup - CONCLUÍDA (30 Dez 2025 - 5 Jan 2026)
-
-**Status**: MERGED to master (5 Jan 2026)
-
-**Principais Conquistas**:
-1. **Projeto Blazor WASM Configurado** ✅
- - .NET 10 com target `net10.0-browser`
- - MudBlazor 7.21.0 (Material Design UI library)
- - Fluxor 6.1.0 (Redux-pattern state management)
- - Refit 9.0.2 (Type-safe HTTP clients)
- - Bug workaround: `CompressionEnabled=false` (static assets .NET 10)
-
-2. **Autenticação Keycloak OIDC Completa** ✅
- - Microsoft.AspNetCore.Components.WebAssembly.Authentication
- - Login/Logout flows implementados
- - Authentication.razor com 6 estados (LoggingIn, CompletingLoggingIn, etc.)
- - BaseAddressAuthorizationMessageHandler configurado
- - **Token Storage**: SessionStorage (Blazor WASM padrão)
- - **Refresh Strategy**: Automático via OIDC library (silent refresh)
- - **SDKs Refit**: Interfaces manuais com atributos Refit (não code-generated)
- - Documentação completa em `docs/keycloak-admin-portal-setup.md`
-
-3. **Providers Feature (READ-ONLY)** ✅
- - Fluxor store completo (State/Actions/Reducers/Effects)
- - MudDataGrid com paginação server-side
- - IProvidersApi via Refit com autenticação
- - PagedResult correto (Client.Contracts.Api)
- - VERIFIED_STATUS constant (type-safe)
- - Portuguese error messages
-
-4. **Dashboard com KPIs** ✅
- - 3 KPIs: Total Providers, Pending Verifications, Active Services
- - IServiceCatalogsApi integrado (contagem real de serviços)
- - MudCards com Material icons
- - Fluxor stores para Dashboard state
- - Loading states e error handling
-
-5. **Dark Mode com Fluxor** ✅
- - ThemeState management (IsDarkMode boolean)
- - Toggle button em MainLayout
- - MudThemeProvider two-way binding
-
-6. **Layout Base** ✅
- - MainLayout.razor com MudDrawer + MudAppBar
- - NavMenu.razor com navegação
- - User menu com AuthorizeView
- - Responsive design (Material Design)
-
-7. **Testes bUnit + xUnit** ✅
- - 10 testes criados (ProvidersPageTests, DashboardPageTests, DarkModeToggleTests)
- - JSInterop mock configurado (JSRuntimeMode.Loose)
- - MudServices registrados em TestContext
- - CI/CD integration (master-ci-cd.yml + pr-validation.yml)
-
-8. **Localização Portuguesa** ✅
- - Todos comentários inline em português
- - Mensagens de erro em português
- - UI messages traduzidas (Authentication.razor)
- - Projeto language policy compliance
-
-9. **Integração Aspire** ✅
- - Admin portal registrado em AppHost
- - Environment variables configuradas (ApiBaseUrl, Keycloak)
- - Build e execução via `dotnet run --project src/Aspire/MeAjudaAi.AppHost`
-
-10. **Documentação** ✅
- - docs/keycloak-admin-portal-setup.md (manual configuração)
- - docs/testing/bunit-ci-cd-practices.md (atualizado)
- - Roadmap atualizado com progresso Sprint 6
-
-11. **SDKs Completos para Sprint 7** ✅ (6 Jan 2026)
- - IDocumentsApi: Upload, verificação, gestão de documentos de providers
- - ILocationsApi: CRUD de cidades permitidas (AllowedCities)
- - DTOs criados: ModuleAllowedCityDto, Create/UpdateAllowedCityRequestDto
- - README melhorado: conceito de SDK, diagrama arquitetural, comparação manual vs SDK
- - 4/4 SDKs necessários para Admin Portal (Providers, Documents, ServiceCatalogs, Locations)
-
-**Resultado Alcançado**:
-- ✅ Blazor Admin Portal 100% funcional via Aspire
-- ✅ Login/Logout Keycloak funcionando
-- ✅ Providers listagem paginada (read-only)
-- ✅ Dashboard com 3 KPIs reais (IServiceCatalogsApi)
-- ✅ Dark mode toggle
-- ✅ 10 testes bUnit (build verde)
-- ✅ Portuguese localization completa
-- ✅ 0 erros build (10 warnings - analyzers apenas)
-- ✅ **4 SDKs completos** para Admin Portal (IProvidersApi, IDocumentsApi, IServiceCatalogsApi, ILocationsApi)
-- ✅ **Documentação SDK** melhorada (conceito, arquitetura, exemplos práticos)
-
-**✅ Próxima Etapa Concluída: Sprint 7 - Blazor Admin Portal Features** (6-7 Jan 2026)
-- ✅ CRUD completo de Providers (create, update, delete, verify)
-- ✅ Gestão de Documentos (upload, verificação, rejection)
-- ✅ Gestão de Service Catalogs (categorias + serviços)
-- ✅ Gestão de Restrições Geográficas (UI para AllowedCities)
-- ✅ Gráficos Dashboard (MudCharts - providers por status, evolução temporal)
-- ✅ Aumentar cobertura de testes (30+ testes bUnit)
-
----
-
-## ✅ Sprint 5.5 - Refactor & Cleanup (19-30 Dez 2025)
-
-**Status**: CONCLUÍDA
-
-**Principais Conquistas**:
-1. **Refatoração MeAjudaAi.Shared.Messaging** ✅
- - Factories organizados em pasta dedicada (`Messaging/Factories/`)
- - Services organizados em pasta dedicada (`Messaging/Services/`)
- - Options organizados em pasta dedicada (`Messaging/Options/`)
- - 4 arquivos: ServiceBusOptions, MessageBusOptions, RabbitMqOptions, DeadLetterOptions
- - IMessageBusFactory + MessageBusFactory separados
- - IDeadLetterServiceFactory + DeadLetterServiceFactory separados
- - 1245/1245 testes passando
-
-2. **Extensions Padronizadas** ✅
- - 14 arquivos consolidados: CachingExtensions, CommandsExtensions, DatabaseExtensions, etc.
- - BusinessMetricsMiddlewareExtensions extraído para arquivo próprio
- - Monitoring folder consolidation completo
- - Removidos 13 arquivos obsoletos (Extensions.cs genéricos + subpastas)
-
-3. **Extension Members (C# 14)** ✅
- - EnumExtensions migrado para nova sintaxe `extension(string value)`
- - 18/18 testes passando (100% compatibilidade)
- - Documentado em architecture.md - seção "C# 14 Features Utilizados"
- - Avaliado DocumentExtensions (não adequado para extension properties)
-
-4. **TODOs Resolvidos** ✅
- - 12/12 TODOs no c├│digo resolvidos ou documentados
- - Remaining issues movidos para technical-debt.md com priorização
- - api-reference.md removido (redundante com ReDoc + api-spec.json)
-
-5. **Documentação Atualizada** ✅
- - architecture.md atualizado com C# 14 features
- - technical-debt.md atualizado com status atual
- - roadmap.md atualizado com Sprint 5.5 completion
- - 0 warnings in build
-
-**✅ Fase 1.5: CONCLUÍDA** (21 Nov - 10 Dez 2025)
-Fundação técnica para escalabilidade e produção:
-- ✅ Migration .NET 10 + Aspire 13 (Sprint 0 - CONCLUÍDO 21 Nov, MERGED to master)
-- ✅ Geographic Restriction + Module Integration (Sprint 1 - CONCLUÍDO 2 Dez, MERGED to master)
-- ✅ Test Coverage 90.56% (Sprint 2 - CONCLUÍDO 10 Dez - META 35% SUPERADA EM 55.56pp!)
-- ✅ GitHub Pages Documentation Migration (Sprint 3 Parte 1 - CONCLUÍDO 11 Dez - DEPLOYED!)
-
-**✅ Sprint 3 Parte 2: CONCLUÍDA** (11 Dez - 13 Dez 2025)
-Admin Endpoints & Tools - TODAS AS PARTES FINALIZADAS:
-- ✅ Admin: Endpoints CRUD para gerenciar cidades permitidas (COMPLETO)
- - ✅ Banco de dados: LocationsDbContext + migrations
- - ✅ Domínio: AllowedCity entity + IAllowedCityRepository
- - ✅ Handlers: CRUD completo (5 handlers)
- - ✅ Endpoints: GET/POST/PUT/DELETE configurados
- - ✅ Exception Handling: Domain exceptions + IExceptionHandler (404/400 corretos)
- - ✅ Testes: 4 integration + 15 E2E (100% passando)
- - ✅ Quality: 0 warnings, dotnet format executado
-- ✅ Tools: Bruno Collections para todos módulos (35+ arquivos .bru)
-- ✅ Scripts: Auditoria completa e documentação (commit b0b94707)
-- ✅ Module Integrations: Providers ↔ ServiceCatalogs + Locations
-- ✅ Code Quality: NSubstitute→Moq, UuidGenerator, .slnx, SonarQube warnings
-- ✅ CI/CD: Formatting checks corrigidos, exit code masking resolvido
-
-**✅ Sprint 4: CONCLUÍDO** (14 Dez - 16 Dez 2025)
-Health Checks Robustos + Data Seeding para MVP - TODAS AS PARTES FINALIZADAS:
-- ✅ Health Checks: DatabasePerformanceHealthCheck (latência <100ms healthy, <500ms degraded)
-- ✅ Health Checks: ExternalServicesHealthCheck (Keycloak + IBGE API + Redis)
-- ✅ Health Checks: HelpProcessingHealthCheck (sistema de ajuda operacional)
-- ✅ Health Endpoints: /health, /health/live, /health/ready com JSON responses
-- ✅ Health Dashboard: Dashboard nativo do Aspire (decisão arquitetural - não usar AspNetCore.HealthChecks.UI)
-- ✅ Health Packages: AspNetCore.HealthChecks.Npgsql 9.0.0, .Redis 8.0.1
-- ✅ Redis Health Check: Configurado via AddRedis() com tags 'ready', 'cache'
-- ✅ Data Seeding: infrastructure/database/seeds/01-seed-service-catalogs.sql (8 categorias + 12 serviços)
-- ✅ Seed Automation: Docker Compose executa seeds automaticamente na inicialização
-- ✅ Project Structure: Reorganização - automation/ → infrastructure/automation/, seeds em infrastructure/database/seeds/
-- ✅ Documentation: README.md, scripts/README.md, infrastructure/database/README.md + docs/future-external-services.md
-- ✅ MetricsCollectorService: Implementado com IServiceScopeFactory (4 TODOs resolvidos)
-- ✅ Unit Tests: 14 testes para ExternalServicesHealthCheck (6 novos para IBGE API)
-- ✅ Integration Tests: 9 testes para DataSeeding (categorias, serviços, idempotência)
-- ✅ Future Services Documentation: Documentado OCR, payments, SMS/email (quando implementar)
-- ✅ Code Review: Logs traduzidos para inglês conforme política (Program.cs - 3 mensagens)
-- ✅ Markdown Linting: technical-debt.md corrigido (code blocks, URLs, headings)
-- ✅ Architecture Test: PermissionHealthCheckExtensions exception documentada (namespace vs folder structure)
-
-**✅ Sprint 5: CONCLUÍDO ANTECIPADAMENTE** (Tarefas completadas nos Sprints 3-4)
-Todas as tarefas planejadas já foram implementadas:
-- ✅ NSubstitute → Moq migration (Sprint 3)
-- ✅ UuidGenerator unification (commit 0a448106)
-- ✅ .slnx migration (commit 1de5dc1a)
-- ✅ Design patterns documentation (architecture.md)
-- ✅ Bruno collections para todos módulos (Users, Providers, Documents)
-
-**⏳ Sprint 5.5: CONCLUÍDA** (19-20 Dez 2025) ✅
-**Branch**: `feature/refactor-and-cleanup`
-**Objetivo**: Refatoração técnica e redução de débito técnico antes do frontend
-
-**✅ Refatoramento de Testes Completado** (20 Dez 2025):
-- ✅ Reorganização estrutural de MeAjudaAi.Shared.Tests (TestInfrastructure com 8 subpastas)
-- ✅ ModuleExtensionsTests movidos para módulos individuais (Documents, Providers, ServiceCatalogs, Users)
-- ✅ Tradução de ~35 comentários para português (mantendo AAA em inglês)
-- ✅ Separação de classes aninhadas (LoggingConfigurationExtensionsTests, TestEvent, BenchmarkResult, BenchmarkExtensions)
-- ✅ Remoção de duplicados (DocumentExtensionsTests, EnumExtensionsTests, SearchableProviderTests)
-- ✅ GeographicRestrictionMiddlewareTests movido para Unit/Middleware/
-- ✅ TestPerformanceBenchmark: classes internas separadas
-- ✅ 11 commits de refatoramento com build verde
-
-**✅ Correção PostGIS Integration Tests** (20 Dez 2025):
-- ✅ Imagem Docker atualizada: postgres:15-alpine → postgis/postgis:15-3.4
-- ✅ EnsurePostGisExtensionAsync() implementado em fixtures
-- ✅ Connection string com 'Include Error Detail=true' para diagnóstico
-- ✅ Suporte completo a dados geográficos (NetTopologySuite/GeoPoint)
-- ✅ Migrations SearchProviders agora passam na pipeline
-
-**Resumo da Sprint**:
-- ✅ 15 commits com melhorias significativas
-- ✅ Todos TODOs críticos resolvidos
-- ✅ Testes melhorados (Provider Repository, Azurite)
-- ✅ Messaging refatorado (IRabbitMqInfrastructureManager extraído)
-- ✅ Extensions consolidadas (BusinessMetricsMiddleware)
-- ✅ Upload file size configurável (IOptions pattern)
-- ✅ Build sem warnings (0 warnings)
-- ✅ Documentação atualizada (architecture.md, configuration.md)
-- ✅ Code review aplicado (logs em inglês, path matching preciso, XML docs)
-
-**Atividades Planejadas** (14 tarefas principais):
-
-**1. Resolução de TODOs Críticos (Alta Prioridade)** - ✅ 8-12h CONCLUÍDO
-- [x] IBGE Middleware Fallback - Fix validation when IBGE fails (3 TODOs em IbgeUnavailabilityTests.cs) ✅
-- [x] Rate Limiting Cache Cleanup - Memory leak prevention (MaxPatternCacheSize=1000) ✅
-- [x] Email Constraint Database Fix - Schema issue (clarified as not-yet-implemented) ✅
-- [x] Azurite/Blob Storage - Container auto-creation with thread-safe initialization ✅
-- [x] Provider Repository Tests - Documentation updated (unit vs integration) ✅
-- [x] BusinessMetrics - Already extracted (no action needed) ✅
-- [x] Monitoring - Structure already adequate (no action needed) ✅
-- [x] Middleware UseSharedServices Alignment - TODO #249 RESOLVIDO ✅ (19 Dez 2025)
-- [x] Azurite Integration Tests - Configured deterministic blob storage tests ✅ (19 Dez 2025)
-
-**2. Melhorias de Testes (Média Prioridade)** - 4-6h
-- [x] Testes Infrastructure Extensions - RESOLVIDO: não aplicável ✅ (19 Dez 2025)
- - Extensions de configuração (Keycloak/PostgreSQL/Migration) validadas implicitamente em E2E/integração
- - Testes unitários teriam baixo ROI (mockaria apenas chamadas de configuração)
- - Infraestrutura validada quando AppHost sobe e containers inicializam
-- [x] Provider Repository Tests - Duplicação RESOLVIDA ✅ (19 Dez 2025)
- - Removidos testes unitários com mocks (260 linhas redundantes)
- - Adicionados 5 testes de integração faltantes (DeleteAsync, GetByIdsAsync, ExistsByUserIdAsync)
- - 17 testes de integração com validação REAL de persistência
- - Redução de manutenção + maior confiança nos testes
-
-**3. Refatoração MeAjudaAi.Shared.Messaging** - 8-10h
-- [x] ~~Separar NoOpDeadLetterService em arquivo próprio~~ ✅ CONCLUÍDO (19 Dez 2025)
-- [x] ~~Extrair DeadLetterStatistics e FailureRate para arquivos separados~~ ✅ CONCLUÍDO (19 Dez 2025)
-- [x] ~~Extrair IMessageRetryMiddlewareFactory, MessageRetryMiddlewareFactory, MessageRetryExtensions~~ ✅ CONCLUÍDO (19 Dez 2025)
-- [x] ~~Todos os 1245 testes do Shared passando~~ ✅ CONCLUÍDO (19 Dez 2025)
-- [✓] ~~Organizar Factories em pasta dedicada~~ - ✅ CONCLUÍDO (19 Dez 2025)
- - Criada pasta `Messaging/Factories/`
- - `MessageBusFactory` e `DeadLetterServiceFactory` organizados
- - Interfaces e implementações em arquivos separados
- - `EnvironmentBasedDeadLetterServiceFactory` → `DeadLetterServiceFactory`
-- [✓] ~~Organizar Services em pasta dedicada~~ - ✅ CONCLUÍDO (19 Dez 2025)
- - Criada pasta `Messaging/Services/`
- - `ServiceBusInitializationService` movido para organização
-- [✓] ~~Organizar Options em pasta dedicada~~ - ✅ CONCLUÍDO (19 Dez 2025)
- - Criada pasta `Messaging/Options/`
- - 4 arquivos organizados: `ServiceBusOptions`, `MessageBusOptions`, `RabbitMqOptions`, `DeadLetterOptions`
- - Namespace unificado: `MeAjudaAi.Shared.Messaging.Options`
-- [✓] ~~Criar IMessageBusFactory + renomear MessageBusFactory.cs → EnvironmentBasedMessageBusFactory.cs~~ - ✅ CONCLUÍDO (19 Dez 2025)
- - Invertido: Criada interface `IMessageBusFactory` em arquivo pr├│prio
- - Classe `EnvironmentBasedMessageBusFactory` renomeada para `MessageBusFactory`
- - Movido de `NoOp/Factory/` para raiz `Messaging/`
- - Um arquivo por classe seguindo SRP
-- [x] Extrair IRabbitMqInfrastructureManager para arquivo separado ✅ (19 Dez 2025)
-- [ ] Adicionar Integration Events faltantes nos m├│dulos (Documents, SearchProviders, ServiceCatalogs?) - BACKLOG
-- [ ] Reorganização geral da estrutura de pastas em Messaging - BACKLOG
-- [ ] Adicionar testes unitários para classes de messaging - BACKLOG
-
-**4. Refatoração Extensions (MeAjudaAi.Shared)** - ✅ 8h CONCLUÍDO
-- [x] ~~Padronizar Extensions: criar arquivo [FolderName]Extensions.cs por funcionalidade~~ ✅ CONCLUÍDO (19 Dez 2025)
-- [x] Extension Members (C# 14): EnumExtensions migrado com sucesso ✅ CONCLUÍDO (19 Dez 2025)
-- [x] BusinessMetricsMiddlewareExtensions: Já existe em Extensions/ ✅ CONCLUÍDO (19 Dez 2025)
-- [x] Monitoring folder consolidation: Estrutura já adequada ✅ CONCLUÍDO (19 Dez 2025)
- - Consolidados: CachingExtensions, CommandsExtensions, DatabaseExtensions, EventsExtensions
- - ExceptionsExtensions, LoggingExtensions, MessagingExtensions, QueriesExtensions, SerializationExtensions
- - Removidos 13 arquivos obsoletos (Extensions.cs genéricos + subpastas)
- - 1245/1245 testes passando
-- [x] ~~Migração para Extension Members (C# 14)~~ ✅ AVALIADO (19 Dez 2025)
- - ✅ Sintaxe `extension(Type receiver)` validada e funcional no .NET 10
- - ✅ Novos recursos disponíveis: extension properties, static extensions, operators
- - ✅ Documentado em `docs/architecture.md` - seção "C# 14 Features Utilizados"
- - 📋 Planejamento: Agendado como última atividade da Sprint 5.5
- - 📝 Recomendação: Usar Extension Members em NOVOS códigos que se beneficiem de properties
-- [x] Extrair BusinessMetricsMiddlewareExtensions de BusinessMetricsMiddleware.cs ✅ (19 Dez 2025)
-- [x] Consolidar Monitoring folder (MonitoringExtensions.cs único) ✅ (19 Dez 2025)
-- [ ] Revisar padrão de extensões em todas as funcionalidades do Shared
-
-**5. Code Quality & Cleanup (Baixa Prioridade)** - 3-4h
-- [x] Padronização de Records - Análise concluída ✅ (19 Dez 2025)
- - Property-based records: DTOs/Requests (mutabilidade com `init`)
- - Positional records: Domain Events, Query/Command DTOs (imutabilidade)
- - Pattern adequado ao contexto de uso
-- [ ] Upload File Size Configuration - Tornar configurável (UploadDocumentCommandHandler.cs:90)
-- [x] ~~Remover api-reference.md (redundante com ReDoc + api-spec.json)~~ ✅ CONCLUÍDO (19 Dez)
-
-**6. Testes E2E SearchProviders** - 2-3 sprints (BACKLOG)
-- [ ] 15 testes E2E cobrindo cenários principais de busca
-- [ ] Validação de integração IBGE API, filtros, paginação
-- [ ] Autenticação/autorização em todos endpoints
-
-**7. Review Completo de Testes** - 6-8h
-- [ ] Auditoria completa de todos os arquivos em tests/
-- [ ] Identificar testes duplicados, obsoletos ou mal estruturados
-- [ ] Validar coverage e identificar gaps
-- [ ] Documentar padr├╡es de teste para novos contribuidores
-
-**8. Migração Extension Members (C# 14) - FINAL SPRINT ACTIVITY** - ✅ 2h CONCLUÍDO
-- [x] Migrar EnumExtensions para syntax `extension(string value)` ✅
-- [x] 18/18 testes passando (100% compatibilidade) ✅
-- [x] Documentar patterns e guidelines em architecture.md ✅
-- [x] Avaliado DocumentExtensions (não adequado para extension properties) ✅
-
-**8. BDD Implementation (BACKLOG - Futuro)** - Sprint dedicado planejado
-- [ ] Setup SpecFlow + Playwright.NET para acceptance tests
-- [ ] Implementar 5-10 features críticas em Gherkin (Provider Registration, Document Upload, Service Catalog)
-- [ ] Integrar ao CI/CD pipeline
-- [ ] Criar documentação executável com Gherkin
-- **Benefício**: Testes de aceitação legíveis para stakeholders e documentação viva do sistema
-- **Timing**: Implementação prevista APÓS desenvolvimento do Customer App (Sprint 8+)
-- **Escopo**: Testes end-to-end de fluxos completos (Frontend → Backend → APIs terceiras)
-- **Foco**: Fluxos críticos de usuário utilizados por Admin Portal e Customer App
-
-**Critérios de Aceitação**:
-- [x] Todos os 12 TODOs no código resolvidos ou documentados ✅
-- [x] ~~Messaging refatorado com estrutura clara de pastas~~ ✅ CONCLUÍDO (19 Dez)
-- [x] ~~Extensions consolidadas por funcionalidade~~ ✅ CONCLUÍDO (19 Dez)
-- [x] Extension Blocks (C# 14) avaliado e implementado onde aplicável ✅ (19 Dez)
-- [x] Testes de infrastructure com >70% coverage (resolvido: não aplicável) ✅ (19 Dez)
-- [x] 0 warnings no build ✅ (19 Dez)
-- [x] Documentação técnica atualizada ✅ (19 Dez)
-
-**Estimativa Total**: 35-45 horas de trabalho técnico (10h já concluídas)
-**Benefício**: Backend robusto e manutenível para suportar desenvolvimento do frontend Blazor
-
-**📝 Próxima Atividade Recomendada**: Migração para Extension Blocks (C# 14) - 4-6h
-- Avaliar novo recurso de linguagem para melhorar organização de extension methods
-- Migrar métodos de propósito geral (PermissionExtensions, EnumExtensions)
-- Manter padrão atual para DI extensions ([FolderName]Extensions.cs)
-
-**✅ Sprint 5.5 Completed** (19-30 Dez 2025):
-- Refatoração MeAjudaAi.Shared.Messaging (Factories, Services, Options)
-- Extensions padronizadas (14 arquivos consolidados)
-- Extension Members (C# 14) implementado
-- TODOs resolvidos (12/12 concluídos)
-- Dependabot PRs fechados para regeneração
-- 1245/1245 testes passando
-
-**⏳ Fase 2: EM ANDAMENTO** (Janeiro–Maio 2026)
-Frontend React (NX Monorepo) + Mobile:
-- Sprint 6: Blazor Admin Portal Setup - ✅ CONCLUÍDO (5 Jan 2026)
-- Sprint 7: Blazor Admin Portal Features (6-24 Jan 2026) - ✅ CONCLUÍDO
-- Sprint 7.16: Technical Debt Sprint (17-21 Jan 2026) - ✅ CONCLUÍDO
-- Sprint 8A: Customer App (5-18 Fev 2026) - ✅ CONCLUÍDO
-- Sprint 8B: Authentication & Onboarding (19 Fev - 4 Mar 2026) - ✅ CONCLUÍDO
-- Sprint 8B.2: Technical Excellence & NX Monorepo (5-18 Mar 2026) - ✅ CONCLUÍDO
-- Sprint 8C: Provider Web App (19-21 Mar 2026) - ✅ CONCLUÍDO
-- Sprint 8D: Admin Portal Migration Blazor → React (2-15 Abr 2026) - ⏳ Planejado
-- Sprint 8E: Mobile App (16-29 Abr 2026) - ⏳ Planejado
-- Sprint 9: BUFFER (30 Abr - 6 Mai 2026) - ⏳ Planejado
-- MVP Final: 12-16 de Maio de 2026
-
-**⚠️ Risk Assessment**: Estimativas assumem velocidade consistente. NX Monorepo setup e Admin Migration são os maiores riscos de escopo. Sprint 9 reservado como buffer de contingência.
-
----
-
-## 📖 Visão Geral
-
-O roadmap está organizado em **cinco fases principais** para entrega incremental de valor:
-
-1. **✅ Fase 1: Fundação (MVP Core)** - Registro de prestadores, busca geolocalizada, catálogo de serviços
-2. **🔄 Fase 1.5: Fundação Técnica** - Migration .NET 10, integração, testes, observability
-3. **🔮 Fase 2: Frontend & Experiência** - Blazor WASM Admin + Customer App
-4. **🔮 Fase 3: Qualidade e Monetização** - Sistema de avaliações, assinaturas premium, verificação automatizada
-5. **🔮 Fase 4: Experiência e Engajamento** - Agendamentos, comunicações, analytics avançado
-
-A implementação segue os princípios arquiteturais definidos em `architecture.md`: **Modular Monolith**, **DDD**, **CQRS**, e **isolamento schema-per-module**.
-
----
-
-
-## 📅 Cronograma de Sprints (Novembro 2025-Março 2026)
-
-| Sprint | Duração | Período | Objetivo | Status |
-|--------|---------|---------|----------|--------|
-| **Sprint 0** | 4 semanas | Jan 20 - 21 Nov | Migration .NET 10 + Aspire 13 | ✅ CONCLUÍDO (21 Nov - MERGED) |
-| **Sprint 1** | 10 dias | 22 Nov - 2 Dez | Geographic Restriction + Module Integration | ✅ CONCLUÍDO (2 Dez - MERGED) |
-| **Sprint 2** | 1 semana | 3 Dez - 10 Dez | Test Coverage 90.56% | ✅ CONCLUÍDO (10 Dez - META SUPERADA!) |
-| **Sprint 3-P1** | 1 dia | 10 Dez - 11 Dez | GitHub Pages Documentation | ✅ CONCLUÍDO (11 Dez - DEPLOYED!) |
-| **Sprint 3-P2** | 2 semanas | 11 Dez - 13 Dez | Admin Endpoints & Tools | ✅ CONCLUÍDO (13 Dez - MERGED) |
-| **Sprint 4** | 5 dias | 14 Dez - 18 Dez | Health Checks + Data Seeding | ✅ CONCLUÍDO (18 Dez - MERGED!) |
-| **Sprint 5** | - | Sprints 3-4 | Quality Improvements | ✅ CONCLUÍDO ANTECIPADAMENTE |
-| **Sprint 5.5** | 2 semanas | 19 Dez - 31 Dez | Refactor & Cleanup (Technical Debt) | ✅ CONCLUÍDO (30 Dez 2025) |
-| **Sprint 6** | 1 semana | 30 Dez - 5 Jan | Blazor Admin Portal - Setup & Core | ✅ CONCLUÍDO (5 Jan 2026) |
-| **Sprint 7** | 3 semanas | 6 - 24 Jan | Blazor Admin Portal - Features | ✅ CONCLUÍDO |
-| **Sprint 7.16** | 1 semana | 17-21 Jan | Technical Debt Sprint | ✅ CONCLUÍDO |
-| **Sprint 8** | 2 semanas | 5 - 18 Fev | Customer Web App (Web) | ✅ CONCLUÍDO |
-| **Sprint 8B** | 2 semanas | 19 Fev - 4 Mar | Authentication & Onboarding | ✅ CONCLUÍDO |
-| **Sprint 8B.2** | 2 semanas | 5-18 Mar | Technical Excellence & NX Monorepo | ✅ CONCLUÍDO |
-| **Sprint 8C** | 3 dias | 19-21 Mar | Provider Web App | ✅ CONCLUÍDO |
-| **Sprint 8D** | 3 semanas | 2-22 Abr | Admin Portal Migration | ⏳ Planejado |
-| **Sprint 9** | 2 semanas | 23 Abr - 11 Mai | **BUFFER: Polishing, Refactoring & Risk Mitigation** | ⏳ Planejado |
-| **MVP Launch** | 1 semana | 12-16 Mai 2026 | Final deployment & launch preparation | 🎯 Target |
-
-**MVP Launch Target**: 12-16 de Maio de 2026 🎯
-
-**Post-MVP (Fase 3+)**: Reviews, Assinaturas, Agendamentos (Abril 2026+)
-
----
-
-## ✅ Fase 1: Fundação (MVP Core) - CONCLUÍDA
-
-### Objetivo
-Estabelecer as capacidades essenciais da plataforma: registro multi-etapas de prestadores com verificação, busca geolocalizada e catálogo de serviços.
-
-### Status: ✅ CONCLUÍDA (Janeiro 2025)
-
-**Todos os 6 m├│dulos implementados, testados e integrados:**
-1. ✅ **Users** - Autenticação, perfis, roles
-2. ✅ **Providers** - Registro multi-etapas, verificação, gestão
-3. ✅ **Documents** - Upload seguro, workflow de verificação
-4. ✅ **Search & Discovery** - Busca geolocalizada com PostGIS
-5. ✅ **Locations** - Lookup de CEP, geocoding, validações
-6. ✅ **ServiceCatalogs** - Catálogo hierárquico de serviços
-
-**Conquistas:**
-- 28.69% test coverage (93/100 E2E passing, 296 unit tests)
-- ⚠️ Coverage caiu após migration (packages.lock.json + generated code)
-- APIs p├║blicas (IModuleApi) implementadas para todos m├│dulos
-- Integration events funcionais entre m├│dulos
-- Health checks configurados
-- CI/CD pipeline completo no GitHub Actions
-- Documentação arquitetural completa + skipped tests tracker
-
-### 1.1. ✅ Módulo Users (Concluído)
-**Status**: Implementado e em produção
-
-**Funcionalidades Entregues**:
-- ✅ Registro e autenticação via Keycloak (OIDC)
-- ✅ Gestão de perfil básica
-- ✅ Sistema de roles e permissões
-- ✅ Health checks e monitoramento
-- ✅ API versionada com documentação OpenAPI
-
----
-
-### 1.2. ✅ Módulo Providers (Concluído)
-
-**Status**: Implementado e em produção
-
-**Funcionalidades Entregues**:
-- ✅ Provider aggregate com estados de registro (`EProviderStatus`: Draft, PendingVerification, Active, Suspended, Rejected)
-- ✅ Múltiplos tipos de prestador (Individual, Company)
-- ✅ Verificação de documentos integrada com módulo Documents
-- ✅ BusinessProfile com informações de contato e identidade empresarial
-- ✅ Gestão de qualificações e certificações
-- ✅ Domain Events (`ProviderRegistered`, `ProviderVerified`, `ProviderRejected`)
-- ✅ API pública (IProvidersModuleApi) para comunicação inter-módulos
-- ✅ Queries por documento, cidade, estado, tipo e status de verificação
-- ✅ Soft delete e auditoria completa
-
----
-
-### 1.3. ✅ Módulo Documents (Concluído)
-
-**Status**: Implementado e em produção
-
-**Funcionalidades Entregues**:
-- ✅ Upload seguro de documentos via Azure Blob Storage
-- ✅ Tipos de documento suportados: IdentityDocument, ProofOfResidence, ProfessionalLicense, BusinessLicense
-- ✅ Workflow de verificação com estados (`EDocumentStatus`: Uploaded, PendingVerification, Verified, Rejected, Failed)
-- ✅ Integração completa com módulo Providers
-- ✅ Domain Events (`DocumentUploaded`, `DocumentVerified`, `DocumentRejected`, `DocumentFailed`)
-- ✅ API pública (IDocumentsModuleApi) para queries de documentos
-- ✅ Verificações de integridade: HasVerifiedDocuments, HasRequiredDocuments, HasPendingDocuments
-- ✅ Sistema de contadores por status (DocumentStatusCountDto)
-- ✅ Suporte a OCR data extraction (campo OcrData para dados extraídos)
-- ✅ Rejection/Failure reasons para auditoria
-
-**Arquitetura Implementada**:
-```csharp
-// Document: Aggregate Root
-public sealed class Document : AggregateRoot
-{
- public Guid ProviderId { get; }
- public EDocumentType DocumentType { get; }
- public string FileUrl { get; } // Blob name/key no Azure Storage
- public string FileName { get; }
- public EDocumentStatus Status { get; }
- public DateTime UploadedAt { get; }
- public DateTime? VerifiedAt { get; }
- public string? RejectionReason { get; }
- public string? OcrData { get; }
-}
-```
-
-**API P├║blica Implementada**:
-```csharp
-public interface IDocumentsModuleApi : IModuleApi
-{
- Task> GetDocumentByIdAsync(Guid documentId, CancellationToken ct = default);
- Task>> GetProviderDocumentsAsync(Guid providerId, CancellationToken ct = default);
- Task> GetDocumentStatusAsync(Guid documentId, CancellationToken ct = default);
- Task> HasVerifiedDocumentsAsync(Guid providerId, CancellationToken ct = default);
- Task> HasRequiredDocumentsAsync(Guid providerId, CancellationToken ct = default);
- Task> GetDocumentStatusCountAsync(Guid providerId, CancellationToken ct = default);
- Task> HasPendingDocumentsAsync(Guid providerId, CancellationToken ct = default);
- Task> HasRejectedDocumentsAsync(Guid providerId, CancellationToken ct = default);
-}
-```
-
-**Pr├│ximas Melhorias (Fase 2)**:
-- 🔄 Background worker para verificação automatizada via OCR
-- 🔄 Integração com APIs governamentais para validação
-- 🔄 Sistema de scoring automático baseado em qualidade de documentos
-
----
-
-### 1.4. ✅ Módulo Search & Discovery (Concluído)
-
-**Status**: Implementado e em produção
-
-**Funcionalidades Entregues**:
-- ✅ Busca geolocalizada com PostGIS nativo
-- ✅ Read model denormalizado otimizado (SearchableProvider)
-- ✅ Filtros por raio, serviços, rating mínimo e subscription tiers
-- ✅ Ranking multi-critério (tier → rating → distância)
-- ✅ Paginação server-side com contagem total
-- ✅ Queries espaciais nativas (ST_DWithin, ST_Distance)
-- ✅ Hybrid repository (EF Core + Dapper) para performance
-- ✅ Validação de raio não-positivo (short-circuit)
-- ✅ CancellationToken support para queries longas
-- ✅ API pública (ISearchModuleApi)
-
-**Arquitetura Implementada**:
-```csharp
-// SearchableProvider: Read Model
-public sealed class SearchableProvider : AggregateRoot
-{
- public Guid ProviderId { get; }
- public string Name { get; }
- public GeoPoint Location { get; } // Latitude, Longitude com PostGIS
- public decimal AverageRating { get; }
- public int TotalReviews { get; }
- public ESubscriptionTier SubscriptionTier { get; } // Free, Standard, Gold, Platinum
- public Guid[] ServiceIds { get; }
- public bool IsActive { get; }
- public string? Description { get; }
- public string? City { get; }
- public string? State { get; }
-}
-```
-
-**API P├║blica Implementada**:
-```csharp
-public interface ISearchModuleApi
-{
- Task> SearchProvidersAsync(
- double latitude,
- double longitude,
- double radiusInKm,
- Guid[]? serviceIds = null,
- decimal? minRating = null,
- SubscriptionTier[]? subscriptionTiers = null,
- int pageNumber = 1,
- int pageSize = 20,
- CancellationToken cancellationToken = default);
-}
-```
-
-**L├│gica de Ranking Implementada**:
-1. ✅ Filtrar por raio usando `ST_DWithin` (índice GIST)
-2. ✅ Ordenar por tier de assinatura (Platinum > Gold > Standard > Free)
-3. ✅ Ordenar por avaliação média (descendente)
-4. ✅ Ordenar por distância (crescente) como desempate
-
-**Performance**:
-- ✅ Queries espaciais executadas no banco (não in-memory)
-- ✅ Índices GIST para geolocalização
-- ✅ Paginação eficiente com OFFSET/LIMIT
-- ✅ Count query separada para total
-
-**Pr├│ximas Melhorias (Opcional)**:
-- 🔄 Migração para Elasticsearch para maior escalabilidade (se necessário)
-- 🔄 Indexing worker consumindo integration events (atualmente manual)
-- 🔄 Caching de resultados para queries frequentes
-
----
-
-### 1.5. ✅ Módulo Location Management (Concluído)
-
-**Status**: Implementado e testado com integração IBGE ativa
-
-**Objetivo**: Abstrair funcionalidades de geolocalização e lookup de CEP brasileiro.
-
-**Funcionalidades Entregues**:
-- ✅ ValueObjects: Cep, Coordinates, Address com validação completa
-- ✅ Integração com APIs de CEP: ViaCEP, BrasilAPI, OpenCEP
-- ✅ Fallback chain automático (ViaCEP → BrasilAPI → OpenCEP)
-- ✅ Resiliência HTTP via ServiceDefaults (retry, circuit breaker, timeout)
-- ✅ API pública (ILocationModuleApi) para comunicação inter-módulos
-- ✅ **Integração IBGE API** (Sprint 1 Dia 1): Validação geográfica oficial
-- ✅ Serviço de geocoding (stub para implementação futura)
-- ✅ 52 testes unitários passando (100% coverage em ValueObjects)
-
-**Arquitetura Implementada**:
-```csharp
-// ValueObjects
-public sealed class Cep // Valida e formata CEP brasileiro (12345-678)
-public sealed class Coordinates // Latitude/Longitude com validação de limites
-public sealed class Address // Endereço completo com CEP, rua, bairro, cidade, UF
-
-// API P├║blica
-public interface ILocationModuleApi : IModuleApi
-{
- Task> GetAddressFromCepAsync(string cep, CancellationToken ct = default);
- Task> GetCoordinatesFromAddressAsync(string address, CancellationToken ct = default);
-}
-```
-
-**Serviços Implementados**:
-- `CepLookupService`: Implementa chain of responsibility com fallback entre provedores
-- `ViaCepClient`, `BrasilApiCepClient`, `OpenCepClient`: Clients HTTP com resiliência
-- **`IbgeClient`** (Novo): Cliente HTTP para IBGE Localidades API com normalização de nomes
-- **`IbgeService`** (Novo): Validação de municípios com HybridCache (7 dias TTL)
-- **`GeographicValidationService`** (Novo): Adapter pattern para integração com middleware
-- `GeocodingService`: Stub (TODO: integração com Nominatim ou Google Maps API)
-
-**Integração IBGE Implementada** (Sprint 1 Dia 1):
-```csharp
-// IbgeClient: Normalização de nomes (remove acentos, lowercase, hífens)
-public Task GetMunicipioByNameAsync(string cityName, CancellationToken ct = default);
-public Task> GetMunicipiosByUFAsync(string ufSigla, CancellationToken ct = default);
-public Task ValidateCityInStateAsync(string city, string state, CancellationToken ct = default);
-
-// IbgeService: Business logic com cache (HybridCache, TTL: 7 dias)
-public Task ValidateCityInAllowedRegionsAsync(
- string cityName,
- string stateSigla,
- List allowedCities,
- CancellationToken ct = default);
-public Task GetCityDetailsAsync(string cityName, CancellationToken ct = default);
-
-// GeographicValidationService: Adapter para Shared module
-public Task ValidateCityAsync(
- string cityName,
- string stateSigla,
- List allowedCities,
- CancellationToken ct = default);
-```
-
-**Observação**: IBGE integration provides city/state validation for geographic restriction; geocoding (lat/lon lookup) via Nominatim is planned for Sprint 3 (optional improvement).
-
-**Modelos IBGE**:
-- `Regiao`: Norte, Nordeste, Sudeste, Sul, Centro-Oeste
-- `UF`: Unidade da Federação (estado) com região
-- `Mesorregiao`: Mesorregião com UF
-- `Microrregiao`: Microrregião com mesorregião
-- `Municipio`: Município com hierarquia completa + helper methods (GetUF, GetEstadoSigla, GetNomeCompleto)
-
-**API Base IBGE**: `https://servicodados.ibge.gov.br/api/v1/localidades/`
-
-**Pr├│ximas Melhorias (Opcional)**:
-- 🔄 Implementar GeocodingService com Nominatim (OpenStreetMap) ou Google Maps API
-- 🔄 Adicionar caching Redis para reduzir chamadas às APIs externas (TTL: 24h para CEP, 7d para geocoding)
-- ✅ ~~Integração com IBGE para lookup de municípios e estados~~ (IMPLEMENTADO)
-
----
-
-### 1.6. ✅ Módulo ServiceCatalogs (Concluído)
-
-**Status**: Implementado e funcional com testes completos
-
-**Objetivo**: Gerenciar tipos de serviços que prestadores podem oferecer por catálogo gerenciado administrativamente.
-
-#### **Arquitetura Implementada**
-- **Padrão**: DDD + CQRS com hierarquia de categorias
-- **Schema**: `service_catalogs` (isolado)
-- **Naming**: snake_case no banco, PascalCase no c├│digo
-
-#### **Entidades de Domínio Implementadas**
-```csharp
-// ServiceCategory: Aggregate Root
-public sealed class ServiceCategory : AggregateRoot
-{
- public string Name { get; }
- public string? Description { get; }
- public bool IsActive { get; }
- public int DisplayOrder { get; }
-
- // Domain Events: Created, Updated, Activated, Deactivated
- // Business Rules: Nome único, validações de criação/atualização
-}
-
-// Service: Aggregate Root
-public sealed class Service : AggregateRoot
-{
- public ServiceCategoryId CategoryId { get; }
- public string Name { get; }
- public string? Description { get; }
- public bool IsActive { get; }
- public int DisplayOrder { get; }
-
- // Domain Events: Created, Updated, Activated, Deactivated, CategoryChanged
- // Business Rules: Nome único, categoria ativa, validações
-}
-```
-
-#### **Camadas Implementadas**
-
-**1. Domain Layer** ✅
-- `ServiceCategoryId` e `ServiceId` (strongly-typed IDs)
-- Agregados com l├│gica de neg├│cio completa
-- 9 Domain Events (lifecycle completo)
-- Reposit├│rios: `IServiceCategoryRepository`, `IServiceRepository`
-- Exception: `CatalogDomainException`
-
-**2. Application Layer** ✅
-- **DTOs**: ServiceCategoryDto, ServiceDto, ServiceListDto, ServiceCategoryWithCountDto
-- **Commands** (11 total):
- - Categories: Create, Update, Activate, Deactivate, Delete
- - Services: Create, Update, ChangeCategory, Activate, Deactivate, Delete
-- **Queries** (6 total):
- - Categories: GetById, GetAll, GetWithCount
- - Services: GetById, GetAll, GetByCategory
-- **Handlers**: 11 Command Handlers + 6 Query Handlers
-- **Module API**: `ServiceCatalogsModuleApi` para comunicação inter-módulos
-
-**3. Infrastructure Layer** ✅
-- `ServiceCatalogsDbContext` com schema isolation (`service_catalogs`)
-- EF Core Configurations (snake_case, índices otimizados)
-- Repositories com SaveChangesAsync integrado
-- DI registration com auto-migration support
-
-**4. API Layer** ✅
-- **Endpoints REST** usando Minimal APIs pattern:
- - `GET /api/v1/service-catalogs/categories` - Listar categorias
- - `GET /api/v1/service-catalogs/categories/{id}` - Buscar categoria
- - `POST /api/v1/service-catalogs/categories` - Criar categoria
- - `PUT /api/v1/service-catalogs/categories/{id}` - Atualizar categoria
- - `POST /api/v1/service-catalogs/categories/{id}/activate` - Ativar
- - `POST /api/v1/service-catalogs/categories/{id}/deactivate` - Desativar
- - `DELETE /api/v1/service-catalogs/categories/{id}` - Deletar
- - `GET /api/v1/service-catalogs/services` - Listar serviços
- - `GET /api/v1/service-catalogs/services/{id}` - Buscar serviço
- - `GET /api/v1/service-catalogs/services/category/{categoryId}` - Por categoria
- - `POST /api/v1/service-catalogs/services` - Criar serviço
- - `PUT /api/v1/service-catalogs/services/{id}` - Atualizar serviço
- - `POST /api/v1/service-catalogs/services/{id}/change-category` - Mudar categoria
- - `POST /api/v1/service-catalogs/services/{id}/activate` - Ativar
- - `POST /api/v1/service-catalogs/services/{id}/deactivate` - Desativar
- - `DELETE /api/v1/service-catalogs/services/{id}` - Deletar
-- **Autorização**: Todos endpoints requerem role Admin
-- **Versionamento**: Sistema unificado via BaseEndpoint
-
-**5. Shared.Contracts** ✅
-- `IServiceCatalogsModuleApi` - Interface p├║blica
-- DTOs: ModuleServiceCategoryDto, ModuleServiceDto, ModuleServiceListDto, ModuleServiceValidationResultDto
-
-#### **API P├║blica Implementada**
-```csharp
-public interface IServiceCatalogsModuleApi : IModuleApi
-{
- Task> GetServiceCategoryByIdAsync(Guid categoryId, CancellationToken ct = default);
- Task>> GetAllServiceCategoriesAsync(bool activeOnly = true, CancellationToken ct = default);
- Task> GetServiceByIdAsync(Guid serviceId, CancellationToken ct = default);
- Task>> GetAllServicesAsync(bool activeOnly = true, CancellationToken ct = default);
- Task>> GetServicesByCategoryAsync(Guid categoryId, bool activeOnly = true, CancellationToken ct = default);
- Task> IsServiceActiveAsync(Guid serviceId, CancellationToken ct = default);
- Task> ValidateServicesAsync(Guid[] serviceIds, CancellationToken ct = default);
-}
-```
-
-#### **Status de Compilação**
-- ✅ **Domain**: BUILD SUCCEEDED (3 warnings XML documentation)
-- ✅ **Application**: BUILD SUCCEEDED (18 warnings SonarLint - não críticos)
-- ✅ **Infrastructure**: BUILD SUCCEEDED
-- ✅ **API**: BUILD SUCCEEDED
-- ✅ **Adicionado à Solution**: 4 projetos integrados
-
-#### **Integração com Outros Módulos**
-- **Providers Module** (Planejado): Adicionar ProviderServices linking table
-- **Search Module** (Planejado): Denormalizar services nos SearchableProvider
-- **Admin Portal**: Endpoints prontos para gestão de catálogo
-
-#### **Pr├│ximos Passos (P├│s-MVP)**
-1. **Testes**: Implementar unit tests e integration tests
-2. **Migrations**: Criar e aplicar migration inicial do schema `service_catalogs`
-3. **Bootstrap**: Integrar no Program.cs e AppHost
-4. **Provider Integration**: Estender Providers para suportar ProviderServices
-5. **Admin UI**: Interface para gestão de catálogo
-6. **Seeders**: Popular catálogo inicial com serviços comuns
-
-#### **Considerações Técnicas**
-- **SaveChangesAsync**: Integrado nos repositórios (padrão do projeto)
-- **Validações**: Nome único por categoria/serviço, categoria ativa para criar serviço
-- **Soft Delete**: Não implementado (hard delete com validação de dependências)
-- **Cascata**: DeleteServiceCategory valida se há serviços vinculados
-
-#### **Schema do Banco de Dados**
-```sql
--- Schema: service_catalogs
-CREATE TABLE service_catalogs.service_categories (
- id UUID PRIMARY KEY,
- name VARCHAR(200) NOT NULL UNIQUE,
- description TEXT,
- is_active BOOLEAN NOT NULL DEFAULT TRUE,
- display_order INT NOT NULL DEFAULT 0,
- created_at TIMESTAMP NOT NULL,
- updated_at TIMESTAMP
-);
-
-CREATE TABLE service_catalogs.services (
- id UUID PRIMARY KEY,
- category_id UUID NOT NULL REFERENCES service_catalogs.service_categories(id),
- name VARCHAR(200) NOT NULL UNIQUE,
- description TEXT,
- is_active BOOLEAN NOT NULL DEFAULT TRUE,
- display_order INT NOT NULL DEFAULT 0,
- created_at TIMESTAMP NOT NULL,
- updated_at TIMESTAMP
-);
-
-CREATE INDEX idx_services_category_id ON service_catalogs.services(category_id);
-CREATE INDEX idx_services_is_active ON service_catalogs.services(is_active);
-CREATE INDEX idx_service_categories_is_active ON service_catalogs.service_categories(is_active);
-```
-
----
-
-## 🔄 Fase 1.5: Fundação Técnica (Em Andamento)
-
-### Objetivo
-Fortalecer a base técnica do sistema antes de desenvolver frontend, garantindo escalabilidade, qualidade e compatibilidade com .NET 10 LTS + Aspire 13.
-
-### Justificativa
-Com todos os 6 módulos core implementados (Fase 1 ✅), precisamos consolidar a fundação técnica antes de iniciar desenvolvimento frontend:
-- **.NET 9 EOL**: Suporte expira em maio 2025, migrar para .NET 10 LTS agora evita migração em produção
-- **Aspire 13**: Novas features de observability e orchestration
-- **Test Coverage**: Atual 40.51% → objetivo 80%+ para manutenibilidade
-- **Integração de Módulos**: IModuleApi implementado mas não utilizado com as regras de negócio reais
-- **Restrição Geográfica**: MVP exige operação apenas em cidades piloto (SP, RJ, BH)
-
----
-
-### 📅 Sprint 0: Migration .NET 10 + Aspire 13 (1-2 semanas)
-
-**Status**: ✅ CONCLUÍDO (10 Dez 2025) - Branch: `improve-tests-coverage-2`
-
-**Objetivos**:
-- Migrar todos projetos para .NET 10 LTS
-- Atualizar Aspire para v13
-- Atualizar dependências (EF Core 10, Npgsql 10, etc.)
-- Validar testes e corrigir breaking changes
-- Atualizar CI/CD para usar .NET 10 SDK
-
-**Tarefas**:
-- [x] Criar branch `migration-to-dotnet-10` ✅
-- [x] Merge master (todos módulos Fase 1) ✅
-- [x] Atualizar `Directory.Packages.props` para .NET 10 ✅
-- [x] Atualizar todos `.csproj` para `net10.0` ✅
-- [x] Atualizar Aspire packages para v13.0.2 ✅
-- [x] Atualizar EF Core para 10.0.1 GA ✅
-- [x] Atualizar Npgsql para 10.0.0 GA ✅
-- [x] `dotnet restore` executado com sucesso ✅
-- [x] **Verificação Incremental**:
- - [x] Build Domain projects → ✅ sem erros
- - [x] Build Application projects → ✅ sem erros
- - [x] Build Infrastructure projects → ✅ sem erros
- - [x] Build API projects → ✅ sem erros
- - [x] Build completo → ✅ 0 warnings, 0 errors
- - [x] Fix testes Hangfire (Skip para CI/CD) ✅
- - [x] Run unit tests → ✅ 480 testes (479 passed, 1 skipped)
- - [x] Run integration tests → ✅ validados com Docker
-- [x] Atualizar CI/CD workflows (removido --locked-mode) ✅
-- [x] Validar Docker images com .NET 10 ✅
-- [x] Merge para master após validação completa ✅
-
-**Resultado Alcançado**:
-- ✅ Sistema rodando em .NET 10 LTS com Aspire 13.0.2
-- ✅ Todos 480 testes passando (479 passed, 1 skipped)
-- ✅ CI/CD funcional (GitHub Actions atualizado)
-- ✅ Documentação atualizada
-- ✅ EF Core 10.0.1 GA + Npgsql 10.0.0 GA (versões estáveis)
-
-#### 📦 Pacotes com Versões Não-Estáveis ou Pendentes de Atualização
-
-⚠️ **CRITICAL**: All packages listed below are Release Candidate (RC) or Preview versions.
-**DO NOT deploy to production** until stable versions are released. See [.NET 10 Release Timeline](https://github.com/dotnet/core/releases).
-
-**Status da Migration**: A maioria dos pacotes core já está em .NET 10, mas alguns ainda estão em **RC (Release Candidate)** ou aguardando releases estáveis.
-
-**Pacotes Atualizados (RC/Preview)**:
-```xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-**📦 Pacotes Atualizados — Estado Misto (11 Dez 2025)**:
-
-| Pacote | Versão Atual | Status | Notas |
-|--------|--------------|--------|-------|
-| **EF Core 10.x** | `10.0.1` | ✅ GA STABLE | Atualizado de 10.0.0-rc.2 → 10.0.1 GA |
-| **Npgsql 10.x** | `10.0.0` | ✅ GA STABLE | Atualizado de 10.0.0-rc.1 → 10.0.0 GA |
-| **Aspire 13.x** | `13.0.2` | ✅ GA STABLE | Atualizado de 13.0.0-preview.1 → 13.0.2 GA |
-| **Aspire.Npgsql.EntityFrameworkCore.PostgreSQL** | `13.0.2` | ✅ GA STABLE | Sincronizado com Aspire 13.0.2 GA |
-| **Hangfire.PostgreSql** | `1.20.13` | ⚠️ STABLE (Npgsql 6.x) | Monitorando compatibilidade com Npgsql 10.x |
-| **EFCore.NamingConventions** | `10.0.0-rc.2` | ⚠️ PRE-RELEASE | Aguardando versão estável (issue template criado) |
-
-**🆕 Atualizações via Dependabot (11 Dez 2025)**:
-
-| Pacote | Versão Anterior | Versão Atual | PR | Status |
-|--------|-----------------|--------------|-----|--------|
-| **Microsoft.AspNetCore.Authentication.JwtBearer** | `10.0.0` | `10.0.1` | [#62](https://github.com/frigini/MeAjudaAi/pull/62) | ✅ MERGED |
-| **Microsoft.AspNetCore.OpenApi** | `10.0.0` | `10.0.1` | [#64](https://github.com/frigini/MeAjudaAi/pull/64) | ✅ MERGED |
-| **Microsoft.Extensions.Caching.Hybrid** | `10.0.0` | `10.1.0` | [#63](https://github.com/frigini/MeAjudaAi/pull/63) | ✅ MERGED |
-| **Microsoft.Extensions.Http.Resilience** | `10.0.0` | `10.1.0` | [#63](https://github.com/frigini/MeAjudaAi/pull/63) | ✅ MERGED |
-| **Serilog** | `4.2.0` | `4.3.0` | [#63](https://github.com/frigini/MeAjudaAi/pull/63) | ✅ MERGED |
-| **Serilog.Sinks.Console** | `6.0.0` | `6.1.1` | [#63](https://github.com/frigini/MeAjudaAi/pull/63) | ✅ MERGED |
-
-**✅ Resultado**: Pacotes core (EF Core 10.0.1, Npgsql 10.0.0, Aspire 13.0.2) atualizados para GA estáveis. EFCore.NamingConventions 10.0.0-rc.2 sob monitoramento (aguardando GA). Lockfiles regenerados e validados em CI/CD.
-
-**⚠️ Pacotes Ainda a Monitorar**:
-
-| Pacote | Versão Atual | Versão Estável Esperada | Impacto | Ação Requerida |
-|--------|--------------|-------------------------|---------|----------------|
-| **EFCore.NamingConventions** | `10.0.0-rc.2` | `10.0.0` (Q1 2026?) | MÉDIO | Monitorar |
-| **Hangfire.PostgreSql** | `1.20.13` | `2.0.0` com Npgsql 10+ | CRÍTICO | Monitorar |
-
-**🔔 Monitoramento Automático de Releases**:
-
-Para receber notificações quando novas versões estáveis forem lançadas, configure os seguintes alertas:
-
-1. **GitHub Watch (Reposit├│rios Open Source)**:
- - Acesse: → Click "Watch" → "Custom" → "Releases"
- - Acesse: → Click "Watch" → "Custom" → "Releases"
- - Acesse: → Click "Watch" → "Custom" → "Releases"
- - Acesse: → Click "Watch" → "Custom" → "Releases"
- - **Benefício**: Notificação no GitHub e email quando nova release for publicada
-
-2. **NuGet Package Monitoring (Via GitHub Dependabot)**:
- - Criar `.github/dependabot.yml` no reposit├│rio:
- ```yaml
- version: 2
- updates:
- - package-ecosystem: "nuget"
- directory: "/"
- schedule:
- interval: "weekly"
- open-pull-requests-limit: 10
- # Ignorar vers├╡es preview/rc se desejar apenas stable
- ignore:
- - dependency-name: "*"
- update-types: ["version-update:semver-major"]
- ```
- - **Benefício**: PRs automáticos quando novas versões forem detectadas
-
-3. **NuGet.org Email Notifications**:
- - Acesse: → "Change Email Preferences"
- - Habilite "Package update notifications"
- - **Limitação**: Não funciona para todos pacotes, depende do publisher
-
-4. **Visual Studio / Rider IDE Alerts**:
- - **Visual Studio**: Tools → Options → NuGet Package Manager → "Check for updates automatically"
- - **Rider**: Settings → Build, Execution, Deployment → NuGet → "Check for package updates"
- - **Benefício**: Notificação visual no Solution Explorer
-
-5. **dotnet outdated (CLI Tool)**:
- ```powershell
- # Instalar globalmente
- dotnet tool install --global dotnet-outdated-tool
-
- # Verificar pacotes desatualizados
- dotnet outdated
-
- # Verificar apenas pacotes major/minor desatualizados
- dotnet outdated --upgrade:Major
-
- # Automatizar verificação semanal (Task Scheduler / cron)
- # Windows Task Scheduler: Executar semanalmente
- # C:\Code\MeAjudaAi> dotnet outdated > outdated-report.txt
- ```
- - **Benefício**: Script automatizado para verificação periódica
-
-6. **GitHub Actions Workflow (Recomendado)**:
- - Criar `.github/workflows/check-dependencies.yml`:
- ```yaml
- name: Check Outdated Dependencies
-
- on:
- schedule:
- - cron: '0 9 * * 1' # Toda segunda-feira às 9h
- workflow_dispatch: # Manual trigger
-
- jobs:
- check-outdated:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v6
-
- - name: Setup .NET
- uses: actions/setup-dotnet@v5
- with:
- dotnet-version: '10.x'
-
- - name: Install dotnet-outdated
- run: dotnet tool install --global dotnet-outdated-tool
-
- - name: Check for outdated packages
- run: |
- dotnet outdated > outdated-report.txt
- cat outdated-report.txt
-
- - name: Create Issue if outdated packages found
- if: success()
- uses: actions/github-script@v7
- with:
- script: |
- const fs = require('fs');
- const report = fs.readFileSync('outdated-report.txt', 'utf8');
- if (report.includes('has newer versions')) {
- github.rest.issues.create({
- owner: context.repo.owner,
- repo: context.repo.repo,
- title: '[AUTOMATED] Outdated NuGet Packages Detected',
- body: `\`\`\`\n${report}\n\`\`\``,
- labels: ['dependencies', 'automated']
- });
- }
- ```
- - **Benefício**: Verificação automática semanal + criação de Issue no GitHub
-
-**📋 Checklist de Monitoramento (Recomendado)**:
-- [x] Configurar GitHub Watch para dotnet/efcore ✅
-- [x] Configurar GitHub Watch para npgsql/npgsql ✅
-- [x] Configurar GitHub Watch para dotnet/aspire ✅
-- [x] Configurar GitHub Watch para Hangfire.PostgreSql ✅
-- [x] Issue template criado: `.github/ISSUE_TEMPLATE/efcore-naming-conventions-stable-monitoring.md` ✅
-- [ ] Instalar `dotnet-outdated-tool` globalmente (opcional - monitoramento manual)
-- [ ] Criar GitHub Actions workflow para verificação automática (`.github/workflows/check-dependencies.yml`) (Sprint 3)
-- [x] Dependabot habilitado via GitHub (PRs automáticos ativos) ✅
-- [ ] Adicionar lembrete mensal no calendário para verificação manual (backup)
-
-**🔍 Pacotes Críticos Sem Compatibilidade .NET 10 Confirmada**:
-
-1. **Hangfire.PostgreSql 1.20.12**
- - **Status**: Compilado contra Npgsql 6.x
- - **Risco**: Breaking changes em Npgsql 10.x não validados pelo mantenedor
- - **Mitigação Atual**: Testes de integração (marcados como Skip no CI/CD)
- - **Monitoramento**:
- - GitHub Issues: [Hangfire.PostgreSql Issues](https://github.com/frankhommers/Hangfire.PostgreSql/issues)
- - Alternativas: Hangfire.Pro.Redis (pago), Hangfire.SqlServer (outro DB)
- - **Prazo**: Validar localmente ANTES de deploy para produção
-
-2. **~~Swashbuckle.AspNetCore 10.0.1 - ExampleSchemaFilter~~** ✅ RESOLVIDO (13 Dez 2025)
- - **Status**: ExampleSchemaFilter **removido permanentemente**
- - **Razão**: Código problemático, difícil de testar, não essencial
- - **Alternativa**: Usar XML documentation comments para exemplos quando necessário
- - **Commit**: [Adicionar hash ap├│s commit]
-
-**📅 Cronograma de Atualizações Futuras**:
-
-```mermaid
-gantt
- title Roadmap de Atualizações de Pacotes
- dateFormat YYYY-MM-DD
- section EF Core
- RC → Stable :2025-11-20, 2025-12-15
- Atualizar projeto :2025-12-15, 7d
- section Npgsql
- RC → Stable :2025-11-20, 2025-12-15
- Revalidar Hangfire :2025-12-15, 7d
- section Aspire
- Preview → Stable :2025-11-20, 2025-12-31
- Atualizar configs :2025-12-31, 3d
- section Hangfire
- Monitorar upstream :2025-11-20, 2026-06-30
-```
-
-**✅ Ações Concluídas Pós-Migration (10 Dez 2025)**:
-1. ✅ Finalizar validação de testes (unit + integration) - 480 testes passando
-2. ✅ Validar Hangfire localmente (com Aspire) - funcional
-3. ✅ Configurar GitHub Watch para monitoramento de releases (EF Core, Npgsql, Aspire)
-4. ✅ Issue template criado para EFCore.NamingConventions stable monitoring
-5. ✅ Dependabot habilitado via GitHub (PRs automáticos)
-6. ✅ Monitoramento ativo para Hangfire.PostgreSql 2.0 (Issue #39)
-
-**📝 Notas de Compatibilidade**:
-- **EF Core 10 RC**: Sem breaking changes conhecidos desde RC.1
-- **Npgsql 10 RC**: Breaking changes documentados em
-- **Aspire 13 Preview**: API estável, apenas features novas em desenvolvimento
-
----
-
-### 📅 Sprint 1: Geographic Restriction + Module Integration (10 dias)
-
-**Status**: 🔄 DIAS 1-6 CONCLUÍDOS | FINALIZANDO (22-25 Nov 2025)
-**Branches**: `feature/geographic-restriction` (merged ✅), `feature/module-integration` (em review), `improve-tests-coverage` (criada)
-**Documentação**: Análise integrada em [testing/coverage.md](./testing/coverage.md)
-
-**Conquistas**:
-- ✅ Sprint 0 concluído: Migration .NET 10 + Aspire 13 merged (21 Nov)
-- ✅ Middleware de restrição geográfica implementado com IBGE API integration
-- ✅ 4 Module APIs implementados (Documents, ServiceCatalogs, SearchProviders, Locations)
-- ✅ Testes reativados: 28 testes (11 AUTH + 9 IBGE + 2 ServiceCatalogs + 3 IBGE unavailability + 3 duplicates removed)
-- ✅ Skipped tests reduzidos: 20 (26%) → 11 (11.5%) ⬇️ **-14.5%**
-- ✅ Integration events: Providers → SearchProviders indexing
-- ✅ Schema fixes: search_providers standardization
-- ✅ CI/CD fix: Workflow secrets validation removido
-
-**Objetivos Alcançados**:
-- ✅ Implementar middleware de restrição geográfica (compliance legal)
-- ✅ Implementar 4 Module APIs usando IModuleApi entre módulos
-- ✅ Reativar 28 testes E2E skipped (auth refactor + race condition fixes)
-- ✅ Integração cross-module: Providers ↔ Documents, Providers ↔ SearchProviders
-- ⏳ Aumentar coverage: 35.11% → 80%+ (MOVIDO PARA SPRINT 2)
-
-**Estrutura (2 Branches + Pr├│xima Sprint)**:
-
-#### Branch 1: `feature/geographic-restriction` (Dias 1-2) ✅ CONCLUÍDO
-- [x] GeographicRestrictionMiddleware (validação cidade/estado) ✅
-- [x] GeographicRestrictionOptions (configuration) ✅
-- [x] Feature toggle (Development: disabled, Production: enabled) ✅
-- [x] Unit tests (29 tests) + Integration tests (8 tests, skipped) ✅
-- [x] **Integração IBGE API** (validação oficial de municípios) ✅
- - [x] IbgeClient com normalização de nomes (Muriaé → muriae) ✅
- - [x] IbgeService com HybridCache (7 dias TTL) ✅
- - [x] GeographicValidationService (adapter pattern) ✅
- - [x] 2-layer validation (IBGE primary, simple fallback) ✅
- - [x] 15 unit tests IbgeClient ✅
- - [x] Configuração de APIs (ViaCep, BrasilApi, OpenCep, IBGE) ✅
- - [x] Remoção de hardcoded URLs (enforce configuration) ✅
-- [x] **Commit**: feat(locations): Integrate IBGE API for geographic validation (520069a) ✅
-- **Target**: 28.69% → 30% coverage ✅ (CONCLUÍDO: 92/104 testes passando)
-- **Merged**: 25 Nov 2025 ✅
-
-#### Branch 2: `feature/module-integration` (Dias 3-10) ✅ DIAS 3-6 CONCLUÍDOS | 🔄 DIA 7-10 CODE REVIEW
-- [x] **Dia 3**: Refactor ConfigurableTestAuthenticationHandler (reativou 11 AUTH tests) ✅
-- [x] **Dia 3**: Fix race conditions (identificados 2 para Sprint 2) ✅
-- [x] **Dia 4**: IDocumentsModuleApi implementation (7 métodos) ✅
-- [x] **Dia 5**: IServiceCatalogsModuleApi (3 métodos stub) + ISearchModuleApi (2 novos métodos) ✅
-- [x] **Dia 6**: Integration events (Providers → SearchProviders indexing) ✅
- - [x] DocumentVerifiedIntegrationEvent + handler ✅
- - [x] ProviderActivatedIntegrationEventHandler ✅
- - [x] SearchProviders schema fix (search → search_providers) ✅
- - [x] Clean InitialCreate migration ✅
-- [x] **Dia 7**: Naming standardization (Module APIs) ✅
- - [x] ILocationModuleApi → ILocationsModuleApi ✅
- - [x] ISearchModuleApi → ISearchProvidersModuleApi ✅
- - [x] SearchModuleApi → SearchProvidersModuleApi ✅
- - [x] ProviderIndexingDto → ModuleProviderIndexingDto ✅
-- [x] **Dia 7**: Test cleanup (remove diagnostics) ✅
-- [ ] **Dia 7-10**: Code review & documentation 🔄
-- **Target**: 30% → 35% coverage, 93/100 → 98/100 E2E tests
-- **Atual**: 2,076 tests (2,065 passing - 99.5%, 11 skipped - 0.5%)
-- **Commits**: 25+ total (583 commits total na branch)
-- **Status**: Aguardando code review antes de merge
-
-**Integrações Implementadas**:
-- ✅ **Providers → Documents**: ActivateProviderCommandHandler valida documentos (4 checks)
-- ✅ **Providers → SearchProviders**: ProviderActivatedIntegrationEventHandler indexa providers
-- ✅ **Documents → Providers**: DocumentVerifiedDomainEventHandler publica integration event
-- ⏳ **Providers → ServiceCatalogs**: API criada, aguarda implementação de gestão de serviços
-- ⏳ **Providers → Locations**: CEP lookup (baixa prioridade)
-
-**Bugs Críticos Corrigidos**:
-- ✅ AUTH Race Condition (ConfigurableTestAuthenticationHandler thread-safety)
-- ✅ IBGE Fail-Closed Bug (GeographicValidationService + IbgeService)
-- ✅ MunicipioNotFoundException criada para fallback correto
-- ✅ SearchProviders schema hardcoded (search → search_providers)
-
-#### 🆕 Coverage Improvement: ✅ CONCLUÍDO NO SPRINT 2
-- ✅ Coverage aumentado 28.2% → **90.56%** (+62.36pp - META 35% SUPERADA EM 55.56pp!)
-- ✅ 480 testes (479 passing, 1 skipped) - Suite completa validada em CI/CD
-- ✅ E2E tests para provider indexing flow implementados
-- ✅ Integration tests completos com Docker/TestContainers
-- ⏳ Criar .bru API collections para módulos (Sprint 3)
-- ⏳ Atualizar tools/ projects (MigrationTool, etc.) (Sprint 3)
-- **Resultado**: Sprint 2 concluído (10 Dez 2025) - Coverage report consolidado gerado
-
-**Tarefas Detalhadas**:
-
-#### 1. Integração Providers ↔ Documents ✅ CONCLUÍDO
-- [x] Providers: Validar `HasVerifiedDocuments` antes de aprovar prestador ✅
-- [x] Providers: Bloquear ativação se `HasRejectedDocuments` ou `HasPendingDocuments` ✅
-- [x] Documents: Publicar `DocumentVerified` event para atualizar status de Providers ✅
-- [x] Integration test: Fluxo completo de verificação de prestador ✅
-
-#### 2. Integração Providers ↔ ServiceCatalogs ✅ IMPLEMENTADO
-- [x] ServiceCatalogs: IServiceCatalogsModuleApi com 8 métodos implementados ✅
-- [x] ServiceCatalogs: ValidateServicesAsync implementado ✅
-- [x] ServiceCatalogs: Repository pattern com ServiceCategoryRepository ✅
-- [x] Integration tests: 15 testes passando ✅
-- ⏳ Providers: Integração de validação de serviços (Sprint 3)
-- ⏳ Admin Portal: UI para gestão de categorias/serviços (Sprint 3)
-
-#### 3. Integração SearchProviders ↔ Providers ✅ CONCLUÍDO
-- [x] Search: Métodos IndexProviderAsync e RemoveProviderAsync implementados ✅
-- [x] Search: Background handler consumindo ProviderVerificationStatusUpdated events ✅
-- [x] Search: ISearchProvidersModuleApi com 2 métodos ✅
-- [x] Integration test: Busca retorna apenas prestadores verificados ✅
-
-#### 4. Integração Providers ↔ Locations ✅ IMPLEMENTADO
-- [x] Locations: ILocationsModuleApi implementada ✅
-- [x] Locations: GetAddressFromCepAsync com 3 providers (ViaCEP, BrasilAPI, OpenCEP) ✅
-- [x] Locations: IBGE API integration para validação de municípios ✅
-- [x] Unit tests: 67 testes passando (Locations module) ✅
-- ⏳ Providers: Integração automática de CEP lookup (Sprint 3)
-
-#### 5. Restrição Geográfica (MVP Blocker) ✅ CONCLUÍDO
-- [x] Criar `AllowedCities` configuration em appsettings ✅
-- [x] GeographicRestrictionMiddleware implementado com IBGE integration ✅
-- [x] Fail-open fallback para validação simples quando IBGE unavailable ✅
-- [x] Integration test: 24 testes passando ✅
-- ⏳ Admin: Endpoint para gerenciar cidades permitidas (Sprint 3 - GitHub Pages docs)
-
-**Resultado Alcançado (Sprint 1)**:
-- ✅ Módulos integrados com business rules reais (Providers ↔ Documents, Providers ↔ SearchProviders)
-- ✅ Operação restrita a cidades piloto configuradas (IBGE API validation)
-- ✅ Background workers consumindo integration events (ProviderActivated, DocumentVerified)
-- ✅ Validações cross-module funcionando (HasVerifiedDocuments, HasRejectedDocuments)
-- ✅ Naming standardization (ILocationsModuleApi, ISearchProvidersModuleApi)
-- ✅ CI/CD fix (secrets validation removido)
-- ✅ **MERGED para master** (branch improve-tests-coverage-2 ativa para continuação)
-
----
-
-### 📅 Sprint 2: Test Coverage Improvement - Phase 1 (2 semanas)
-
-**Status**: ✅ CONCLUÍDO em 10 Dez 2025
-**Branches**: `improve-tests-coverage` (merged ✅), `improve-tests-coverage-2` (ativa - branch atual)
-
-**Conquistas (26 Nov - 10 Dez)**:
-- ✅ **improve-tests-coverage** branch merged (39 novos testes Shared)
- - ✅ ValidationBehavior: 9 testes (+2-3% coverage)
- - ✅ TopicStrategySelector: 11 testes (+3% coverage)
- - ✅ Shared core classes: 39 unit tests total
- - ✅ Coverage pipeline habilitado para todos módulos
- - ✅ Roadmap documentado com análise completa de gaps
-- ✅ **improve-tests-coverage-2** branch (2 Dez 2025 - 5 commits)
- - ✅ **Task 1 - PermissionMetricsService**: Concurrency fix (Dictionary → ConcurrentDictionary)
- - Commit: aabba3d - 813 testes passando (was 812)
- - ✅ **Task 2 - DbContext Transactions**: 10 testes criados (4 passing, 6 skipped/documented)
- - Commit: 5ff84df - DbContextTransactionTests.cs (458 lines)
- - Helper: ShortId() for 8-char GUIDs (Username max 30 chars)
- - 6 flaky tests documented (TestContainers concurrency issues)
- - ⏭️ **Task 3 - DbContextFactory**: SKIPPED (design-time only, não existe em runtime)
- - ⏭️ **Task 4 - SchemaIsolationInterceptor**: SKIPPED (component doesn't exist)
- - ✅ **Task 5 - Health Checks**: 47 testes totais (4 health checks cobertos)
- - Commit: 88eaef8 - ExternalServicesHealthCheck (9 testes, Keycloak availability)
- - Commit: 1ddbf4d - Refactor reflection removal (3 classes: internal → public)
- - Commit: fbf02b9 - HelpProcessing (9 testes) + DatabasePerformance (9 testes)
- - PerformanceHealthCheck: 20 testes (já existiam anteriormente)
- - ✅ **Code Quality**: Removida reflection de todos health checks (maintainability)
- - ✅ **Warning Fixes**: CA2000 reduzido de 16 → 5 (using statements adicionados)
- - ✅ **Shared Tests**: 841 testes passando (eram 813, +28 novos)
-
-**Progresso Coverage (2 Dez 2025)**:
-- Baseline: 45% (antes das branches - incluía código de teste)
-- **Atual: 27.9%** (14,504/51,841 lines) - **MEDIÇÃO REAL excluindo código gerado**
- - **Com código gerado**: 28.2% (14,695/52,054 lines) - diferença de -0.3%
- - **Código gerado excluído**: 213 linhas via ExcludeByFile patterns:
- - `**/*OpenApi*.generated.cs`
- - `**/System.Runtime.CompilerServices*.cs`
- - `**/*RegexGenerator.g.cs`
- - **Análise Correta**: 27.9% é coverage do **código de produção escrito manualmente**
-- **Branch Coverage**: 21.7% (2,264/10,422 branches) - sem c├│digo gerado
-- **Method Coverage**: 40.9% (2,168/5,294 métodos) - sem código gerado
-- **Test Suite**: 1,407 testes totais (1,393 passing - 99.0%, 14 skipped - 1.0%, 0 failing)
-- Target Phase 1: 35% (+7.1 percentage points from 27.9% baseline)
-- Target Final Sprint 2: 50%+ (revised from 80% - more realistic)
-
-**📊 Progressão de Coverage - Sprint 2 (Audit Trail)**:
-
-| Medição | Valor | Data | Notas |
-|---------|-------|------|-------|
-| **Baseline Pré-Refactor** | 28.2% | 2 Dez | Estado inicial Sprint 2 |
-| **Baseline Ajustado** | 27.9% | 2 Dez | Exclusão código gerado (OpenAPI + Regex) |
-| **Pós-Adição de Testes** | 90.56% | 10 Dez | 40+ novos testes + consolidação |
-
-**📈 Ganho Total**: +62.36 percentage points (28.2% → 90.56%)
-
-**Coverage por Assembly (Top 5 - Maiores)**:
-1. **MeAjudaAi.Modules.Users.Tests**: 0% (test code, expected)
-2. **MeAjudaAi.Modules.Users.Application**: 55.6% (handlers, queries, DTOs)
-3. **MeAjudaAi.Modules.Users.Infrastructure**: 53.9% (Keycloak, repos, events)
-4. **MeAjudaAi.Modules.Users.Domain**: 49.1% (entities, value objects, events)
-5. **MeAjudaAi.Shared**: 41.2% (authorization, caching, behaviors)
-
-**Coverage por Assembly (Bottom 5 - Gaps Críticos)**:
-1. **MeAjudaAi.ServiceDefaults**: 20.7% (health checks, extensions) ⚠️
-2. **MeAjudaAi.Modules.ServiceCatalogs.Domain**: 27.6% (domain events 25-50%)
-3. **MeAjudaAi.Shared.Tests**: 7.3% (test infrastructure code)
-4. **MeAjudaAi.ApiService**: 55.5% (middlewares, extensions) - better than expected
-5. **MeAjudaAi.Modules.Users.API**: 31.8% (endpoints, extensions)
-
-**Gaps Identificados (Coverage < 30%)**:
-- ⚠️ **ServiceDefaults.HealthChecks**: 0% (ExternalServicesHealthCheck, PostgresHealthCheck, GeolocationHealth)
- - **Motivo**: Classes estão no ServiceDefaults (AppHost), não no Shared (testado)
- - **Ação**: Mover health checks para Shared.Monitoring ou criar testes no AppHost
-- ⚠️ **Shared.Logging**: 0% (SerilogConfigurator, CorrelationIdEnricher, LoggingContextMiddleware)
- - **Ação**: Unit tests para enrichers, integration tests para middleware
-- ⚠️ **Shared.Jobs**: 14.8% → **85%+** (HangfireHealthCheck, HangfireAuthorizationFilter testes criados - 20 Dez 2025)
- - ✅ **HangfireHealthCheck**: 7 unit tests (validação de status, thresholds, null checks)
- - ✅ **HangfireAuthorizationFilter**: 11 unit tests (ACL admin, ambientes, auth checks)
- - **Ação Completada**: Testes unitários criados, coverage estimada 85%+
-- ⚠️ **Shared.Messaging.RabbitMq**: 12% (RabbitMqMessageBus)
- - **Motivo**: Integration tests require RabbitMQ container
- - **Ação**: TestContainers RabbitMQ ou mocks
-- ⚠️ **Shared.Database.Exceptions**: 17% (PostgreSqlExceptionProcessor)
- - **Ação**: Unit tests para constraint exception handling
-
-**Progresso Phase 1 (Improve-Tests-Coverage-2)**:
-- ✅ **5 Commits**: aabba3d, 5ff84df, 88eaef8, 1ddbf4d, fbf02b9
-- ✅ **40 New Tests**: Task 2 (10 DbContext) + Task 5 (27 health checks) + Task 1 (+3 fixes)
-- ✅ **Test Success Rate**: 99.0% (1,393/1,407 passing)
-- ✅ **Build Time**: ~25 minutes (full suite with Docker integration tests)
-- ✅ **Health Checks Coverage**:
- - ✅ ExternalServicesHealthCheck: 9/9 (Shared/Monitoring) - 100%
- - ✅ HelpProcessingHealthCheck: 9/9 (Shared/Monitoring) - 100%
- - ✅ DatabasePerformanceHealthCheck: 9/9 (Shared/Monitoring) - 100%
- - ✅ PerformanceHealthCheck: 20/20 (Shared/Monitoring) - 100% (pré-existente)
- - ❌ ServiceDefaults.HealthChecks.*: 0% (not in test scope yet)
-
-**Technical Decisions Validated**:
-- ✅ **No Reflection**: All health check classes changed from internal → public
- - Reason: "Não é para usar reflection, é difícil manter código com reflection"
- - Result: Direct instantiation `new MeAjudaAiHealthChecks.HealthCheckName(...)`
-- ✅ **TestContainers**: Real PostgreSQL for integration tests (no InMemory)
- - Result: 4 core transaction tests passing, 6 advanced scenarios documented
-- ✅ **Moq.Protected()**: HttpMessageHandler mocking for HttpClient tests
- - Result: 9 ExternalServicesHealthCheck tests passing
-- ✅ **Flaky Test Documentation**: TestContainers concurrency issues documented, not ignored
- - Files: DbContextTransactionTests.cs (lines with Skip attribute + detailed explanations)
-
-**Phase 1 Completion** - ✅ CONCLUÍDO (10 Dez 2025):
-- ✅ **Coverage Report Generated**: coverage/report/index.html + Summary.txt
-- ✅ **Roadmap Update**: Documento atualizado com coverage 90.56% alcançado
-- ✅ **Warnings**: Build limpo, zero warnings críticos
-- ✅ **Merged to Master**: PR #35 merged com sucesso
-
-**Phase 2 Completion** - ✅ CONCLUÍDO (10 Dez 2025):
-- ✅ **ServiceDefaults Health Checks**: Coberto via integration tests (coverage consolidada)
- - ✅ PostgresHealthCheck: Testado via TestContainers nos módulos
- - ✅ GeolocationHealthOptions: 67 testes no módulo Locations
- - ✅ Health checks architecture: 47 testes em Shared/Monitoring
-
-- ✅ **Logging Infrastructure**: Cobertura via testes de módulos
- - ✅ Logging testado através de integration tests
- - ✅ CorrelationId tracking validado em E2E tests
- - ✅ LoggingContextMiddleware: Funcional em todos módulos
-
-- ✅ **Messaging Resilience**: Coberto via integration events
- - ✅ Integration events: ProviderActivated, DocumentVerified testados
- - ✅ Event handlers: 15+ handlers com testes unitários
- - ✅ Message publishing: Validado em integration tests
-
-- ✅ **Middlewares**: Testados via E2E e integration tests
- - ✅ GeographicRestrictionMiddleware: 24 integration tests
- - ✅ Authorization: Validado em 100+ E2E tests com auth
- - ✅ Request/Response pipeline: Coberto em ApiService.Tests
-
-- ✅ **Database Exception Handling**: Coberto nos módulos
- - ✅ Repository pattern: Testado em todos 6 módulos
- - ✅ Constraint violations: Validados em integration tests
- - ✅ Transaction handling: Coberto em unit tests
-
-- ✅ **Documents Module**: Implementado e testado
- - ✅ Document validation: 45+ testes unitários
- - ✅ DocumentRepository: Integration tests completos
- - ✅ Module API: IDocumentsModuleApi com 7 métodos testados
-
-**Pr├│ximas Tarefas (Sprint 3 - GitHub Pages Documentation)**:
-- [ ] Migrar documentação para MkDocs Material
-- [ ] Criar .bru API collections para teste manual
-- [ ] Implementar data seeding scripts
-- [ ] Admin endpoints para geographic restrictions
-- [ ] Finalizar integrações cross-module pendentes
-
-**Objetivos Fase 1 (Dias 1-7) - ✅ CONCLUÍDO 2 DEZ 2025**:
-- ✅ Aumentar coverage Shared de baseline para 28.2% (medição real)
-- ✅ Focar em componentes críticos (Health Checks - 4/7 implementados)
-- ✅ Documentar testes flaky (6 TestContainers scope issues documented)
-- ✅ **NO REFLECTION** - todas classes public para manutenibilidade
-- ✅ 40 novos testes criados (5 commits, 1,393/1,407 passing)
-- ✅ Coverage report consolidado gerado (HTML + Text)
-
-**Objetivos Fase 2 (Dias 8-14) - ✅ CONCLUÍDO 10 DEZ 2025**:
-- ✅ ServiceDefaults: Coverage integrado ao report consolidado
-- ✅ Shared.Logging: Cobertura aumentada com testes de módulos
-- ✅ Shared.Messaging: Cobertura aumentada com testes de integração
-- ✅ Shared.Database.Exceptions: Cobertura aumentada com testes de módulos
-- ✅ **Overall Target SUPERADO**: 28.2% → **90.56%** (+62.36 percentage points!)
-
-**Decisões Técnicas**:
-- ✅ TestContainers para PostgreSQL (no InMemory databases)
-- ✅ Moq para HttpMessageHandler (HttpClient mocking)
-- ✅ FluentAssertions para assertions
-- ✅ xUnit 3.1.5 como framework
-- ✅ Classes public em vez de internal (no reflection needed)
-- ⚠️ Testes flaky com concurrent scopes marcados como Skip (documentados)
-
-**Health Checks Implementation** - ✅ CONCLUÍDO:
-- ✅ **ExternalServicesHealthCheck**: Keycloak availability (9 testes - Shared/Monitoring)
-- ✅ **PerformanceHealthCheck**: Memory, GC, thread pool (20 testes - Shared/Monitoring)
-- ✅ **HelpProcessingHealthCheck**: Business logic operational (9 testes - Shared/Monitoring)
-- ✅ **DatabasePerformanceHealthCheck**: DB metrics configured (9 testes - Shared/Monitoring)
-- ✅ **ServiceDefaults.HealthChecks.PostgresHealthCheck**: Testado via TestContainers (integration tests)
-- ✅ **Locations**: APIs de CEP health validadas (67 testes - ViaCEP, BrasilAPI, IBGE, OpenCEP)
-- ✅ **Documents**: Module health validado via integration tests
-- ✅ **Search**: PostGIS testado via SearchProviders integration tests
-
-**Arquitetura de Health Checks** - ✅ DEFINIDA:
-- **Shared/Monitoring**: 4 health checks implementados e testados (47 testes, 100% coverage)
-- **ServiceDefaults/HealthChecks**: Configurações base para ASP.NET Core health checks
-- **Módulos**: Cada módulo com seus próprios health checks específicos
-- **Decisão**: Arquitetura híbrida - Shared para componentes globais, módulos para checks específicos
-
-**Data Seeding** (SPRINT 3):
-- [ ] Seeder de ServiceCatalogs: 10 categorias + 50 serviços (estrutura pronta, dados pendentes)
-- [ ] Seeder de Providers: 20 prestadores fictícios
-- [ ] Seeder de Users: Admin + 10 customers
-- [ ] Script: `dotnet run --seed-dev-data`
-
-**Resultado Alcançado Sprint 2 (10 Dez 2025)**:
-- ✅ **Overall coverage**: **90.56% line**, 78.2% branch, 93.4% method (Cobertura Aggregated Direct)
-- ✅ **Covered lines**: 12,487 de 14,371 coverable lines
-- ✅ **Test suite**: **480 testes** (479 passing - 99.8%, 1 skipped - 0.2%, 0 failing)
-- ✅ **Assemblies**: 25 assemblies cobertos
-- ✅ **Classes**: 528 classes, 491 files
-- ✅ **Build quality**: Zero warnings críticos, build limpo
-- ✅ **Code quality**: Zero reflection, todas classes public
-- ✅ **Target SUPERADO**: Meta original 35% → **90.56% alcançado** (+55.56pp acima da meta!)
- - *Nota: Target Phase 2 original era 80%, revisado para 50% mid-sprint por realismo; ambos superados*
-- ✅ **CI/CD**: Todos workflows atualizados e funcionais (.NET 10 + Aspire 13)
-
-### Phase 2 Task Breakdown & Release Gates - ✅ CONCLUÍDO (10 Dez 2025)
-
-#### Coverage Targets (Progressive) - ✅ SUPERADO
-- ~~**Minimum (CI Warning Threshold)**: Line 70%, Branch 60%, Method 70%~~
-- ~~**Recommended**: Line 85%, Branch 75%, Method 85%~~
-- ✅ **ALCANÇADO**: Line **90.56%**, Branch **78.2%**, Method **93.4%** (EXCELLENT tier!)
-
-**Resultado**: Coverage inicial (28.2%) elevado para **90.56%** (+62.36pp). Todos os targets superados!
-
-#### Phase 2 Task Matrix - ✅ TODAS TAREFAS CONCLUÍDAS
-
-| Task | Priority | Estimated Tests | Target Coverage | Completed | Status |
-|------|----------|-----------------|-----------------|-----------|--------|
-| ServiceDefaults.HealthChecks | CRITICAL | 15-20 | 35%+ line | 10 Dez 2025 | ✅ DONE - Testado via integration tests |
-| Shared.Logging | CRITICAL | 10-12 | 30%+ line | 10 Dez 2025 | ✅ DONE - Coberto nos módulos |
-| Shared.Messaging.RabbitMq | CRITICAL | 20-25 | 40%+ line | 10 Dez 2025 | ✅ DONE - Integration events testados |
-| Shared.Database.Exceptions | HIGH | 15-20 | 50%+ line | 10 Dez 2025 | ✅ DONE - Repository pattern coberto |
-| Shared.Middlewares | HIGH | 12-15 | 45%+ line | 10 Dez 2025 | ✅ DONE - E2E tests validados |
-
-#### Release Gate Criteria - ✅ TODOS CRITÉRIOS ATENDIDOS
-
-**Phase 2 Merge to Master** (Required):
-- ✅ Line Coverage: **90.56%** (target 35%+ - SUPERADO)
-- ✅ Health Checks: 100% para Shared/Monitoring (47 testes)
-- ✅ Test Suite: **480 testes** (target 1,467 - redefinido para qualidade)
-- ✅ All Tests Passing: **99.8%** (479 passing, 1 skipped)
-- ✅ Code Quality: 0 warnings críticos, build limpo
-
-**Production Deployment** (Ready):
-- ✅ Critical Paths: 90%+ para todos módulos (Users, Providers, Documents, etc.)
-- ✅ End-to-End Tests: Todos fluxos principais passando (E2E.Tests + Integration.Tests)
-- ✅ Performance: Health checks validados, métricas ok
-- ✅ Security: .NET 10 GA + Aspire 13.0.2 GA (sem vulnerabilidades conhecidas)
-
-**Decisão**: ✅ Phase 2 **MERGED para master** (PR #35) - Todos gates atendidos!
-
-**Decisões Estratégicas Sprint 2 - ✅ EXECUTADAS**:
-1. ✅ **Componentes críticos cobertos**: ServiceDefaults, Logging, Messaging - 90.56% overall
-2. ✅ **Duplicação investigada**: Arquitetura híbrida definida (Shared/Monitoring + módulos)
-3. ✅ **TestContainers implementado**: PostgreSQL validado em 11 integration test suites
-4. ✅ **Flaky tests documentados**: 1 teste skipped (ServiceCatalogs debug), documentado
-5. ✅ **Target SUPERADO**: 90.56% alcançado (original 35% + realista 80% ambos superados!)
-6. ✅ **📚 Documentation Hosting**: Sprint 3 iniciado - branch `migrate-docs-github-pages` criada
- - ✅ **Decisão confirmada**: MkDocs Material com GitHub Pages
- - ✅ **Branch criada**: 10 Dez 2025
- - **Próximos passos**: Ver seção "Sprint 3: GitHub Pages Documentation" acima
-
----
-
-## 🚀 Próximos Passos (Pós Sprint 0 e Sprint 2)
-
-### 1️⃣ Sprint 3: Code & Documentation Organization + Final Integrations (PRÓXIMA TAREFA)
-
-**Branch**: `migrate-docs-github-pages` (criada em 10 Dez 2025)
-**Status**: 🔄 EM PROGRESSO (Parte 1 iniciada 11 Dez 2025)
-**Prioridade**: ALTA - Organização completa do projeto antes de prosseguir
-**Estimativa**: 2-3 semanas
-**Data prevista**: 11-30 Dez 2025
-
-**📅 Cronograma Detalhado com Gates Semanais**:
-
-| Semana | Período | Tarefa Principal | Entregável | Gate de Qualidade |
-|--------|---------|------------------|------------|-------------------|
-| **1** | 10-11 Dez | **Parte 1**: Docs Audit + MkDocs | `mkdocs.yml` live, 0 links quebrados | ✅ GitHub Pages deployment |
-| **2** | 11-17 Dez | **Parte 2**: Admin Endpoints + Tools | Endpoints de cidades + Bruno collections | ✅ CRUD + 15 E2E tests passing |
-| **3** | 18-24 Dez | **Parte 3**: Module Integrations | Provider ↔ ServiceCatalogs/Locations | ✅ Integration tests passing |
-| **4** | 25-30 Dez | **Parte 4**: Code Quality & Standardization | Moq, UuidGenerator, .slnx, OpenAPI | ✅ Build + tests 100% passing |
-
-**Estado Atual** (12 Dez 2025):
-- ✅ **Sprint 3 Parte 1 CONCLUÍDA**: GitHub Pages deployed em [GitHub Pages](https://frigini.github.io/MeAjudaAi/)
-- ✅ **Sprint 3 Parte 2 CONCLUÍDA**: Admin Endpoints + Tools
-- ✅ **Sprint 3 Parte 3 CONCLUÍDA**: Module Integrations
-- ✅ **Sprint 3 Parte 4 CONCLUÍDA**: Code Quality & Standardization
-- 🎯 **SPRINT 3 COMPLETA - 100% das tarefas realizadas!**
-
-**Resumo dos Avanços**:
-
-**Parte 1: Documentation Migration to GitHub Pages** ✅
-- ✅ Audit completo: 43 arquivos .md consolidados
-- ✅ mkdocs.yml: Configurado com navegação hierárquica
-- ✅ GitHub Actions: Workflow `.github/workflows/docs.yml` funcionando
-- ✅ Build & Deploy: Validado e publicado
-
-**Parte 2: Admin Endpoints + Tools** ✅
-- ✅ Admin endpoints AllowedCities implementados (5 endpoints CRUD)
-- ✅ Bruno Collections para Locations/AllowedCities (6 arquivos)
-- ✅ Testes: 4 integration + 15 E2E (100% passando)
-- ✅ Exception handling completo
-- ✅ Build quality: 0 erros, 71 arquivos formatados
-- ✅ Commit d1ce7456: "fix: corrigir erros de compilação e exception handling em E2E tests"
-- ✅ Code Quality & Security Fixes (Commit e334c4d7):
- - Removed hardcoded DB credentials (2 arquivos)
- - Fixed build errors: CS0234, CS0246
- - Fixed compiler warnings: CS8603, CS8602, CS8604
- - Added null-safe normalization in AllowedCityRepository
- - Fixed test assertions (6 arquivos)
- - Fixed XML documentation warnings
- - Updated Bruno API documentation
- - Fixed bare URLs in documentation
-
-**Parte 3: Module Integrations** ✅
-- ✅ Providers ↔ ServiceCatalogs Integration (Commit 53943da8):
- - Add/Remove services to providers (CQRS handlers)
- - Validação via IServiceCatalogsModuleApi
- - POST/DELETE endpoints com autorização SelfOrAdmin
- - Bruno collections (2 arquivos)
- - Domain events: ProviderServiceAdded/RemovedDomainEvent
-- ✅ Aspire Migrations (Commit 3d2b260b):
- - MigrationExtensions.cs com WithMigrations()
- - MigrationHostedService automático
- - Removida pasta tools/MigrationTool
- - Integração nativa com Aspire AppHost
-- ✅ Data Seeding Automático (Commit fe5a964c):
- - IDevelopmentDataSeeder interface
- - DevelopmentDataSeeder implementação
- - Seed automático após migrations (Development only)
- - ServiceCatalogs + Locations populados
-- ✅ Data Seeding Scripts (Commit ae659293):
- - seed-dev-data.ps1 (PowerShell)
- - seed-dev-data.sh (Bash)
- - Idempotente, autenticação Keycloak
- - Documentação em scripts/README.md
-
-**Parte 4: Code Quality & Standardization** ✅
-- ✅ NSubstitute → Moq (Commit e8683c08):
- - 4 arquivos de teste padronizados
- - Removida dependência NSubstitute
-- ✅ UuidGenerator Unification (Commit 0a448106):
- - 9 arquivos convertidos para UuidGenerator.NewId()
- - L├│gica centralizada em Shared.Time
-- ✅ Migração .slnx (Commit 1de5dc1a):
- - MeAjudaAi.slnx criado (formato XML)
- - 40 projetos validados
- - 3 workflows CI/CD atualizados
- - Benefícios: 5x mais rápido, menos conflitos git
-- ✅ OpenAPI Automation (Commit ae6ef2d0):
- - GitHub Actions para atualizar api-spec.json
- - Deploy automático para GitHub Pages com ReDoc
- - Documentação em docs/api-automation.md
-
-**Build Status Final**: ✅ 0 erros, 100% dos testes passando, código formatado
-
----
-
-## 🎯 Sprint 5 (19 Dez 2025 - 3 Jan 2026) - ✅ CONCLUÍDA ANTECIPADAMENTE!
-
-**Branch**: `refactor/code-quality-standardization` - Tarefas completadas nas Sprints 3-4
-
-**Status**: ✅ TODAS as tarefas foram concluídas em sprints anteriores:
-
-**✅ Prioridade 1 - Crítico (COMPLETO)**:
-
-1. ✅ **Substituir NSubstitute por Moq** (Sprint 3):
- - 3 arquivos migrados (ServiceDefaults.Tests, ApiService.Tests x2)
- - Padronização completa - projeto usa 100% Moq
- - Dependência duplicada removida
-
-2. ✅ **Unificar UuidGenerator** (Commit 0a448106 - Sprint 3):
- - ~26 ocorrências de `Guid.CreateVersion7()` substituídas
- - L├│gica centralizada em `MeAjudaAi.Shared.Time.UuidGenerator`
- - Preparado para futura customização
-
-3. ✅ **Migrar para .slnx** (Commit 1de5dc1a - Sprint 3):
- - `MeAjudaAi.slnx` criado (formato XML)
- - 40 projetos validados, build completo passando
- - 3 workflows CI/CD atualizados (.sln → .slnx)
- - Benefícios confirmados: 5x mais rápido, menos conflitos git
-
-4. ✅ **Design Patterns Documentation** (Sprint 3-4):
- - Seção completa em `docs/architecture.md`
- - Padr├╡es documentados: Repository, CQRS, Domain Events, Factory, Strategy, Middleware Pipeline
- - Exemplos reais de código incluídos (AllowedCityRepository, Commands/Queries)
- - Seção anti-patterns evitados adicionada
-
-**✅ Prioridade 2 - Desejável (COMPLETO)**:
-
-5. ✅ **Bruno Collections** (Sprint 3):
- - ✅ **Users**: 6 arquivos .bru (CreateUser, DeleteUser, GetUsers, GetUserById, UpdateUser, GetUserByEmail)
- - ✅ **Providers**: 16 arquivos .bru (CRUD completo + Services + Verification)
- - ✅ **Documents**: 3 arquivos .bru (Upload, GetProviderDocuments, Verify)
- - ✅ **ServiceCatalogs**: 35+ arquivos .bru (Categories + Services CRUD)
- - ✅ **Locations**: 6 arquivos .bru (AllowedCities CRUD + README)
-
-**⏸️ Tarefas Remanescentes** (Prioridade 3 - Baixa urgência, mover para Sprint 6 ou posterior):
-- 🔒 Avaliar migração AspNetCoreRateLimit library
-- 📊 Verificar completude Logging Estruturado (Seq, Domain Events, Performance)
-- 🔗 Providers ↔ Locations Integration (auto-populate cidade/estado via CEP)
-
----
-
-## 🎯 Próximos Passos - Sprint 6 (6 Jan - 24 Jan 2026)
-
-**Foco**: Frontend Blazor - Admin Portal Setup + Customer App Início
-
-**Branch Sugerida**: `feature/blazor-admin-portal`
-
-**Objetivo Geral**: Iniciar desenvolvimento frontend com Blazor WASM para Admin Portal e MAUI Hybrid para Customer App.
-
-**Estimativa Total**: 6-9 dias ├║teis (considerando feriados de fim de ano)
-
----
-
-#### 📚 Parte 1: Documentation Migration to GitHub Pages (1 semana)
-
-**Objetivos**:
-- Migrar ~50 arquivos .md do diret├│rio `docs/` para GitHub Pages
-- Implementar MkDocs Material para site navegável
-- Consolidar e eliminar documentação duplicada/obsoleta
-- Estabelecer estrutura hierárquica lógica (max 3 níveis)
-- Deploy automático via GitHub Actions
-
-**Processo de Migração** (iterativo, documento a documento):
-1. **Auditoria inicial**: Listar todos os .md e categorizar (atual/defasado/duplicado)
-2. **Consolidação**: Mesclar conteúdo duplicado (ex: ci-cd.md vs ci-cd/workflows-overview.md)
-3. **Limpeza**: Remover informações obsoletas ou mover para `docs/archive/`
-4. **Reorganização**: Estruturar hierarquia (Getting Started → Architecture → Testing → CI/CD → API)
-5. **Validação**: Revisar links internos, atualizar referências cruzadas
-6. **Navegação**: Configurar `mkdocs.yml` com estrutura final
-7. **Deploy**: Habilitar GitHub Pages e testar site completo
-
-**Critérios de Qualidade**:
-- ✅ Zero duplicação de conteúdo
-- ✅ Informações datadas removidas ou arquivadas
-- ✅ Navegação intuitiva (max 3 níveis de profundidade)
-- ✅ Todos links internos funcionando
-- ✅ Search global funcional
-- ✅ Mobile-friendly + dark mode
-
-**Arquivos a Criar**:
-- `mkdocs.yml` (configuração principal)
-- `.github/workflows/deploy-docs.yml` (CI/CD workflow)
-- `docs/requirements.txt` (dependências Python: mkdocs-material, plugins)
-
-**URL Final**: `https://frigini.github.io/MeAjudaAi/`
-
----
-
-#### 🔧 Parte 2: Scripts & Tools Organization (3-4 dias)
-
-**Objetivos**:
-- Revisar e atualizar scripts em `scripts/`
-- Atualizar ferramentas em `tools/` (MigrationTool, etc.)
-- Criar .bru API collections para teste manual dos m├│dulos
-- Implementar data seeding scripts
-
-**Tarefas Detalhadas**:
-- [ ] **Scripts Cleanup**:
- - [ ] Revisar `scripts/generate-clean-coverage.ps1` (funcionando, documentar melhor)
- - [ ] Atualizar scripts de build/deploy se necessário
- - [ ] Criar script de data seeding: `scripts/seed-dev-data.ps1`
-
-- [ ] **Tools/ Projects**:
- - [ ] Atualizar MigrationTool para .NET 10
- - [ ] Validar ferramentas auxiliares
- - [ ] Documentar uso de cada tool
-
-- [ ] **API Collections (.bru)**:
- - [ ] Criar collection para m├│dulo Users
- - [ ] Criar collection para m├│dulo Providers
- - [ ] Criar collection para m├│dulo Documents
- - [ ] Criar collection para m├│dulo ServiceCatalogs
- - [ ] Criar collection para m├│dulo Locations
- - [ ] Criar collection para m├│dulo SearchProviders
- - [ ] Documentar setup e uso das collections
-
-- [ ] **Data Seeding**:
- - [ ] Seeder de ServiceCatalogs: 10 categorias + 50 serviços
- - [ ] Seeder de Providers: 20 prestadores fictícios
- - [ ] Seeder de Users: Admin + 10 customers
- - [ ] Script: `dotnet run --seed-dev-data`
-
----
-
-#### 🔗 Parte 3: Final Module Integrations (3-5 dias)
-
-**Objetivos**:
-- Finalizar integrações cross-module pendentes
-- Implementar admin endpoints para gestão
-- Validar fluxos end-to-end completos
-
-**Tarefas Detalhadas**:
-
-**1. Providers Γåö ServiceCatalogs Integration**:
-- [ ] Providers: Adicionar `ProviderServices` linking table (many-to-many)
-- [ ] Providers: Validar services via `IServiceCatalogsModuleApi.ValidateServicesAsync`
-- [ ] Providers: Bloquear serviços inativos ou inexistentes
-- [ ] Integration tests: Validação completa do fluxo
-
-**2. Providers Γåö Locations Integration**:
-- [ ] Providers: Usar `ILocationsModuleApi.GetAddressFromCepAsync` no registro
-- [ ] Providers: Auto-populate cidade/estado via Locations
-- [ ] Unit test: Mock de ILocationsModuleApi em Providers.Application
-
-**3. Geographic Restrictions Admin**:
-- ✅ **Database**: LocationsDbContext + AllowedCity entity (migration 20251212002108_InitialAllowedCities)
-- ✅ **Repository**: IAllowedCityRepository implementado com queries otimizadas
-- ✅ **Handlers**: CreateAllowedCityHandler, UpdateAllowedCityHandler, DeleteAllowedCityHandler, GetAllowedCityByIdHandler, GetAllAllowedCitiesHandler
-- ✅ **Domain Exceptions**: NotFoundException, AllowedCityNotFoundException, BadRequestException, DuplicateAllowedCityException
-- ✅ **Exception Handling**: LocationsExceptionHandler (IExceptionHandler) + GlobalExceptionHandler com ArgumentException
-- ✅ **Endpoints**:
- - GET /api/v1/admin/allowed-cities (listar todas)
- - GET /api/v1/admin/allowed-cities/{id} (buscar por ID)
- - POST /api/v1/admin/allowed-cities (criar nova)
- - PUT /api/v1/admin/allowed-cities/{id} (atualizar)
- - DELETE /api/v1/admin/allowed-cities/{id} (deletar)
-- ✅ **Bruno Collections**: 6 arquivos .bru criados (CRUD completo + README)
-- ✅ **Testes**: 4 integration tests + 15 E2E tests (100% passando - 12 Dez)
-- ✅ **Compilação**: 7 erros corrigidos (MetricsCollectorService, SerilogConfigurator, DeadLetterServices, IbgeClient, GeographicValidationServiceTests)
-- ✅ **Exception Handling Fix**: Program.cs com módulos registrados ANTES de AddSharedServices (ordem crítica para LIFO handler execution)
-- ✅ **Code Quality**: 0 erros, dotnet format executado (71 arquivos formatados)
-- ✅ **Commit**: d1ce7456 - "fix: corrigir erros de compilação e exception handling em E2E tests"
-
-**4. ServiceCatalogs Admin UI Integration**:
-- [ ] Admin Portal: Endpoint para associar serviços a prestadores
-- [ ] API endpoints: CRUD de categorias e serviços
-- [ ] Documentação: Workflows de gestão
-
----
-
-#### 🎯 Parte 4: Code Quality & Standardization (5-8 dias)
-
-**Objetivos**:
-- Padronizar uso de bibliotecas de teste (substituir NSubstitute por Moq)
-- Unificar geração de IDs (usar UuidGenerator em todo código)
-- Migrar para novo formato .slnx (performance e versionamento)
-- Automatizar documentação OpenAPI no GitHub Pages
-- **NOVO**: Documentar Design Patterns implementados
-- **NOVO**: Avaliar migração para AspNetCoreRateLimit library
-- **NOVO**: Verificar completude do Logging Estruturado (Seq, Domain Events, Performance)
-
-**Tarefas Detalhadas**:
-
-**1. Substituir NSubstitute por Moq** ⚠️ CRÍTICO:
-- [ ] **Análise**: 3 arquivos usando NSubstitute detectados
- - `tests/MeAjudaAi.ServiceDefaults.Tests/ExtensionsTests.cs`
- - `tests/MeAjudaAi.ApiService.Tests/Extensions/SecurityExtensionsTests.cs`
- - `tests/MeAjudaAi.ApiService.Tests/Extensions/PerformanceExtensionsTests.cs`
-- [ ] Substituir `using NSubstitute` por `using Moq`
-- [ ] Atualizar syntax: `Substitute.For()` → `new Mock()`
-- [ ] Remover PackageReference NSubstitute dos .csproj:
- - `tests/MeAjudaAi.ServiceDefaults.Tests/MeAjudaAi.ServiceDefaults.Tests.csproj`
- - `tests/MeAjudaAi.ApiService.Tests/MeAjudaAi.ApiService.Tests.csproj`
-- [ ] Executar testes para validar substituição
-- [ ] **Razão**: Padronizar com resto do projeto (todos outros testes usam Moq)
-
-**2. Unificar geração de IDs com UuidGenerator** 📋:
-- [ ] **Análise**: ~26 ocorrências de `Guid.CreateVersion7()` detectadas
- - **C├│digo fonte** (2 arquivos):
- - `src/Modules/Users/Infrastructure/Services/LocalDevelopment/LocalDevelopmentUserDomainService.cs` (linha 30)
- - `src/Shared/Time/UuidGenerator.cs` (3 linhas - já correto, implementação base)
- - **Testes unitários** (18 locais em 3 arquivos):
- - `src/Modules/Providers/Tests/Unit/Application/Queries/GetProviderByDocumentQueryHandlerTests.cs` (2x)
- - `src/Modules/SearchProviders/Tests/Unit/Infrastructure/Repositories/SearchableProviderRepositoryTests.cs` (14x)
- - `src/Modules/Documents/Tests/Integration/DocumentsInfrastructureIntegrationTests.cs` (2x)
- - **Testes de integração/E2E** (6 locais em 4 arquivos):
- - `tests/MeAjudaAi.Integration.Tests/Modules/Users/UserRepositoryIntegrationTests.cs` (1x)
- - `tests/MeAjudaAi.Integration.Tests/Modules/Documents/DocumentRepositoryIntegrationTests.cs` (1x)
- - `tests/MeAjudaAi.Integration.Tests/Modules/Providers/ProviderRepositoryIntegrationTests.cs` (1x)
- - `tests/MeAjudaAi.Shared.Tests/Auth/ConfigurableTestAuthenticationHandler.cs` (1x)
- - `tests/MeAjudaAi.E2E.Tests/Integration/UsersModuleTests.cs` (2x)
-- [ ] Substituir todas ocorrências por `UuidGenerator.NewId()`
-- [ ] Adicionar `using MeAjudaAi.Shared.Time;` onde necessário
-- [ ] Executar build completo para validar
-- [ ] Executar test suite completo (~480 testes)
-- [ ] **Razão**: Centralizar lógica de geração de UUIDs v7, facilitar futura customização (ex: timestamp override para testes)
-
-**3. Migrar solução para formato .slnx** 🚀:
-- [ ] **Contexto**: Novo formato XML introduzido no .NET 9 SDK
- - **Benefícios**:
- - Formato legível e versionável (XML vs binário)
- - Melhor performance de load/save (até 5x mais rápido)
- - Suporte nativo no VS 2022 17.12+ e dotnet CLI 9.0+
- - Mais fácil de fazer merge em git (conflitos reduzidos)
- - **Compatibilidade**: .NET 10 SDK já suporta nativamente
-- [ ] **Migração**:
- - [ ] Criar backup: `Copy-Item MeAjudaAi.sln MeAjudaAi.sln.backup`
- - [ ] Executar: `dotnet sln MeAjudaAi.sln migrate` (comando nativo .NET 9+)
- - [ ] Validar: `dotnet sln list` (verificar todos 37 projetos listados)
- - [ ] Build completo: `dotnet build MeAjudaAi.slnx`
- - [ ] Testes: `dotnet test MeAjudaAi.slnx`
- - [ ] Atualizar CI/CD: `.github/workflows/*.yml` (trocar .sln por .slnx)
- - [ ] Remover `.sln` após validação completa
-- [ ] **Rollback Plan**: Manter `.sln.backup` por 1 sprint
-- [ ] **Decisão**: Fazer em branch separada ou na atual?
- - **Recomendação**: Branch separada `migrate-to-slnx` (isolamento de mudança estrutural)
- - **Alternativa**: Na branch atual se sprint já estiver avançada
-
-**4. OpenAPI Documentation no GitHub Pages** 📖:
-- [ ] **Análise**: Arquivo `api/api-spec.json` já existe
-- [ ] **Implementação**:
- - [ ] Configurar GitHub Action para extrair OpenAPI spec:
- - Opção 1: Usar action `bump-sh/github-action@v1` (Bump.sh integration)
- - Opção 2: Usar action `seeebiii/redoc-cli-github-action@v10` (ReDoc UI)
- - Opção 3: Custom com Swagger UI estático
- - [ ] Criar workflow `.github/workflows/update-api-docs.yml`:
- ```yaml
- - uses: actions/checkout@v4
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: '10.0.x'
- - name: Extract OpenAPI spec
- run: |
- dotnet build
- dotnet run --project tools/OpenApiExtractor/OpenApiExtractor.csproj
- - name: Generate API docs
- uses: seeebiii/redoc-cli-github-action@v10
- with:
- args: bundle api/api-spec.json -o docs/api/index.html
- - name: Deploy to GitHub Pages
- # (integrar com mkdocs deploy existente)
- ```
- - [ ] Adicionar seção "API Reference" no mkdocs.yml
- - [ ] Substituir seção atual de API reference por link dinâmico
- - [ ] Validar UI renderizada corretamente (testar endpoints, schemas)
-- [ ] **Ferramentas disponíveis**:
- - ✅ `api/api-spec.json` existe (gerado manualmente ou via tool?)
- - [ ] Verificar se existe tool em `tools/` para extração automática
- - [ ] Se não existir, criar `tools/OpenApiExtractor` para CI/CD
-- [ ] **Benefícios**:
- - Documentação sempre atualizada com código
- - UI interativa (try-it-out)
- - Melhor DX para consumidores da API
-
-**5. Health Checks & Monitoring** 🏥:
-- [x] **Health Checks Core**: ✅ IMPLEMENTADO
- - `src/Shared/Monitoring/HealthChecks.cs`: 4 health checks implementados
- - 47 testes, 100% coverage
- - Componentes: ExternalServicesHealthCheck, PerformanceHealthCheck, HelpProcessingHealthCheck, DatabasePerformanceHealthCheck
- - Endpoints: `/health`, `/health/live`, `/health/ready`
-- [x] **Dashboard**: ✅ DECISÃO ARQUITETURAL
- - **Usar dashboard nativo do .NET Aspire** (não AspNetCore.HealthChecks.UI)
- - Aspire fornece dashboard integrado com telemetria, traces e métricas
- - Health checks expostos via endpoints JSON consumidos pelo Aspire
- - Melhor integração com ecossistema .NET 9+ e cloud-native deployments
- - **Rationale**: Evitar dependência extra, melhor DX, alinhamento com roadmap .NET
-
-**6. Design Patterns Documentation** 📚:
-- [ ] **Branch**: `docs/design-patterns`
-- [ ] **Objetivo**: Documentar padr├╡es arquiteturais implementados no projeto
-- [ ] **Tarefas**:
- - [ ] Atualizar `docs/architecture.md` com seção "Design Patterns Implementados":
- - **Repository Pattern**: `I*Repository` interfaces + implementações Dapper
- - **Unit of Work**: Transaction management nos repositories
- - **CQRS**: Separação de Commands e Queries (implementação própria com CommandDispatcher/QueryDispatcher)
- - **Domain Events**: `IDomainEvent` + handlers
- - **Factory Pattern**: `UuidGenerator`, `SerilogConfigurator`
- - **Middleware Pipeline**: ASP.NET Core middlewares customizados
- - **Strategy Pattern**: Feature toggles (FeatureManagement)
- - **Options Pattern**: Configuração fortemente tipada
- - **Dependency Injection**: Service lifetimes (Scoped, Singleton, Transient)
- - [ ] Adicionar exemplos de código reais (não pseudo-código):
- - Exemplo Repository Pattern: `UserRepository.cs` (método `GetByIdAsync`)
- - Exemplo CQRS: `CreateUserCommand` + `CreateUserCommandHandler`
- - Exemplo Domain Events: `UserCreatedEvent` + `UserCreatedEventHandler`
- - [ ] Criar diagramas (opcional, usar Mermaid):
- - Diagrama CQRS flow
- - Diagrama Repository + UnitOfWork
- - Diagrama Middleware Pipeline
- - [ ] Adicionar seção "Anti-Patterns Evitados":
- - ❌ Anemic Domain Model (mitigado com domain services)
- - ❌ God Objects (mitigado com separação por módulos)
- - ❌ Service Locator (substituído por DI container)
- - [ ] Referências externas:
- - Martin Fowler: Patterns of Enterprise Application Architecture
- - Microsoft: eShopOnContainers (referência de DDD + Clean Architecture)
- - .NET Microservices: Architecture e-book
-- [ ] **Estimativa**: 1-2 dias
-
-**7. Rate Limiting com AspNetCoreRateLimit** ΓÜí:
-- [x] **Rate Limiting Custom**: ✅ JÁ IMPLEMENTADO
- - `src/Bootstrapper/MeAjudaAi.ApiService/Middlewares/RateLimitingMiddleware.cs`
- - Usa `IMemoryCache` (in-memory)
- - Testes unitários implementados
- - Configuração via `RateLimitOptions` (appsettings)
-- [ ] **Decisão Estratégica** ⚠️ AVALIAR:
- - **Opção A**: Migrar para `AspNetCoreRateLimit` library
- - ✅ Vantagens:
- - Distributed rate limiting com Redis (multi-instance)
- - Configuração rica (whitelist, blacklist, custom rules)
- - Suporte a rate limiting por endpoint, IP, client ID
- - Throttling policies (burst, sustained)
- - Community-tested e bem documentado
- - ❌ Desvantagens:
- - Dependência adicional (biblioteca de terceiros)
- - Configuração mais complexa
- - Overhead de Redis (infraestrutura adicional)
- - **Opção B**: Manter middleware custom
- - ✅ Vantagens:
- - Controle total sobre l├│gica
- - Zero dependências externas
- - Performance (in-memory cache)
- - Simplicidade
- - ❌ Desvantagens:
- - Não funciona em multi-instance (sem Redis)
- - Features limitadas vs biblioteca
- - Manutenção nossa
- - [ ] **Recomendação**: Manter custom para MVP, avaliar migração para Aspire 13+ (tem rate limiting nativo)
- - [ ] **Se migrar**:
- - [ ] Instalar: `AspNetCoreRateLimit` (v5.0+)
- - [ ] Configurar Redis distributed cache
- - [ ] Migrar `RateLimitOptions` para configuração da biblioteca
- - [ ] Atualizar testes
- - [ ] Documentar nova configuração
-- [ ] **Estimativa (se migração)**: 1-2 dias
-
-**8. Logging Estruturado - Verificação de Completude** 📊:
-- [x] **Core Logging**: ✅ JÁ IMPLEMENTADO
- - Serilog configurado (`src/Shared/Logging/SerilogConfigurator.cs`)
- - CorrelationId enricher implementado
- - LoggingContextMiddleware funcional
- - Cobertura testada via integration tests
-- [x] **Azure Application Insights**: ✅ CONFIGURADO
- - OpenTelemetry integration (`src/Aspire/MeAjudaAi.ServiceDefaults/Extensions.cs` linha 116-120)
- - Variável de ambiente: `APPLICATIONINSIGHTS_CONNECTION_STRING`
- - Suporte a traces, metrics, logs
-- [x] **Seq Integration**: ✅ JÁ CONFIGURADO
- - `appsettings.Development.json` linha 24-28: serverUrl `http://localhost:5341`
- - `appsettings.Production.json` linha 20-24: variáveis de ambiente `SEQ_SERVER_URL` e `SEQ_API_KEY`
- - Serilog.Sinks.Seq já instalado e funcional
-- [ ] **Tarefas de Verificação** ⚠️ PENDENTES:
- - [ ] **Seq Local**: Validar que Seq container está rodando (Docker Compose)
- - [ ] **Domain Events Logging**: Verificar se todos domain events estão sendo logados
- - [ ] Adicionar correlation ID aos domain events (se ainda não tiver)
- - [ ] Verificar log level apropriado (Information para eventos de neg├│cio)
- - [ ] Exemplos: `UserCreatedEvent`, `ProviderRegisteredEvent`, etc.
- - [ ] **Performance Logging**: Verificar se performance metrics estão sendo logados
- - [ ] Middleware de performance já existe? (verificar `PerformanceExtensions.cs`)
- - [ ] Adicionar logs para queries lentas (> 1s)
- - [ ] Adicionar logs para endpoints lentos (> 3s)
- - [ ] **Documentação**: Atualizar `docs/development.md` com instruções de uso do Seq
- - [ ] Como acessar Seq UI (`http://localhost:5341`)
- - [ ] Como filtrar logs por CorrelationId
- - [ ] Como criar queries customizadas
- - [ ] Screenshot da UI do Seq com exemplo de query
-- [ ] **Estimativa**: 1 dia (apenas verificação e pequenas adições)
-- [ ] **Decisão de ferramenta**:
- - **ReDoc**: UI moderna, read-only, melhor para documentação (recomendado)
- - **Swagger UI**: Try-it-out interativo, melhor para desenvolvimento
- - **Bump.sh**: Versionamento de API, diff tracking (mais complexo)
- - **Recomendação inicial**: ReDoc (simplicidade + qualidade visual)
-
----
-
-#### ✅ Critérios de Conclusão Sprint 3 (Atualizado)
-
-**Parte 1 - Documentation** (✅ CONCLUÍDO 11 Dez):
-- ✅ GitHub Pages live em `https://frigini.github.io/MeAjudaAi/`
-- ✅ Todos .md files revisados e organizados (43 arquivos)
-- ✅ Zero links quebrados
-- ✅ Search funcional
-- ✅ Deploy automático via GitHub Actions
-
-**Parte 2 - Admin Endpoints & Tools** (✅ CONCLUÍDA - 13 Dez):
-- ✅ Admin API de cidades permitidas implementada (5 endpoints CRUD)
-- ✅ Bruno Collections para Locations/AllowedCities (6 arquivos .bru)
-- ✅ Bruno Collections para todos módulos (Users: 6, Providers: 13, Documents: 0, ServiceCatalogs: 13, SearchProviders: 3)
-- ✅ Testes: 4 integration + 15 E2E (100% passando)
-- ✅ Exception handling completo (LocationsExceptionHandler + GlobalExceptionHandler)
-- ✅ Build quality: 0 erros, dotnet format executado
-- ✅ Scripts documentados e auditoria completa (commit b0b94707)
-- ✅ Data seeding funcional (DevelopmentDataSeeder.cs - ServiceCatalogs, Providers, Users)
-- ✅ MigrationTool migrado para Aspire AppHost (commit 3d2b260b)
-
-**Parte 3 - Module Integrations** (✅ CONCLUÍDA - 12 Dez):
-- ✅ Providers ↔ ServiceCatalogs: Completo (commit 53943da8 - ProviderServices many-to-many)
-- ✅ Providers ↔ Locations: Completo (ILocationsModuleApi integrado)
-- ✅ ServiceCatalogs Admin endpoints: CRUD implementado (13 endpoints .bru)
-- ✅ Integration tests: Todos fluxos validados (E2E tests passando)
-
-**Parte 4 - Code Quality & Standardization** (✅ CONCLUÍDA - 12 Dez):
-- ✅ NSubstitute substituído por Moq (commit e8683c08 - padronização completa)
-- ✅ Guid.CreateVersion7() substituído por UuidGenerator (commit 0a448106 - ~26 locais)
-- ✅ Migração para .slnx concluída (commit 1de5dc1a - formato .NET 9+)
-- ✅ OpenAPI docs no GitHub Pages automatizado (commit ae6ef2d0)
-- ✅ Design Patterns Documentation (5000+ linhas em architecture.md)
-- ✅ SonarQube warnings resolution (commit d8bb00dc - ~135 warnings resolvidos)
-- ✅ Rate Limiting: Avaliado - decisão de manter custom para MVP
-- ✅ Logging Estruturado: Serilog + Seq + App Insights + Correlation IDs completo
-
-**Quality Gates Gerais**:
-- ✅ Build: 100% sucesso (Sprint 3 concluída - 13 Dez)
-- ✅ Tests: 480 testes passando (99.8% - 1 skipped)
-- ✅ Coverage: 90.56% line (target superado em 55.56pp)
-- ✅ Documentation: GitHub Pages deployed (https://frigini.github.io/MeAjudaAi/)
-- ✅ API Reference: Automatizada via OpenAPI (GitHub Pages)
-- ✅ Code Standardization: 100% Moq, 100% UuidGenerator
-- ✅ SonarQube: ~135 warnings resolvidos sem pragma suppressions
-- ✅ CI/CD: Formatting checks + exit code masking corrigidos
-
-**Resultado Esperado**: Projeto completamente organizado, padronizado, documentado, e com todas integrações core finalizadas. Pronto para avançar para Admin Portal (Sprint 4) ou novos módulos.
-
----
-
diff --git a/docs/roadmap.md b/docs/roadmap.md
index 0bf5c0bac..36afaec39 100644
--- a/docs/roadmap.md
+++ b/docs/roadmap.md
@@ -1,46 +1,86 @@
-# 🗺️ Roadmap - MeAjudaAi
+# 🗺️ Roadmap MeAjudaAi
-Este documento consolida o planejamento estratégico e tático da plataforma MeAjudaAi. Para facilitar a leitura e manutenção, o roadmap foi dividido em seções específicas:
+Este é o planejamento estratégico unificado da plataforma MeAjudaAi.
---
-## 🚀 [Roadmap Atual](./roadmap-current.md)
-**Status**: Fase 2 em andamento (Frontend React + Mobile).
-Contém o status atual das sprints, o cronograma detalhado até o MVP e o plano de mitigação de riscos.
-- **Sprint Atual**: 9 (Buffer & Risk Mitigation)
-- **Sprint Concluída**: 8E (E2E Tests & React Unit Infrastructure)
-- **Sprint em Andamento**: 9E (Buffer & Risk Mitigation + Módulo Comunicações)
-- **Meta MVP**: Maio 2026 (12-16)
+## 📊 Status Atual (Abril 2026)
+
+**Sprint Atual**: 10 (Qualidade & Onboarding)
+**Status**: 🚧 Em andamento
+**Meta MVP**: 12 - 16 de Maio de 2026
+
+**Stack Principal**: .NET 10 LTS + Aspire 13 + PostgreSQL + NX Monorepo + React 19 + Next.js 15 + Tailwind v4
---
-## 📜 [Histórico do Roadmap](./roadmap-history.md)
-Contém o registro de todas as fases e sprints já concluídos.
-- **Fase 1**: Fundação (Janeiro 2025)
-- **Fase 1.5**: Fundação Técnica (Março 2026)
-- **Sprints**: 0 a 8B.1
+## 🚀 Sprint 10 - Qualidade & Onboarding (12 Abr - 26 Abr 2026) 🚧 [EM ANDAMENTO]
+
+**Objetivo**: Estabelecer confiança na plataforma através de avaliações e simplificar o acesso de novos prestadores.
+
+### 🔴 MUST-HAVE:
+
+#### 1. 🌟 Ratings Module (Módulo de Avaliações) ✅ [CONCLUÍDO]
+* **Arquitetura**: **Consistência Eventual**. O módulo de busca (`SearchProviders`) não fará Join com o módulo de Ratings. Sempre que um review for aprovado, um `ReviewApprovedIntegrationEvent` será disparado e o módulo de busca atualizará o campo `AverageRating` no seu próprio registro desnormalizado.
+* **Funcionalidades**:
+ * ✅ **Avaliação de Prestadores**: Clientes podem adicionar nota (1 a 5 estrelas) e comentário textual após a conclusão de um serviço.
+ * ✅ **Moderação de Conteúdo**: Filtro automático via Regex e manual para comentários que violem as regras (xingamentos, ofensas, SPAM).
+ * ✅ **Ranking de Busca**: Algoritmo de busca priorizando prestadores com melhor média e maior volume de avaliações verificadas.
+* **Schema DB**: `ratings` | **ModuleName**: `Ratings`.
+
+#### 2. 🔑 Login Social (Instagram) - ISSUE #141 ✅ [CONCLUÍDO]
+* **Ação**: Configuração de Identity Provider nativo do Instagram no Keycloak para permitir que prestadores usem seu perfil do Instagram para autenticação.
+
+#### 3. 🛡️ OpenAPI Breaking Change Gating (CI) ✅ [CONCLUÍDO]
+* **Ação**: Novo step no workflow de PR para comparar o `api-base.json` com `api-current/api-spec.json` usando `breaking-only` e `fail-on-diff` para falhar o build caso existam mudanças destrutivas na API. Corrigido para rodar em ambiente `Testing` para evitar dependências de segredos reais.
+
+#### 4. 📋 Coleções Bruno (.bru) ✅ [CONCLUÍDO]
+* **Ação**: Documentação técnica de 100% dos endpoints existentes em `src/Modules/*/API/API.Client/`.
---
-## 🔮 [Roadmap Futuro](./roadmap-future.md)
-Contém os objetivos pós-MVP e ideias para o backlog de longo prazo.
-- **Fase 3**: Qualidade e Monetização
-- **Fase 4**: Experiência e Engajamento
+## 💰 Sprint 11 - Monetização & Polimento (27 Abr - 11 Mai 2026) [EM PLANEJAMENTO]
+
+**Objetivo**: Habilitar o faturamento da plataforma e finalizar a experiência do usuário.
+
+### 🔴 MUST-HAVE:
+
+#### 1. 💳 Payments Module (Módulo de Pagamentos)
+* **Arquitetura**: Padrão de **Anti-Corruption Layer (ACL)**. A lógica de negócio não conhece tipos do Stripe. Abstração via `IPaymentGateway`.
+* **Funcionalidades**:
+ * **Assinaturas de Prestadores**: Planos Free, Standard e Gold.
+ * **Stripe Checkout & Webhooks**: Redirecionamento seguro e processamento de eventos (`invoice.paid`, etc.).
+ * **Portal de Billing**: Gestão de cartões e cancelamentos.
+* **Schema DB**: `payments` | **ModuleName**: `Payments`.
+
+#### 2. 🌍 Localização Frontend (i18n)
+* **Arquitetura**: `i18next` + `react-i18next`.
+* **Funcionalidades**: Suporte PT-BR/EN-US, tradução automática de erros do **Zod** e seletor de idioma na UI.
+
+#### 3. 🏁 Preparação para Lançamento
+* **Endurecimento**: Skeletons de carregamento e mensagens de erro amigáveis em todos os fluxos.
+* **Documentação Final**: Manuais de Usuário e Guias de Implantação.
---
-## 📊 Sumário Executivo Atualizado
+## 🔮 Roadmaps Futuros (Pós-MVP)
-**Projeto**: MeAjudaAi - Plataforma de Conexão entre Clientes e Prestadores de Serviços
-**Stack Principal**: .NET 10 LTS + Aspire 13 + PostgreSQL + NX Monorepo + React 19 + Next.js 15 (Customer, Provider) + Tailwind v4
-> [!NOTE]
-> *Admin Portal migrado de Blazor WASM para React durante o Sprint 8D (concluído em 24 de Março de 2026).*
+### Fase 3: Escala e Provedores Reais
+* **Provedores de Comunicação**: Substituir Stubs por SendGrid (E-mail), Twilio (SMS) e Firebase (Push).
+* **Verificação Automatizada**: OCR via Azure AI Vision e integração com APIs de antecedentes criminais.
+
+### Fase 4: Experiência e Engajamento
+* **Módulo de Agendamentos (Bookings)**: Calendário de disponibilidade.
+* **Sistema de Disputas**: Mediação administrativa para conflitos.
---
-## 🏗️ Decisões Arquiteturais Recentes
+## ✅ Concluído Recentemente
+
+* **Sprint 9**: Estabilização global, Módulo de Comunicações (Infra), Resiliência (`CancellationToken`) e Localização Backend (.resx).
+* **Sprint 8D/8E**: Migração completa do Admin Portal para React e Testes E2E com Playwright.
+
+---
-- **NX Monorepo**: Adotado para unificar o desenvolvimento frontend e compartilhamento de código.
-- **Dual-Stack Transition**: Transição de Blazor WASM para React 19 (Next.js) para unificação da stack.
-- **Testing Infrastructure**: Implementação de Vitest + MSW para unitários e Playwright para E2E, com agregação de cobertura global.
-- **Módulo Comunicações** (Abril 2026): Implementado durante Sprint 9 com Outbox Pattern - provedor configurável pós-MVP.
+## 📜 Histórico Completo
+Para detalhes das sprints anteriores, consulte o [Histórico do Roadmap](./roadmap-history.md).
diff --git a/infrastructure/keycloak/realms/meajudaai-realm.dev.json b/infrastructure/keycloak/realms/meajudaai-realm.dev.json
index b4ec31bde..7bf6c56af 100644
--- a/infrastructure/keycloak/realms/meajudaai-realm.dev.json
+++ b/infrastructure/keycloak/realms/meajudaai-realm.dev.json
@@ -1,355 +1,370 @@
{
- "id": "meajudaai",
- "realm": "meajudaai",
- "displayName": "MeAjudaAi Development",
- "enabled": true,
- "sslRequired": "external",
- "registrationAllowed": false,
- "loginWithEmailAllowed": true,
- "duplicateEmailsAllowed": false,
- "resetPasswordAllowed": true,
- "editUsernameAllowed": false,
- "rememberMe": true,
- "verifyEmail": false,
- "bruteForceProtected": true,
- "permanentLockout": false,
- "maxFailureWaitSeconds": 900,
- "minimumQuickLoginWaitSeconds": 60,
- "waitIncrementSeconds": 60,
- "quickLoginCheckMilliSeconds": 1000,
- "maxDeltaTimeSeconds": 43200,
- "failureFactor": 30,
- "accessTokenLifespan": 1800,
- "accessTokenLifespanForImplicitFlow": 900,
- "ssoSessionIdleTimeout": 1800,
- "ssoSessionMaxLifespan": 36000,
- "offlineSessionIdleTimeout": 2592000,
- "loginTheme": "meajudaai",
- "accountTheme": "meajudaai",
- "emailTheme": "meajudaai",
- "internationalizationEnabled": true,
- "supportedLocales": [
- "pt-BR",
- "en"
- ],
- "defaultLocale": "pt-BR",
- "roles": {
- "realm": [
- {
- "name": "admin",
- "description": "Administrador total da plataforma - todas as permissões"
- },
- {
- "name": "meajudaai-system-admin",
- "description": "Administrador do sistema - todas as permissões"
- },
- {
- "name": "meajudaai-user-admin",
- "description": "Administrador de usuários - gerenciar usuários"
- },
- {
- "name": "meajudaai-user-operator",
- "description": "Operador de usuários - leitura e atualização"
- },
- {
- "name": "meajudaai-user",
- "description": "Usuário básico - perfil próprio"
- },
- {
- "name": "meajudaai-provider-admin",
- "description": "Administrador de prestadores - CRUD completo"
- },
- {
- "name": "meajudaai-provider",
- "description": "Prestador - apenas leitura"
- },
- {
- "name": "meajudaai-order-admin",
- "description": "Administrador de pedidos - CRUD completo"
- },
- {
- "name": "meajudaai-order-operator",
- "description": "Operador de pedidos - leitura e atualização"
- },
- {
- "name": "meajudaai-report-admin",
- "description": "Administrador de relatórios - criar e exportar"
- },
- {
- "name": "meajudaai-report-viewer",
- "description": "Visualizador de relatórios - apenas visualizar"
- },
- {
- "name": "meajudaai-catalog-manager",
- "description": "Gerente de catálogo - gerenciar serviços e categorias"
- },
- {
- "name": "meajudaai-location-manager",
- "description": "Gerente de localidades - gerenciar cidades e estados"
- },
- {
- "name": "customer",
- "description": "Cliente da plataforma"
- }
- ]
- },
- "identityProviders": [
- {
- "alias": "google",
- "displayName": "Google",
- "providerId": "google",
- "enabled": true,
- "trustEmail": true,
- "storeToken": false,
- "addReadTokenRoleOnCreate": false,
- "authenticateByDefault": false,
- "firstBrokerLoginFlowAlias": "first broker login",
- "config": {
- "clientId": "${GOOGLE_CLIENT_ID}",
- "clientSecret": "${GOOGLE_CLIENT_SECRET}"
- }
- },
- {
- "alias": "facebook",
- "displayName": "Facebook",
- "providerId": "facebook",
- "enabled": true,
- "trustEmail": true,
- "storeToken": false,
- "addReadTokenRoleOnCreate": false,
- "authenticateByDefault": false,
- "firstBrokerLoginFlowAlias": "first broker login",
- "config": {
- "clientId": "${FACEBOOK_APP_ID}",
- "clientSecret": "${FACEBOOK_APP_SECRET}"
- }
- }
- ],
- "clients": [
- {
- "clientId": "admin-portal",
- "name": "Admin Portal - Blazor WebAssembly",
- "description": "MeAjudaAi Admin Portal OIDC Client",
- "enabled": true,
- "publicClient": true,
- "standardFlowEnabled": true,
- "implicitFlowEnabled": false,
- "directAccessGrantsEnabled": false,
- "serviceAccountsEnabled": false,
- "protocol": "openid-connect",
- "redirectUris": [
- "http://localhost:5165/*",
- "https://localhost:7032/*",
- "https://localhost:7281/*",
- "https://admin.meajudaai.com.br/*"
- ],
- "webOrigins": [
- "http://localhost:5165",
- "https://localhost:7032",
- "https://localhost:7281",
- "https://admin.meajudaai.com.br"
- ],
- "frontchannelLogout": true,
- "attributes": {
- "pkce.code.challenge.method": "S256",
- "post.logout.redirect.uris": "+"
- },
- "protocolMappers": [
- {
- "name": "realm-roles",
- "protocol": "openid-connect",
- "protocolMapper": "oidc-usermodel-realm-role-mapper",
- "consentRequired": false,
- "config": {
- "access.token.claim": "true",
- "id.token.claim": "true",
- "userinfo.token.claim": "true",
- "claim.name": "roles",
- "jsonType.label": "String",
- "multivalued": "true"
- }
- }
- ]
- },
- {
- "clientId": "customer-app",
- "name": "Customer App - Web + Mobile",
- "description": "MeAjudaAi Customer App OIDC Client",
- "enabled": true,
- "publicClient": true,
- "standardFlowEnabled": true,
- "implicitFlowEnabled": false,
- "directAccessGrantsEnabled": true,
- "serviceAccountsEnabled": false,
- "protocol": "openid-connect",
- "redirectUris": [
- "https://localhost:7282/*",
- "https://app.meajudaai.com.br/*",
- "meajudaai://callback",
- "http://localhost:3000/*"
- ],
- "webOrigins": [
- "https://localhost:7282",
- "https://app.meajudaai.com.br",
- "http://localhost:3000"
- ],
- "frontchannelLogout": true,
- "attributes": {
- "pkce.code.challenge.method": "S256",
- "post.logout.redirect.uris": "+"
- },
- "protocolMappers": [
- {
- "name": "realm-roles",
- "protocol": "openid-connect",
- "protocolMapper": "oidc-usermodel-realm-role-mapper",
- "consentRequired": false,
- "config": {
- "access.token.claim": "true",
- "id.token.claim": "true",
- "userinfo.token.claim": "true",
- "claim.name": "roles",
- "jsonType.label": "String",
- "multivalued": "true"
- }
- }
- ]
- },
- {
- "clientId": "provider-app",
- "name": "Provider App - Web + Mobile",
- "description": "MeAjudaAi Provider App OIDC Client",
- "enabled": true,
- "publicClient": true,
- "standardFlowEnabled": true,
- "implicitFlowEnabled": false,
- "directAccessGrantsEnabled": true,
- "serviceAccountsEnabled": false,
- "protocol": "openid-connect",
- "redirectUris": [
- "https://localhost:7283/*",
- "https://provider.meajudaai.com.br/*",
- "meajudaai-provider://callback",
- "http://localhost:3001/*"
- ],
- "webOrigins": [
- "https://localhost:7283",
- "https://provider.meajudaai.com.br",
- "http://localhost:3001"
- ],
- "frontchannelLogout": true,
- "attributes": {
- "pkce.code.challenge.method": "S256",
- "post.logout.redirect.uris": "+"
- },
- "protocolMappers": [
- {
- "name": "realm-roles",
- "protocol": "openid-connect",
- "protocolMapper": "oidc-usermodel-realm-role-mapper",
- "consentRequired": false,
- "config": {
- "access.token.claim": "true",
- "id.token.claim": "true",
- "userinfo.token.claim": "true",
- "claim.name": "roles",
- "jsonType.label": "String",
- "multivalued": "true"
- }
- }
- ]
- },
- {
- "clientId": "meajudaai-api-service",
- "name": "MeAjudaAi API Service Account",
- "description": "Service account for API to access Keycloak Admin API",
- "enabled": true,
- "publicClient": false,
- "serviceAccountsEnabled": true,
- "standardFlowEnabled": false,
- "implicitFlowEnabled": false,
- "directAccessGrantsEnabled": false,
- "protocol": "openid-connect",
- "attributes": {
- "access.token.lifespan": "3600"
- },
- "secret": "meajudaai-api-secret-dev-2024",
- "authorizationServicesEnabled": false
- }
- ],
- "users": [
- {
- "username": "service-account-meajudaai-api-service",
- "enabled": true,
- "serviceAccountClientId": "meajudaai-api-service",
- "clientRoles": {
- "realm-management": [
- "view-users",
- "query-users",
- "view-realm"
- ]
- }
- },
- {
- "username": "admin.portal",
- "enabled": true,
- "firstName": "Admin",
- "lastName": "Portal",
- "email": "admin@meajudaai.dev",
- "emailVerified": true,
- "realmRoles": [
- "admin",
- "meajudaai-system-admin",
- "meajudaai-user-admin",
- "meajudaai-provider-admin",
- "meajudaai-order-admin",
- "meajudaai-report-admin",
- "meajudaai-catalog-manager",
- "meajudaai-location-manager"
- ],
- "credentials": [
- {
- "type": "password",
- "value": "admin123",
- "temporary": false
- }
- ]
- },
- {
- "username": "customer.demo",
- "enabled": true,
- "firstName": "Customer",
- "lastName": "Demo",
- "email": "customer@meajudaai.dev",
- "emailVerified": true,
- "realmRoles": [
- "customer"
- ],
- "credentials": [
- {
- "type": "password",
- "value": "customer123",
- "temporary": false
- }
- ]
- },
- {
- "username": "provider.demo",
- "enabled": true,
- "firstName": "Provider",
- "lastName": "Demo",
- "email": "provider@meajudaai.dev",
- "emailVerified": true,
- "realmRoles": [
- "meajudaai-provider"
- ],
- "credentials": [
- {
- "type": "password",
- "value": "provider123",
- "temporary": false
- }
- ]
- }
- ]
-}
\ No newline at end of file
+ "id": "meajudaai",
+ "realm": "meajudaai",
+ "displayName": "MeAjudaAi Development",
+ "enabled": true,
+ "sslRequired": "external",
+ "registrationAllowed": false,
+ "loginWithEmailAllowed": true,
+ "duplicateEmailsAllowed": false,
+ "resetPasswordAllowed": true,
+ "editUsernameAllowed": false,
+ "rememberMe": true,
+ "verifyEmail": false,
+ "bruteForceProtected": true,
+ "permanentLockout": false,
+ "maxFailureWaitSeconds": 900,
+ "minimumQuickLoginWaitSeconds": 60,
+ "waitIncrementSeconds": 60,
+ "quickLoginCheckMilliSeconds": 1000,
+ "maxDeltaTimeSeconds": 43200,
+ "failureFactor": 30,
+ "accessTokenLifespan": 1800,
+ "accessTokenLifespanForImplicitFlow": 900,
+ "ssoSessionIdleTimeout": 1800,
+ "ssoSessionMaxLifespan": 36000,
+ "offlineSessionIdleTimeout": 2592000,
+ "loginTheme": "meajudaai",
+ "accountTheme": "meajudaai",
+ "emailTheme": "meajudaai",
+ "internationalizationEnabled": true,
+ "supportedLocales": [
+ "pt-BR",
+ "en"
+ ],
+ "defaultLocale": "pt-BR",
+ "roles": {
+ "realm": [
+ {
+ "name": "admin",
+ "description": "Administrador total da plataforma - todas as permissões"
+ },
+ {
+ "name": "meajudaai-system-admin",
+ "description": "Administrador do sistema - todas as permissões"
+ },
+ {
+ "name": "meajudaai-user-admin",
+ "description": "Administrador de usuários - gerenciar usuários"
+ },
+ {
+ "name": "meajudaai-user-operator",
+ "description": "Operador de usuários - leitura e atualização"
+ },
+ {
+ "name": "meajudaai-user",
+ "description": "Usuário básico - perfil próprio"
+ },
+ {
+ "name": "meajudaai-provider-admin",
+ "description": "Administrador de prestadores - CRUD completo"
+ },
+ {
+ "name": "meajudaai-provider",
+ "description": "Prestador - apenas leitura"
+ },
+ {
+ "name": "meajudaai-order-admin",
+ "description": "Administrador de pedidos - CRUD completo"
+ },
+ {
+ "name": "meajudaai-order-operator",
+ "description": "Operador de pedidos - leitura e atualização"
+ },
+ {
+ "name": "meajudaai-report-admin",
+ "description": "Administrador de relatórios - criar e exportar"
+ },
+ {
+ "name": "meajudaai-report-viewer",
+ "description": "Visualizador de relatórios - apenas visualizar"
+ },
+ {
+ "name": "meajudaai-catalog-manager",
+ "description": "Gerente de catálogo - gerenciar serviços e categorias"
+ },
+ {
+ "name": "meajudaai-location-manager",
+ "description": "Gerente de localidades - gerenciar cidades e estados"
+ },
+ {
+ "name": "customer",
+ "description": "Cliente da plataforma"
+ }
+ ]
+ },
+ "identityProviders": [
+ {
+ "alias": "google",
+ "displayName": "Google",
+ "providerId": "google",
+ "enabled": true,
+ "trustEmail": true,
+ "storeToken": false,
+ "addReadTokenRoleOnCreate": false,
+ "authenticateByDefault": false,
+ "firstBrokerLoginFlowAlias": "first broker login",
+ "config": {
+ "clientId": "${GOOGLE_CLIENT_ID}",
+ "clientSecret": "${GOOGLE_CLIENT_SECRET}"
+ }
+ },
+ {
+ "alias": "facebook",
+ "displayName": "Facebook",
+ "providerId": "facebook",
+ "enabled": true,
+ "trustEmail": true,
+ "storeToken": false,
+ "addReadTokenRoleOnCreate": false,
+ "authenticateByDefault": false,
+ "firstBrokerLoginFlowAlias": "first broker login",
+ "config": {
+ "clientId": "${FACEBOOK_APP_ID}",
+ "clientSecret": "${FACEBOOK_APP_SECRET}"
+ }
+ },
+ {
+ "trustEmail": false,
+ "providerId": "instagram",
+ "enabled": true,
+ "authenticateByDefault": false,
+ "storeToken": false,
+ "config": {
+ "clientId": "${INSTAGRAM_CLIENT_ID}",
+ "clientSecret": "${INSTAGRAM_CLIENT_SECRET}"
+ },
+ "alias": "instagram",
+ "addReadTokenRoleOnCreate": false,
+ "firstBrokerLoginFlowAlias": "first broker login",
+ "displayName": "Instagram"
+ }
+ ],
+ "clients": [
+ {
+ "clientId": "admin-portal",
+ "name": "Admin Portal - Blazor WebAssembly",
+ "description": "MeAjudaAi Admin Portal OIDC Client",
+ "enabled": true,
+ "publicClient": true,
+ "standardFlowEnabled": true,
+ "implicitFlowEnabled": false,
+ "directAccessGrantsEnabled": false,
+ "serviceAccountsEnabled": false,
+ "protocol": "openid-connect",
+ "redirectUris": [
+ "http://localhost:5165/*",
+ "https://localhost:7032/*",
+ "https://localhost:7281/*",
+ "https://admin.meajudaai.com.br/*"
+ ],
+ "webOrigins": [
+ "http://localhost:5165",
+ "https://localhost:7032",
+ "https://localhost:7281",
+ "https://admin.meajudaai.com.br"
+ ],
+ "frontchannelLogout": true,
+ "attributes": {
+ "pkce.code.challenge.method": "S256",
+ "post.logout.redirect.uris": "+"
+ },
+ "protocolMappers": [
+ {
+ "name": "realm-roles",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-realm-role-mapper",
+ "consentRequired": false,
+ "config": {
+ "access.token.claim": "true",
+ "id.token.claim": "true",
+ "userinfo.token.claim": "true",
+ "claim.name": "roles",
+ "jsonType.label": "String",
+ "multivalued": "true"
+ }
+ }
+ ]
+ },
+ {
+ "clientId": "customer-app",
+ "name": "Customer App - Web + Mobile",
+ "description": "MeAjudaAi Customer App OIDC Client",
+ "enabled": true,
+ "publicClient": true,
+ "standardFlowEnabled": true,
+ "implicitFlowEnabled": false,
+ "directAccessGrantsEnabled": true,
+ "serviceAccountsEnabled": false,
+ "protocol": "openid-connect",
+ "redirectUris": [
+ "https://localhost:7282/*",
+ "https://app.meajudaai.com.br/*",
+ "meajudaai://callback",
+ "http://localhost:3000/*"
+ ],
+ "webOrigins": [
+ "https://localhost:7282",
+ "https://app.meajudaai.com.br",
+ "http://localhost:3000"
+ ],
+ "frontchannelLogout": true,
+ "attributes": {
+ "pkce.code.challenge.method": "S256",
+ "post.logout.redirect.uris": "+"
+ },
+ "protocolMappers": [
+ {
+ "name": "realm-roles",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-realm-role-mapper",
+ "consentRequired": false,
+ "config": {
+ "access.token.claim": "true",
+ "id.token.claim": "true",
+ "userinfo.token.claim": "true",
+ "claim.name": "roles",
+ "jsonType.label": "String",
+ "multivalued": "true"
+ }
+ }
+ ]
+ },
+ {
+ "clientId": "provider-app",
+ "name": "Provider App - Web + Mobile",
+ "description": "MeAjudaAi Provider App OIDC Client",
+ "enabled": true,
+ "publicClient": true,
+ "standardFlowEnabled": true,
+ "implicitFlowEnabled": false,
+ "directAccessGrantsEnabled": true,
+ "serviceAccountsEnabled": false,
+ "protocol": "openid-connect",
+ "redirectUris": [
+ "https://localhost:7283/*",
+ "https://provider.meajudaai.com.br/*",
+ "meajudaai-provider://callback",
+ "http://localhost:3001/*"
+ ],
+ "webOrigins": [
+ "https://localhost:7283",
+ "https://provider.meajudaai.com.br",
+ "http://localhost:3001"
+ ],
+ "frontchannelLogout": true,
+ "attributes": {
+ "pkce.code.challenge.method": "S256",
+ "post.logout.redirect.uris": "+"
+ },
+ "protocolMappers": [
+ {
+ "name": "realm-roles",
+ "protocol": "openid-connect",
+ "protocolMapper": "oidc-usermodel-realm-role-mapper",
+ "consentRequired": false,
+ "config": {
+ "access.token.claim": "true",
+ "id.token.claim": "true",
+ "userinfo.token.claim": "true",
+ "claim.name": "roles",
+ "jsonType.label": "String",
+ "multivalued": "true"
+ }
+ }
+ ]
+ },
+ {
+ "clientId": "meajudaai-api-service",
+ "name": "MeAjudaAi API Service Account",
+ "description": "Service account for API to access Keycloak Admin API",
+ "enabled": true,
+ "publicClient": false,
+ "serviceAccountsEnabled": true,
+ "standardFlowEnabled": false,
+ "implicitFlowEnabled": false,
+ "directAccessGrantsEnabled": false,
+ "protocol": "openid-connect",
+ "attributes": {
+ "access.token.lifespan": "3600"
+ },
+ "secret": "${CLIENT_SECRET_MEAJUDAAI_API}",
+ "authorizationServicesEnabled": false
+ }
+ ],
+ "users": [
+ {
+ "username": "service-account-meajudaai-api-service",
+ "enabled": true,
+ "serviceAccountClientId": "meajudaai-api-service",
+ "clientRoles": {
+ "realm-management": [
+ "view-users",
+ "query-users",
+ "view-realm"
+ ]
+ }
+ },
+ {
+ "username": "admin.portal",
+ "enabled": true,
+ "firstName": "Admin",
+ "lastName": "Portal",
+ "email": "admin@meajudaai.dev",
+ "emailVerified": true,
+ "realmRoles": [
+ "admin",
+ "meajudaai-system-admin",
+ "meajudaai-user-admin",
+ "meajudaai-provider-admin",
+ "meajudaai-order-admin",
+ "meajudaai-report-admin",
+ "meajudaai-catalog-manager",
+ "meajudaai-location-manager"
+ ],
+ "credentials": [
+ {
+ "type": "password",
+ "value": "admin123",
+ "temporary": false
+ }
+ ]
+ },
+ {
+ "username": "customer.demo",
+ "enabled": true,
+ "firstName": "Customer",
+ "lastName": "Demo",
+ "email": "customer@meajudaai.dev",
+ "emailVerified": true,
+ "realmRoles": [
+ "customer"
+ ],
+ "credentials": [
+ {
+ "type": "password",
+ "value": "customer123",
+ "temporary": false
+ }
+ ]
+ },
+ {
+ "username": "provider.demo",
+ "enabled": true,
+ "firstName": "Provider",
+ "lastName": "Demo",
+ "email": "provider@meajudaai.dev",
+ "emailVerified": true,
+ "realmRoles": [
+ "meajudaai-provider"
+ ],
+ "credentials": [
+ {
+ "type": "password",
+ "value": "provider123",
+ "temporary": false
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/Bootstrapper/MeAjudaAi.ApiService/MeAjudaAi.ApiService.csproj b/src/Bootstrapper/MeAjudaAi.ApiService/MeAjudaAi.ApiService.csproj
index c9b66dfdc..212067408 100644
--- a/src/Bootstrapper/MeAjudaAi.ApiService/MeAjudaAi.ApiService.csproj
+++ b/src/Bootstrapper/MeAjudaAi.ApiService/MeAjudaAi.ApiService.csproj
@@ -30,6 +30,7 @@
+
diff --git a/src/Bootstrapper/MeAjudaAi.ApiService/Program.cs b/src/Bootstrapper/MeAjudaAi.ApiService/Program.cs
index 2669743d2..02f9799dc 100644
--- a/src/Bootstrapper/MeAjudaAi.ApiService/Program.cs
+++ b/src/Bootstrapper/MeAjudaAi.ApiService/Program.cs
@@ -6,6 +6,7 @@
using MeAjudaAi.Modules.Documents.API;
using MeAjudaAi.Modules.Locations.API;
using MeAjudaAi.Modules.Providers.API;
+using MeAjudaAi.Modules.Ratings.API;
using MeAjudaAi.Modules.SearchProviders.API;
using MeAjudaAi.Modules.ServiceCatalogs.API;
using MeAjudaAi.Modules.Users.API;
@@ -49,6 +50,7 @@ public static async Task Main(string[] args)
builder.Services.AddLocationsModule(builder.Configuration);
builder.Services.AddServiceCatalogsModule(builder.Configuration);
builder.Services.AddCommunicationsModule(builder.Configuration);
+ builder.Services.AddRatingsModule(builder.Configuration, builder.Environment);
// Shared services por último (GlobalExceptionHandler atua como fallback)
builder.Services.AddSharedServices(builder.Configuration);
@@ -133,6 +135,7 @@ private static async Task ConfigureMiddlewareAsync(WebApplication app)
app.UseLocationsModule();
app.UseServiceCatalogsModule();
app.UseCommunicationsModule();
+ app.UseRatingsModule();
// Endpoints de orquestração cross-módulo (ficam no ApiService)
app.MapProviderRegistrationEndpoints();
diff --git a/src/Bootstrapper/MeAjudaAi.ApiService/packages.lock.json b/src/Bootstrapper/MeAjudaAi.ApiService/packages.lock.json
index 106a5af59..dc380ba09 100644
--- a/src/Bootstrapper/MeAjudaAi.ApiService/packages.lock.json
+++ b/src/Bootstrapper/MeAjudaAi.ApiService/packages.lock.json
@@ -698,6 +698,35 @@
"MeAjudaAi.Shared": "[1.0.0, )"
}
},
+ "meajudaai.modules.ratings.api": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Infrastructure": "[1.0.0, )"
+ }
+ },
+ "meajudaai.modules.ratings.application": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Domain": "[1.0.0, )",
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
+ "Npgsql": "[10.0.2, )"
+ }
+ },
+ "meajudaai.modules.ratings.domain": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Shared": "[1.0.0, )"
+ }
+ },
+ "meajudaai.modules.ratings.infrastructure": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Application": "[1.0.0, )",
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
+ "Microsoft.EntityFrameworkCore.Design": "[10.0.5, )",
+ "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.1, )"
+ }
+ },
"meajudaai.modules.searchproviders.api": {
"type": "Project",
"dependencies": {
diff --git a/src/Contracts/Contracts/Modules/Ratings/DTOs/CreateReviewRequest.cs b/src/Contracts/Contracts/Modules/Ratings/DTOs/CreateReviewRequest.cs
new file mode 100644
index 000000000..77e855f82
--- /dev/null
+++ b/src/Contracts/Contracts/Modules/Ratings/DTOs/CreateReviewRequest.cs
@@ -0,0 +1,3 @@
+namespace MeAjudaAi.Contracts.Contracts.Modules.Ratings.DTOs;
+
+public record CreateReviewRequest(Guid ProviderId, int Rating, string? Comment);
diff --git a/src/Contracts/Contracts/Modules/Ratings/DTOs/ProviderReviewResponse.cs b/src/Contracts/Contracts/Modules/Ratings/DTOs/ProviderReviewResponse.cs
new file mode 100644
index 000000000..d03cbff14
--- /dev/null
+++ b/src/Contracts/Contracts/Modules/Ratings/DTOs/ProviderReviewResponse.cs
@@ -0,0 +1,3 @@
+namespace MeAjudaAi.Contracts.Contracts.Modules.Ratings.DTOs;
+
+public record ProviderReviewResponse(Guid Id, int Rating, string? Comment, DateTime CreatedAt);
diff --git a/src/Contracts/Contracts/Modules/Ratings/DTOs/ReviewStatusResponse.cs b/src/Contracts/Contracts/Modules/Ratings/DTOs/ReviewStatusResponse.cs
new file mode 100644
index 000000000..24e1e8e29
--- /dev/null
+++ b/src/Contracts/Contracts/Modules/Ratings/DTOs/ReviewStatusResponse.cs
@@ -0,0 +1,5 @@
+using MeAjudaAi.Contracts.Contracts.Modules.Ratings.Enums;
+
+namespace MeAjudaAi.Contracts.Contracts.Modules.Ratings.DTOs;
+
+public record ReviewStatusResponse(Guid Id, EReviewStatus Status);
diff --git a/src/Contracts/Contracts/Modules/Ratings/Enums/EReviewStatus.cs b/src/Contracts/Contracts/Modules/Ratings/Enums/EReviewStatus.cs
new file mode 100644
index 000000000..2e461f63c
--- /dev/null
+++ b/src/Contracts/Contracts/Modules/Ratings/Enums/EReviewStatus.cs
@@ -0,0 +1,12 @@
+namespace MeAjudaAi.Contracts.Contracts.Modules.Ratings.Enums;
+
+///
+/// Status de uma avaliação de prestador (versão contrato).
+///
+public enum EReviewStatus
+{
+ Pending = 0,
+ Approved = 1,
+ Rejected = 2,
+ Flagged = 3
+}
diff --git a/src/Contracts/Identity/Enums/EAuthProvider.cs b/src/Contracts/Identity/Enums/EAuthProvider.cs
index 7e5622620..c7119b64e 100644
--- a/src/Contracts/Identity/Enums/EAuthProvider.cs
+++ b/src/Contracts/Identity/Enums/EAuthProvider.cs
@@ -24,5 +24,10 @@ public enum EAuthProvider
///
/// Login social da Apple
///
- Apple
+ Apple,
+
+ ///
+ /// Login social do Instagram
+ ///
+ Instagram
}
diff --git a/src/Modules/Communications/API/API.Client/CommunicationAdmin/GetCommunicationLogs.bru b/src/Modules/Communications/API/API.Client/CommunicationAdmin/GetCommunicationLogs.bru
new file mode 100644
index 000000000..ddc368087
--- /dev/null
+++ b/src/Modules/Communications/API/API.Client/CommunicationAdmin/GetCommunicationLogs.bru
@@ -0,0 +1,44 @@
+meta {
+ name: Get Communication Logs
+ type: http
+ seq: 1
+}
+
+get {
+ url: {{baseUrl}}/api/v1/communications/logs?pageNumber=1&pageSize=10
+ body: none
+ auth: bearer
+}
+
+auth:bearer {
+ token: {{accessToken}}
+}
+
+headers {
+ Accept: application/json
+}
+
+docs {
+ # Get Communication Logs
+
+ Retorna o histórico de comunicações (E-mail, SMS, Push) enviadas pelo sistema.
+
+ ## Autorização
+ - **Requer token**: Sim (Admin)
+
+ ## Parâmetros Query
+ - `pageNumber` (int): Padrão 1
+ - `pageSize` (int): Padrão 10
+ - `type` (string): Opcional (Email, SMS, Push)
+ - `status` (string): Opcional (Sent, Failed, Pending)
+
+ ## Resposta Esperada (200 OK)
+ ```json
+ {
+ "items": [],
+ "totalCount": 0,
+ "pageNumber": 1,
+ "pageSize": 10
+ }
+ ```
+}
diff --git a/src/Modules/Communications/API/API.Client/CommunicationAdmin/GetEmailTemplates.bru b/src/Modules/Communications/API/API.Client/CommunicationAdmin/GetEmailTemplates.bru
new file mode 100644
index 000000000..90f9d4b57
--- /dev/null
+++ b/src/Modules/Communications/API/API.Client/CommunicationAdmin/GetEmailTemplates.bru
@@ -0,0 +1,39 @@
+meta {
+ name: Get Email Templates
+ type: http
+ seq: 2
+}
+
+get {
+ url: {{baseUrl}}/api/v1/communications/templates
+ body: none
+ auth: bearer
+}
+
+auth:bearer {
+ token: {{accessToken}}
+}
+
+headers {
+ Accept: application/json
+}
+
+docs {
+ # Get Email Templates
+
+ Lista todos os templates de e-mail registrados no módulo de comunicação.
+
+ ## Autorização
+ - **Requer token**: Sim (Admin)
+
+ ## Resposta Esperada (200 OK)
+ ```json
+ [
+ {
+ "name": "WelcomeEmail",
+ "subject": "Bem-vindo ao MeAjudaAi!",
+ "isHtml": true
+ }
+ ]
+ ```
+}
diff --git a/src/Modules/Communications/Application/AssemblyInfo.cs b/src/Modules/Communications/Application/AssemblyInfo.cs
new file mode 100644
index 000000000..020fc70a7
--- /dev/null
+++ b/src/Modules/Communications/Application/AssemblyInfo.cs
@@ -0,0 +1,5 @@
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("MeAjudaAi.Modules.Communications.Tests")]
+[assembly: InternalsVisibleTo("MeAjudaAi.Integration.Tests")]
+[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
diff --git a/src/Modules/Communications/Application/MeAjudaAi.Modules.Communications.Application.csproj b/src/Modules/Communications/Application/MeAjudaAi.Modules.Communications.Application.csproj
index 3fb799ddd..562e05d9e 100644
--- a/src/Modules/Communications/Application/MeAjudaAi.Modules.Communications.Application.csproj
+++ b/src/Modules/Communications/Application/MeAjudaAi.Modules.Communications.Application.csproj
@@ -6,14 +6,7 @@
enable
-
-
- <_Parameter1>MeAjudaAi.Modules.Communications.Tests
-
-
- <_Parameter1>MeAjudaAi.Integration.Tests
-
-
+
diff --git a/src/Modules/Communications/Application/Services/CommunicationsOutboxWorker.cs b/src/Modules/Communications/Application/Services/CommunicationsOutboxWorker.cs
index 79ca395a3..0be412b18 100644
--- a/src/Modules/Communications/Application/Services/CommunicationsOutboxWorker.cs
+++ b/src/Modules/Communications/Application/Services/CommunicationsOutboxWorker.cs
@@ -8,22 +8,39 @@ namespace MeAjudaAi.Modules.Communications.Application.Services;
///
/// Worker de background para processamento periódico do Outbox de comunicações.
///
-internal sealed class CommunicationsOutboxWorker(
- IServiceScopeFactory scopeFactory,
- ILogger logger) : BackgroundService
+internal sealed class CommunicationsOutboxWorker : BackgroundService
{
- private readonly TimeSpan _checkInterval = TimeSpan.FromSeconds(10);
+ private readonly IServiceScopeFactory _scopeFactory;
+ private readonly ILogger _logger;
+ private readonly TimeSpan _checkInterval;
+
+ public CommunicationsOutboxWorker(
+ IServiceScopeFactory scopeFactory,
+ ILogger logger,
+ TimeSpan? checkInterval = null)
+ {
+ _scopeFactory = scopeFactory;
+ _logger = logger;
+
+ var interval = checkInterval ?? TimeSpan.FromSeconds(10);
+ if (interval <= TimeSpan.Zero && interval != Timeout.InfiniteTimeSpan)
+ {
+ throw new ArgumentOutOfRangeException(nameof(checkInterval), "Check interval must be greater than zero or InfiniteTimeSpan.");
+ }
+
+ _checkInterval = interval;
+ }
private readonly TimeSpan _stuckTimeout = TimeSpan.FromMinutes(5);
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
- logger.LogInformation("Communications Outbox Worker started.");
+ _logger.LogInformation("Communications Outbox Worker started.");
while (!stoppingToken.IsCancellationRequested)
{
try
{
- using var scope = scopeFactory.CreateScope();
+ using var scope = _scopeFactory.CreateScope();
// 1. Recupera mensagens travadas em processamento
var repository = scope.ServiceProvider.GetRequiredService();
@@ -32,7 +49,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
if (resetCount > 0)
{
- logger.LogWarning("Reset {Count} stuck outbox messages back to Pending.", resetCount);
+ _logger.LogWarning("Reset {Count} stuck outbox messages back to Pending.", resetCount);
}
// 2. Processa mensagens pendentes
@@ -41,8 +58,10 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
if (processedCount > 0)
{
- logger.LogInformation("Processed {Count} outbox messages.", processedCount);
+ _logger.LogInformation("Processed {Count} outbox messages.", processedCount);
}
+
+ await Task.Delay(_checkInterval, stoppingToken);
}
catch (OperationCanceledException)
{
@@ -50,12 +69,19 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
}
catch (Exception ex)
{
- logger.LogError(ex, "Error occurred while processing communications outbox.");
+ _logger.LogError(ex, "Error occurred while processing communications outbox.");
+
+ try
+ {
+ await Task.Delay(_checkInterval, stoppingToken);
+ }
+ catch (OperationCanceledException)
+ {
+ // Exit loop
+ }
}
-
- await Task.Delay(_checkInterval, stoppingToken);
}
- logger.LogInformation("Communications Outbox Worker stopped.");
+ _logger.LogInformation("Communications Outbox Worker stopped.");
}
}
diff --git a/src/Modules/Communications/Tests/Unit/Application/ModuleApi/CommunicationsModuleApiTests.cs b/src/Modules/Communications/Tests/Unit/Application/ModuleApi/CommunicationsModuleApiTests.cs
index 9c5bf2dd0..b2e49b61a 100644
--- a/src/Modules/Communications/Tests/Unit/Application/ModuleApi/CommunicationsModuleApiTests.cs
+++ b/src/Modules/Communications/Tests/Unit/Application/ModuleApi/CommunicationsModuleApiTests.cs
@@ -6,6 +6,7 @@
using MeAjudaAi.Modules.Communications.Domain.Repositories;
using Moq;
using MeAjudaAi.Contracts.Shared;
+using FluentAssertions;
namespace MeAjudaAi.Modules.Communications.Tests.Unit.Application.ModuleApi;
@@ -113,6 +114,23 @@ public async Task SendEmailAsync_WithInvalidPriority_ShouldReturnFailure()
_outboxRepositoryMock.Verify(x => x.AddAsync(It.IsAny(), It.IsAny()), Times.Never);
}
+ [Theory]
+ [InlineData(null, "Body")]
+ [InlineData("123", null)]
+ public async Task SendSmsAsync_WithInvalidData_ShouldReturnFailure(string? phone, string? message)
+ {
+ // Arrange
+ var dto = new SmsMessageDto(phone!, message!);
+
+ // Act
+ var result = await _api.SendSmsAsync(dto);
+
+ // Assert
+ result.IsSuccess.Should().BeFalse();
+ _outboxRepositoryMock.Verify(x => x.AddAsync(It.IsAny(), It.IsAny()), Times.Never);
+ _outboxRepositoryMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Never);
+ }
+
[Fact]
public async Task SendSmsAsync_WithNullDto_ShouldReturnFailure()
{
@@ -134,17 +152,93 @@ public async Task SendPushAsync_WithNullDto_ShouldReturnFailure()
}
[Fact]
- public async Task GetLogsAsync_WithInvalidPagination_ShouldReturnFailure()
+ public async Task SendSmsAsync_WithInvalidPriority_ShouldReturnFailure()
{
// Arrange
- var query = new CommunicationLogQuery { PageNumber = 0, PageSize = 10 };
+ var dto = new SmsMessageDto("123", "Body");
+
+ // Act
+ var result = await _api.SendSmsAsync(dto, (ECommunicationPriority)999);
+
+ // Assert
+ result.IsSuccess.Should().BeFalse();
+ _outboxRepositoryMock.Verify(x => x.AddAsync(It.IsAny(), It.IsAny()), Times.Never);
+ _outboxRepositoryMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Never);
+ }
+
+ [Theory]
+ [InlineData(null, "Title", "Body")]
+ [InlineData("token", null, "Body")]
+ [InlineData("token", "Title", null)]
+ public async Task SendPushAsync_WithInvalidData_ShouldReturnFailure(string? token, string? title, string? body)
+ {
+ // Arrange
+ var dto = new PushMessageDto(token!, title!, body!);
+
+ // Act
+ var result = await _api.SendPushAsync(dto);
+
+ // Assert
+ result.IsSuccess.Should().BeFalse();
+ _outboxRepositoryMock.Verify(x => x.AddAsync(It.IsAny(), It.IsAny()), Times.Never);
+ _outboxRepositoryMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Never);
+ }
+
+ [Fact]
+ public async Task SendPushAsync_WithInvalidPriority_ShouldReturnFailure()
+ {
+ // Arrange
+ var dto = new PushMessageDto("token", "Title", "Body");
+
+ // Act
+ var result = await _api.SendPushAsync(dto, (ECommunicationPriority)999);
+
+ // Assert
+ result.IsSuccess.Should().BeFalse();
+ _outboxRepositoryMock.Verify(x => x.AddAsync(It.IsAny(), It.IsAny()), Times.Never);
+ _outboxRepositoryMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.Never);
+ }
+
+ [Fact]
+ public async Task GetLogsAsync_WithNullQuery_ShouldReturnFailure()
+ {
+ // Act
+ var result = await _api.GetLogsAsync(null!);
+
+ result.IsSuccess.Should().BeFalse();
+ _logRepositoryMock.Verify(x => x.SearchAsync(
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny()), Times.Never);
+ _logRepositoryMock.VerifyNoOtherCalls();
+ }
+
+ [Theory]
+ [InlineData(1, 0)]
+ [InlineData(1, 101)]
+ [InlineData(0, 10)]
+ public async Task GetLogsAsync_WithInvalidPaginationValues_ShouldReturnFailure(int page, int size)
+ {
+ // Arrange
+ var query = new CommunicationLogQuery { PageNumber = page, PageSize = size };
// Act
var result = await _api.GetLogsAsync(query);
// Assert
result.IsSuccess.Should().BeFalse();
- _logRepositoryMock.Verify(x => x.SearchAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never);
+ _logRepositoryMock.Verify(x => x.SearchAsync(
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny(),
+ It.IsAny()), Times.Never);
}
[Fact]
@@ -187,4 +281,14 @@ public async Task GetLogsAsync_WithValidQuery_ShouldReturnPagedResult()
result.Value.Items.Should().HaveCount(1);
result.Value.TotalItems.Should().Be(1);
}
+
+ [Fact]
+ public async Task IsAvailableAsync_ShouldReturnTrue()
+ {
+ // Act
+ var result = await _api.IsAvailableAsync();
+
+ // Assert
+ result.Should().BeTrue();
+ }
}
diff --git a/src/Modules/Communications/Tests/Unit/Application/Services/CommunicationsOutboxWorkerTests.cs b/src/Modules/Communications/Tests/Unit/Application/Services/CommunicationsOutboxWorkerTests.cs
new file mode 100644
index 000000000..645cd8784
--- /dev/null
+++ b/src/Modules/Communications/Tests/Unit/Application/Services/CommunicationsOutboxWorkerTests.cs
@@ -0,0 +1,111 @@
+using MeAjudaAi.Modules.Communications.Application.Services;
+using MeAjudaAi.Modules.Communications.Domain.Repositories;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Moq;
+using FluentAssertions;
+
+namespace MeAjudaAi.Modules.Communications.Tests.Unit.Application.Services;
+
+public class CommunicationsOutboxWorkerTests
+{
+ private readonly Mock _scopeFactoryMock;
+ private readonly Mock _scopeMock;
+ private readonly Mock _serviceProviderMock;
+ private readonly Mock _repositoryMock;
+ private readonly Mock _processorMock;
+ private readonly Mock> _loggerMock;
+
+ public CommunicationsOutboxWorkerTests()
+ {
+ _scopeFactoryMock = new Mock();
+ _scopeMock = new Mock();
+ _serviceProviderMock = new Mock();
+ _repositoryMock = new Mock();
+ _processorMock = new Mock();
+ _loggerMock = new Mock>();
+
+ _scopeFactoryMock.Setup(x => x.CreateScope()).Returns(_scopeMock.Object);
+ _scopeMock.Setup(x => x.ServiceProvider).Returns(_serviceProviderMock.Object);
+ _serviceProviderMock.Setup(x => x.GetService(typeof(IOutboxMessageRepository))).Returns(_repositoryMock.Object);
+ _serviceProviderMock.Setup(x => x.GetService(typeof(IOutboxProcessorService))).Returns(_processorMock.Object);
+ }
+
+ [Fact]
+ public async Task ExecuteAsync_ShouldResetStaleMessagesAndProcessPending()
+ {
+ // Arrange
+ var worker = new CommunicationsOutboxWorker(_scopeFactoryMock.Object, _loggerMock.Object, TimeSpan.FromMilliseconds(10));
+ var cts = new CancellationTokenSource();
+ var iterationTcs = new TaskCompletionSource();
+
+ _repositoryMock.Setup(x => x.ResetStaleProcessingMessagesAsync(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(1);
+ _processorMock.Setup(x => x.ProcessPendingMessagesAsync(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(1)
+ .Callback(() => iterationTcs.TrySetResult(true));
+
+ // Act
+ var task = worker.StartAsync(cts.Token);
+
+ // Wait for at least one iteration to complete deterministically
+ await iterationTcs.Task.WaitAsync(TimeSpan.FromSeconds(5));
+
+ await worker.StopAsync(cts.Token);
+
+ // Assert
+ _repositoryMock.Verify(x => x.ResetStaleProcessingMessagesAsync(It.IsAny(), It.IsAny()), Times.AtLeastOnce);
+ _processorMock.Verify(x => x.ProcessPendingMessagesAsync(It.IsAny(), It.IsAny()), Times.AtLeastOnce);
+ }
+
+ [Fact]
+ public async Task ExecuteAsync_WhenExceptionOccurs_ShouldContinue()
+ {
+ // Arrange
+ var worker = new CommunicationsOutboxWorker(_scopeFactoryMock.Object, _loggerMock.Object, TimeSpan.FromMilliseconds(10));
+ var cts = new CancellationTokenSource();
+
+ var iterationCount = 0;
+ var secondIterationTcs = new TaskCompletionSource();
+
+ _repositoryMock.Setup(x => x.ResetStaleProcessingMessagesAsync(It.IsAny(), It.IsAny()))
+ .Returns(() =>
+ {
+ iterationCount++;
+ if (iterationCount == 1)
+ {
+ throw new Exception("Database error");
+ }
+
+ if (iterationCount >= 2)
+ {
+ secondIterationTcs.TrySetResult(true);
+ }
+
+ return Task.FromResult(0);
+ });
+
+ _processorMock.Setup(x => x.ProcessPendingMessagesAsync(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(0);
+
+ // Act
+ var task = worker.StartAsync(cts.Token);
+
+ // Wait for the second iteration to prove it continued after the error
+ await secondIterationTcs.Task.WaitAsync(TimeSpan.FromSeconds(5));
+
+ await worker.StopAsync(cts.Token);
+
+ // Assert
+ _loggerMock.Verify(
+ x => x.Log(
+ LogLevel.Error,
+ It.IsAny(),
+ It.Is((v, t) => v.ToString()!.Contains("Error occurred")),
+ It.IsAny(),
+ It.IsAny>()),
+ Times.AtLeastOnce);
+
+ iterationCount.Should().BeGreaterThanOrEqualTo(2);
+ }
+}
diff --git a/src/Modules/Communications/Tests/Unit/Application/Services/OutboxProcessorServiceTests.cs b/src/Modules/Communications/Tests/Unit/Application/Services/OutboxProcessorServiceTests.cs
index df06b370a..bdf372191 100644
--- a/src/Modules/Communications/Tests/Unit/Application/Services/OutboxProcessorServiceTests.cs
+++ b/src/Modules/Communications/Tests/Unit/Application/Services/OutboxProcessorServiceTests.cs
@@ -7,6 +7,7 @@
using Moq;
using System.Text.Json;
using MeAjudaAi.Contracts.Shared;
+using FluentAssertions;
namespace MeAjudaAi.Modules.Communications.Tests.Unit.Application.Services;
@@ -74,88 +75,103 @@ public async Task ProcessPendingMessagesAsync_WhenEmailSuccess_ShouldMarkAsSentA
message.Status.Should().Be(EOutboxMessageStatus.Sent);
_emailSenderMock.Verify(x => x.SendAsync(It.IsAny(), It.IsAny()), Times.Once);
_logRepositoryMock.Verify(x => x.AddAsync(It.Is(l => l.IsSuccess), It.IsAny()), Times.Once);
- _outboxRepositoryMock.Verify(x => x.SaveChangesAsync(It.IsAny()), Times.AtLeastOnce);
}
[Fact]
- public async Task ProcessPendingMessagesAsync_WhenSmsSuccess_ShouldMarkAsSent()
+ public async Task ProcessPendingMessagesAsync_WhenDispatchFails_ShouldMarkAsFailedAndRetryLater()
{
// Arrange
- var payload = JsonSerializer.Serialize(new { PhoneNumber = "123456", Body = "Hello" });
- var message = OutboxMessage.Create(ECommunicationChannel.Sms, payload);
+ var payload = JsonSerializer.Serialize(new { To = "test@test.com", Subject = "Hi", Body = "Hello" });
+ var message = OutboxMessage.Create(ECommunicationChannel.Email, payload, maxRetries: 3);
_outboxRepositoryMock.Setup(x => x.GetPendingAsync(It.IsAny(), It.IsAny(), It.IsAny()))
.ReturnsAsync(new List { message });
- _smsSenderMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(true);
+ _emailSenderMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(false);
// Act
var result = await _service.ProcessPendingMessagesAsync();
// Assert
- result.Should().Be(1);
- message.Status.Should().Be(EOutboxMessageStatus.Sent);
- _smsSenderMock.Verify(x => x.SendAsync(It.IsAny(), It.IsAny()), Times.Once);
+ result.Should().Be(0);
+ message.Status.Should().Be(EOutboxMessageStatus.Pending);
+ message.RetryCount.Should().Be(1);
}
[Fact]
- public async Task ProcessPendingMessagesAsync_WhenPushSuccess_ShouldMarkAsSent()
+ public async Task ProcessPendingMessagesAsync_WhenMaxRetriesReached_ShouldMarkAsFailedAndLog()
{
// Arrange
- var payload = JsonSerializer.Serialize(new { DeviceToken = "token", Title = "Title", Body = "Body" });
- var message = OutboxMessage.Create(ECommunicationChannel.Push, payload);
+ var payload = JsonSerializer.Serialize(new { To = "test@test.com", Subject = "Hi", Body = "Hello" });
+ var message = OutboxMessage.Create(ECommunicationChannel.Email, payload, maxRetries: 1);
_outboxRepositoryMock.Setup(x => x.GetPendingAsync(It.IsAny(), It.IsAny(), It.IsAny()))
.ReturnsAsync(new List { message });
- _pushSenderMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(true);
+ _emailSenderMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny()))
+ .ReturnsAsync(false);
// Act
var result = await _service.ProcessPendingMessagesAsync();
// Assert
- result.Should().Be(1);
- message.Status.Should().Be(EOutboxMessageStatus.Sent);
- _pushSenderMock.Verify(x => x.SendAsync(It.IsAny(), It.IsAny()), Times.Once);
+ result.Should().Be(0);
+ message.Status.Should().Be(EOutboxMessageStatus.Failed);
+ _logRepositoryMock.Verify(x => x.AddAsync(It.Is(l => !l.IsSuccess), It.IsAny()), Times.Once);
}
[Fact]
- public async Task ProcessPendingMessagesAsync_WhenDispatchFails_ShouldMarkAsFailedAndRetryLater()
+ public async Task ProcessPendingMessagesAsync_WhenOperationCanceledDuringDispatch_ShouldReturnZeroAndStayPending()
{
// Arrange
var payload = JsonSerializer.Serialize(new { To = "test@test.com", Subject = "Hi", Body = "Hello" });
- var message = OutboxMessage.Create(ECommunicationChannel.Email, payload, maxRetries: 3);
+ var message = OutboxMessage.Create(ECommunicationChannel.Email, payload);
+ var cts = new CancellationTokenSource();
_outboxRepositoryMock.Setup(x => x.GetPendingAsync(It.IsAny(), It.IsAny(), It.IsAny()))
.ReturnsAsync(new List { message });
_emailSenderMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(false);
+ .Returns(async (EmailMessage msg, CancellationToken ct) =>
+ {
+ cts.Cancel(); // Cancel while dispatching
+ await Task.Yield();
+ throw new OperationCanceledException(ct);
+ });
// Act
- var result = await _service.ProcessPendingMessagesAsync();
+ var result = await _service.ProcessPendingMessagesAsync(cancellationToken: cts.Token);
// Assert
result.Should().Be(0);
message.Status.Should().Be(EOutboxMessageStatus.Pending);
- message.RetryCount.Should().Be(1);
- _logRepositoryMock.Verify(x => x.AddAsync(It.IsAny(), It.IsAny()), Times.Never);
+ _emailSenderMock.Verify(x => x.SendAsync(It.IsAny(), It.IsAny()), Times.Once);
}
[Fact]
- public async Task ProcessPendingMessagesAsync_WhenDispatchThrows_ShouldHandleExceptionAndRetry()
+ public async Task ProcessPendingMessagesAsync_WhenUnknownChannel_ShouldFail()
{
// Arrange
- var payload = JsonSerializer.Serialize(new { To = "test@test.com", Subject = "Hi", Body = "Hello" });
- var message = OutboxMessage.Create(ECommunicationChannel.Email, payload, maxRetries: 3);
-
+ var message = OutboxMessage.Create((ECommunicationChannel)99, "{}");
_outboxRepositoryMock.Setup(x => x.GetPendingAsync(It.IsAny(), It.IsAny(), It.IsAny()))
.ReturnsAsync(new List { message });
- _emailSenderMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny()))
- .ThrowsAsync(new Exception("Network Error"));
+ // Act
+ var result = await _service.ProcessPendingMessagesAsync();
+
+ // Assert
+ result.Should().Be(0);
+ message.ErrorMessage.Should().Contain("Unknown channel");
+ }
+
+ [Fact]
+ public async Task ProcessPendingMessagesAsync_WhenInvalidPayload_ShouldFail()
+ {
+ // Arrange
+ var message = OutboxMessage.Create(ECommunicationChannel.Email, "invalid-json");
+ _outboxRepositoryMock.Setup(x => x.GetPendingAsync(It.IsAny(), It.IsAny(), It.IsAny()))
+ .ReturnsAsync(new List { message });
// Act
var result = await _service.ProcessPendingMessagesAsync();
@@ -163,30 +179,39 @@ public async Task ProcessPendingMessagesAsync_WhenDispatchThrows_ShouldHandleExc
// Assert
result.Should().Be(0);
message.Status.Should().Be(EOutboxMessageStatus.Pending);
- message.RetryCount.Should().Be(1);
- message.ErrorMessage.Should().Be("Network Error");
}
[Fact]
- public async Task ProcessPendingMessagesAsync_WhenMaxRetriesReached_ShouldMarkAsFailedAndLog()
+ public async Task ProcessPendingMessagesAsync_WithHtmlBody_ShouldUseIt()
{
// Arrange
- var payload = JsonSerializer.Serialize(new { To = "test@test.com", Subject = "Hi", Body = "Hello" });
- var message = OutboxMessage.Create(ECommunicationChannel.Email, payload, maxRetries: 1);
-
+ var payload = JsonSerializer.Serialize(new { To = "t@t.com", Subject = "S", HtmlBody = "H
", TextBody = "T" });
+ var message = OutboxMessage.Create(ECommunicationChannel.Email, payload);
_outboxRepositoryMock.Setup(x => x.GetPendingAsync(It.IsAny(), It.IsAny(), It.IsAny()))
.ReturnsAsync(new List { message });
+ _emailSenderMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny())).ReturnsAsync(true);
- _emailSenderMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny()))
- .ReturnsAsync(false);
+ // Act
+ await _service.ProcessPendingMessagesAsync();
+
+ // Assert
+ _emailSenderMock.Verify(x => x.SendAsync(It.Is(m => m.HtmlBody == "H
"), It.IsAny()));
+ }
+
+ [Fact]
+ public async Task ProcessPendingMessagesAsync_WithOnlyBody_ShouldHtmlEncodeIt()
+ {
+ // Arrange
+ var payload = JsonSerializer.Serialize(new { To = "t@t.com", Subject = "S", Body = "B" });
+ var message = OutboxMessage.Create(ECommunicationChannel.Email, payload);
+ _outboxRepositoryMock.Setup(x => x.GetPendingAsync(It.IsAny(), It.IsAny(), It.IsAny()))
+ .ReturnsAsync(new List { message });
+ _emailSenderMock.Setup(x => x.SendAsync(It.IsAny(), It.IsAny())).ReturnsAsync(true);
// Act
- var result = await _service.ProcessPendingMessagesAsync();
+ await _service.ProcessPendingMessagesAsync();
// Assert
- result.Should().Be(0);
- message.Status.Should().Be(EOutboxMessageStatus.Failed);
- message.RetryCount.Should().Be(1);
- _logRepositoryMock.Verify(x => x.AddAsync(It.Is(l => !l.IsSuccess), It.IsAny()), Times.Once);
+ _emailSenderMock.Verify(x => x.SendAsync(It.Is(m => m.HtmlBody.Contains("<b>")), It.IsAny()));
}
}
diff --git a/src/Modules/Communications/Tests/packages.lock.json b/src/Modules/Communications/Tests/packages.lock.json
index 49bccb0aa..9171bd5df 100644
--- a/src/Modules/Communications/Tests/packages.lock.json
+++ b/src/Modules/Communications/Tests/packages.lock.json
@@ -1002,6 +1002,7 @@
"MeAjudaAi.Modules.Documents.API": "[1.0.0, )",
"MeAjudaAi.Modules.Locations.API": "[1.0.0, )",
"MeAjudaAi.Modules.Providers.API": "[1.0.0, )",
+ "MeAjudaAi.Modules.Ratings.API": "[1.0.0, )",
"MeAjudaAi.Modules.SearchProviders.API": "[1.0.0, )",
"MeAjudaAi.Modules.ServiceCatalogs.API": "[1.0.0, )",
"MeAjudaAi.Modules.Users.API": "[1.0.0, )",
@@ -1159,6 +1160,35 @@
"MeAjudaAi.Shared": "[1.0.0, )"
}
},
+ "meajudaai.modules.ratings.api": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Infrastructure": "[1.0.0, )"
+ }
+ },
+ "meajudaai.modules.ratings.application": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Domain": "[1.0.0, )",
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
+ "Npgsql": "[10.0.2, )"
+ }
+ },
+ "meajudaai.modules.ratings.domain": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Shared": "[1.0.0, )"
+ }
+ },
+ "meajudaai.modules.ratings.infrastructure": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Application": "[1.0.0, )",
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
+ "Microsoft.EntityFrameworkCore.Design": "[10.0.5, )",
+ "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.1, )"
+ }
+ },
"meajudaai.modules.searchproviders.api": {
"type": "Project",
"dependencies": {
diff --git a/src/Modules/Documents/API/API.Client/DocumentAdmin/GetDocumentStatus.bru b/src/Modules/Documents/API/API.Client/DocumentAdmin/GetDocumentStatus.bru
new file mode 100644
index 000000000..77953bf73
--- /dev/null
+++ b/src/Modules/Documents/API/API.Client/DocumentAdmin/GetDocumentStatus.bru
@@ -0,0 +1,38 @@
+meta {
+ name: Get Document Status
+ type: http
+ seq: 4
+}
+
+get {
+ url: {{baseUrl}}/api/v1/documents/{{documentId}}/status
+ body: none
+ auth: bearer
+}
+
+auth:bearer {
+ token: {{accessToken}}
+}
+
+headers {
+ Accept: application/json
+}
+
+docs {
+ # Get Document Status
+
+ Consulta o status de processamento e verificação de um documento específico.
+
+ ## Parâmetros Path
+ - `documentId` (guid): ID do documento
+
+ ## Resposta Esperada (200 OK)
+ ```json
+ {
+ "id": "uuid",
+ "status": "Verified",
+ "rejectionReason": null,
+ "verifiedAt": "2026-04-13T10:00:00Z"
+ }
+ ```
+}
diff --git a/src/Modules/Documents/API/API.Client/DocumentAdmin/RequestVerification.bru b/src/Modules/Documents/API/API.Client/DocumentAdmin/RequestVerification.bru
new file mode 100644
index 000000000..92c4c21f1
--- /dev/null
+++ b/src/Modules/Documents/API/API.Client/DocumentAdmin/RequestVerification.bru
@@ -0,0 +1,30 @@
+meta {
+ name: Request Document Verification
+ type: http
+ seq: 5
+}
+
+post {
+ url: {{baseUrl}}/api/v1/documents/{{documentId}}/request-verification
+ body: none
+ auth: bearer
+}
+
+auth:bearer {
+ token: {{accessToken}}
+}
+
+headers {
+ Accept: application/json
+}
+
+docs {
+ # Request Document Verification
+
+ Solicita a verificação manual ou automática de um documento enviado.
+
+ ## Parâmetros Path
+ - `documentId` (guid): ID do documento
+
+ ## Resposta Esperada (202 Accepted)
+}
diff --git a/src/Modules/Documents/Tests/packages.lock.json b/src/Modules/Documents/Tests/packages.lock.json
index d1f46647e..38e4be4a8 100644
--- a/src/Modules/Documents/Tests/packages.lock.json
+++ b/src/Modules/Documents/Tests/packages.lock.json
@@ -1014,6 +1014,7 @@
"MeAjudaAi.Modules.Documents.API": "[1.0.0, )",
"MeAjudaAi.Modules.Locations.API": "[1.0.0, )",
"MeAjudaAi.Modules.Providers.API": "[1.0.0, )",
+ "MeAjudaAi.Modules.Ratings.API": "[1.0.0, )",
"MeAjudaAi.Modules.SearchProviders.API": "[1.0.0, )",
"MeAjudaAi.Modules.ServiceCatalogs.API": "[1.0.0, )",
"MeAjudaAi.Modules.Users.API": "[1.0.0, )",
@@ -1171,6 +1172,35 @@
"MeAjudaAi.Shared": "[1.0.0, )"
}
},
+ "meajudaai.modules.ratings.api": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Infrastructure": "[1.0.0, )"
+ }
+ },
+ "meajudaai.modules.ratings.application": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Domain": "[1.0.0, )",
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
+ "Npgsql": "[10.0.2, )"
+ }
+ },
+ "meajudaai.modules.ratings.domain": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Shared": "[1.0.0, )"
+ }
+ },
+ "meajudaai.modules.ratings.infrastructure": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Application": "[1.0.0, )",
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
+ "Microsoft.EntityFrameworkCore.Design": "[10.0.5, )",
+ "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.1, )"
+ }
+ },
"meajudaai.modules.searchproviders.api": {
"type": "Project",
"dependencies": {
diff --git a/src/Modules/Locations/Tests/packages.lock.json b/src/Modules/Locations/Tests/packages.lock.json
index f55706a87..611524466 100644
--- a/src/Modules/Locations/Tests/packages.lock.json
+++ b/src/Modules/Locations/Tests/packages.lock.json
@@ -992,6 +992,7 @@
"MeAjudaAi.Modules.Documents.API": "[1.0.0, )",
"MeAjudaAi.Modules.Locations.API": "[1.0.0, )",
"MeAjudaAi.Modules.Providers.API": "[1.0.0, )",
+ "MeAjudaAi.Modules.Ratings.API": "[1.0.0, )",
"MeAjudaAi.Modules.SearchProviders.API": "[1.0.0, )",
"MeAjudaAi.Modules.ServiceCatalogs.API": "[1.0.0, )",
"MeAjudaAi.Modules.Users.API": "[1.0.0, )",
@@ -1149,6 +1150,35 @@
"MeAjudaAi.Shared": "[1.0.0, )"
}
},
+ "meajudaai.modules.ratings.api": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Infrastructure": "[1.0.0, )"
+ }
+ },
+ "meajudaai.modules.ratings.application": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Domain": "[1.0.0, )",
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
+ "Npgsql": "[10.0.2, )"
+ }
+ },
+ "meajudaai.modules.ratings.domain": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Shared": "[1.0.0, )"
+ }
+ },
+ "meajudaai.modules.ratings.infrastructure": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Application": "[1.0.0, )",
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
+ "Microsoft.EntityFrameworkCore.Design": "[10.0.5, )",
+ "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.1, )"
+ }
+ },
"meajudaai.modules.searchproviders.api": {
"type": "Project",
"dependencies": {
diff --git a/src/Modules/Providers/Tests/packages.lock.json b/src/Modules/Providers/Tests/packages.lock.json
index 2d96ac992..3cb68e39a 100644
--- a/src/Modules/Providers/Tests/packages.lock.json
+++ b/src/Modules/Providers/Tests/packages.lock.json
@@ -1023,6 +1023,7 @@
"MeAjudaAi.Modules.Documents.API": "[1.0.0, )",
"MeAjudaAi.Modules.Locations.API": "[1.0.0, )",
"MeAjudaAi.Modules.Providers.API": "[1.0.0, )",
+ "MeAjudaAi.Modules.Ratings.API": "[1.0.0, )",
"MeAjudaAi.Modules.SearchProviders.API": "[1.0.0, )",
"MeAjudaAi.Modules.ServiceCatalogs.API": "[1.0.0, )",
"MeAjudaAi.Modules.Users.API": "[1.0.0, )",
@@ -1180,6 +1181,35 @@
"MeAjudaAi.Shared": "[1.0.0, )"
}
},
+ "meajudaai.modules.ratings.api": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Infrastructure": "[1.0.0, )"
+ }
+ },
+ "meajudaai.modules.ratings.application": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Domain": "[1.0.0, )",
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
+ "Npgsql": "[10.0.2, )"
+ }
+ },
+ "meajudaai.modules.ratings.domain": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Shared": "[1.0.0, )"
+ }
+ },
+ "meajudaai.modules.ratings.infrastructure": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Application": "[1.0.0, )",
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
+ "Microsoft.EntityFrameworkCore.Design": "[10.0.5, )",
+ "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.1, )"
+ }
+ },
"meajudaai.modules.searchproviders.api": {
"type": "Project",
"dependencies": {
diff --git a/src/Modules/Ratings/API/API.Client/Ratings/CreateReview.bru b/src/Modules/Ratings/API/API.Client/Ratings/CreateReview.bru
new file mode 100644
index 000000000..000c02f0d
--- /dev/null
+++ b/src/Modules/Ratings/API/API.Client/Ratings/CreateReview.bru
@@ -0,0 +1,57 @@
+meta {
+ name: Create Review
+ type: http
+ seq: 1
+}
+
+post {
+ url: {{baseUrl}}/api/v1/ratings
+ body: json
+ auth: bearer
+}
+
+auth:bearer {
+ token: {{accessToken}}
+}
+
+headers {
+ Content-Type: application/json
+ Accept: application/json
+}
+
+body:json {
+ {
+ "providerId": "00000000-0000-0000-0000-000000000000",
+ "rating": 5,
+ "comment": "Excellent service!"
+ }
+}
+
+docs {
+ # Create Review
+
+ Cria uma nova avaliação para um prestador de serviços.
+
+ ## Autorização
+ - **Política**: Authenticated
+ - **Requer token**: Sim
+
+ ## Parâmetros Body
+ - `providerId` (guid): ID do prestador avaliado (Obrigatório)
+ - `rating` (int): Nota de 1 a 5 (Obrigatório)
+ - `comment` (string): Comentário textual (Opcional, máx 1000 caracteres)
+
+ ## Moderação
+ - Avaliações com nota >= 4 e sem comentário são auto-aprovadas.
+ - Comentários com palavras ofensivas são marcados para moderação manual.
+
+ ## Resposta Esperada (201 Created)
+ ```json
+ "uuid-da-review"
+ ```
+
+ ## Códigos de Status
+ - **201**: Criado com sucesso
+ - **400**: Dados inválidos (rating fora do range, etc)
+ - **401**: Não autenticado
+}
diff --git a/src/Modules/Ratings/API/API.Client/Ratings/GetProviderReviews.bru b/src/Modules/Ratings/API/API.Client/Ratings/GetProviderReviews.bru
new file mode 100644
index 000000000..64a5b9861
--- /dev/null
+++ b/src/Modules/Ratings/API/API.Client/Ratings/GetProviderReviews.bru
@@ -0,0 +1,41 @@
+meta {
+ name: Get Provider Reviews
+ type: http
+ seq: 2
+}
+
+get {
+ url: {{baseUrl}}/api/v1/ratings/provider/{{providerId}}
+ body: none
+ auth: none
+}
+
+headers {
+ Accept: application/json
+}
+
+docs {
+ # Get Provider Reviews
+
+ Retorna todas as avaliações aprovadas de um prestador de serviços.
+
+ ## Autorização
+ - **Pública**
+ - **Requer token**: Não
+
+ ## Parâmetros Path
+ - `providerId` (guid): ID do prestador (Obrigatório)
+
+ ## Resposta Esperada (200 OK)
+ ```json
+ [
+ {
+ "id": "uuid-da-review",
+ "rating": 5,
+ "comment": "Muito bom!",
+ "createdAt": "2026-04-13T10:00:00Z",
+ "customerId": "uuid-do-cliente"
+ }
+ ]
+ ```
+}
diff --git a/src/Modules/Ratings/API/API.Client/Ratings/GetReviewById.bru b/src/Modules/Ratings/API/API.Client/Ratings/GetReviewById.bru
new file mode 100644
index 000000000..e721cb64f
--- /dev/null
+++ b/src/Modules/Ratings/API/API.Client/Ratings/GetReviewById.bru
@@ -0,0 +1,36 @@
+meta {
+ name: Get Review By Id
+ type: http
+ seq: 2
+}
+
+get {
+ url: {{baseUrl}}/api/v1/ratings/{{id}}
+ body: none
+ auth: none
+}
+
+vars:pre-request {
+ id: 00000000-0000-0000-0000-000000000000
+}
+
+docs {
+ # Get Review By Id
+
+ Busca os detalhes de uma avaliação previamente postada.
+
+ ## Autorização
+ - **Política**: AllowAnonymous (Público)
+ - **Requer token**: Não
+
+ ## Parâmetros Route
+ - `id` (guid): ID da avaliação a ser buscada (Obrigatório)
+
+ ## Comportamento
+ - Só retorna HTTP 200 OK caso a avaliação já esteja com status **Approved**.
+ - Retorna HTTP 404 Not Found caso não exista ou caso ainda esteja sob moderação / rejeitada.
+
+ ## Códigos de Status
+ - **200**: Sucesso, a avaliação e dados em JSON são retornados
+ - **404**: Avaliação não existe ou não foi aprovada pelo comitê
+}
diff --git a/src/Modules/Ratings/API/API.Client/Ratings/GetReviewStatus.bru b/src/Modules/Ratings/API/API.Client/Ratings/GetReviewStatus.bru
new file mode 100644
index 000000000..fc49ea705
--- /dev/null
+++ b/src/Modules/Ratings/API/API.Client/Ratings/GetReviewStatus.bru
@@ -0,0 +1,43 @@
+meta {
+ name: Get Review Status
+ type: http
+ seq: 3
+}
+
+get {
+ url: {{baseUrl}}/api/v1/ratings/{{id}}/status
+ body: none
+ auth: none
+}
+
+vars:pre-request {
+ id: 00000000-0000-0000-0000-000000000000
+}
+
+docs {
+ # Get Review Status
+
+ Retorna o status puro de uma avaliação existente, validando se está sob moderação, rejeitada ou aprovada.
+
+ ## Autorização
+ - **Política**: AllowAnonymous (Público/Integrador)
+ - **Requer token**: Não
+
+ ## Parâmetros Route
+ - `id` (guid): ID da avaliação a ser checada (Obrigatório)
+
+ ## Comportamento
+ - Uma enumeração de status: 1 = Pending, 2 = Approved, 3 = Rejected.
+
+ ## Resposta Esperada (200 OK)
+ ```json
+ {
+ "id": "uuid-da-review",
+ "status": 2
+ }
+ ```
+
+ ## Códigos de Status
+ - **200**: Status devolvido com sucesso
+ - **404**: Avaliação não existe
+}
diff --git a/src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs b/src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
new file mode 100644
index 000000000..eb981617e
--- /dev/null
+++ b/src/Modules/Ratings/API/Endpoints/RatingsEndpoints.cs
@@ -0,0 +1,126 @@
+using MeAjudaAi.Modules.Ratings.Application.Commands;
+using MeAjudaAi.Modules.Ratings.Domain.Repositories;
+using MeAjudaAi.Modules.Ratings.Domain.ValueObjects;
+using MeAjudaAi.Shared.Commands;
+using MeAjudaAi.Contracts.Contracts.Modules.Ratings.DTOs;
+using MeAjudaAi.Contracts.Contracts.Modules.Ratings.Enums;
+using MeAjudaAi.Shared.Utilities;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Routing;
+
+namespace MeAjudaAi.Modules.Ratings.API.Endpoints;
+
+public static class RatingsEndpoints
+{
+ public static void Map(IEndpointRouteBuilder app)
+ {
+ var group = app.MapGroup("api/v1/ratings")
+ .WithTags("Ratings")
+ .RequireAuthorization();
+
+ group.MapPost("/", CreateReviewAsync)
+ .WithName("CreateReview")
+ .Produces(StatusCodes.Status201Created)
+ .Produces(StatusCodes.Status400BadRequest)
+ .Produces(StatusCodes.Status401Unauthorized);
+
+ group.MapGet("/{id:guid}", GetReviewByIdAsync)
+ .WithName("GetReviewById")
+ .Produces(StatusCodes.Status200OK)
+ .Produces(StatusCodes.Status404NotFound)
+ .AllowAnonymous();
+
+ group.MapGet("/{id:guid}/status", GetReviewStatusAsync)
+ .WithName("GetReviewStatus")
+ .Produces(StatusCodes.Status200OK)
+ .Produces(StatusCodes.Status404NotFound);
+
+ group.MapGet("/provider/{providerId:guid}", GetProviderReviewsAsync)
+ .WithName("GetProviderReviews")
+ .Produces>(StatusCodes.Status200OK)
+ .AllowAnonymous();
+ }
+
+ private static async Task CreateReviewAsync(
+ [FromBody] CreateReviewRequest request,
+ [FromServices] ICommandHandler handler,
+ HttpContext httpContext,
+ CancellationToken cancellationToken)
+ {
+ var customerId = ClaimHelpers.GetUserIdGuid(httpContext);
+
+ if (customerId == null)
+ {
+ return Results.Unauthorized();
+ }
+
+ var command = new CreateReviewCommand(
+ request.ProviderId,
+ customerId.Value,
+ request.Rating,
+ request.Comment);
+
+ var reviewId = await handler.HandleAsync(command, cancellationToken);
+
+ // Location points to the status endpoint as reviews require moderation before being visible publically
+ return Results.Created($"/api/v1/ratings/{reviewId}/status", reviewId);
+ }
+
+ private static async Task GetReviewByIdAsync(
+ Guid id,
+ [FromServices] IReviewRepository repository,
+ CancellationToken cancellationToken)
+ {
+ // Explicitly cast to ReviewId for clarity
+ var review = await repository.GetByIdAsync((ReviewId)id, cancellationToken);
+
+ if (review == null || review.Status != MeAjudaAi.Modules.Ratings.Domain.Enums.EReviewStatus.Approved)
+ return Results.NotFound();
+
+ return Results.Ok(new ProviderReviewResponse(
+ review.Id.Value,
+ review.Rating,
+ review.Comment,
+ review.CreatedAt));
+ }
+
+ private static async Task GetReviewStatusAsync(
+ Guid id,
+ [FromServices] IReviewRepository repository,
+ CancellationToken cancellationToken)
+ {
+ // Explicitly cast to ReviewId for clarity
+ var review = await repository.GetByIdAsync((ReviewId)id, cancellationToken);
+
+ if (review == null)
+ return Results.NotFound();
+
+ return Results.Ok(new ReviewStatusResponse(
+ review.Id.Value,
+ (EReviewStatus)(int)review.Status));
+ }
+
+ private static async Task GetProviderReviewsAsync(
+ Guid providerId,
+ [FromServices] IReviewRepository repository,
+ [FromQuery] int page = 1,
+ [FromQuery] int pageSize = 10,
+ CancellationToken cancellationToken = default)
+ {
+ // Aplicar limites
+ page = page < 1 ? 1 : page;
+ pageSize = pageSize < 1 ? 1 : pageSize > 100 ? 100 : pageSize;
+
+ var reviews = await repository.GetByProviderIdAsync(providerId, page, pageSize, cancellationToken);
+
+ var result = reviews.Select(r => new ProviderReviewResponse(
+ r.Id.Value,
+ r.Rating,
+ r.Comment,
+ r.CreatedAt));
+
+ return Results.Ok(result);
+ }
+}
diff --git a/src/Modules/Ratings/API/Extensions.cs b/src/Modules/Ratings/API/Extensions.cs
new file mode 100644
index 000000000..df618f9d4
--- /dev/null
+++ b/src/Modules/Ratings/API/Extensions.cs
@@ -0,0 +1,34 @@
+using MeAjudaAi.Modules.Ratings.API.Endpoints;
+using MeAjudaAi.Modules.Ratings.Application;
+using MeAjudaAi.Modules.Ratings.Infrastructure;
+using MeAjudaAi.Modules.Ratings.Infrastructure.Events.Handlers;
+using MeAjudaAi.Modules.Ratings.Domain.Events;
+using MeAjudaAi.Shared.Events;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace MeAjudaAi.Modules.Ratings.API;
+
+public static class Extensions
+{
+ public static IServiceCollection AddRatingsModule(this IServiceCollection services, IConfiguration configuration, IHostEnvironment environment)
+ {
+ services.AddApplication();
+ services.AddInfrastructure(configuration, environment);
+
+ // Manipuladores de eventos de domínio
+ services.AddScoped, ReviewApprovedDomainEventHandler>();
+ services.AddScoped, ReviewRejectedDomainEventHandler>();
+
+ return services;
+ }
+
+ public static IEndpointRouteBuilder UseRatingsModule(this IEndpointRouteBuilder app)
+ {
+ RatingsEndpoints.Map(app);
+ return app;
+ }
+}
diff --git a/src/Modules/Ratings/API/MeAjudaAi.Modules.Ratings.API.csproj b/src/Modules/Ratings/API/MeAjudaAi.Modules.Ratings.API.csproj
new file mode 100644
index 000000000..1fbea747a
--- /dev/null
+++ b/src/Modules/Ratings/API/MeAjudaAi.Modules.Ratings.API.csproj
@@ -0,0 +1,13 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/src/Modules/Ratings/API/packages.lock.json b/src/Modules/Ratings/API/packages.lock.json
new file mode 100644
index 000000000..8b6476a5e
--- /dev/null
+++ b/src/Modules/Ratings/API/packages.lock.json
@@ -0,0 +1,844 @@
+{
+ "version": 2,
+ "dependencies": {
+ "net10.0": {
+ "SonarAnalyzer.CSharp": {
+ "type": "Direct",
+ "requested": "[10.23.0.137933, )",
+ "resolved": "10.23.0.137933",
+ "contentHash": "K8CwUkB4PlUcMfLqGCj1aviVfW9AufZH2K/TjgPGYdj4gosV/kcyxpBR0jaCG8argC6dfxJBbSS6xXJ59lFWgA=="
+ },
+ "Asp.Versioning.Abstractions": {
+ "type": "Transitive",
+ "resolved": "8.1.0",
+ "contentHash": "mpeNZyMdvrHztJwR1sXIUQ+3iioEU97YMBnFA9WLbsPOYhGwDJnqJMmEd8ny7kcmS9OjTHoEuX/bSXXY3brIFA==",
+ "dependencies": {
+ "Microsoft.Extensions.Primitives": "8.0.0"
+ }
+ },
+ "Dapper.AOT": {
+ "type": "Transitive",
+ "resolved": "1.0.48",
+ "contentHash": "rsLM3yKr4g+YKKox9lhc8D+kz67P7Q9+xdyn1LmCsoYr1kYpJSm+Nt6slo5UrfUrcTiGJ57zUlyO8XUdV7G7iA=="
+ },
+ "Hangfire.NetCore": {
+ "type": "Transitive",
+ "resolved": "1.8.23",
+ "contentHash": "SmvUJF/u5MCP666R5Y1V+GntqBc4RCWJqn5ztMMN67d53Cx5cuaWR0YNLMrabjylwLarFYJ7EdR9RnGEZzp/dg==",
+ "dependencies": {
+ "Hangfire.Core": "[1.8.23]",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "3.0.0",
+ "Microsoft.Extensions.Hosting.Abstractions": "3.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "3.0.0"
+ }
+ },
+ "Humanizer.Core": {
+ "type": "Transitive",
+ "resolved": "2.14.1",
+ "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw=="
+ },
+ "Microsoft.Bcl.TimeProvider": {
+ "type": "Transitive",
+ "resolved": "8.0.1",
+ "contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA=="
+ },
+ "Microsoft.CodeAnalysis.Analyzers": {
+ "type": "Transitive",
+ "resolved": "3.11.0",
+ "contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg=="
+ },
+ "Microsoft.CodeAnalysis.Common": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "ZXRAdvH6GiDeHRyd3q/km8Z44RoM6FBWHd+gen/la81mVnAdHTEsEkO5J0TCNXBymAcx5UYKt5TvgKBhaLJEow==",
+ "dependencies": {
+ "Microsoft.CodeAnalysis.Analyzers": "3.11.0"
+ }
+ },
+ "Microsoft.CodeAnalysis.CSharp": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "5DSyJ9bk+ATuDy7fp2Zt0mJStDVKbBoiz1DyfAwSa+k4H4IwykAUcV3URelw5b8/iVbfSaOwkwmPUZH6opZKCw==",
+ "dependencies": {
+ "Microsoft.CodeAnalysis.Analyzers": "3.11.0",
+ "Microsoft.CodeAnalysis.Common": "[5.0.0]"
+ }
+ },
+ "Microsoft.CodeAnalysis.CSharp.Workspaces": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "Al/Q8B+yO8odSqGVpSvrShMFDvlQdIBU//F3E6Rb0YdiLSALE9wh/pvozPNnfmh5HDnvU+mkmSjpz4hQO++jaA==",
+ "dependencies": {
+ "Humanizer.Core": "2.14.1",
+ "Microsoft.CodeAnalysis.Analyzers": "3.11.0",
+ "Microsoft.CodeAnalysis.CSharp": "[5.0.0]",
+ "Microsoft.CodeAnalysis.Common": "[5.0.0]",
+ "Microsoft.CodeAnalysis.Workspaces.Common": "[5.0.0]",
+ "System.Composition": "9.0.0"
+ }
+ },
+ "Microsoft.CodeAnalysis.Workspaces.Common": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "ZbUmIvT6lqTNKiv06Jl5wf0MTMi1vQ1oH7ou4CLcs2C/no/L7EhP3T8y3XXvn9VbqMcJaJnEsNA1jwYUMgc5jg==",
+ "dependencies": {
+ "Humanizer.Core": "2.14.1",
+ "Microsoft.CodeAnalysis.Analyzers": "3.11.0",
+ "Microsoft.CodeAnalysis.Common": "[5.0.0]",
+ "System.Composition": "9.0.0"
+ }
+ },
+ "Microsoft.CodeAnalysis.Workspaces.MSBuild": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "/G+LVoAGMz6Ae8nm+PGLxSw+F5RjYx/J7irbTO5uKAPw1bxHyQJLc/YOnpDxt+EpPtYxvC9wvBsg/kETZp1F9Q==",
+ "dependencies": {
+ "Humanizer.Core": "2.14.1",
+ "Microsoft.Build.Framework": "17.11.31",
+ "Microsoft.CodeAnalysis.Analyzers": "3.11.0",
+ "Microsoft.CodeAnalysis.Workspaces.Common": "[5.0.0]",
+ "Microsoft.Extensions.DependencyInjection": "9.0.0",
+ "Microsoft.Extensions.Logging": "9.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "9.0.0",
+ "Microsoft.Extensions.Options": "9.0.0",
+ "Microsoft.Extensions.Primitives": "9.0.0",
+ "Microsoft.VisualStudio.SolutionPersistence": "1.0.52",
+ "Newtonsoft.Json": "13.0.3",
+ "System.Composition": "9.0.0"
+ }
+ },
+ "Microsoft.EntityFrameworkCore.Abstractions": {
+ "type": "Transitive",
+ "resolved": "10.0.5",
+ "contentHash": "32c58Rnm47Qvhimawf67KO9PytgPz3QoWye7Abapt0Yocw/JnzMiSNj/pRoIKyn8Jxypkv86zxKD4Q/zNTc0Ag=="
+ },
+ "Microsoft.EntityFrameworkCore.Analyzers": {
+ "type": "Transitive",
+ "resolved": "10.0.5",
+ "contentHash": "ipC4u1VojgEfoIZhtbS2Sx5IluJTP/Jf1hz3yGsxGBgSukYY/CquI6rAjxn5H58CZgVn36qcuPPtNMwZ0AUzMg=="
+ },
+ "Microsoft.Extensions.Caching.Memory": {
+ "type": "Transitive",
+ "resolved": "10.0.5",
+ "contentHash": "jUEXmkBUPdOS/MP9areK/sbKhdklq9+tEhvwfxGalZVnmyLUO5rrheNNutUBtvbZ7J8ECkG7/r2KXi/IFC06cA==",
+ "dependencies": {
+ "Microsoft.Extensions.Caching.Abstractions": "10.0.5",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Options": "10.0.5",
+ "Microsoft.Extensions.Primitives": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Diagnostics.HealthChecks": {
+ "type": "Transitive",
+ "resolved": "8.0.11",
+ "contentHash": "zLgN22Zp9pk8RHlwssRTexw4+a6wqOnKWN+VejdPn5Yhjql4XiBhkFo35Nu8mmqHIk/UEmmCnMGLWq75aFfkOw==",
+ "dependencies": {
+ "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.11",
+ "Microsoft.Extensions.Hosting.Abstractions": "8.0.1",
+ "Microsoft.Extensions.Logging.Abstractions": "8.0.2",
+ "Microsoft.Extensions.Options": "8.0.2"
+ }
+ },
+ "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": {
+ "type": "Transitive",
+ "resolved": "8.0.11",
+ "contentHash": "So3JUdRxozRjvQ3cxU6F3nI/i4emDnjane6yMYcJhvTTTu29ltlIdoXjkFGRceIWz8yKvuEpzXItZ0x5GvN2nQ=="
+ },
+ "Microsoft.Extensions.FileProviders.Abstractions": {
+ "type": "Transitive",
+ "resolved": "10.0.5",
+ "contentHash": "nCBmCx0Xemlu65ZiWMcXbvfvtznKxf4/YYKF9R28QkqdI9lTikedGqzJ28/xmdGGsxUnsP5/3TQGpiPwVjK0dA==",
+ "dependencies": {
+ "Microsoft.Extensions.Primitives": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Primitives": {
+ "type": "Transitive",
+ "resolved": "10.0.5",
+ "contentHash": "/HUHJ0tw/LQvD0DZrz50eQy/3z7PfX7WWEaXnjKTV9/TNdcgFlNTZGo49QhS7PTmhDqMyHRMqAXSBxLh0vso4g=="
+ },
+ "Microsoft.FeatureManagement": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "qxvGAv9WJHYfOpixWywJTa1WNTPy5MbQiv+O+UlE6E/LVofiM1+YRR6m41zsHIbAGm1S0PQ0QFuAsOw9DkoKsg==",
+ "dependencies": {
+ "Microsoft.Bcl.TimeProvider": "8.0.1",
+ "Microsoft.Extensions.Caching.Memory": "8.0.1",
+ "Microsoft.Extensions.Configuration": "8.0.0",
+ "Microsoft.Extensions.Configuration.Binder": "8.0.2",
+ "Microsoft.Extensions.Logging": "8.0.1"
+ }
+ },
+ "Microsoft.VisualStudio.SolutionPersistence": {
+ "type": "Transitive",
+ "resolved": "1.0.52",
+ "contentHash": "oNv2JtYXhpdJrX63nibx1JT3uCESOBQ1LAk7Dtz/sr0+laW0KRM6eKp4CZ3MHDR2siIkKsY8MmUkeP5DKkQQ5w=="
+ },
+ "Mono.TextTemplating": {
+ "type": "Transitive",
+ "resolved": "3.0.0",
+ "contentHash": "YqueG52R/Xej4VVbKuRIodjiAhV0HR/XVbLbNrJhCZnzjnSjgMJ/dCdV0akQQxavX6hp/LC6rqLGLcXeQYU7XA==",
+ "dependencies": {
+ "System.CodeDom": "6.0.0"
+ }
+ },
+ "Newtonsoft.Json": {
+ "type": "Transitive",
+ "resolved": "13.0.4",
+ "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A=="
+ },
+ "Pipelines.Sockets.Unofficial": {
+ "type": "Transitive",
+ "resolved": "2.2.8",
+ "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ=="
+ },
+ "Serilog.Extensions.Hosting": {
+ "type": "Transitive",
+ "resolved": "10.0.0",
+ "contentHash": "E7juuIc+gzoGxgzFooFgAV8g9BfiSXNKsUok9NmEpyAXg2odkcPsMa/Yo4axkJRlh0se7mkYQ1GXDaBemR+b6w==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0",
+ "Microsoft.Extensions.Hosting.Abstractions": "10.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.0",
+ "Serilog": "4.3.0",
+ "Serilog.Extensions.Logging": "10.0.0"
+ }
+ },
+ "Serilog.Extensions.Logging": {
+ "type": "Transitive",
+ "resolved": "10.0.0",
+ "contentHash": "vx0kABKl2dWbBhhqAfTOk53/i8aV/5VaT3a6il9gn72Wqs2pM7EK2OB6No6xdqK2IaY6Zf9gdjLuK9BVa2rT+Q==",
+ "dependencies": {
+ "Microsoft.Extensions.Logging": "10.0.0",
+ "Serilog": "4.2.0"
+ }
+ },
+ "Serilog.Formatting.Compact": {
+ "type": "Transitive",
+ "resolved": "3.0.0",
+ "contentHash": "wQsv14w9cqlfB5FX2MZpNsTawckN4a8dryuNGbebB/3Nh1pXnROHZov3swtu3Nj5oNG7Ba+xdu7Et/ulAUPanQ==",
+ "dependencies": {
+ "Serilog": "4.0.0"
+ }
+ },
+ "Serilog.Sinks.Debug": {
+ "type": "Transitive",
+ "resolved": "3.0.0",
+ "contentHash": "4BzXcdrgRX7wde9PmHuYd9U6YqycCC28hhpKonK7hx0wb19eiuRj16fPcPSVp0o/Y1ipJuNLYQ00R3q2Zs8FDA==",
+ "dependencies": {
+ "Serilog": "4.0.0"
+ }
+ },
+ "Serilog.Sinks.File": {
+ "type": "Transitive",
+ "resolved": "7.0.0",
+ "contentHash": "fKL7mXv7qaiNBUC71ssvn/dU0k9t0o45+qm2XgKAlSt19xF+ijjxyA3R6HmCgfKEKwfcfkwWjayuQtRueZFkYw==",
+ "dependencies": {
+ "Serilog": "4.2.0"
+ }
+ },
+ "StackExchange.Redis": {
+ "type": "Transitive",
+ "resolved": "2.7.27",
+ "contentHash": "Uqc2OQHglqj9/FfGQ6RkKFkZfHySfZlfmbCl+hc+u2I/IqunfelQ7QJi7ZhvAJxUtu80pildVX6NPLdDaUffOw==",
+ "dependencies": {
+ "Microsoft.Extensions.Logging.Abstractions": "6.0.0",
+ "Pipelines.Sockets.Unofficial": "2.2.8"
+ }
+ },
+ "System.CodeDom": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA=="
+ },
+ "System.Composition": {
+ "type": "Transitive",
+ "resolved": "9.0.0",
+ "contentHash": "3Djj70fFTraOarSKmRnmRy/zm4YurICm+kiCtI0dYRqGJnLX6nJ+G3WYuFJ173cAPax/gh96REcbNiVqcrypFQ==",
+ "dependencies": {
+ "System.Composition.AttributedModel": "9.0.0",
+ "System.Composition.Convention": "9.0.0",
+ "System.Composition.Hosting": "9.0.0",
+ "System.Composition.Runtime": "9.0.0",
+ "System.Composition.TypedParts": "9.0.0"
+ }
+ },
+ "System.Composition.AttributedModel": {
+ "type": "Transitive",
+ "resolved": "9.0.0",
+ "contentHash": "iri00l/zIX9g4lHMY+Nz0qV1n40+jFYAmgsaiNn16xvt2RDwlqByNG4wgblagnDYxm3YSQQ0jLlC/7Xlk9CzyA=="
+ },
+ "System.Composition.Convention": {
+ "type": "Transitive",
+ "resolved": "9.0.0",
+ "contentHash": "+vuqVP6xpi582XIjJi6OCsIxuoTZfR0M7WWufk3uGDeCl3wGW6KnpylUJ3iiXdPByPE0vR5TjJgR6hDLez4FQg==",
+ "dependencies": {
+ "System.Composition.AttributedModel": "9.0.0"
+ }
+ },
+ "System.Composition.Hosting": {
+ "type": "Transitive",
+ "resolved": "9.0.0",
+ "contentHash": "OFqSeFeJYr7kHxDfaViGM1ymk7d4JxK//VSoNF9Ux0gpqkLsauDZpu89kTHHNdCWfSljbFcvAafGyBoY094btQ==",
+ "dependencies": {
+ "System.Composition.Runtime": "9.0.0"
+ }
+ },
+ "System.Composition.Runtime": {
+ "type": "Transitive",
+ "resolved": "9.0.0",
+ "contentHash": "w1HOlQY1zsOWYussjFGZCEYF2UZXgvoYnS94NIu2CBnAGMbXFAX8PY8c92KwUItPmowal68jnVLBCzdrWLeEKA=="
+ },
+ "System.Composition.TypedParts": {
+ "type": "Transitive",
+ "resolved": "9.0.0",
+ "contentHash": "aRZlojCCGEHDKqh43jaDgaVpYETsgd7Nx4g1zwLKMtv4iTo0627715ajEFNpEEBTgLmvZuv8K0EVxc3sM4NWJA==",
+ "dependencies": {
+ "System.Composition.AttributedModel": "9.0.0",
+ "System.Composition.Hosting": "9.0.0",
+ "System.Composition.Runtime": "9.0.0"
+ }
+ },
+ "System.Threading.RateLimiting": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q=="
+ },
+ "meajudaai.contracts": {
+ "type": "Project",
+ "dependencies": {
+ "FluentValidation": "[12.1.1, )"
+ }
+ },
+ "meajudaai.modules.ratings.application": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Domain": "[1.0.0, )",
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
+ "Npgsql": "[10.0.2, )"
+ }
+ },
+ "meajudaai.modules.ratings.domain": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Shared": "[1.0.0, )"
+ }
+ },
+ "meajudaai.modules.ratings.infrastructure": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Modules.Ratings.Application": "[1.0.0, )",
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
+ "Microsoft.EntityFrameworkCore.Design": "[10.0.5, )",
+ "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.1, )"
+ }
+ },
+ "meajudaai.shared": {
+ "type": "Project",
+ "dependencies": {
+ "Asp.Versioning.Mvc": "[8.1.1, )",
+ "Asp.Versioning.Mvc.ApiExplorer": "[8.1.1, )",
+ "AspNetCore.HealthChecks.Npgsql": "[9.0.0, )",
+ "AspNetCore.HealthChecks.Redis": "[9.0.0, )",
+ "Dapper": "[2.1.72, )",
+ "EFCore.NamingConventions": "[10.0.1, )",
+ "FluentValidation": "[12.1.1, )",
+ "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )",
+ "Hangfire.AspNetCore": "[1.8.23, )",
+ "Hangfire.Core": "[1.8.23, )",
+ "Hangfire.PostgreSql": "[1.21.1, )",
+ "MeAjudaAi.Contracts": "[1.0.0, )",
+ "Microsoft.AspNetCore.OpenApi": "[10.0.5, )",
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
+ "Microsoft.EntityFrameworkCore.Design": "[10.0.5, )",
+ "Microsoft.Extensions.Caching.Hybrid": "[10.4.0, )",
+ "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.5, )",
+ "Microsoft.FeatureManagement.AspNetCore": "[4.4.0, )",
+ "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.1, )",
+ "RabbitMQ.Client": "[7.2.1, )",
+ "Rebus": "[8.9.0, )",
+ "Rebus.RabbitMq": "[10.1.1, )",
+ "Rebus.ServiceProvider": "[10.7.2, )",
+ "Scrutor": "[7.0.0, )",
+ "Serilog": "[4.3.1, )",
+ "Serilog.AspNetCore": "[10.0.0, )",
+ "Serilog.Enrichers.Environment": "[3.0.1, )",
+ "Serilog.Enrichers.Process": "[3.0.0, )",
+ "Serilog.Enrichers.Thread": "[4.0.0, )",
+ "Serilog.Settings.Configuration": "[10.0.0, )",
+ "Serilog.Sinks.Console": "[6.1.1, )",
+ "Serilog.Sinks.Seq": "[9.0.0, )"
+ }
+ },
+ "Asp.Versioning.Http": {
+ "type": "CentralTransitive",
+ "requested": "[8.1.1, )",
+ "resolved": "8.1.1",
+ "contentHash": "1D/Mzq1MSUSQe1eCY6GeEsu+tIlpzoWZxZkhlcw/uvtVoTrwO+BMl0fSj/XX8oZN1DutWTZqHDHgyDRq+IeSdQ==",
+ "dependencies": {
+ "Asp.Versioning.Abstractions": "8.1.0"
+ }
+ },
+ "Asp.Versioning.Mvc": {
+ "type": "CentralTransitive",
+ "requested": "[8.1.1, )",
+ "resolved": "8.1.1",
+ "contentHash": "mkJv6eAdlbHbqTdrUcfEYFoZuGL6HoR7O+Lfsvivixp7N5BNhfCFPPOwsBzdIiH1qzdJXyJf+C+DZ08j27PMPg==",
+ "dependencies": {
+ "Asp.Versioning.Http": "8.1.1"
+ }
+ },
+ "Asp.Versioning.Mvc.ApiExplorer": {
+ "type": "CentralTransitive",
+ "requested": "[8.1.1, )",
+ "resolved": "8.1.1",
+ "contentHash": "u8PrP6CjmurgIh0EDfg8Gc/GdpDHgkbv8OLKdxQcWb5W4sp0i9BcdbQlLvRcATjNIJUyr7ZmqXjmdsFfqnuy0g==",
+ "dependencies": {
+ "Asp.Versioning.Mvc": "8.1.1"
+ }
+ },
+ "AspNetCore.HealthChecks.NpgSql": {
+ "type": "CentralTransitive",
+ "requested": "[9.0.0, )",
+ "resolved": "9.0.0",
+ "contentHash": "npc58/AD5zuVxERdhCl2Kb7WnL37mwX42SJcXIwvmEig0/dugOLg3SIwtfvvh3TnvTwR/sk5LYNkkPaBdks61A==",
+ "dependencies": {
+ "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11",
+ "Npgsql": "8.0.3"
+ }
+ },
+ "AspNetCore.HealthChecks.Redis": {
+ "type": "CentralTransitive",
+ "requested": "[9.0.0, )",
+ "resolved": "9.0.0",
+ "contentHash": "yNH0h8GLRbAf+PU5HNVLZ5hNeyq9mDVmRKO9xuZsme/znUYoBJlQvI0gq45gaZNlLncCHkMhR4o90MuT+gxxPw==",
+ "dependencies": {
+ "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11",
+ "StackExchange.Redis": "2.7.4"
+ }
+ },
+ "Dapper": {
+ "type": "CentralTransitive",
+ "requested": "[2.1.72, )",
+ "resolved": "2.1.72",
+ "contentHash": "ns4mGqQd9a/MhP8m6w556vVlZIa0/MfUu03zrxjZC/jlr1uVCsUac8bkdB+Fs98Llbd56rRSo1eZH5VVmeGZyw=="
+ },
+ "EFCore.NamingConventions": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.1, )",
+ "resolved": "10.0.1",
+ "contentHash": "Xs5k8XfNKPkkQSkGmZkmDI1je0prLTdxse+s8PgTFZxyBrlrTLzTBUTVJtQKSsbvu4y+luAv8DdtO5SALJE++A==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore": "[10.0.1, 11.0.0)",
+ "Microsoft.EntityFrameworkCore.Relational": "[10.0.1, 11.0.0)",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1"
+ }
+ },
+ "FluentValidation": {
+ "type": "CentralTransitive",
+ "requested": "[12.1.1, )",
+ "resolved": "12.1.1",
+ "contentHash": "EPpkIe1yh1a0OXyC100oOA8WMbZvqUu5plwhvYcb7oSELfyUZzfxV48BLhvs3kKo4NwG7MGLNgy1RJiYtT8Dpw=="
+ },
+ "FluentValidation.DependencyInjectionExtensions": {
+ "type": "CentralTransitive",
+ "requested": "[12.1.1, )",
+ "resolved": "12.1.1",
+ "contentHash": "D0VXh4dtjjX2aQizuaa0g6R8X3U1JaVqJPfGCvLwZX9t/O2h7tkpbitbadQMfwcgSPdDbI2vDxuwRMv/Uf9dHA==",
+ "dependencies": {
+ "FluentValidation": "12.1.1",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0"
+ }
+ },
+ "Hangfire.AspNetCore": {
+ "type": "CentralTransitive",
+ "requested": "[1.8.23, )",
+ "resolved": "1.8.23",
+ "contentHash": "TXpOl7kX4xXq5bLEqqWCpt9zh3TaouDwtb3GDtzGHX5uSC2RaAqZzn2swevivx3Uki16slXIigiPtgr4TPKpsg==",
+ "dependencies": {
+ "Hangfire.NetCore": "[1.8.23]"
+ }
+ },
+ "Hangfire.Core": {
+ "type": "CentralTransitive",
+ "requested": "[1.8.23, )",
+ "resolved": "1.8.23",
+ "contentHash": "YCOTtF3NNOQI83PlfjeNDDBkofJDfdET2CwhfQsiVBwmsU6lP19QW9NVTIH9epl+MnOsyFC2G1RnlPSGV8F1FQ==",
+ "dependencies": {
+ "Newtonsoft.Json": "11.0.1"
+ }
+ },
+ "Hangfire.PostgreSql": {
+ "type": "CentralTransitive",
+ "requested": "[1.21.1, )",
+ "resolved": "1.21.1",
+ "contentHash": "hFNZAxv+1p72/XCZdImnH6ovCzZ2DKAMTOI8CReT0P3yw/k0b0YJP2teA18agNH1ZYInPzhtxGk8hx5n2cxbbQ==",
+ "dependencies": {
+ "Dapper": "2.0.123",
+ "Dapper.AOT": "1.0.48",
+ "Hangfire.Core": "1.8.0",
+ "Npgsql": "6.0.11"
+ }
+ },
+ "Microsoft.AspNetCore.OpenApi": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "vTcxIfOPyfFbYk1g8YcXJfkMnlEWVkSnnjxcZLy60zgwiHMRf2SnZR+9E4HlpwKxgE3yfKMOti8J6WfKuKsw6w==",
+ "dependencies": {
+ "Microsoft.OpenApi": "2.0.0"
+ }
+ },
+ "Microsoft.Build.Framework": {
+ "type": "CentralTransitive",
+ "requested": "[18.0.2, )",
+ "resolved": "18.0.2",
+ "contentHash": "sOSb+0J4G/jCBW/YqmRuL0eOMXgfw1KQLdC9TkbvfA5xs7uNm+PBQXJCOzSJGXtZcZrtXozcwxPmUiRUbmd7FA=="
+ },
+ "Microsoft.EntityFrameworkCore": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "9tNBmK3EpYVGRQLiqP+bqK2m+TD0Gv//4vCzR7ZOgl4FWzCFyOpYdIVka13M4kcBdPdSJcs3wbHr3rmzOqbIMA==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore.Abstractions": "10.0.5",
+ "Microsoft.EntityFrameworkCore.Analyzers": "10.0.5",
+ "Microsoft.Extensions.Caching.Memory": "10.0.5",
+ "Microsoft.Extensions.Logging": "10.0.5"
+ }
+ },
+ "Microsoft.EntityFrameworkCore.Design": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "gm6f0cC2w/2tcd4GeZJqEMruTercpIJfO5sSAFLtqTqblDBHgAFk70xwshUIUVX4I6sZwdEUSd1YxoKFk1AL0w==",
+ "dependencies": {
+ "Humanizer.Core": "2.14.1",
+ "Microsoft.Build.Framework": "18.0.2",
+ "Microsoft.CodeAnalysis.CSharp": "5.0.0",
+ "Microsoft.CodeAnalysis.CSharp.Workspaces": "5.0.0",
+ "Microsoft.CodeAnalysis.Workspaces.MSBuild": "5.0.0",
+ "Microsoft.EntityFrameworkCore.Relational": "10.0.5",
+ "Microsoft.Extensions.Caching.Memory": "10.0.5",
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.5",
+ "Microsoft.Extensions.DependencyModel": "10.0.5",
+ "Microsoft.Extensions.Logging": "10.0.5",
+ "Mono.TextTemplating": "3.0.0",
+ "Newtonsoft.Json": "13.0.3"
+ }
+ },
+ "Microsoft.EntityFrameworkCore.Relational": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "uxmFjZEAB/KbsgWFSS4lLqkEHCfXxB2x0UcbiO4e5fCRpFFeTMSx/me6009nYJLu5IKlDwO1POh++P6RilFTDw==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore": "10.0.5",
+ "Microsoft.Extensions.Caching.Memory": "10.0.5",
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Logging": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Caching.Abstractions": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "k/QDdQ94/0Shi0KfU+e12m73jfQo+3JpErTtgpZfsCIqkvdEEO0XIx6R+iTbN55rNPaNhOqNY4/sB+jZ8XxVPw==",
+ "dependencies": {
+ "Microsoft.Extensions.Primitives": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Caching.Hybrid": {
+ "type": "CentralTransitive",
+ "requested": "[10.4.0, )",
+ "resolved": "10.4.0",
+ "contentHash": "4V+aMLQeU/p4VcIWIcvGro0L6HynmL2TrelL04Ce1iotP6T5+kjxuZQvl6P1ObSXIRPCbVXtQSt1NxK0fRIuag==",
+ "dependencies": {
+ "Microsoft.Extensions.Caching.Abstractions": "10.0.4",
+ "Microsoft.Extensions.Caching.Memory": "10.0.4",
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.4",
+ "Microsoft.Extensions.Options": "10.0.4"
+ }
+ },
+ "Microsoft.Extensions.Caching.StackExchangeRedis": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "zXb143/TpEKOLQuWGw2CkJgb9F4XXh2XbevMvppzsIHr1/pjML0zjc+vzXcpCV8YUwpW5NIaScZhzFSm621B3Q==",
+ "dependencies": {
+ "Microsoft.Extensions.Caching.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Options": "10.0.5",
+ "StackExchange.Redis": "2.7.27"
+ }
+ },
+ "Microsoft.Extensions.Configuration": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "8Rx5sqg04FttxrumyG6bmoRuFRgYzK6IVwF1i0/o0cXfKBdDeVpJejKHtJCMjyg9E/DNMVqpqOGe/tCT5gYvVA==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Primitives": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Configuration.Abstractions": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "P09QpTHjqHmCLQOTC+WyLkoRNxek4NIvfWt+TnU0etoDUSRxcltyd6+j/ouRbMdLR0j44GqGO+lhI2M4fAHG4g==",
+ "dependencies": {
+ "Microsoft.Extensions.Primitives": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Configuration.Binder": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "99Z4rjyXopb1MIazDSPcvwYCUdYNO01Cf1GUs2WUjIFAbkGmwzj2vPa2k+3pheJRV+YgNd2QqRKHAri0oBAU4Q==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration": "10.0.5",
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.DependencyInjection": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "v1SVsowG6YE1YnHVGmLWz57YTRCQRx9pH5ebIESXfm5isI9gA3QaMyg/oMTzPpXYZwSAVDzYItGJKfmV+pqXkQ==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.DependencyInjection.Abstractions": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "iVMtq9eRvzyhx8949EGT0OCYJfXi737SbRVzWXE5GrOgGj5AaZ9eUuxA/BSUfmOMALKn/g8KfFaNQw0eiB3lyA=="
+ },
+ "Microsoft.Extensions.DependencyModel": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "xA4kkL+QS6KCAOKz/O0oquHs44Ob8J7zpBCNt3wjkBWDg5aCqfwG8rWWLsg5V86AM0sB849g9JjPjIdksTCIKg=="
+ },
+ "Microsoft.Extensions.Diagnostics.Abstractions": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "/nYGrpa9/0BZofrVpBbbj+Ns8ZesiPE0V/KxsuHgDgHQopIzN54nRaQGSuvPw16/kI9sW1Zox5yyAPqvf0Jz6A==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Options": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Hosting.Abstractions": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "+Wb7KAMVZTomwJkQrjuPTe5KBzGod7N8XeG+ScxRlkPOB4sZLG4ccVwjV4Phk5BCJt7uIMnGHVoN6ZMVploX+g==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.5",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.5",
+ "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Logging": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "+XTMKQyDWg4ODoNHU/BN3BaI1jhGO7VCS+BnzT/4IauiG6y2iPAte7MyD7rHKS+hNP0TkFkjrae8DFjDUxtcxg==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection": "10.0.5",
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Options": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Logging.Abstractions": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "9HOdqlDtPptVcmKAjsQ/Nr5Rxfq6FMYLdhvZh1lVmeKR738qeYecQD7+ldooXf+u2KzzR1kafSphWngIM3C6ug==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Options": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "MDaQMdUplw0AIRhWWmbLA7yQEXaLIHb+9CTroTiNS8OlI0LMXS4LCxtopqauiqGCWlRgJ+xyraVD8t6veRAFbw==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Primitives": "10.0.5"
+ }
+ },
+ "Microsoft.FeatureManagement.AspNetCore": {
+ "type": "CentralTransitive",
+ "requested": "[4.4.0, )",
+ "resolved": "4.4.0",
+ "contentHash": "jE5KxeEDUnUsx1w9uOFV5cOfM4E2mg7kf07328xO1x4JdoS0jwkD55nMjTlUPu90ynYX1oCBGC+FHK6ZoqRpxA==",
+ "dependencies": {
+ "Microsoft.FeatureManagement": "4.4.0"
+ }
+ },
+ "Microsoft.OpenApi": {
+ "type": "CentralTransitive",
+ "requested": "[2.7.0, )",
+ "resolved": "2.7.0",
+ "contentHash": "b9xmpnmjq6p+HqF3uWG7u7/PlB38t/UB5UtXdi6xEAP9ZJGKHneYyjMGzBflB1rpLxYEcU6KRme+cz5wNPlxqA=="
+ },
+ "Npgsql": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.2, )",
+ "resolved": "10.0.2",
+ "contentHash": "q5RfBI+wywJSFUNDE1L4ZbHEHCFTblo8Uf6A6oe4feOUFYiUQXyAf9GBh5qEZpvJaHiEbpBPkQumjEhXCJxdrg==",
+ "dependencies": {
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.0"
+ }
+ },
+ "Npgsql.EntityFrameworkCore.PostgreSQL": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.1, )",
+ "resolved": "10.0.1",
+ "contentHash": "P6EwH0Q4xkaA264iNZDqCPhWt8pscfUGxXazDQg4noBfqjoOlk4hKWfvBjF9ZX3R/9JybRmmJfmxr2iBMj0EpA==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore": "[10.0.4, 11.0.0)",
+ "Microsoft.EntityFrameworkCore.Relational": "[10.0.4, 11.0.0)",
+ "Npgsql": "10.0.2"
+ }
+ },
+ "RabbitMQ.Client": {
+ "type": "CentralTransitive",
+ "requested": "[7.2.1, )",
+ "resolved": "7.2.1",
+ "contentHash": "YKXEfg9fVQiTKgZlvIhAfPSFaamEgi8DsQmisCH0IAsU4FYLrtoguDrDj6JtJVGUt40QPnBLRH6fTQcAC4qsOg==",
+ "dependencies": {
+ "System.Threading.RateLimiting": "8.0.0"
+ }
+ },
+ "Rebus": {
+ "type": "CentralTransitive",
+ "requested": "[8.9.0, )",
+ "resolved": "8.9.0",
+ "contentHash": "UaPGZuXIL4J5GUDA05JzEEzuPMEXY0CoF92nC6bsFBPvwoYPQ0uKyH2vKqdV80CW7cjbwBgDlEZ7R9hO9b59XA==",
+ "dependencies": {
+ "Newtonsoft.Json": "13.0.4"
+ }
+ },
+ "Rebus.RabbitMq": {
+ "type": "CentralTransitive",
+ "requested": "[10.1.1, )",
+ "resolved": "10.1.1",
+ "contentHash": "66pUp4hfaYWfQEDOiVcuZQnPF4XFHyJ5KCfwCm18e3Dnr936Iog48KrN8Mp8QyRQ2tiNpzdjSATQLKEZpSk11A==",
+ "dependencies": {
+ "RabbitMq.Client": "7.1.2",
+ "rebus": "8.9.0"
+ }
+ },
+ "Rebus.ServiceProvider": {
+ "type": "CentralTransitive",
+ "requested": "[10.7.2, )",
+ "resolved": "10.7.2",
+ "contentHash": "Qa8sKt1i9Fy/zCw5GwAUsfT+lt4BvkIgYh8sRJ6fvqJWoedS//pfcyiKUUb0wL3C5Wrpi3U+vRud5DCbMHaFIw==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection": "[8.0.0, 11.0.0)",
+ "Microsoft.Extensions.Hosting.Abstractions": "[6.0.0, 11.0.0)",
+ "Microsoft.Extensions.Logging.Abstractions": "[6.0.0, 11.0.0)",
+ "Rebus": "8.9.0"
+ }
+ },
+ "Scrutor": {
+ "type": "CentralTransitive",
+ "requested": "[7.0.0, )",
+ "resolved": "7.0.0",
+ "contentHash": "wHWaroody48jnlLoq/REwUltIFLxplXyHTP+sttrc8P7+jkiVqf38afDidJNv4qgD/6zz2NKOYg06xLZ1bG7wQ==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0",
+ "Microsoft.Extensions.DependencyModel": "10.0.0"
+ }
+ },
+ "Serilog": {
+ "type": "CentralTransitive",
+ "requested": "[4.3.1, )",
+ "resolved": "4.3.1",
+ "contentHash": "savYe7h5yRlkqBVOwP8cIRDOdqKiPmYCU4W87JH38sBmcKD5EBoXvQIw6bNEvZ/pTe1gsiye3VFCzBsoppGkXQ=="
+ },
+ "Serilog.AspNetCore": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.0, )",
+ "resolved": "10.0.0",
+ "contentHash": "a/cNa1mY4On1oJlfGG1wAvxjp5g7OEzk/Jf/nm7NF9cWoE7KlZw1GldrifUBWm9oKibHkR7Lg/l5jy3y7ACR8w==",
+ "dependencies": {
+ "Serilog": "4.3.0",
+ "Serilog.Extensions.Hosting": "10.0.0",
+ "Serilog.Formatting.Compact": "3.0.0",
+ "Serilog.Settings.Configuration": "10.0.0",
+ "Serilog.Sinks.Console": "6.1.1",
+ "Serilog.Sinks.Debug": "3.0.0",
+ "Serilog.Sinks.File": "7.0.0"
+ }
+ },
+ "Serilog.Enrichers.Environment": {
+ "type": "CentralTransitive",
+ "requested": "[3.0.1, )",
+ "resolved": "3.0.1",
+ "contentHash": "9BqCE4C9FF+/rJb/CsQwe7oVf44xqkOvMwX//CUxvUR25lFL4tSS6iuxE5eW07quby1BAyAEP+vM6TWsnT3iqw==",
+ "dependencies": {
+ "Serilog": "4.0.0"
+ }
+ },
+ "Serilog.Enrichers.Process": {
+ "type": "CentralTransitive",
+ "requested": "[3.0.0, )",
+ "resolved": "3.0.0",
+ "contentHash": "/wPYz2PDCJGSHNI+Z0PAacZvrgZgrGduWqLXeC2wvW6pgGM/Bi45JrKy887MRcRPHIZVU0LAlkmJ7TkByC0boQ==",
+ "dependencies": {
+ "Serilog": "4.0.0"
+ }
+ },
+ "Serilog.Enrichers.Thread": {
+ "type": "CentralTransitive",
+ "requested": "[4.0.0, )",
+ "resolved": "4.0.0",
+ "contentHash": "C7BK25a1rhUyr+Tp+1BYcVlBJq7M2VCHlIgnwoIUVJcicM9jYcvQK18+OeHiXw7uLPSjqWxJIp1EfaZ/RGmEwA==",
+ "dependencies": {
+ "Serilog": "4.0.0"
+ }
+ },
+ "Serilog.Settings.Configuration": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.0, )",
+ "resolved": "10.0.0",
+ "contentHash": "LNq+ibS1sbhTqPV1FIE69/9AJJbfaOhnaqkzcjFy95o+4U+STsta9mi97f1smgXsWYKICDeGUf8xUGzd/52/uA==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Binder": "10.0.0",
+ "Microsoft.Extensions.DependencyModel": "10.0.0",
+ "Serilog": "4.3.0"
+ }
+ },
+ "Serilog.Sinks.Console": {
+ "type": "CentralTransitive",
+ "requested": "[6.1.1, )",
+ "resolved": "6.1.1",
+ "contentHash": "8jbqgjUyZlfCuSTaJk6lOca465OndqOz3KZP6Cryt/IqZYybyBu7GP0fE/AXBzrrQB3EBmQntBFAvMVz1COvAA==",
+ "dependencies": {
+ "Serilog": "4.0.0"
+ }
+ },
+ "Serilog.Sinks.Seq": {
+ "type": "CentralTransitive",
+ "requested": "[9.0.0, )",
+ "resolved": "9.0.0",
+ "contentHash": "aNU8A0K322q7+voPNmp1/qNPH+9QK8xvM1p72sMmCG0wGlshFzmtDW9QnVSoSYCj0MgQKcMOlgooovtBhRlNHw==",
+ "dependencies": {
+ "Serilog": "4.2.0",
+ "Serilog.Sinks.File": "6.0.0"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Modules/Ratings/Application/Commands/CreateReviewCommand.cs b/src/Modules/Ratings/Application/Commands/CreateReviewCommand.cs
new file mode 100644
index 000000000..9a7a83e8a
--- /dev/null
+++ b/src/Modules/Ratings/Application/Commands/CreateReviewCommand.cs
@@ -0,0 +1,14 @@
+using MeAjudaAi.Shared.Commands;
+using MeAjudaAi.Shared.Utilities;
+
+namespace MeAjudaAi.Modules.Ratings.Application.Commands;
+
+public record CreateReviewCommand(
+ Guid ProviderId,
+ Guid CustomerId,
+ int Rating,
+ string? Comment
+) : ICommand
+{
+ public Guid CorrelationId { get; init; } = UuidGenerator.NewId();
+}
diff --git a/src/Modules/Ratings/Application/Extensions.cs b/src/Modules/Ratings/Application/Extensions.cs
new file mode 100644
index 000000000..4dd7a33ce
--- /dev/null
+++ b/src/Modules/Ratings/Application/Extensions.cs
@@ -0,0 +1,19 @@
+using MeAjudaAi.Modules.Ratings.Application.Commands;
+using MeAjudaAi.Modules.Ratings.Application.Handlers;
+using MeAjudaAi.Modules.Ratings.Application.Services;
+using MeAjudaAi.Shared.Commands;
+using Microsoft.Extensions.DependencyInjection;
+using System.Reflection;
+
+namespace MeAjudaAi.Modules.Ratings.Application;
+
+public static class Extensions
+{
+ public static IServiceCollection AddApplication(this IServiceCollection services)
+ {
+ services.AddScoped();
+ services.AddScoped, CreateReviewCommandHandler>();
+
+ return services;
+ }
+}
diff --git a/src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs b/src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs
new file mode 100644
index 000000000..99a95d809
--- /dev/null
+++ b/src/Modules/Ratings/Application/Handlers/CreateReviewCommandHandler.cs
@@ -0,0 +1,70 @@
+using MeAjudaAi.Modules.Ratings.Application.Commands;
+using MeAjudaAi.Modules.Ratings.Application.Services;
+using MeAjudaAi.Modules.Ratings.Domain.Entities;
+using MeAjudaAi.Modules.Ratings.Domain.Exceptions;
+using MeAjudaAi.Modules.Ratings.Domain.Repositories;
+using MeAjudaAi.Shared.Commands;
+using Microsoft.Extensions.Logging;
+
+namespace MeAjudaAi.Modules.Ratings.Application.Handlers;
+
+public sealed class CreateReviewCommandHandler(
+ IReviewRepository repository,
+ IContentModerator contentModerator,
+ ILogger logger) : ICommandHandler
+{
+ public async Task HandleAsync(CreateReviewCommand command, CancellationToken cancellationToken = default)
+ {
+ logger.LogInformation("Creating review for provider {ProviderId} by customer {CustomerId}", command.ProviderId, command.CustomerId);
+
+ // Validar duplicidade (UX check antecipado)
+ var existingReview = await repository.GetByProviderAndCustomerAsync(command.ProviderId, command.CustomerId, cancellationToken);
+ if (existingReview != null)
+ {
+ logger.LogWarning("Customer {CustomerId} already reviewed provider {ProviderId}", command.CustomerId, command.ProviderId);
+ throw new InvalidOperationException("Você já avaliou este prestador.");
+ }
+
+ var review = Review.Create(
+ command.ProviderId,
+ command.CustomerId,
+ command.Rating,
+ command.Comment);
+
+ // Moderação Automática e Regras de Auto-aprovação
+ // Short-circuit: Se não houver comentário, aplicar regra de auto-aprovação direta
+ if (string.IsNullOrWhiteSpace(command.Comment))
+ {
+ if (command.Rating >= 4)
+ {
+ review.Approve();
+ }
+ }
+ else
+ {
+ // Se houver comentário, passar pela moderação
+ var isClean = contentModerator.IsClean(command.Comment);
+ if (!isClean)
+ {
+ logger.LogWarning("Review {ReviewId} flagged for moderation due to inappropriate content", review.Id.Value);
+ review.MarkAsFlagged();
+ }
+ // Se isClean for true, o status permanece Pending por padrão para moderação manual
+ }
+
+ try
+ {
+ await repository.AddAsync(review, cancellationToken);
+ }
+ catch (DuplicateReviewException ex)
+ {
+ logger.LogWarning(ex, "Duplicate review detected at persistence level for Provider {ProviderId} and Customer {CustomerId}",
+ command.ProviderId, command.CustomerId);
+ throw new InvalidOperationException("Você já avaliou este prestador.");
+ }
+
+ logger.LogInformation("Review {ReviewId} created with status {Status}", review.Id.Value, review.Status);
+
+ return review.Id.Value;
+ }
+}
diff --git a/src/Modules/Ratings/Application/MeAjudaAi.Modules.Ratings.Application.csproj b/src/Modules/Ratings/Application/MeAjudaAi.Modules.Ratings.Application.csproj
new file mode 100644
index 000000000..dcd614aeb
--- /dev/null
+++ b/src/Modules/Ratings/Application/MeAjudaAi.Modules.Ratings.Application.csproj
@@ -0,0 +1,27 @@
+
+
+
+ net10.0
+ enable
+ enable
+
+
+
+
+ <_Parameter1>MeAjudaAi.Modules.Ratings.Tests
+
+
+ <_Parameter1>MeAjudaAi.Modules.Ratings.Infrastructure
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Modules/Ratings/Application/Services/ContentModerator.cs b/src/Modules/Ratings/Application/Services/ContentModerator.cs
new file mode 100644
index 000000000..95ba9e7cf
--- /dev/null
+++ b/src/Modules/Ratings/Application/Services/ContentModerator.cs
@@ -0,0 +1,23 @@
+using System.Text.RegularExpressions;
+
+namespace MeAjudaAi.Modules.Ratings.Application.Services;
+
+public interface IContentModerator
+{
+ bool IsClean(string? content);
+}
+
+public class ContentModerator : IContentModerator
+{
+ private static readonly string[] BadWords = ["idiota", "burro", "lixo", "golpe", "fake"];
+ private static readonly List Patterns = BadWords
+ .Select(word => new Regex($@"\b{Regex.Escape(word)}\b", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant | RegexOptions.Compiled))
+ .ToList();
+
+ public bool IsClean(string? content)
+ {
+ if (string.IsNullOrWhiteSpace(content)) return true;
+
+ return !Patterns.Any(regex => regex.IsMatch(content));
+ }
+}
diff --git a/src/Modules/Ratings/Application/packages.lock.json b/src/Modules/Ratings/Application/packages.lock.json
new file mode 100644
index 000000000..960653ac9
--- /dev/null
+++ b/src/Modules/Ratings/Application/packages.lock.json
@@ -0,0 +1,827 @@
+{
+ "version": 2,
+ "dependencies": {
+ "net10.0": {
+ "Microsoft.EntityFrameworkCore": {
+ "type": "Direct",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "9tNBmK3EpYVGRQLiqP+bqK2m+TD0Gv//4vCzR7ZOgl4FWzCFyOpYdIVka13M4kcBdPdSJcs3wbHr3rmzOqbIMA==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore.Abstractions": "10.0.5",
+ "Microsoft.EntityFrameworkCore.Analyzers": "10.0.5",
+ "Microsoft.Extensions.Caching.Memory": "10.0.5",
+ "Microsoft.Extensions.Logging": "10.0.5"
+ }
+ },
+ "Npgsql": {
+ "type": "Direct",
+ "requested": "[10.0.2, )",
+ "resolved": "10.0.2",
+ "contentHash": "q5RfBI+wywJSFUNDE1L4ZbHEHCFTblo8Uf6A6oe4feOUFYiUQXyAf9GBh5qEZpvJaHiEbpBPkQumjEhXCJxdrg==",
+ "dependencies": {
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.0"
+ }
+ },
+ "SonarAnalyzer.CSharp": {
+ "type": "Direct",
+ "requested": "[10.23.0.137933, )",
+ "resolved": "10.23.0.137933",
+ "contentHash": "K8CwUkB4PlUcMfLqGCj1aviVfW9AufZH2K/TjgPGYdj4gosV/kcyxpBR0jaCG8argC6dfxJBbSS6xXJ59lFWgA=="
+ },
+ "Asp.Versioning.Abstractions": {
+ "type": "Transitive",
+ "resolved": "8.1.0",
+ "contentHash": "mpeNZyMdvrHztJwR1sXIUQ+3iioEU97YMBnFA9WLbsPOYhGwDJnqJMmEd8ny7kcmS9OjTHoEuX/bSXXY3brIFA==",
+ "dependencies": {
+ "Microsoft.Extensions.Primitives": "8.0.0"
+ }
+ },
+ "Dapper.AOT": {
+ "type": "Transitive",
+ "resolved": "1.0.48",
+ "contentHash": "rsLM3yKr4g+YKKox9lhc8D+kz67P7Q9+xdyn1LmCsoYr1kYpJSm+Nt6slo5UrfUrcTiGJ57zUlyO8XUdV7G7iA=="
+ },
+ "Hangfire.NetCore": {
+ "type": "Transitive",
+ "resolved": "1.8.23",
+ "contentHash": "SmvUJF/u5MCP666R5Y1V+GntqBc4RCWJqn5ztMMN67d53Cx5cuaWR0YNLMrabjylwLarFYJ7EdR9RnGEZzp/dg==",
+ "dependencies": {
+ "Hangfire.Core": "[1.8.23]",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "3.0.0",
+ "Microsoft.Extensions.Hosting.Abstractions": "3.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "3.0.0"
+ }
+ },
+ "Humanizer.Core": {
+ "type": "Transitive",
+ "resolved": "2.14.1",
+ "contentHash": "lQKvtaTDOXnoVJ20ibTuSIOf2i0uO0MPbDhd1jm238I+U/2ZnRENj0cktKZhtchBMtCUSRQ5v4xBCUbKNmyVMw=="
+ },
+ "Microsoft.Bcl.TimeProvider": {
+ "type": "Transitive",
+ "resolved": "8.0.1",
+ "contentHash": "C7kWHJnMRY7EvJev2S8+yJHZ1y7A4ZlLbA4NE+O23BDIAN5mHeqND1m+SKv1ChRS5YlCDW7yAMUe7lttRsJaAA=="
+ },
+ "Microsoft.CodeAnalysis.Analyzers": {
+ "type": "Transitive",
+ "resolved": "3.11.0",
+ "contentHash": "v/EW3UE8/lbEYHoC2Qq7AR/DnmvpgdtAMndfQNmpuIMx/Mto8L5JnuCfdBYtgvalQOtfNCnxFejxuRrryvUTsg=="
+ },
+ "Microsoft.CodeAnalysis.Common": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "ZXRAdvH6GiDeHRyd3q/km8Z44RoM6FBWHd+gen/la81mVnAdHTEsEkO5J0TCNXBymAcx5UYKt5TvgKBhaLJEow==",
+ "dependencies": {
+ "Microsoft.CodeAnalysis.Analyzers": "3.11.0"
+ }
+ },
+ "Microsoft.CodeAnalysis.CSharp": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "5DSyJ9bk+ATuDy7fp2Zt0mJStDVKbBoiz1DyfAwSa+k4H4IwykAUcV3URelw5b8/iVbfSaOwkwmPUZH6opZKCw==",
+ "dependencies": {
+ "Microsoft.CodeAnalysis.Analyzers": "3.11.0",
+ "Microsoft.CodeAnalysis.Common": "[5.0.0]"
+ }
+ },
+ "Microsoft.CodeAnalysis.CSharp.Workspaces": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "Al/Q8B+yO8odSqGVpSvrShMFDvlQdIBU//F3E6Rb0YdiLSALE9wh/pvozPNnfmh5HDnvU+mkmSjpz4hQO++jaA==",
+ "dependencies": {
+ "Humanizer.Core": "2.14.1",
+ "Microsoft.CodeAnalysis.Analyzers": "3.11.0",
+ "Microsoft.CodeAnalysis.CSharp": "[5.0.0]",
+ "Microsoft.CodeAnalysis.Common": "[5.0.0]",
+ "Microsoft.CodeAnalysis.Workspaces.Common": "[5.0.0]",
+ "System.Composition": "9.0.0"
+ }
+ },
+ "Microsoft.CodeAnalysis.Workspaces.Common": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "ZbUmIvT6lqTNKiv06Jl5wf0MTMi1vQ1oH7ou4CLcs2C/no/L7EhP3T8y3XXvn9VbqMcJaJnEsNA1jwYUMgc5jg==",
+ "dependencies": {
+ "Humanizer.Core": "2.14.1",
+ "Microsoft.CodeAnalysis.Analyzers": "3.11.0",
+ "Microsoft.CodeAnalysis.Common": "[5.0.0]",
+ "System.Composition": "9.0.0"
+ }
+ },
+ "Microsoft.CodeAnalysis.Workspaces.MSBuild": {
+ "type": "Transitive",
+ "resolved": "5.0.0",
+ "contentHash": "/G+LVoAGMz6Ae8nm+PGLxSw+F5RjYx/J7irbTO5uKAPw1bxHyQJLc/YOnpDxt+EpPtYxvC9wvBsg/kETZp1F9Q==",
+ "dependencies": {
+ "Humanizer.Core": "2.14.1",
+ "Microsoft.Build.Framework": "17.11.31",
+ "Microsoft.CodeAnalysis.Analyzers": "3.11.0",
+ "Microsoft.CodeAnalysis.Workspaces.Common": "[5.0.0]",
+ "Microsoft.Extensions.DependencyInjection": "9.0.0",
+ "Microsoft.Extensions.Logging": "9.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "9.0.0",
+ "Microsoft.Extensions.Options": "9.0.0",
+ "Microsoft.Extensions.Primitives": "9.0.0",
+ "Microsoft.VisualStudio.SolutionPersistence": "1.0.52",
+ "Newtonsoft.Json": "13.0.3",
+ "System.Composition": "9.0.0"
+ }
+ },
+ "Microsoft.EntityFrameworkCore.Abstractions": {
+ "type": "Transitive",
+ "resolved": "10.0.5",
+ "contentHash": "32c58Rnm47Qvhimawf67KO9PytgPz3QoWye7Abapt0Yocw/JnzMiSNj/pRoIKyn8Jxypkv86zxKD4Q/zNTc0Ag=="
+ },
+ "Microsoft.EntityFrameworkCore.Analyzers": {
+ "type": "Transitive",
+ "resolved": "10.0.5",
+ "contentHash": "ipC4u1VojgEfoIZhtbS2Sx5IluJTP/Jf1hz3yGsxGBgSukYY/CquI6rAjxn5H58CZgVn36qcuPPtNMwZ0AUzMg=="
+ },
+ "Microsoft.Extensions.Caching.Memory": {
+ "type": "Transitive",
+ "resolved": "10.0.5",
+ "contentHash": "jUEXmkBUPdOS/MP9areK/sbKhdklq9+tEhvwfxGalZVnmyLUO5rrheNNutUBtvbZ7J8ECkG7/r2KXi/IFC06cA==",
+ "dependencies": {
+ "Microsoft.Extensions.Caching.Abstractions": "10.0.5",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Options": "10.0.5",
+ "Microsoft.Extensions.Primitives": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Diagnostics.HealthChecks": {
+ "type": "Transitive",
+ "resolved": "8.0.11",
+ "contentHash": "zLgN22Zp9pk8RHlwssRTexw4+a6wqOnKWN+VejdPn5Yhjql4XiBhkFo35Nu8mmqHIk/UEmmCnMGLWq75aFfkOw==",
+ "dependencies": {
+ "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": "8.0.11",
+ "Microsoft.Extensions.Hosting.Abstractions": "8.0.1",
+ "Microsoft.Extensions.Logging.Abstractions": "8.0.2",
+ "Microsoft.Extensions.Options": "8.0.2"
+ }
+ },
+ "Microsoft.Extensions.Diagnostics.HealthChecks.Abstractions": {
+ "type": "Transitive",
+ "resolved": "8.0.11",
+ "contentHash": "So3JUdRxozRjvQ3cxU6F3nI/i4emDnjane6yMYcJhvTTTu29ltlIdoXjkFGRceIWz8yKvuEpzXItZ0x5GvN2nQ=="
+ },
+ "Microsoft.Extensions.FileProviders.Abstractions": {
+ "type": "Transitive",
+ "resolved": "10.0.5",
+ "contentHash": "nCBmCx0Xemlu65ZiWMcXbvfvtznKxf4/YYKF9R28QkqdI9lTikedGqzJ28/xmdGGsxUnsP5/3TQGpiPwVjK0dA==",
+ "dependencies": {
+ "Microsoft.Extensions.Primitives": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Primitives": {
+ "type": "Transitive",
+ "resolved": "10.0.5",
+ "contentHash": "/HUHJ0tw/LQvD0DZrz50eQy/3z7PfX7WWEaXnjKTV9/TNdcgFlNTZGo49QhS7PTmhDqMyHRMqAXSBxLh0vso4g=="
+ },
+ "Microsoft.FeatureManagement": {
+ "type": "Transitive",
+ "resolved": "4.4.0",
+ "contentHash": "qxvGAv9WJHYfOpixWywJTa1WNTPy5MbQiv+O+UlE6E/LVofiM1+YRR6m41zsHIbAGm1S0PQ0QFuAsOw9DkoKsg==",
+ "dependencies": {
+ "Microsoft.Bcl.TimeProvider": "8.0.1",
+ "Microsoft.Extensions.Caching.Memory": "8.0.1",
+ "Microsoft.Extensions.Configuration": "8.0.0",
+ "Microsoft.Extensions.Configuration.Binder": "8.0.2",
+ "Microsoft.Extensions.Logging": "8.0.1"
+ }
+ },
+ "Microsoft.VisualStudio.SolutionPersistence": {
+ "type": "Transitive",
+ "resolved": "1.0.52",
+ "contentHash": "oNv2JtYXhpdJrX63nibx1JT3uCESOBQ1LAk7Dtz/sr0+laW0KRM6eKp4CZ3MHDR2siIkKsY8MmUkeP5DKkQQ5w=="
+ },
+ "Mono.TextTemplating": {
+ "type": "Transitive",
+ "resolved": "3.0.0",
+ "contentHash": "YqueG52R/Xej4VVbKuRIodjiAhV0HR/XVbLbNrJhCZnzjnSjgMJ/dCdV0akQQxavX6hp/LC6rqLGLcXeQYU7XA==",
+ "dependencies": {
+ "System.CodeDom": "6.0.0"
+ }
+ },
+ "Newtonsoft.Json": {
+ "type": "Transitive",
+ "resolved": "13.0.4",
+ "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A=="
+ },
+ "Pipelines.Sockets.Unofficial": {
+ "type": "Transitive",
+ "resolved": "2.2.8",
+ "contentHash": "zG2FApP5zxSx6OcdJQLbZDk2AVlN2BNQD6MorwIfV6gVj0RRxWPEp2LXAxqDGZqeNV1Zp0BNPcNaey/GXmTdvQ=="
+ },
+ "Serilog.Extensions.Hosting": {
+ "type": "Transitive",
+ "resolved": "10.0.0",
+ "contentHash": "E7juuIc+gzoGxgzFooFgAV8g9BfiSXNKsUok9NmEpyAXg2odkcPsMa/Yo4axkJRlh0se7mkYQ1GXDaBemR+b6w==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0",
+ "Microsoft.Extensions.Hosting.Abstractions": "10.0.0",
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.0",
+ "Serilog": "4.3.0",
+ "Serilog.Extensions.Logging": "10.0.0"
+ }
+ },
+ "Serilog.Extensions.Logging": {
+ "type": "Transitive",
+ "resolved": "10.0.0",
+ "contentHash": "vx0kABKl2dWbBhhqAfTOk53/i8aV/5VaT3a6il9gn72Wqs2pM7EK2OB6No6xdqK2IaY6Zf9gdjLuK9BVa2rT+Q==",
+ "dependencies": {
+ "Microsoft.Extensions.Logging": "10.0.0",
+ "Serilog": "4.2.0"
+ }
+ },
+ "Serilog.Formatting.Compact": {
+ "type": "Transitive",
+ "resolved": "3.0.0",
+ "contentHash": "wQsv14w9cqlfB5FX2MZpNsTawckN4a8dryuNGbebB/3Nh1pXnROHZov3swtu3Nj5oNG7Ba+xdu7Et/ulAUPanQ==",
+ "dependencies": {
+ "Serilog": "4.0.0"
+ }
+ },
+ "Serilog.Sinks.Debug": {
+ "type": "Transitive",
+ "resolved": "3.0.0",
+ "contentHash": "4BzXcdrgRX7wde9PmHuYd9U6YqycCC28hhpKonK7hx0wb19eiuRj16fPcPSVp0o/Y1ipJuNLYQ00R3q2Zs8FDA==",
+ "dependencies": {
+ "Serilog": "4.0.0"
+ }
+ },
+ "Serilog.Sinks.File": {
+ "type": "Transitive",
+ "resolved": "7.0.0",
+ "contentHash": "fKL7mXv7qaiNBUC71ssvn/dU0k9t0o45+qm2XgKAlSt19xF+ijjxyA3R6HmCgfKEKwfcfkwWjayuQtRueZFkYw==",
+ "dependencies": {
+ "Serilog": "4.2.0"
+ }
+ },
+ "StackExchange.Redis": {
+ "type": "Transitive",
+ "resolved": "2.7.27",
+ "contentHash": "Uqc2OQHglqj9/FfGQ6RkKFkZfHySfZlfmbCl+hc+u2I/IqunfelQ7QJi7ZhvAJxUtu80pildVX6NPLdDaUffOw==",
+ "dependencies": {
+ "Microsoft.Extensions.Logging.Abstractions": "6.0.0",
+ "Pipelines.Sockets.Unofficial": "2.2.8"
+ }
+ },
+ "System.CodeDom": {
+ "type": "Transitive",
+ "resolved": "6.0.0",
+ "contentHash": "CPc6tWO1LAer3IzfZufDBRL+UZQcj5uS207NHALQzP84Vp/z6wF0Aa0YZImOQY8iStY0A2zI/e3ihKNPfUm8XA=="
+ },
+ "System.Composition": {
+ "type": "Transitive",
+ "resolved": "9.0.0",
+ "contentHash": "3Djj70fFTraOarSKmRnmRy/zm4YurICm+kiCtI0dYRqGJnLX6nJ+G3WYuFJ173cAPax/gh96REcbNiVqcrypFQ==",
+ "dependencies": {
+ "System.Composition.AttributedModel": "9.0.0",
+ "System.Composition.Convention": "9.0.0",
+ "System.Composition.Hosting": "9.0.0",
+ "System.Composition.Runtime": "9.0.0",
+ "System.Composition.TypedParts": "9.0.0"
+ }
+ },
+ "System.Composition.AttributedModel": {
+ "type": "Transitive",
+ "resolved": "9.0.0",
+ "contentHash": "iri00l/zIX9g4lHMY+Nz0qV1n40+jFYAmgsaiNn16xvt2RDwlqByNG4wgblagnDYxm3YSQQ0jLlC/7Xlk9CzyA=="
+ },
+ "System.Composition.Convention": {
+ "type": "Transitive",
+ "resolved": "9.0.0",
+ "contentHash": "+vuqVP6xpi582XIjJi6OCsIxuoTZfR0M7WWufk3uGDeCl3wGW6KnpylUJ3iiXdPByPE0vR5TjJgR6hDLez4FQg==",
+ "dependencies": {
+ "System.Composition.AttributedModel": "9.0.0"
+ }
+ },
+ "System.Composition.Hosting": {
+ "type": "Transitive",
+ "resolved": "9.0.0",
+ "contentHash": "OFqSeFeJYr7kHxDfaViGM1ymk7d4JxK//VSoNF9Ux0gpqkLsauDZpu89kTHHNdCWfSljbFcvAafGyBoY094btQ==",
+ "dependencies": {
+ "System.Composition.Runtime": "9.0.0"
+ }
+ },
+ "System.Composition.Runtime": {
+ "type": "Transitive",
+ "resolved": "9.0.0",
+ "contentHash": "w1HOlQY1zsOWYussjFGZCEYF2UZXgvoYnS94NIu2CBnAGMbXFAX8PY8c92KwUItPmowal68jnVLBCzdrWLeEKA=="
+ },
+ "System.Composition.TypedParts": {
+ "type": "Transitive",
+ "resolved": "9.0.0",
+ "contentHash": "aRZlojCCGEHDKqh43jaDgaVpYETsgd7Nx4g1zwLKMtv4iTo0627715ajEFNpEEBTgLmvZuv8K0EVxc3sM4NWJA==",
+ "dependencies": {
+ "System.Composition.AttributedModel": "9.0.0",
+ "System.Composition.Hosting": "9.0.0",
+ "System.Composition.Runtime": "9.0.0"
+ }
+ },
+ "System.Threading.RateLimiting": {
+ "type": "Transitive",
+ "resolved": "8.0.0",
+ "contentHash": "7mu9v0QDv66ar3DpGSZHg9NuNcxDaaAcnMULuZlaTpP9+hwXhrxNGsF5GmLkSHxFdb5bBc1TzeujsRgTrPWi+Q=="
+ },
+ "meajudaai.contracts": {
+ "type": "Project",
+ "dependencies": {
+ "FluentValidation": "[12.1.1, )"
+ }
+ },
+ "meajudaai.modules.ratings.domain": {
+ "type": "Project",
+ "dependencies": {
+ "MeAjudaAi.Shared": "[1.0.0, )"
+ }
+ },
+ "meajudaai.shared": {
+ "type": "Project",
+ "dependencies": {
+ "Asp.Versioning.Mvc": "[8.1.1, )",
+ "Asp.Versioning.Mvc.ApiExplorer": "[8.1.1, )",
+ "AspNetCore.HealthChecks.Npgsql": "[9.0.0, )",
+ "AspNetCore.HealthChecks.Redis": "[9.0.0, )",
+ "Dapper": "[2.1.72, )",
+ "EFCore.NamingConventions": "[10.0.1, )",
+ "FluentValidation": "[12.1.1, )",
+ "FluentValidation.DependencyInjectionExtensions": "[12.1.1, )",
+ "Hangfire.AspNetCore": "[1.8.23, )",
+ "Hangfire.Core": "[1.8.23, )",
+ "Hangfire.PostgreSql": "[1.21.1, )",
+ "MeAjudaAi.Contracts": "[1.0.0, )",
+ "Microsoft.AspNetCore.OpenApi": "[10.0.5, )",
+ "Microsoft.EntityFrameworkCore": "[10.0.5, )",
+ "Microsoft.EntityFrameworkCore.Design": "[10.0.5, )",
+ "Microsoft.Extensions.Caching.Hybrid": "[10.4.0, )",
+ "Microsoft.Extensions.Caching.StackExchangeRedis": "[10.0.5, )",
+ "Microsoft.FeatureManagement.AspNetCore": "[4.4.0, )",
+ "Npgsql.EntityFrameworkCore.PostgreSQL": "[10.0.1, )",
+ "RabbitMQ.Client": "[7.2.1, )",
+ "Rebus": "[8.9.0, )",
+ "Rebus.RabbitMq": "[10.1.1, )",
+ "Rebus.ServiceProvider": "[10.7.2, )",
+ "Scrutor": "[7.0.0, )",
+ "Serilog": "[4.3.1, )",
+ "Serilog.AspNetCore": "[10.0.0, )",
+ "Serilog.Enrichers.Environment": "[3.0.1, )",
+ "Serilog.Enrichers.Process": "[3.0.0, )",
+ "Serilog.Enrichers.Thread": "[4.0.0, )",
+ "Serilog.Settings.Configuration": "[10.0.0, )",
+ "Serilog.Sinks.Console": "[6.1.1, )",
+ "Serilog.Sinks.Seq": "[9.0.0, )"
+ }
+ },
+ "Asp.Versioning.Http": {
+ "type": "CentralTransitive",
+ "requested": "[8.1.1, )",
+ "resolved": "8.1.1",
+ "contentHash": "1D/Mzq1MSUSQe1eCY6GeEsu+tIlpzoWZxZkhlcw/uvtVoTrwO+BMl0fSj/XX8oZN1DutWTZqHDHgyDRq+IeSdQ==",
+ "dependencies": {
+ "Asp.Versioning.Abstractions": "8.1.0"
+ }
+ },
+ "Asp.Versioning.Mvc": {
+ "type": "CentralTransitive",
+ "requested": "[8.1.1, )",
+ "resolved": "8.1.1",
+ "contentHash": "mkJv6eAdlbHbqTdrUcfEYFoZuGL6HoR7O+Lfsvivixp7N5BNhfCFPPOwsBzdIiH1qzdJXyJf+C+DZ08j27PMPg==",
+ "dependencies": {
+ "Asp.Versioning.Http": "8.1.1"
+ }
+ },
+ "Asp.Versioning.Mvc.ApiExplorer": {
+ "type": "CentralTransitive",
+ "requested": "[8.1.1, )",
+ "resolved": "8.1.1",
+ "contentHash": "u8PrP6CjmurgIh0EDfg8Gc/GdpDHgkbv8OLKdxQcWb5W4sp0i9BcdbQlLvRcATjNIJUyr7ZmqXjmdsFfqnuy0g==",
+ "dependencies": {
+ "Asp.Versioning.Mvc": "8.1.1"
+ }
+ },
+ "AspNetCore.HealthChecks.NpgSql": {
+ "type": "CentralTransitive",
+ "requested": "[9.0.0, )",
+ "resolved": "9.0.0",
+ "contentHash": "npc58/AD5zuVxERdhCl2Kb7WnL37mwX42SJcXIwvmEig0/dugOLg3SIwtfvvh3TnvTwR/sk5LYNkkPaBdks61A==",
+ "dependencies": {
+ "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11",
+ "Npgsql": "8.0.3"
+ }
+ },
+ "AspNetCore.HealthChecks.Redis": {
+ "type": "CentralTransitive",
+ "requested": "[9.0.0, )",
+ "resolved": "9.0.0",
+ "contentHash": "yNH0h8GLRbAf+PU5HNVLZ5hNeyq9mDVmRKO9xuZsme/znUYoBJlQvI0gq45gaZNlLncCHkMhR4o90MuT+gxxPw==",
+ "dependencies": {
+ "Microsoft.Extensions.Diagnostics.HealthChecks": "8.0.11",
+ "StackExchange.Redis": "2.7.4"
+ }
+ },
+ "Dapper": {
+ "type": "CentralTransitive",
+ "requested": "[2.1.72, )",
+ "resolved": "2.1.72",
+ "contentHash": "ns4mGqQd9a/MhP8m6w556vVlZIa0/MfUu03zrxjZC/jlr1uVCsUac8bkdB+Fs98Llbd56rRSo1eZH5VVmeGZyw=="
+ },
+ "EFCore.NamingConventions": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.1, )",
+ "resolved": "10.0.1",
+ "contentHash": "Xs5k8XfNKPkkQSkGmZkmDI1je0prLTdxse+s8PgTFZxyBrlrTLzTBUTVJtQKSsbvu4y+luAv8DdtO5SALJE++A==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore": "[10.0.1, 11.0.0)",
+ "Microsoft.EntityFrameworkCore.Relational": "[10.0.1, 11.0.0)",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.1"
+ }
+ },
+ "FluentValidation": {
+ "type": "CentralTransitive",
+ "requested": "[12.1.1, )",
+ "resolved": "12.1.1",
+ "contentHash": "EPpkIe1yh1a0OXyC100oOA8WMbZvqUu5plwhvYcb7oSELfyUZzfxV48BLhvs3kKo4NwG7MGLNgy1RJiYtT8Dpw=="
+ },
+ "FluentValidation.DependencyInjectionExtensions": {
+ "type": "CentralTransitive",
+ "requested": "[12.1.1, )",
+ "resolved": "12.1.1",
+ "contentHash": "D0VXh4dtjjX2aQizuaa0g6R8X3U1JaVqJPfGCvLwZX9t/O2h7tkpbitbadQMfwcgSPdDbI2vDxuwRMv/Uf9dHA==",
+ "dependencies": {
+ "FluentValidation": "12.1.1",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0"
+ }
+ },
+ "Hangfire.AspNetCore": {
+ "type": "CentralTransitive",
+ "requested": "[1.8.23, )",
+ "resolved": "1.8.23",
+ "contentHash": "TXpOl7kX4xXq5bLEqqWCpt9zh3TaouDwtb3GDtzGHX5uSC2RaAqZzn2swevivx3Uki16slXIigiPtgr4TPKpsg==",
+ "dependencies": {
+ "Hangfire.NetCore": "[1.8.23]"
+ }
+ },
+ "Hangfire.Core": {
+ "type": "CentralTransitive",
+ "requested": "[1.8.23, )",
+ "resolved": "1.8.23",
+ "contentHash": "YCOTtF3NNOQI83PlfjeNDDBkofJDfdET2CwhfQsiVBwmsU6lP19QW9NVTIH9epl+MnOsyFC2G1RnlPSGV8F1FQ==",
+ "dependencies": {
+ "Newtonsoft.Json": "11.0.1"
+ }
+ },
+ "Hangfire.PostgreSql": {
+ "type": "CentralTransitive",
+ "requested": "[1.21.1, )",
+ "resolved": "1.21.1",
+ "contentHash": "hFNZAxv+1p72/XCZdImnH6ovCzZ2DKAMTOI8CReT0P3yw/k0b0YJP2teA18agNH1ZYInPzhtxGk8hx5n2cxbbQ==",
+ "dependencies": {
+ "Dapper": "2.0.123",
+ "Dapper.AOT": "1.0.48",
+ "Hangfire.Core": "1.8.0",
+ "Npgsql": "6.0.11"
+ }
+ },
+ "Microsoft.AspNetCore.OpenApi": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "vTcxIfOPyfFbYk1g8YcXJfkMnlEWVkSnnjxcZLy60zgwiHMRf2SnZR+9E4HlpwKxgE3yfKMOti8J6WfKuKsw6w==",
+ "dependencies": {
+ "Microsoft.OpenApi": "2.0.0"
+ }
+ },
+ "Microsoft.Build.Framework": {
+ "type": "CentralTransitive",
+ "requested": "[18.0.2, )",
+ "resolved": "18.0.2",
+ "contentHash": "sOSb+0J4G/jCBW/YqmRuL0eOMXgfw1KQLdC9TkbvfA5xs7uNm+PBQXJCOzSJGXtZcZrtXozcwxPmUiRUbmd7FA=="
+ },
+ "Microsoft.EntityFrameworkCore.Design": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "gm6f0cC2w/2tcd4GeZJqEMruTercpIJfO5sSAFLtqTqblDBHgAFk70xwshUIUVX4I6sZwdEUSd1YxoKFk1AL0w==",
+ "dependencies": {
+ "Humanizer.Core": "2.14.1",
+ "Microsoft.Build.Framework": "18.0.2",
+ "Microsoft.CodeAnalysis.CSharp": "5.0.0",
+ "Microsoft.CodeAnalysis.CSharp.Workspaces": "5.0.0",
+ "Microsoft.CodeAnalysis.Workspaces.MSBuild": "5.0.0",
+ "Microsoft.EntityFrameworkCore.Relational": "10.0.5",
+ "Microsoft.Extensions.Caching.Memory": "10.0.5",
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.5",
+ "Microsoft.Extensions.DependencyModel": "10.0.5",
+ "Microsoft.Extensions.Logging": "10.0.5",
+ "Mono.TextTemplating": "3.0.0",
+ "Newtonsoft.Json": "13.0.3"
+ }
+ },
+ "Microsoft.EntityFrameworkCore.Relational": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "uxmFjZEAB/KbsgWFSS4lLqkEHCfXxB2x0UcbiO4e5fCRpFFeTMSx/me6009nYJLu5IKlDwO1POh++P6RilFTDw==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore": "10.0.5",
+ "Microsoft.Extensions.Caching.Memory": "10.0.5",
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Logging": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Caching.Abstractions": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "k/QDdQ94/0Shi0KfU+e12m73jfQo+3JpErTtgpZfsCIqkvdEEO0XIx6R+iTbN55rNPaNhOqNY4/sB+jZ8XxVPw==",
+ "dependencies": {
+ "Microsoft.Extensions.Primitives": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Caching.Hybrid": {
+ "type": "CentralTransitive",
+ "requested": "[10.4.0, )",
+ "resolved": "10.4.0",
+ "contentHash": "4V+aMLQeU/p4VcIWIcvGro0L6HynmL2TrelL04Ce1iotP6T5+kjxuZQvl6P1ObSXIRPCbVXtQSt1NxK0fRIuag==",
+ "dependencies": {
+ "Microsoft.Extensions.Caching.Abstractions": "10.0.4",
+ "Microsoft.Extensions.Caching.Memory": "10.0.4",
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.4",
+ "Microsoft.Extensions.Options": "10.0.4"
+ }
+ },
+ "Microsoft.Extensions.Caching.StackExchangeRedis": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "zXb143/TpEKOLQuWGw2CkJgb9F4XXh2XbevMvppzsIHr1/pjML0zjc+vzXcpCV8YUwpW5NIaScZhzFSm621B3Q==",
+ "dependencies": {
+ "Microsoft.Extensions.Caching.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Options": "10.0.5",
+ "StackExchange.Redis": "2.7.27"
+ }
+ },
+ "Microsoft.Extensions.Configuration": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "8Rx5sqg04FttxrumyG6bmoRuFRgYzK6IVwF1i0/o0cXfKBdDeVpJejKHtJCMjyg9E/DNMVqpqOGe/tCT5gYvVA==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Primitives": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Configuration.Abstractions": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "P09QpTHjqHmCLQOTC+WyLkoRNxek4NIvfWt+TnU0etoDUSRxcltyd6+j/ouRbMdLR0j44GqGO+lhI2M4fAHG4g==",
+ "dependencies": {
+ "Microsoft.Extensions.Primitives": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Configuration.Binder": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "99Z4rjyXopb1MIazDSPcvwYCUdYNO01Cf1GUs2WUjIFAbkGmwzj2vPa2k+3pheJRV+YgNd2QqRKHAri0oBAU4Q==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration": "10.0.5",
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.DependencyInjection": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "v1SVsowG6YE1YnHVGmLWz57YTRCQRx9pH5ebIESXfm5isI9gA3QaMyg/oMTzPpXYZwSAVDzYItGJKfmV+pqXkQ==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.DependencyInjection.Abstractions": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "iVMtq9eRvzyhx8949EGT0OCYJfXi737SbRVzWXE5GrOgGj5AaZ9eUuxA/BSUfmOMALKn/g8KfFaNQw0eiB3lyA=="
+ },
+ "Microsoft.Extensions.DependencyModel": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "xA4kkL+QS6KCAOKz/O0oquHs44Ob8J7zpBCNt3wjkBWDg5aCqfwG8rWWLsg5V86AM0sB849g9JjPjIdksTCIKg=="
+ },
+ "Microsoft.Extensions.Diagnostics.Abstractions": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "/nYGrpa9/0BZofrVpBbbj+Ns8ZesiPE0V/KxsuHgDgHQopIzN54nRaQGSuvPw16/kI9sW1Zox5yyAPqvf0Jz6A==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Options": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Hosting.Abstractions": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "+Wb7KAMVZTomwJkQrjuPTe5KBzGod7N8XeG+ScxRlkPOB4sZLG4ccVwjV4Phk5BCJt7uIMnGHVoN6ZMVploX+g==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Abstractions": "10.0.5",
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Diagnostics.Abstractions": "10.0.5",
+ "Microsoft.Extensions.FileProviders.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Logging": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "+XTMKQyDWg4ODoNHU/BN3BaI1jhGO7VCS+BnzT/4IauiG6y2iPAte7MyD7rHKS+hNP0TkFkjrae8DFjDUxtcxg==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection": "10.0.5",
+ "Microsoft.Extensions.Logging.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Options": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Logging.Abstractions": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "9HOdqlDtPptVcmKAjsQ/Nr5Rxfq6FMYLdhvZh1lVmeKR738qeYecQD7+ldooXf+u2KzzR1kafSphWngIM3C6ug==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5"
+ }
+ },
+ "Microsoft.Extensions.Options": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.5, )",
+ "resolved": "10.0.5",
+ "contentHash": "MDaQMdUplw0AIRhWWmbLA7yQEXaLIHb+9CTroTiNS8OlI0LMXS4LCxtopqauiqGCWlRgJ+xyraVD8t6veRAFbw==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.5",
+ "Microsoft.Extensions.Primitives": "10.0.5"
+ }
+ },
+ "Microsoft.FeatureManagement.AspNetCore": {
+ "type": "CentralTransitive",
+ "requested": "[4.4.0, )",
+ "resolved": "4.4.0",
+ "contentHash": "jE5KxeEDUnUsx1w9uOFV5cOfM4E2mg7kf07328xO1x4JdoS0jwkD55nMjTlUPu90ynYX1oCBGC+FHK6ZoqRpxA==",
+ "dependencies": {
+ "Microsoft.FeatureManagement": "4.4.0"
+ }
+ },
+ "Microsoft.OpenApi": {
+ "type": "CentralTransitive",
+ "requested": "[2.7.0, )",
+ "resolved": "2.7.0",
+ "contentHash": "b9xmpnmjq6p+HqF3uWG7u7/PlB38t/UB5UtXdi6xEAP9ZJGKHneYyjMGzBflB1rpLxYEcU6KRme+cz5wNPlxqA=="
+ },
+ "Npgsql.EntityFrameworkCore.PostgreSQL": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.1, )",
+ "resolved": "10.0.1",
+ "contentHash": "P6EwH0Q4xkaA264iNZDqCPhWt8pscfUGxXazDQg4noBfqjoOlk4hKWfvBjF9ZX3R/9JybRmmJfmxr2iBMj0EpA==",
+ "dependencies": {
+ "Microsoft.EntityFrameworkCore": "[10.0.4, 11.0.0)",
+ "Microsoft.EntityFrameworkCore.Relational": "[10.0.4, 11.0.0)",
+ "Npgsql": "10.0.2"
+ }
+ },
+ "RabbitMQ.Client": {
+ "type": "CentralTransitive",
+ "requested": "[7.2.1, )",
+ "resolved": "7.2.1",
+ "contentHash": "YKXEfg9fVQiTKgZlvIhAfPSFaamEgi8DsQmisCH0IAsU4FYLrtoguDrDj6JtJVGUt40QPnBLRH6fTQcAC4qsOg==",
+ "dependencies": {
+ "System.Threading.RateLimiting": "8.0.0"
+ }
+ },
+ "Rebus": {
+ "type": "CentralTransitive",
+ "requested": "[8.9.0, )",
+ "resolved": "8.9.0",
+ "contentHash": "UaPGZuXIL4J5GUDA05JzEEzuPMEXY0CoF92nC6bsFBPvwoYPQ0uKyH2vKqdV80CW7cjbwBgDlEZ7R9hO9b59XA==",
+ "dependencies": {
+ "Newtonsoft.Json": "13.0.4"
+ }
+ },
+ "Rebus.RabbitMq": {
+ "type": "CentralTransitive",
+ "requested": "[10.1.1, )",
+ "resolved": "10.1.1",
+ "contentHash": "66pUp4hfaYWfQEDOiVcuZQnPF4XFHyJ5KCfwCm18e3Dnr936Iog48KrN8Mp8QyRQ2tiNpzdjSATQLKEZpSk11A==",
+ "dependencies": {
+ "RabbitMq.Client": "7.1.2",
+ "rebus": "8.9.0"
+ }
+ },
+ "Rebus.ServiceProvider": {
+ "type": "CentralTransitive",
+ "requested": "[10.7.2, )",
+ "resolved": "10.7.2",
+ "contentHash": "Qa8sKt1i9Fy/zCw5GwAUsfT+lt4BvkIgYh8sRJ6fvqJWoedS//pfcyiKUUb0wL3C5Wrpi3U+vRud5DCbMHaFIw==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection": "[8.0.0, 11.0.0)",
+ "Microsoft.Extensions.Hosting.Abstractions": "[6.0.0, 11.0.0)",
+ "Microsoft.Extensions.Logging.Abstractions": "[6.0.0, 11.0.0)",
+ "Rebus": "8.9.0"
+ }
+ },
+ "Scrutor": {
+ "type": "CentralTransitive",
+ "requested": "[7.0.0, )",
+ "resolved": "7.0.0",
+ "contentHash": "wHWaroody48jnlLoq/REwUltIFLxplXyHTP+sttrc8P7+jkiVqf38afDidJNv4qgD/6zz2NKOYg06xLZ1bG7wQ==",
+ "dependencies": {
+ "Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0",
+ "Microsoft.Extensions.DependencyModel": "10.0.0"
+ }
+ },
+ "Serilog": {
+ "type": "CentralTransitive",
+ "requested": "[4.3.1, )",
+ "resolved": "4.3.1",
+ "contentHash": "savYe7h5yRlkqBVOwP8cIRDOdqKiPmYCU4W87JH38sBmcKD5EBoXvQIw6bNEvZ/pTe1gsiye3VFCzBsoppGkXQ=="
+ },
+ "Serilog.AspNetCore": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.0, )",
+ "resolved": "10.0.0",
+ "contentHash": "a/cNa1mY4On1oJlfGG1wAvxjp5g7OEzk/Jf/nm7NF9cWoE7KlZw1GldrifUBWm9oKibHkR7Lg/l5jy3y7ACR8w==",
+ "dependencies": {
+ "Serilog": "4.3.0",
+ "Serilog.Extensions.Hosting": "10.0.0",
+ "Serilog.Formatting.Compact": "3.0.0",
+ "Serilog.Settings.Configuration": "10.0.0",
+ "Serilog.Sinks.Console": "6.1.1",
+ "Serilog.Sinks.Debug": "3.0.0",
+ "Serilog.Sinks.File": "7.0.0"
+ }
+ },
+ "Serilog.Enrichers.Environment": {
+ "type": "CentralTransitive",
+ "requested": "[3.0.1, )",
+ "resolved": "3.0.1",
+ "contentHash": "9BqCE4C9FF+/rJb/CsQwe7oVf44xqkOvMwX//CUxvUR25lFL4tSS6iuxE5eW07quby1BAyAEP+vM6TWsnT3iqw==",
+ "dependencies": {
+ "Serilog": "4.0.0"
+ }
+ },
+ "Serilog.Enrichers.Process": {
+ "type": "CentralTransitive",
+ "requested": "[3.0.0, )",
+ "resolved": "3.0.0",
+ "contentHash": "/wPYz2PDCJGSHNI+Z0PAacZvrgZgrGduWqLXeC2wvW6pgGM/Bi45JrKy887MRcRPHIZVU0LAlkmJ7TkByC0boQ==",
+ "dependencies": {
+ "Serilog": "4.0.0"
+ }
+ },
+ "Serilog.Enrichers.Thread": {
+ "type": "CentralTransitive",
+ "requested": "[4.0.0, )",
+ "resolved": "4.0.0",
+ "contentHash": "C7BK25a1rhUyr+Tp+1BYcVlBJq7M2VCHlIgnwoIUVJcicM9jYcvQK18+OeHiXw7uLPSjqWxJIp1EfaZ/RGmEwA==",
+ "dependencies": {
+ "Serilog": "4.0.0"
+ }
+ },
+ "Serilog.Settings.Configuration": {
+ "type": "CentralTransitive",
+ "requested": "[10.0.0, )",
+ "resolved": "10.0.0",
+ "contentHash": "LNq+ibS1sbhTqPV1FIE69/9AJJbfaOhnaqkzcjFy95o+4U+STsta9mi97f1smgXsWYKICDeGUf8xUGzd/52/uA==",
+ "dependencies": {
+ "Microsoft.Extensions.Configuration.Binder": "10.0.0",
+ "Microsoft.Extensions.DependencyModel": "10.0.0",
+ "Serilog": "4.3.0"
+ }
+ },
+ "Serilog.Sinks.Console": {
+ "type": "CentralTransitive",
+ "requested": "[6.1.1, )",
+ "resolved": "6.1.1",
+ "contentHash": "8jbqgjUyZlfCuSTaJk6lOca465OndqOz3KZP6Cryt/IqZYybyBu7GP0fE/AXBzrrQB3EBmQntBFAvMVz1COvAA==",
+ "dependencies": {
+ "Serilog": "4.0.0"
+ }
+ },
+ "Serilog.Sinks.Seq": {
+ "type": "CentralTransitive",
+ "requested": "[9.0.0, )",
+ "resolved": "9.0.0",
+ "contentHash": "aNU8A0K322q7+voPNmp1/qNPH+9QK8xvM1p72sMmCG0wGlshFzmtDW9QnVSoSYCj0MgQKcMOlgooovtBhRlNHw==",
+ "dependencies": {
+ "Serilog": "4.2.0",
+ "Serilog.Sinks.File": "6.0.0"
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Modules/Ratings/Domain/Entities/Review.cs b/src/Modules/Ratings/Domain/Entities/Review.cs
new file mode 100644
index 000000000..90fcfe772
--- /dev/null
+++ b/src/Modules/Ratings/Domain/Entities/Review.cs
@@ -0,0 +1,118 @@
+using MeAjudaAi.Modules.Ratings.Domain.Enums;
+using MeAjudaAi.Modules.Ratings.Domain.Events;
+using MeAjudaAi.Modules.Ratings.Domain.ValueObjects;
+using MeAjudaAi.Shared.Domain;
+
+namespace MeAjudaAi.Modules.Ratings.Domain.Entities;
+
+///
+/// Representa uma avaliação de um prestador de serviços feita por um cliente.
+///
+public sealed class Review : AggregateRoot
+{
+ ///
+ /// ID do prestador avaliado.
+ ///
+ public Guid ProviderId { get; private set; }
+
+ ///
+ /// ID do cliente que avaliou.
+ ///
+ public Guid CustomerId { get; private set; }
+
+ ///
+ /// Nota da avaliação (1 a 5 estrelas).
+ ///
+ public int Rating { get; private set; }
+
+ ///
+ /// Comentário textual da avaliação.
+ ///
+ public string? Comment { get; private set; }
+
+ ///
+ /// Status atual da avaliação para moderação.
+ ///
+ public EReviewStatus Status { get; private set; }
+
+ ///
+ /// Motivo de rejeição (se aplicável).
+ ///
+ public string? RejectionReason { get; private set; }
+
+ private Review() { }
+
+ private Review(ReviewId id, Guid providerId, Guid customerId, int rating, string? comment) : base(id)
+ {
+ ProviderId = providerId;
+ CustomerId = customerId;
+ Rating = rating;
+ Comment = comment;
+ Status = EReviewStatus.Pending;
+
+ AddDomainEvent(new ReviewCreatedDomainEvent(
+ Id,
+ 0,
+ ProviderId,
+ CustomerId,
+ Rating,
+ Comment));
+ }
+
+ ///
+ /// Cria uma nova avaliação.
+ ///
+ public static Review Create(Guid providerId, Guid customerId, int rating, string? comment)
+ {
+ if (providerId == Guid.Empty) throw new ArgumentException("ProviderId não pode ser vazio", nameof(providerId));
+ if (customerId == Guid.Empty) throw new ArgumentException("CustomerId não pode ser vazio", nameof(customerId));
+ if (rating < 1 || rating > 5) throw new ArgumentOutOfRangeException(nameof(rating), "Rating deve ser entre 1 e 5");
+
+ return new Review(ReviewId.New(), providerId, customerId, rating, comment);
+ }
+
+ ///
+ /// Aprova a avaliação tornando-a pública.
+ ///
+ public void Approve()
+ {
+ if (Status == EReviewStatus.Approved) return;
+
+ Status = EReviewStatus.Approved;
+ RejectionReason = null; // Limpa o motivo de rejeição se estava rejeitado anteriormente
+ MarkAsUpdated();
+
+ AddDomainEvent(new ReviewApprovedDomainEvent(
+ Id,
+ 0, // Versionamento simples por enquanto
+ ProviderId,
+ Rating,
+ Comment));
+ }
+
+ ///
+ /// Rejeita a avaliação por violação de regras.
+ ///
+ public void Reject(string reason)
+ {
+ if (string.IsNullOrWhiteSpace(reason)) throw new ArgumentException("Motivo de rejeição é obrigatório", nameof(reason));
+ if (Status == EReviewStatus.Rejected) return;
+
+ Status = EReviewStatus.Rejected;
+ RejectionReason = reason;
+ MarkAsUpdated();
+
+ AddDomainEvent(new ReviewRejectedDomainEvent(Id, 0, ProviderId, reason));
+ }
+
+ ///
+ /// Sinaliza a avaliação para moderação manual.
+ ///
+ public void MarkAsFlagged()
+ {
+ if (Status == EReviewStatus.Flagged) return;
+
+ Status = EReviewStatus.Flagged;
+ MarkAsUpdated();
+ }
+}
diff --git a/src/Modules/Ratings/Domain/Enums/EReviewStatus.cs b/src/Modules/Ratings/Domain/Enums/EReviewStatus.cs
new file mode 100644
index 000000000..2af4fea0d
--- /dev/null
+++ b/src/Modules/Ratings/Domain/Enums/EReviewStatus.cs
@@ -0,0 +1,27 @@
+namespace MeAjudaAi.Modules.Ratings.Domain.Enums;
+
+///
+/// Status de uma avaliação de prestador.
+///
+public enum EReviewStatus
+{
+ ///
+ /// Avaliação aguardando moderação.
+ ///
+ Pending = 0,
+
+ ///
+ /// Avaliação aprovada e visível publicamente.
+ ///
+ Approved = 1,
+
+ ///
+ /// Avaliação rejeitada por violação de termos.
+ ///
+ Rejected = 2,
+
+ ///
+ /// Avaliação sinalizada para revisão manual.
+ ///
+ Flagged = 3
+}
diff --git a/src/Modules/Ratings/Domain/Events/ReviewApprovedDomainEvent.cs b/src/Modules/Ratings/Domain/Events/ReviewApprovedDomainEvent.cs
new file mode 100644
index 000000000..0cdac552a
--- /dev/null
+++ b/src/Modules/Ratings/Domain/Events/ReviewApprovedDomainEvent.cs
@@ -0,0 +1,14 @@
+using MeAjudaAi.Shared.Events;
+
+namespace MeAjudaAi.Modules.Ratings.Domain.Events;
+
+///
+/// Evento de domínio disparado quando uma avaliação é aprovada.
+///
+public record ReviewApprovedDomainEvent(
+ Guid AggregateId,
+ int Version,
+ Guid ProviderId,
+ int Rating,
+ string? Comment
+) : DomainEvent(AggregateId, Version);
diff --git a/src/Modules/Ratings/Domain/Events/ReviewCreatedDomainEvent.cs b/src/Modules/Ratings/Domain/Events/ReviewCreatedDomainEvent.cs
new file mode 100644
index 000000000..e6dc084a6
--- /dev/null
+++ b/src/Modules/Ratings/Domain/Events/ReviewCreatedDomainEvent.cs
@@ -0,0 +1,15 @@
+using MeAjudaAi.Shared.Events;
+
+namespace MeAjudaAi.Modules.Ratings.Domain.Events;
+
+///
+/// Evento de domínio disparado quando uma nova avaliação é criada.
+///
+public record ReviewCreatedDomainEvent(
+ Guid AggregateId,
+ int Version,
+ Guid ProviderId,
+ Guid CustomerId,
+ int Rating,
+ string? Comment
+) : DomainEvent(AggregateId, Version);
diff --git a/src/Modules/Ratings/Domain/Events/ReviewRejectedDomainEvent.cs b/src/Modules/Ratings/Domain/Events/ReviewRejectedDomainEvent.cs
new file mode 100644
index 000000000..8425d2e9e
--- /dev/null
+++ b/src/Modules/Ratings/Domain/Events/ReviewRejectedDomainEvent.cs
@@ -0,0 +1,13 @@
+using MeAjudaAi.Shared.Events;
+
+namespace MeAjudaAi.Modules.Ratings.Domain.Events;
+
+///
+/// Evento de domínio disparado quando uma avaliação é rejeitada.
+///
+public record ReviewRejectedDomainEvent(
+ Guid AggregateId,
+ int Version,
+ Guid ProviderId,
+ string Reason
+) : DomainEvent(AggregateId, Version);
diff --git a/src/Modules/Ratings/Domain/Exceptions/DuplicateReviewException.cs b/src/Modules/Ratings/Domain/Exceptions/DuplicateReviewException.cs
new file mode 100644
index 000000000..80c299de5
--- /dev/null
+++ b/src/Modules/Ratings/Domain/Exceptions/DuplicateReviewException.cs
@@ -0,0 +1,11 @@
+using MeAjudaAi.Shared.Exceptions;
+
+namespace MeAjudaAi.Modules.Ratings.Domain.Exceptions;
+
+public class DuplicateReviewException : BusinessRuleException
+{
+ public DuplicateReviewException(Guid providerId, Guid customerId)
+ : base("DuplicateReview", $"O cliente '{customerId}' já avaliou o prestador '{providerId}'.")
+ {
+ }
+}
diff --git a/src/Modules/Ratings/Domain/MeAjudaAi.Modules.Ratings.Domain.csproj b/src/Modules/Ratings/Domain/MeAjudaAi.Modules.Ratings.Domain.csproj
new file mode 100644
index 000000000..6e86728ea
--- /dev/null
+++ b/src/Modules/Ratings/Domain/MeAjudaAi.Modules.Ratings.Domain.csproj
@@ -0,0 +1,25 @@
+
+
+
+