package admin import ( "context" "encoding/json" "strings" "github.com/google/uuid" "github.com/yuxingu/digital-psychology/apps/api/internal/repository" ) var ( ErrInvalidStatusEdge = errString("invalid status transition") ErrReasonRequired = errString("reason required") ) var allowedStatusEdges = map[string]map[string]struct{}{ "active": {"disabled": {}, "banned": {}, "suspended": {}}, "disabled": {"active": {}, "banned": {}}, "suspended": {"active": {}, "banned": {}, "disabled": {}}, "banned": {"active": {}, "disabled": {}}, } // TransitionUserStatus migrates UserStatus with audit. func (s *Service) TransitionUserStatus(ctx context.Context, adminID, userID uuid.UUID, toStatus, reason string) error { toStatus = strings.TrimSpace(toStatus) reason = strings.TrimSpace(reason) if reason == "" { return ErrReasonRequired } ok, err := s.Repo.UserExists(ctx, userID) if err != nil { return err } if !ok { return ErrUserNotFound } from, err := s.Repo.GetUserStatus(ctx, userID) if err != nil || from == "" { return ErrUserNotFound } if from == toStatus { return ErrInvalidStatusEdge } next, okEdge := allowedStatusEdges[from] if !okEdge { return ErrInvalidStatusEdge } if _, okEdge = next[toStatus]; !okEdge { return ErrInvalidStatusEdge } meta, _ := json.Marshal(map[string]string{ "from": from, "to": toStatus, "reason": reason, }) return s.Repo.TransitionUserStatusWithAudit(ctx, adminID, userID, from, toStatus, reason, meta) } // ListStatusTransitions returns recent transitions. func (s *Service) ListStatusTransitions(ctx context.Context, userID uuid.UUID, limit int) ([]repository.AccountTransition, error) { ok, err := s.Repo.UserExists(ctx, userID) if err != nil { return nil, err } if !ok { return nil, ErrUserNotFound } items, err := s.Repo.ListStatusTransitions(ctx, userID, limit) if err != nil { return nil, err } if items == nil { items = []repository.AccountTransition{} } return items, nil }