Files
fm_be/internal/service/base_service.go
2026-07-16 22:16:45 +07:00

152 lines
4.7 KiB
Go

package service
import (
"context"
"errors"
"strings"
"time"
"wucher/internal/domain/base"
"wucher/internal/shared/pkg/appctx"
"wucher/internal/shared/pkg/sortkey"
)
type BaseService struct {
repo base.Repository
}
func NewBaseService(repo base.Repository) *BaseService {
return &BaseService{repo: repo}
}
var errInvalidBaseCategoryType = errors.New("invalid base category type")
func (s *BaseService) CreateBase(ctx context.Context, row *base.Base, categoryType string) error {
category, ok := base.NormalizeCategoryType(categoryType)
if !ok {
return errInvalidBaseCategoryType
}
if err := validateBaseCoordinates(row); err != nil {
return err
}
row.DefaultStartTimeType = normalizeBaseShiftTimeType(row.DefaultStartTimeType)
row.DefaultEndTimeType = normalizeBaseShiftTimeType(row.DefaultEndTimeType)
if row.DefaultStartTimeType == base.ShiftTimeTypeFixed && strings.TrimSpace(row.DefaultShiftStart) == "" {
return errors.New("default_shift_start is required")
}
if row.DefaultEndTimeType == base.ShiftTimeTypeFixed && strings.TrimSpace(row.DefaultShiftEnd) == "" {
return errors.New("default_shift_end is required")
}
return s.repo.CreateBase(ctx, row, category)
}
func (s *BaseService) UpdateBase(ctx context.Context, row *base.Base, categoryType string) error {
category, ok := base.NormalizeCategoryType(categoryType)
if !ok {
return errInvalidBaseCategoryType
}
if err := validateBaseCoordinates(row); err != nil {
return err
}
row.DefaultStartTimeType = normalizeBaseShiftTimeType(row.DefaultStartTimeType)
row.DefaultEndTimeType = normalizeBaseShiftTimeType(row.DefaultEndTimeType)
if row.DefaultStartTimeType == base.ShiftTimeTypeFixed && strings.TrimSpace(row.DefaultShiftStart) == "" {
return errors.New("default_shift_start is required")
}
if row.DefaultEndTimeType == base.ShiftTimeTypeFixed && strings.TrimSpace(row.DefaultShiftEnd) == "" {
return errors.New("default_shift_end is required")
}
return s.repo.UpdateBase(ctx, row, category)
}
func (s *BaseService) DeleteBase(ctx context.Context, id []byte, categoryType string) (*base.Base, error) {
category, ok := base.NormalizeCategoryType(categoryType)
if !ok {
return nil, errInvalidBaseCategoryType
}
// fetch existing row before deleting so we can return a copy
row, err := s.repo.GetBaseByID(ctx, id, category)
if err != nil {
return nil, err
}
if err := s.repo.DeleteBase(ctx, id, category); err != nil {
return nil, err
}
// if we had a row, populate DeletedAt/DeletedBy to reflect the repository update
if row != nil {
now := time.Now().UTC()
row.DeletedAt = &now
// try to pick actor from context if available
if actor := appctx.GetUserID(ctx); len(actor) > 0 {
row.DeletedBy = make([]byte, len(actor))
copy(row.DeletedBy, actor)
}
}
return row, nil
}
func (s *BaseService) GetBaseByID(ctx context.Context, id []byte, categoryType string) (*base.Base, error) {
category, ok := base.NormalizeCategoryType(categoryType)
if !ok {
return nil, errInvalidBaseCategoryType
}
return s.repo.GetBaseByID(ctx, id, category)
}
func (s *BaseService) ListBases(ctx context.Context, filter, sort string, limit, offset int, categoryType string, dul *bool) ([]base.Base, int64, error) {
if strings.TrimSpace(categoryType) == "" {
return s.repo.ListBases(ctx, filter, normalizeBaseSort(sort), limit, offset, "", dul)
}
category, ok := base.NormalizeCategoryType(categoryType)
if !ok {
return nil, 0, errInvalidBaseCategoryType
}
return s.repo.ListBases(ctx, filter, normalizeBaseSort(sort), limit, offset, category, dul)
}
func normalizeBaseSort(sort string) string {
return normalizeSortByRules(sort,
sortExpr(
joinSortClauses(sortkey.ActivePositiveSortClauses("", "is_active", "sortkey", "base", false)),
joinSortClauses(sortkey.ActivePositiveSortClauses("", "is_active", "sortkey", "base", true)),
"sortkey",
),
sortField("base"),
sortField("base_abbreviation"),
sortField("notes", "note", "notes"),
sortField("is_active"),
sortField("created_at"),
sortField("updated_at"),
)
}
func validateBaseCoordinates(row *base.Base) error {
if row == nil {
return nil
}
if row.Latitude < -90 || row.Latitude > 90 {
return errors.New("latitude must be between -90 and 90")
}
if row.Longitude < -180 || row.Longitude > 180 {
return errors.New("longitude must be between -180 and 180")
}
return nil
}
func normalizeBaseShiftTimeType(raw string) string {
switch strings.ToUpper(strings.TrimSpace(raw)) {
case "", base.ShiftTimeTypeFixed:
return base.ShiftTimeTypeFixed
case base.ShiftTimeTypeBMCT:
return base.ShiftTimeTypeBMCT
case base.ShiftTimeTypeECET:
return base.ShiftTimeTypeECET
default:
return strings.ToUpper(strings.TrimSpace(raw))
}
}