Backend - Serviços¶
Este documento descreve todos os serviços do backend, suas responsabilidades, métodos principais e dependências.
Índice¶
- Serviços de Domínio (Método Billings)
- Serviços de Autenticação
- Serviços de Profissional
- Serviços Financeiros
- Serviços de Comunicação
- Serviços de Integração
- Serviços de Cursos e Vídeo (Marketplace)
Serviços de Domínio (Método Billings)¶
FertilityService¶
Serviço responsável por calcular o status de fertilidade baseado nas regras do Método Billings.
Localização: internal/modules/general/services/fertility_service.go
Dependências:
- *gorm.DB - Conexão com banco de dados
Métodos Principais:
GetFertilityStatus¶
Calcula o estado de fertilidade para uma data específica.
func (s *FertilityService) GetFertilityStatus(userID uint, date time.Time) (*FertilityStatus, error)
Parâmetros:
- userID - ID do usuário
- date - Data para calcular o status
Retorna:
- *FertilityStatus - Status de fertilidade
- error - Erro se não houver ciclo ativo
FertilityStatus:
type FertilityStatus struct {
IsFertile bool // Se está fértil
IntercourseStatus string // "not_allowed", "allowed_with_risk", "allowed_no_risk"
DaysAfterPeak *int // Dias após o ápice (nil se não houver)
HasBleeding bool // Se há sangramento
PeakDate *time.Time // Data do ápice (nil se não houver)
}
Regras Aplicadas (em ordem): 1. Regra 1: Não permitir relação nos dias de sangramento 2. Regra 2: Não permitir relação nos primeiros 15 dias 3. Regra 3: Aplicar regras baseadas no objetivo (engravidar/espaçar) 4. Regra 4: Após 4 dias do ápice, permitir relação sem risco
Exemplo:
fertilityService := services.NewFertilityService(db)
status, err := fertilityService.GetFertilityStatus(userID, time.Now())
if err != nil {
// Tratar erro
}
// status.IsFertile, status.IntercourseStatus, etc.
GetFertilityStatusForDateRange¶
Calcula status de fertilidade para um intervalo de datas.
func (s *FertilityService) GetFertilityStatusForDateRange(userID uint, startDate, endDate time.Time) (map[string]*FertilityStatus, error)
Retorna: Map onde a chave é a data (YYYY-MM-DD) e o valor é o status.
Documentação Completa: Ver FERTILITY_RULES.md
Atualização 2026-03-09 (arquitetura interna):
- regras compartilhadas extraídas para internal/modules/general/services/fertility/
- FertilityService segue como fachada pública do cálculo, sem mudança de contrato.
CycleService¶
Serviço responsável por gerenciar operações relacionadas a ciclos menstruais.
Localização: internal/modules/general/services/cycle_service.go
Dependências:
- *gorm.DB - Conexão com banco de dados
Métodos Principais:
GetActiveCycle¶
Retorna o ciclo ativo do usuário.
func (s *CycleService) GetActiveCycle(userID uint) (*models.Cycle, error)
Retorna:
- *models.Cycle - Ciclo ativo ou nil se não houver
- error - Erro de banco de dados
CalculateCycleDay¶
Calcula o dia do ciclo para uma data específica.
func (s *CycleService) CalculateCycleDay(cycle *models.Cycle, date time.Time) (int, error)
Retorna:
- int - Dia do ciclo (1 = primeiro dia)
- error - Erro se ciclo inválido
EnsureSingleActiveCycle¶
Garante que apenas um ciclo esteja ativo por usuário.
func (s *CycleService) EnsureSingleActiveCycle(userID uint, newCycleID uint) error
Comportamento:
- Se newCycleID for 0, desativa todos os ciclos ativos
- Caso contrário, desativa todos os outros ciclos ativos, exceto o novo
Regra operacional (criação do primeiro ciclo):
- O primeiro ciclo não é criado automaticamente no onboarding.
- A criação ocorre via POST /cycles.
- No primeiro ciclo, start_date deve ser não futura e dentro da janela de 12 meses.
CycleValidationsService¶
Serviço responsável por validações relacionadas a ciclos.
Localização: internal/modules/general/services/cycle_validations.go
Métodos Principais:
ValidateDateInCycle¶
Valida se uma data está dentro de um ciclo.
func (s *CycleValidationsService) ValidateDateInCycle(date time.Time, cycle *models.Cycle) error
Validações: - Data não pode ser anterior ao início do ciclo - Se ciclo tem fim, data não pode ser posterior - Data não pode ser futura
CanEditCycle¶
Verifica se um ciclo pode ser editado.
func (s *CycleValidationsService) CanEditCycle(cycle *models.Cycle) bool
Retorna: true se ciclo está ativo e não tem data de fim.
ShouldShowFirstDayOfMenstruation¶
Verifica se deve mostrar o campo "primeiro dia da menstruação".
func (s *CycleValidationsService) ShouldShowFirstDayOfMenstruation(cycle *models.Cycle, date time.Time) bool
Retorna: true se já passaram 15 dias desde o início do ciclo.
SymbolRulesService¶
Serviço responsável por gerenciar regras de símbolos do Método Billings.
Localização: internal/modules/general/services/symbol_rules.go
Dependências:
- *gorm.DB - Conexão com banco de dados
Métodos Principais:
GetAllowedSymbols¶
Retorna os IDs dos símbolos permitidos para um contexto específico.
func (s *SymbolRulesService) GetAllowedSymbols(cycleID uint, day int, sensationID *uint, appearanceID *uint) ([]uint, error)
Parâmetros:
- cycleID - ID do ciclo
- day - Dia do ciclo
- sensationID - ID da sensação (opcional)
- appearanceID - ID da aparência (opcional)
Lógica:
1. Busca regras baseadas em dias (DayBasedRule) aplicáveis
2. Coleta símbolos permitidos das regras
3. Aplica restrições das regras
4. Se não houver regras, permite todos os símbolos
ValidateSymbol¶
Valida se um símbolo é permitido para uma observação.
func (s *SymbolRulesService) ValidateSymbol(observation *models.Observation, symbolID uint) error
Validações:
- Observação deve ter cycle_id
- Calcula dia do ciclo
- Verifica se símbolo está na lista permitida
CheckRuleViolation¶
Verifica se houve infração de regras no dia atual.
func (s *SymbolRulesService) CheckRuleViolation(userID uint, date time.Time) (bool, error)
Retorna: true se houve infração, false caso contrário.
MOBRulesService¶
Serviço responsável por aplicar regras específicas do Método de Ovulação Billings (MOB).
Localização: internal/modules/general/services/mob_rules_service.go
Responsabilidades: - Aplicar regras específicas do MOB - Validar padrões de infertilidade - Calcular períodos férteis baseados em regras MOB
Serviços de Autenticação¶
AuthService¶
Serviço responsável por autenticação, registro e gerenciamento de usuários.
Localização: internal/services/auth/auth_service.go
Dependências:
- repo.Repository - Repository de autenticação
- *email.EmailService - Serviço de email
Métodos Principais:
Login¶
Autentica um usuário por email e senha.
func (s *Service) Login(email, password string) (*LoginResult, error)
Validações: - Email e senha devem ser válidos - Usuário deve estar ativo - Email deve estar verificado
Retorna:
type LoginResult struct {
Token string
RefreshToken string
User *models.User
}
Erros:
- INVALID_CREDENTIALS - Credenciais inválidas
- USER_INACTIVE - Usuário inativo
- EMAIL_NOT_VERIFIED - Email não verificado
Register¶
Cria um novo usuário e envia email de verificação.
func (s *Service) Register(req RegisterRequest) (*models.User, string, error)
RegisterRequest:
type RegisterRequest struct {
Username string
Email string
Password string
Name string
UserType string
}
Processo:
1. Valida tipo de usuário
2. Verifica se email já existe
3. Hash da senha (bcrypt)
4. Cria usuário com email_verified = false
5. Gera token de verificação
6. Envia email de verificação
7. Retorna usuário e mensagem
VerifyEmail¶
Verifica email do usuário.
func (s *Service) VerifyEmail(token string) error
Processo: 1. Busca token de verificação 2. Valida expiração 3. Marca email como verificado 4. Marca token como usado
ForgotPassword¶
Solicita reset de senha.
func (s *Service) ForgotPassword(email string) error
Processo: 1. Busca usuário por email 2. Gera token de reset 3. Salva token no banco 4. Envia email com link de reset
ResetPassword¶
Redefine senha do usuário.
func (s *Service) ResetPassword(token, newPassword string) error
Processo: 1. Valida token 2. Verifica expiração 3. Atualiza senha (hash bcrypt) 4. Marca token como usado
RefreshToken¶
Renova token de acesso usando refresh token.
func (s *Service) RefreshToken(refreshToken string) (string, error)
Retorna: Novo token JWT
OAuth Service¶
Serviço responsável por autenticação OAuth (Google, Apple).
Localização: internal/services/oauth/provider.go
Provedores Suportados: - Google OAuth - Apple Sign In
Métodos Principais:
Authenticate¶
Autentica usuário via OAuth.
func (p *Provider) Authenticate(provider string, token string) (*OAuthResult, error)
Processo:
1. Valida token OAuth com provedor
2. Busca ou cria usuário
3. Cria/atualiza UserAuthProvider
4. Gera tokens JWT
5. Retorna resultado
OAuthResult:
type OAuthResult struct {
Token string
RefreshToken string
User *models.User
IsNewUser bool
}
Provedores:
- google - Google OAuth
- apple - Apple Sign In
Serviços de Profissional¶
ProfessionalProfileService¶
Serviço responsável por gerenciar perfis profissionais.
Localização: internal/services/professional/professional_profile_service.go
Dependências:
- repo.ProfessionalProfileRepository - Repository de perfis
- authRepo.Repository - Repository de autenticação
Métodos Principais:
GetProfile¶
Retorna o perfil do profissional.
func (s *ProfessionalProfileService) GetProfile(userID uint, userType string) (interface{}, error)
Comportamento:
- Se userType == "admin", retorna dados básicos do usuário
- Caso contrário, retorna perfil profissional completo
CreateOrUpdateProfile¶
Cria ou atualiza perfil profissional.
func (s *ProfessionalProfileService) CreateOrUpdateProfile(userID uint, req UpdateProfileRequest) (*models.ProfessionalProfile, error)
Processo:
1. Verifica se perfil existe
2. Se existe, atualiza
3. Se não existe, cria com status pending
4. Retorna perfil
Validações:
- Usuário deve ser do tipo professional
- Campos obrigatórios devem estar preenchidos
ProfessionalPatientService¶
Serviço responsável por gerenciar vínculos entre profissionais e pacientes.
Localização: internal/services/professional/professional_patient_service.go
Dependências:
- repo.ProfessionalPatientRepository - Repository de vínculos
Métodos Principais:
GetPatients¶
Lista pacientes do profissional com paginação.
func (s *ProfessionalPatientService) GetPatients(professionalID uint, search string, page, limit int) (*PatientListResult, error)
Retorna:
type PatientListResult struct {
Data []PatientWithStatus
Total int64
Page int
Limit int
TotalPages int
}
LinkToProfessional¶
Permite que um cliente se vincule a um profissional.
func (s *ProfessionalPatientService) LinkToProfessional(patientID, professionalID uint) (*models.ProfessionalPatient, error)
Validações:
- Profissional deve existir
- Profissional deve ter perfil aprovado
- Cria vínculo com status pending
Notificações: - Cria notificação para o profissional sobre nova solicitação
UnlinkFromProfessional¶
Permite que um paciente encerre o vínculo.
func (s *ProfessionalPatientService) UnlinkFromProfessional(patientID uint) error
Nota: Não requer aprovação - paciente pode encerrar livremente.
ChangeProfessional¶
Permite que um paciente mude de orientadora.
func (s *ProfessionalPatientService) ChangeProfessional(patientID, newProfessionalID uint) (*models.ProfessionalPatient, error)
Processo:
1. Valida novo profissional
2. Remove vínculo antigo
3. Cria novo vínculo com status pending
4. Notifica novo profissional
ApproveLink¶
Permite que a orientadora aprove um vínculo pendente.
func (s *ProfessionalPatientService) ApproveLink(professionalID, patientID uint) (*models.ProfessionalPatient, error)
Notificações: - Cria notificação para paciente sobre aprovação
RejectLink¶
Permite que a orientadora rejeite um vínculo pendente.
func (s *ProfessionalPatientService) RejectLink(professionalID, patientID uint) error
Notificações: - Cria notificação para paciente sobre rejeição
UpdatePatientWomanPattern¶
Atualiza o Padrão Básico de Infertilidade (PBI) do paciente.
func (s *ProfessionalPatientService) UpdatePatientWomanPattern(professionalID, patientID uint, womanPattern string, pbiSensationID, pbiAppearanceID *uint) error
Validações: - Profissional deve estar vinculado ao paciente - Vínculo deve estar aprovado
AvailabilityService¶
Serviço responsável por gerenciar disponibilidade de profissionais.
Localização: internal/services/professional/availability_service.go
Dependências:
- repo.AvailabilityRepository - Repository de disponibilidade
Métodos Principais:
GetAvailability¶
Retorna os horários disponíveis do profissional.
func (s *AvailabilityService) GetAvailability(professionalID uint) ([]models.AvailabilitySlot, error)
CreateSlot¶
Cria um novo slot de disponibilidade.
func (s *AvailabilityService) CreateSlot(professionalID uint, dateStr, startTime, endTime string) (*models.AvailabilitySlot, error)
Validações: - Data não pode ser no passado - Horários devem estar no formato HH:MM - Slot não pode já existir
BulkCreate¶
Cria múltiplos slots de disponibilidade.
func (s *AvailabilityService) BulkCreate(professionalID uint, req BulkCreateRequest) (int, int, error)
BulkCreateRequest:
type BulkCreateRequest struct {
Date string // Data específica
TimeSlots []string // Slots no formato "HH:MM-HH:MM"
DaysOfWeek []int // Dias da semana (0=Domingo, 6=Sábado)
StartDate string // Data inicial (para recorrência)
EndDate string // Data final (para recorrência)
}
Comportamento:
- Se fornecido DaysOfWeek + StartDate + EndDate, cria slots recorrentes
- Se fornecido apenas Date, cria slots para data específica
- Ignora slots duplicados
Retorna: (createdCount, totalCount, error)
UpdateSlot¶
Atualiza um slot de disponibilidade.
func (s *AvailabilityService) UpdateSlot(professionalID uint, slotID uint, req UpdateSlotRequest) (*models.AvailabilitySlot, error)
DeleteSlot¶
Remove um slot de disponibilidade.
func (s *AvailabilityService) DeleteSlot(professionalID uint, slotID uint) error
ProfessionalFinancialService¶
Serviço responsável por gerenciar informações financeiras de profissionais.
Localização: internal/services/professional/professional_financial_service.go
Métodos Principais:
GetFinancialSummary¶
Retorna resumo financeiro do profissional.
func (s *ProfessionalFinancialService) GetFinancialSummary(professionalID uint) (*FinancialSummary, error)
GetBankAccount¶
Retorna conta bancária do profissional.
func (s *ProfessionalFinancialService) GetBankAccount(professionalID uint) (*models.ProfessionalBankAccount, error)
UpdateBankAccount¶
Atualiza conta bancária do profissional.
func (s *ProfessionalFinancialService) UpdateBankAccount(professionalID uint, account *models.ProfessionalBankAccount) error
GetPayments¶
Lista pagamentos do profissional.
func (s *ProfessionalFinancialService) GetPayments(professionalID uint, page, limit int) ([]models.Payment, int64, error)
Serviços Financeiros¶
AccountService¶
Serviço responsável por gerenciar contas financeiras genéricas.
Localização: internal/services/accounts/account_service.go
Dependências:
- repo.Repository - Repository de contas
Métodos Principais:
List¶
Lista contas do usuário.
func (s *Service) List(userID uint, userType string) ([]models.Account, error)
Get¶
Obtém uma conta específica.
func (s *Service) Get(id uint, userID uint, userType string) (*models.Account, error)
Validações de Acesso: - Usuário pode acessar apenas suas próprias contas - Admin e professional podem acessar contas de outros usuários
Create¶
Cria uma nova conta.
func (s *Service) Create(account *models.Account, userID uint) error
Update¶
Atualiza uma conta existente.
func (s *Service) Update(id uint, input *models.Account, userID uint, userType string) (*models.Account, error)
Delete¶
Remove uma conta.
func (s *Service) Delete(id uint, userID uint, userType string) error
PlanService¶
Serviço responsável por gerenciar planos de assinatura.
Localização: internal/services/plans/plan_service.go
Dependências:
- repo.Repository - Repository de planos
Métodos Principais:
List¶
Lista planos com paginação e pesquisa.
func (s *Service) List(userType string, search string, page, limit int) (*ListResult, error)
Validações:
- page mínimo: 1
- limit mínimo: 1, máximo: 100
Retorna:
type ListResult struct {
Data []models.Plan
Total int64
Page int
Limit int
TotalPages int
}
Get¶
Obtém um plano por ID.
func (s *Service) Get(id uint) (*models.Plan, error)
Create¶
Cria um novo plano (apenas admin).
func (s *Service) Create(plan *models.Plan) error
Validações: - Nome é obrigatório - Todos os preços devem ser maiores que zero
Update¶
Atualiza um plano existente (apenas admin).
func (s *Service) Update(id uint, plan *models.Plan) (*models.Plan, error)
Delete¶
Remove um plano (apenas admin).
func (s *Service) Delete(id uint) error
Serviços de Comunicação¶
NotificationService¶
Serviço responsável por criar e gerenciar notificações.
Localização: internal/services/notifications/notification_service.go
Métodos Principais:
CreateMessageNotification¶
Cria notificação para nova mensagem.
func CreateMessageNotification(userID, messageID uint, senderName string) error
CreateAppointmentNotification¶
Cria notificação para agendamento.
func CreateAppointmentNotification(userID, appointmentID uint, appointmentType string, details map[string]interface{}) error
Tipos de Agendamento:
- created - Novo agendamento criado
- confirmed - Agendamento confirmado
- rejected - Agendamento rejeitado
- cancelled - Agendamento cancelado
CreateTaskNotification¶
Cria notificação para tarefa pendente.
func CreateTaskNotification(userID uint, taskType string, title, message string, metadata map[string]interface{}) error
Uso: - Solicitações de vínculo - Aprovações/rejeições - Outras tarefas pendentes
CreateSystemNotification¶
Cria notificação do sistema.
func CreateSystemNotification(userID uint, title, message string) error
CreatePendingRecordNotification¶
Cria notificação de lembrete para registro diário pendente.
func CreatePendingRecordNotification(userID uint, missingDaysCount int, cycleID uint) error
Comportamento:
- Mensagem varia conforme quantidade de dias pendentes
- Inclui metadata com missing_days_count e cycle_id
Serviços de Integração¶
EmailService¶
Serviço responsável por enviar emails.
Localização: internal/services/email/email.go
Dependências:
- EmailProvider - Provedor de email (SendGrid ou SMTP)
- EmailConfig - Configurações de email
Provedores Suportados:
- SendGrid (via sendgrid_provider.go)
- SMTP genérico (via smtp_provider.go)
Métodos Principais:
SendEmail¶
Envia email genérico.
func (es *EmailService) SendEmail(to, subject, body string) error
SendVerificationEmail¶
Envia email de verificação de conta.
func (es *EmailService) SendVerificationEmail(email, token string) error
Template: - Link de verificação - Expira em 24 horas - HTML formatado
SendPasswordResetEmail¶
Envia email de reset de senha.
func (es *EmailService) SendPasswordResetEmail(email, token string) error
Template: - Link de reset - Expira em 24 horas - HTML formatado
Factory:
O serviço é criado via factory (factory.go) que detecta automaticamente qual provedor usar baseado nas variáveis de ambiente.
Serviços de Cursos e Vídeo (Marketplace)¶
Course Service (módulo marketplace/courses)¶
Serviço responsável por catálogo, matrículas, editor de curso do profissional, pipeline de vídeo com Mux e playback protegido.
Localização: internal/modules/marketplace/courses/service.go
Subpastas por ator (registro de rotas):
- internal/modules/marketplace/courses/client
- internal/modules/marketplace/courses/professional
- internal/modules/marketplace/courses/admin
- internal/modules/marketplace/courses/webhooks
Rotas relacionadas:
- cliente: GET /marketplace/enrollments/:enrollment_id/lessons/:lesson_id/playback
- profissional: POST /marketplace/professional/courses/:course_id/lessons/:lesson_id/video-upload-url
- profissional: POST /marketplace/professional/courses/:course_id/lessons/:lesson_id/video-from-url
- profissional: GET /marketplace/professional/courses/:course_id/lessons/:lesson_id/video-status
- webhook: POST /marketplace/webhooks/mux
Pipeline de vídeo atual (Mux):
1. profissional cria upload direto (CreateLessonVideoUpload) ou importa por URL (CreateLessonVideoFromURL);
2. aula entra em video_status=processing;
3. webhook Mux processa eventos idempotentes em video_webhook_events;
4. quando video.asset.ready, aula recebe mux_playback_id e status ready;
5. cliente solicita token assinado de playback (GetPlaybackToken) para render no player.
Status de vídeo da aula:
- pending_upload
- processing
- ready
- errored
Feature flags operacionais:
- marketplace.mux.enabled
- marketplace.mux.webhook.enabled
- marketplace.mux.signed_playback.enabled
Rate limit de playback:
- endpoint protegido por limite por minuto;
- configuração via MARKETPLACE_PLAYBACK_RATE_LIMIT_PER_MINUTE.
Padrões de Uso¶
Inicialização de Serviços¶
// Serviço com dependência de DB
fertilityService := services.NewFertilityService(db)
// Serviço com dependência de Repository
accountRepo := accountsRepo.NewGormAccountRepository(db)
accountService := accounts.NewAccountService(accountRepo)
// Serviço com múltiplas dependências
profileRepo := profRepo.NewGormProfessionalProfileRepository(db)
userRepo := authRepo.NewGormAuthRepository(db)
profileService := professional.NewProfessionalProfileService(profileRepo, userRepo)
Tratamento de Erros¶
Todos os serviços retornam erros que devem ser tratados pelos handlers:
result, err := service.DoSomething()
if err != nil {
// Erro pode ser:
// - Erro de validação (retornar 400)
// - Erro de não encontrado (retornar 404)
// - Erro de permissão (retornar 403)
// - Erro de banco (retornar 500)
return handleError(err)
}
Validações¶
Serviços devem validar: - Dados de entrada - Regras de negócio - Permissões de acesso - Integridade de dados