Files
MeBox/internal/service/subscription_identity.go
T
2026-08-10 19:30:17 +08:00

90 lines
2.7 KiB
Go

package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/ShukeBta/MediaStationGo/internal/model"
)
var ErrSubscriptionAlreadyExists = errors.New("subscription already exists")
type SubscriptionAlreadyExistsError struct {
ExistingID string
}
func (e *SubscriptionAlreadyExistsError) Error() string {
if e == nil || e.ExistingID == "" {
return ErrSubscriptionAlreadyExists.Error()
}
return fmt.Sprintf("%s: %s", ErrSubscriptionAlreadyExists, e.ExistingID)
}
func (e *SubscriptionAlreadyExistsError) Unwrap() error {
return ErrSubscriptionAlreadyExists
}
func newSubscriptionAlreadyExistsError(existingID string) error {
return &SubscriptionAlreadyExistsError{ExistingID: existingID}
}
func SubscriptionAlreadyExistsID(err error) string {
var conflict *SubscriptionAlreadyExistsError
if errors.As(err, &conflict) && conflict != nil {
return conflict.ExistingID
}
return ""
}
func (s *SubscriptionService) subscriptionDuplicate(ctx context.Context, sub *model.Subscription, excludeID string) (*model.Subscription, error) {
if s == nil || s.repo == nil || s.repo.Subscription == nil || sub == nil {
return nil, nil
}
return s.repo.Subscription.FindActiveByIdentity(ctx, sub.UserID, sub.IdentityKey, excludeID)
}
// Update applies API patch fields while recomputing the functional identity.
// The database partial unique index remains the final concurrency guard.
func (s *SubscriptionService) Update(ctx context.Context, id string, updates map[string]any) error {
if s == nil || s.repo == nil || s.repo.DB == nil {
return errors.New("subscription service unavailable")
}
var sub model.Subscription
if err := s.repo.DB.WithContext(ctx).Where("id = ?", id).First(&sub).Error; err != nil {
return err
}
raw, err := json.Marshal(updates)
if err != nil {
return err
}
if err := json.Unmarshal(raw, &sub); err != nil {
return err
}
if sub.Name == "" || sub.FeedURL == "" {
return errors.New("name and feed_url required")
}
normalizeSubscriptionDefaults(&sub)
model.RefreshSubscriptionIdentity(&sub)
if duplicate, err := s.subscriptionDuplicate(ctx, &sub, sub.ID); err != nil {
return err
} else if duplicate != nil {
return newSubscriptionAlreadyExistsError(duplicate.ID)
}
updates["search_mode"] = sub.SearchMode
updates["resolution"] = sub.Resolution
updates["wash_priority"] = sub.WashPriority
updates["priority"] = sub.Priority
updates["identity_key"] = sub.IdentityKey
if err := s.repo.DB.WithContext(ctx).Model(&model.Subscription{}).
Where("id = ?", sub.ID).Updates(updates).Error; err != nil {
if duplicate, lookupErr := s.subscriptionDuplicate(ctx, &sub, sub.ID); lookupErr == nil && duplicate != nil {
return newSubscriptionAlreadyExistsError(duplicate.ID)
}
return err
}
return nil
}