package service import ( "context" "log" "time" "with_you/internal/model" "with_you/internal/repository" "gorm.io/gorm" ) const AccountDeletionCoolDownDays = 90 type AccountCleanupService interface { CleanupExpiredAccounts(ctx context.Context) (int64, error) } type accountCleanupServiceImpl struct { db *gorm.DB userRepo repository.UserRepository logService *LogService } func NewAccountCleanupService( db *gorm.DB, userRepo repository.UserRepository, logService *LogService, ) AccountCleanupService { return &accountCleanupServiceImpl{ db: db, userRepo: userRepo, logService: logService, } } func (s *accountCleanupServiceImpl) CleanupExpiredAccounts(ctx context.Context) (int64, error) { cutoffTime := time.Now().AddDate(0, 0, -AccountDeletionCoolDownDays) users, err := s.userRepo.GetPendingDeletionUsers(cutoffTime) if err != nil { return 0, err } if len(users) == 0 { return 0, nil } var deletedCount int64 for _, user := range users { if err := s.anonymizeAndDeleteUser(ctx, user); err != nil { log.Printf("[AccountCleanup] failed to cleanup user %s: %v", user.ID, err) continue } deletedCount++ } log.Printf("[AccountCleanup] cleaned up %d expired accounts", deletedCount) return deletedCount, nil } func (s *accountCleanupServiceImpl) anonymizeAndDeleteUser(ctx context.Context, user *model.User) error { deletedUserID := "deleted_user" return s.db.Transaction(func(tx *gorm.DB) error { if err := tx.Model(&model.Post{}).Where("user_id = ?", user.ID).Update("user_id", deletedUserID).Error; err != nil { return err } if err := tx.Model(&model.Comment{}).Where("user_id = ?", user.ID).Update("user_id", deletedUserID).Error; err != nil { return err } if err := tx.Where("follower_id = ? OR following_id = ?", user.ID, user.ID).Delete(&model.Follow{}).Error; err != nil { return err } if err := tx.Where("user_id = ?", user.ID).Delete(&model.Favorite{}).Error; err != nil { return err } if err := tx.Where("user_id = ?", user.ID).Delete(&model.PostLike{}).Error; err != nil { return err } if err := tx.Where("user_id = ?", user.ID).Delete(&model.CommentLike{}).Error; err != nil { return err } if err := tx.Unscoped().Delete(&model.User{}, "id = ?", user.ID).Error; err != nil { return err } if s.logService != nil { s.logService.OperationLog.RecordOperation(&model.OperationLog{ UserID: user.ID, UserName: user.Username, Operation: "account_deleted", Module: "user", TargetType: "user", TargetID: user.ID, Status: "success", }) } return nil }) }