210 lines
6.2 KiB
Go
210 lines
6.2 KiB
Go
package usecase
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
"github.com/gofrs/uuid/v5"
|
|
|
|
"github.com/saveinmed/backend-go/internal/domain"
|
|
)
|
|
|
|
// Repository defines DB contract for the core entities.
|
|
type Repository interface {
|
|
CreateCompany(ctx context.Context, company *domain.Company) error
|
|
ListCompanies(ctx context.Context) ([]domain.Company, error)
|
|
GetCompany(ctx context.Context, id uuid.UUID) (*domain.Company, error)
|
|
UpdateCompany(ctx context.Context, company *domain.Company) error
|
|
|
|
CreateProduct(ctx context.Context, product *domain.Product) error
|
|
ListProducts(ctx context.Context) ([]domain.Product, error)
|
|
|
|
CreateOrder(ctx context.Context, order *domain.Order) error
|
|
GetOrder(ctx context.Context, id uuid.UUID) (*domain.Order, error)
|
|
UpdateOrderStatus(ctx context.Context, id uuid.UUID, status domain.OrderStatus) error
|
|
|
|
CreateUser(ctx context.Context, user *domain.User) error
|
|
ListUsers(ctx context.Context, filter domain.UserFilter) ([]domain.User, int64, error)
|
|
GetUser(ctx context.Context, id uuid.UUID) (*domain.User, error)
|
|
GetUserByEmail(ctx context.Context, email string) (*domain.User, error)
|
|
UpdateUser(ctx context.Context, user *domain.User) error
|
|
DeleteUser(ctx context.Context, id uuid.UUID) error
|
|
}
|
|
|
|
// PaymentGateway abstracts Mercado Pago integration.
|
|
type PaymentGateway interface {
|
|
CreatePreference(ctx context.Context, order *domain.Order) (*domain.PaymentPreference, error)
|
|
}
|
|
|
|
type Service struct {
|
|
repo Repository
|
|
pay PaymentGateway
|
|
jwtSecret []byte
|
|
tokenTTL time.Duration
|
|
}
|
|
|
|
// NewService wires use cases together.
|
|
func NewService(repo Repository, pay PaymentGateway, jwtSecret string, tokenTTL time.Duration) *Service {
|
|
return &Service{repo: repo, pay: pay, jwtSecret: []byte(jwtSecret), tokenTTL: tokenTTL}
|
|
}
|
|
|
|
func (s *Service) RegisterCompany(ctx context.Context, company *domain.Company) error {
|
|
company.ID = uuid.Must(uuid.NewV7())
|
|
return s.repo.CreateCompany(ctx, company)
|
|
}
|
|
|
|
func (s *Service) ListCompanies(ctx context.Context) ([]domain.Company, error) {
|
|
return s.repo.ListCompanies(ctx)
|
|
}
|
|
|
|
func (s *Service) GetCompany(ctx context.Context, id uuid.UUID) (*domain.Company, error) {
|
|
return s.repo.GetCompany(ctx, id)
|
|
}
|
|
|
|
func (s *Service) RegisterProduct(ctx context.Context, product *domain.Product) error {
|
|
product.ID = uuid.Must(uuid.NewV7())
|
|
return s.repo.CreateProduct(ctx, product)
|
|
}
|
|
|
|
func (s *Service) ListProducts(ctx context.Context) ([]domain.Product, error) {
|
|
return s.repo.ListProducts(ctx)
|
|
}
|
|
|
|
func (s *Service) CreateOrder(ctx context.Context, order *domain.Order) error {
|
|
order.ID = uuid.Must(uuid.NewV7())
|
|
order.Status = domain.OrderStatusPending
|
|
return s.repo.CreateOrder(ctx, order)
|
|
}
|
|
|
|
func (s *Service) GetOrder(ctx context.Context, id uuid.UUID) (*domain.Order, error) {
|
|
return s.repo.GetOrder(ctx, id)
|
|
}
|
|
|
|
func (s *Service) UpdateOrderStatus(ctx context.Context, id uuid.UUID, status domain.OrderStatus) error {
|
|
return s.repo.UpdateOrderStatus(ctx, id, status)
|
|
}
|
|
|
|
func (s *Service) CreatePaymentPreference(ctx context.Context, id uuid.UUID) (*domain.PaymentPreference, error) {
|
|
order, err := s.repo.GetOrder(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return s.pay.CreatePreference(ctx, order)
|
|
}
|
|
|
|
func (s *Service) CreateUser(ctx context.Context, user *domain.User, password string) error {
|
|
hashed, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
user.ID = uuid.Must(uuid.NewV7())
|
|
user.PasswordHash = string(hashed)
|
|
|
|
return s.repo.CreateUser(ctx, user)
|
|
}
|
|
|
|
func (s *Service) ListUsers(ctx context.Context, filter domain.UserFilter, page, pageSize int) (*domain.UserPage, error) {
|
|
if page < 1 {
|
|
page = 1
|
|
}
|
|
if pageSize <= 0 {
|
|
pageSize = 20
|
|
}
|
|
|
|
filter.Limit = pageSize
|
|
filter.Offset = (page - 1) * pageSize
|
|
|
|
users, total, err := s.repo.ListUsers(ctx, filter)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &domain.UserPage{Users: users, Total: total, Page: page, PageSize: pageSize}, nil
|
|
}
|
|
|
|
func (s *Service) GetUser(ctx context.Context, id uuid.UUID) (*domain.User, error) {
|
|
return s.repo.GetUser(ctx, id)
|
|
}
|
|
|
|
func (s *Service) UpdateUser(ctx context.Context, user *domain.User, newPassword string) error {
|
|
if newPassword != "" {
|
|
hashed, err := bcrypt.GenerateFromPassword([]byte(newPassword), bcrypt.DefaultCost)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
user.PasswordHash = string(hashed)
|
|
}
|
|
|
|
return s.repo.UpdateUser(ctx, user)
|
|
}
|
|
|
|
func (s *Service) DeleteUser(ctx context.Context, id uuid.UUID) error {
|
|
return s.repo.DeleteUser(ctx, id)
|
|
}
|
|
|
|
// RegisterAccount creates a company when needed and persists a user bound to it.
|
|
func (s *Service) RegisterAccount(ctx context.Context, company *domain.Company, user *domain.User, password string) error {
|
|
if company != nil {
|
|
if company.ID == uuid.Nil {
|
|
company.ID = uuid.Must(uuid.NewV7())
|
|
company.IsVerified = false
|
|
if err := s.repo.CreateCompany(ctx, company); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if _, err := s.repo.GetCompany(ctx, company.ID); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
user.CompanyID = company.ID
|
|
}
|
|
|
|
return s.CreateUser(ctx, user, password)
|
|
}
|
|
|
|
// Authenticate validates credentials and emits a signed JWT.
|
|
func (s *Service) Authenticate(ctx context.Context, email, password string) (string, time.Time, error) {
|
|
user, err := s.repo.GetUserByEmail(ctx, email)
|
|
if err != nil {
|
|
return "", time.Time{}, err
|
|
}
|
|
|
|
if err := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)); err != nil {
|
|
return "", time.Time{}, errors.New("invalid credentials")
|
|
}
|
|
|
|
expiresAt := time.Now().Add(s.tokenTTL)
|
|
claims := jwt.MapClaims{
|
|
"sub": user.ID.String(),
|
|
"role": user.Role,
|
|
"company_id": user.CompanyID.String(),
|
|
"exp": expiresAt.Unix(),
|
|
}
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
signed, err := token.SignedString(s.jwtSecret)
|
|
if err != nil {
|
|
return "", time.Time{}, err
|
|
}
|
|
|
|
return signed, expiresAt, nil
|
|
}
|
|
|
|
// VerifyCompany marks a company as verified.
|
|
func (s *Service) VerifyCompany(ctx context.Context, id uuid.UUID) (*domain.Company, error) {
|
|
company, err := s.repo.GetCompany(ctx, id)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
company.IsVerified = true
|
|
|
|
if err := s.repo.UpdateCompany(ctx, company); err != nil {
|
|
return nil, err
|
|
}
|
|
return company, nil
|
|
}
|