305 lines
9.5 KiB
Go
305 lines
9.5 KiB
Go
|
|
package handler
|
||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"net/http"
|
||
|
|
"strconv"
|
||
|
|
|
||
|
|
"github.com/gin-gonic/gin"
|
||
|
|
"gorm.io/gorm"
|
||
|
|
|
||
|
|
"dating-app/internal/dto"
|
||
|
|
"dating-app/internal/models"
|
||
|
|
"dating-app/internal/repository"
|
||
|
|
"dating-app/internal/service"
|
||
|
|
"dating-app/internal/utils"
|
||
|
|
)
|
||
|
|
|
||
|
|
type AdminHandler struct {
|
||
|
|
adminService *service.AdminService
|
||
|
|
optionRepo *repository.OptionRepository
|
||
|
|
questionRepo *repository.QuestionRepository
|
||
|
|
photoRepo *repository.PhotoRepository
|
||
|
|
onboardingService *service.OnboardingService
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewAdminHandler(
|
||
|
|
adminService *service.AdminService,
|
||
|
|
optionRepo *repository.OptionRepository,
|
||
|
|
questionRepo *repository.QuestionRepository,
|
||
|
|
photoRepo *repository.PhotoRepository,
|
||
|
|
onboardingService *service.OnboardingService,
|
||
|
|
) *AdminHandler {
|
||
|
|
return &AdminHandler{
|
||
|
|
adminService: adminService,
|
||
|
|
optionRepo: optionRepo,
|
||
|
|
questionRepo: questionRepo,
|
||
|
|
photoRepo: photoRepo,
|
||
|
|
onboardingService: onboardingService,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func validCategoryKey(key string) (models.CategoryKey, bool) {
|
||
|
|
k := models.CategoryKey(key)
|
||
|
|
for _, valid := range models.AllCategoryKeys {
|
||
|
|
if valid == k {
|
||
|
|
return k, true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return "", false
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------- دستهها ----------------
|
||
|
|
|
||
|
|
// GetCategory GET /admin/categories/:key
|
||
|
|
func (h *AdminHandler) GetCategory(c *gin.Context) {
|
||
|
|
key, ok := validCategoryKey(c.Param("key"))
|
||
|
|
if !ok {
|
||
|
|
utils.Fail(c, http.StatusNotFound, "دستهی نامعتبر است", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
cat, err := h.adminService.GetCategory(key)
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusNotFound, "دسته یافت نشد", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Success(c, http.StatusOK, "", cat)
|
||
|
|
}
|
||
|
|
|
||
|
|
// UpdateCategory PATCH /admin/categories/:key
|
||
|
|
func (h *AdminHandler) UpdateCategory(c *gin.Context) {
|
||
|
|
key, ok := validCategoryKey(c.Param("key"))
|
||
|
|
if !ok {
|
||
|
|
utils.Fail(c, http.StatusNotFound, "دستهی نامعتبر است", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
var req dto.UpdateCategoryRequest
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "ورودی نامعتبر است", err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
cat, err := h.adminService.UpdateCategory(key, req)
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, err.Error(), nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Success(c, http.StatusOK, "دسته بهروزرسانی شد", cat)
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------- گزینهها ----------------
|
||
|
|
|
||
|
|
// ListOptions GET /admin/categories/:key/options
|
||
|
|
func (h *AdminHandler) ListOptions(c *gin.Context) {
|
||
|
|
key, ok := validCategoryKey(c.Param("key"))
|
||
|
|
if !ok {
|
||
|
|
utils.Fail(c, http.StatusNotFound, "دستهی نامعتبر است", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
options, err := h.adminService.ListOptions(key)
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusInternalServerError, "خطا در دریافت گزینهها", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Success(c, http.StatusOK, "", options)
|
||
|
|
}
|
||
|
|
|
||
|
|
// CreateOption POST /admin/categories/:key/options
|
||
|
|
func (h *AdminHandler) CreateOption(c *gin.Context) {
|
||
|
|
key, ok := validCategoryKey(c.Param("key"))
|
||
|
|
if !ok {
|
||
|
|
utils.Fail(c, http.StatusNotFound, "دستهی نامعتبر است", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
var req dto.CreateOptionRequest
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "ورودی نامعتبر است", err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
opt, err := h.adminService.CreateOption(key, req)
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, err.Error(), nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Success(c, http.StatusCreated, "گزینه ساخته شد", opt)
|
||
|
|
}
|
||
|
|
|
||
|
|
// UpdateOption PATCH /admin/options/:id
|
||
|
|
func (h *AdminHandler) UpdateOption(c *gin.Context) {
|
||
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "شناسه نامعتبر است", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
var req dto.UpdateOptionRequest
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "ورودی نامعتبر است", err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
opt, err := h.adminService.UpdateOption(uint(id), req)
|
||
|
|
if err != nil {
|
||
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
|
|
utils.Fail(c, http.StatusNotFound, "گزینه یافت نشد", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Fail(c, http.StatusBadRequest, err.Error(), nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Success(c, http.StatusOK, "گزینه بهروزرسانی شد", opt)
|
||
|
|
}
|
||
|
|
|
||
|
|
// DeleteOption DELETE /admin/options/:id
|
||
|
|
func (h *AdminHandler) DeleteOption(c *gin.Context) {
|
||
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "شناسه نامعتبر است", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if err := h.adminService.DeleteOption(uint(id)); err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "حذف گزینه با خطا مواجه شد", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Success(c, http.StatusOK, "گزینه حذف شد", nil)
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------- بررسی و تایید کاربران ----------------
|
||
|
|
|
||
|
|
// ListPendingUsers GET /admin/users/pending
|
||
|
|
func (h *AdminHandler) ListPendingUsers(c *gin.Context) {
|
||
|
|
users, err := h.adminService.ListPendingUsers()
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusInternalServerError, "خطا در دریافت لیست کاربران", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
result := make([]dto.UserProfile, 0, len(users))
|
||
|
|
for i := range users {
|
||
|
|
result = append(result, toUserProfile(&users[i]))
|
||
|
|
}
|
||
|
|
utils.Success(c, http.StatusOK, "", result)
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetUserDetail GET /admin/users/:id — پروفایل کامل یک کاربر برای بررسی ادمین
|
||
|
|
func (h *AdminHandler) GetUserDetail(c *gin.Context) {
|
||
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "شناسه نامعتبر است", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
user, err := h.adminService.GetUser(uint(id))
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusNotFound, "کاربر یافت نشد", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Success(c, http.StatusOK, "", fullProfilePayload(user, h.optionRepo, h.questionRepo, h.onboardingService, h.photoRepo))
|
||
|
|
}
|
||
|
|
|
||
|
|
// ApproveUser POST /admin/users/:id/approve
|
||
|
|
func (h *AdminHandler) ApproveUser(c *gin.Context) {
|
||
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "شناسه نامعتبر است", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
user, err := h.adminService.ApproveUser(uint(id))
|
||
|
|
if err != nil {
|
||
|
|
h.handleUserReviewError(c, err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Success(c, http.StatusOK, "کاربر تایید شد", toUserProfile(user))
|
||
|
|
}
|
||
|
|
|
||
|
|
// RejectUser POST /admin/users/:id/reject body: {reason}
|
||
|
|
func (h *AdminHandler) RejectUser(c *gin.Context) {
|
||
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "شناسه نامعتبر است", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
var req dto.RejectUserRequest
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "ورودی نامعتبر است (ذکر دلیل رد الزامی است)", err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
user, err := h.adminService.RejectUser(uint(id), req.Reason)
|
||
|
|
if err != nil {
|
||
|
|
h.handleUserReviewError(c, err)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Success(c, http.StatusOK, "کاربر رد شد", toUserProfile(user))
|
||
|
|
}
|
||
|
|
|
||
|
|
func (h *AdminHandler) handleUserReviewError(c *gin.Context, err error) {
|
||
|
|
switch {
|
||
|
|
case errors.Is(err, gorm.ErrRecordNotFound):
|
||
|
|
utils.Fail(c, http.StatusNotFound, "کاربر یافت نشد", nil)
|
||
|
|
case errors.Is(err, service.ErrUserNotOnboarded):
|
||
|
|
utils.Fail(c, http.StatusConflict, err.Error(), nil)
|
||
|
|
default:
|
||
|
|
utils.Fail(c, http.StatusBadRequest, err.Error(), nil)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------- سوالات پروفایل ----------------
|
||
|
|
|
||
|
|
// ListQuestions GET /admin/profile-questions
|
||
|
|
func (h *AdminHandler) ListQuestions(c *gin.Context) {
|
||
|
|
qs, err := h.adminService.ListQuestions()
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusInternalServerError, "خطا در دریافت سوالات", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Success(c, http.StatusOK, "", qs)
|
||
|
|
}
|
||
|
|
|
||
|
|
// CreateQuestion POST /admin/profile-questions
|
||
|
|
func (h *AdminHandler) CreateQuestion(c *gin.Context) {
|
||
|
|
var req dto.CreateProfileQuestionRequest
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "ورودی نامعتبر است", err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
q, err := h.adminService.CreateQuestion(req)
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, err.Error(), nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Success(c, http.StatusCreated, "سوال ساخته شد", q)
|
||
|
|
}
|
||
|
|
|
||
|
|
// UpdateQuestion PATCH /admin/profile-questions/:id
|
||
|
|
func (h *AdminHandler) UpdateQuestion(c *gin.Context) {
|
||
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "شناسه نامعتبر است", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
var req dto.UpdateProfileQuestionRequest
|
||
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "ورودی نامعتبر است", err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
q, err := h.adminService.UpdateQuestion(uint(id), req)
|
||
|
|
if err != nil {
|
||
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
|
|
utils.Fail(c, http.StatusNotFound, "سوال یافت نشد", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Fail(c, http.StatusBadRequest, err.Error(), nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Success(c, http.StatusOK, "سوال بهروزرسانی شد", q)
|
||
|
|
}
|
||
|
|
|
||
|
|
// DeleteQuestion DELETE /admin/profile-questions/:id
|
||
|
|
func (h *AdminHandler) DeleteQuestion(c *gin.Context) {
|
||
|
|
id, err := strconv.ParseUint(c.Param("id"), 10, 64)
|
||
|
|
if err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "شناسه نامعتبر است", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if err := h.adminService.DeleteQuestion(uint(id)); err != nil {
|
||
|
|
utils.Fail(c, http.StatusBadRequest, "حذف سوال با خطا مواجه شد", nil)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
utils.Success(c, http.StatusOK, "سوال حذف شد", nil)
|
||
|
|
}
|