Files
chat_room/internal/service/profile_service.go
T
mobiniqow 1dc96e86fc Add admin user moderation: block/unblock, reports, matches list, photo review
- User blocking (with reason) now closes the auth gap immediately — a
  blocked user's still-valid access token stops working on the next
  request, not just on their next OTP/refresh.
- Admin-created reports (moderation notes) per user, viewable in a history
  list independent of block status.
- GET /admin/users: full paginated, searchable user list with report counts.
- GET /admin/matches: all matches across the app ("friends" who mutually
  liked each other), for admin visibility.
- GET/DELETE /admin/photos: paginated feed of every uploaded photo with
  uploader info, so admins can review and remove content without going
  user-by-user.
- Per-user activity: monitoring requests can now be filtered by user_id, so
  a user's detail page can show their recent API activity.
- Batched the profile-selections query in fullProfilePayload (was 6 queries
  per view, now 1) for consistency with the earlier N+1 fixes.
2026-09-20 05:30:14 +03:30

100 lines
2.7 KiB
Go

package service
import (
"errors"
"dating-app/internal/models"
"dating-app/internal/repository"
)
const MaxPhotosPerUser = 6
var (
ErrTooManyPhotos = errors.New("حداکثر تعداد عکس مجاز رو داری")
ErrPhotoNotOwned = errors.New("این عکس متعلق به تو نیست")
ErrInvalidImage = errors.New("فرمت فایل باید jpg، png یا webp باشه")
ErrImageTooLarge = errors.New("حجم عکس نباید بیشتر از ۵ مگابایت باشه")
)
const MaxPhotoSizeBytes = 5 * 1024 * 1024
type ProfileService struct {
photoRepo *repository.PhotoRepository
userRepo *repository.UserRepository
storage PhotoStorage
}
func NewProfileService(photoRepo *repository.PhotoRepository, userRepo *repository.UserRepository, storage PhotoStorage) *ProfileService {
return &ProfileService{photoRepo: photoRepo, userRepo: userRepo, storage: storage}
}
var allowedImageExts = map[string]bool{
".jpg": true, ".jpeg": true, ".png": true, ".webp": true,
}
func (s *ProfileService) UploadPhoto(userID uint, ext string, data []byte) (*models.Photo, error) {
if !allowedImageExts[ext] {
return nil, ErrInvalidImage
}
if len(data) > MaxPhotoSizeBytes {
return nil, ErrImageTooLarge
}
count, err := s.photoRepo.CountByUser(userID)
if err != nil {
return nil, err
}
if count >= MaxPhotosPerUser {
return nil, ErrTooManyPhotos
}
url, err := s.storage.Save(userID, ext, data)
if err != nil {
return nil, err
}
photo := &models.Photo{UserID: userID, URL: url, SortOrder: int(count)}
if err := s.photoRepo.Create(photo); err != nil {
return nil, err
}
return photo, nil
}
func (s *ProfileService) DeletePhoto(userID, photoID uint) error {
photo, err := s.photoRepo.FindByID(photoID)
if err != nil {
return err
}
if photo.UserID != userID {
return ErrPhotoNotOwned
}
if err := s.photoRepo.Delete(photoID); err != nil {
return err
}
return s.storage.Delete(photo.URL)
}
// DeletePhotoAdmin مثل DeletePhoto ولی بدون چک مالکیت؛ برای نظارت محتوایی
// ادمین که باید بتونه هر عکسی رو صرف‌نظر از صاحبش حذف کنه.
func (s *ProfileService) DeletePhotoAdmin(photoID uint) error {
photo, err := s.photoRepo.FindByID(photoID)
if err != nil {
return err
}
if err := s.photoRepo.Delete(photoID); err != nil {
return err
}
return s.storage.Delete(photo.URL)
}
func (s *ProfileService) ListPhotos(userID uint) ([]models.Photo, error) {
return s.photoRepo.ListByUser(userID)
}
func (s *ProfileService) UpdateLocation(user *models.User, lat, lng float64) error {
return s.userRepo.UpdateFields(user, map[string]interface{}{
"latitude": lat,
"longitude": lng,
})
}