868 lines
25 KiB
Go
868 lines
25 KiB
Go
package education
|
|
|
|
import (
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
|
|
"veza-backend-api/internal/common"
|
|
"veza-backend-api/internal/core/education"
|
|
"veza-backend-api/internal/response"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
// Handler gère les requêtes HTTP pour l'éducation
|
|
type Handler struct {
|
|
courseManager *education.CourseManager
|
|
tutorialManager *education.TutorialManager
|
|
logger *zap.Logger
|
|
}
|
|
|
|
// NewHandler crée un nouveau handler d'éducation
|
|
func NewHandler(courseManager *education.CourseManager, tutorialManager *education.TutorialManager, logger *zap.Logger) *Handler {
|
|
return &Handler{
|
|
courseManager: courseManager,
|
|
tutorialManager: tutorialManager,
|
|
logger: logger,
|
|
}
|
|
}
|
|
|
|
// Request/Response structures
|
|
type CreateCourseRequest struct {
|
|
Title string `json:"title" binding:"required"`
|
|
Description string `json:"description" binding:"required"`
|
|
Instructor string `json:"instructor" binding:"required"`
|
|
Category string `json:"category" binding:"required"`
|
|
Level education.CourseLevel `json:"level" binding:"required"`
|
|
Duration time.Duration `json:"duration" binding:"required"`
|
|
Price float64 `json:"price"`
|
|
Language string `json:"language" binding:"required"`
|
|
Tags []string `json:"tags"`
|
|
}
|
|
|
|
type UpdateCourseRequest struct {
|
|
Title *string `json:"title"`
|
|
Description *string `json:"description"`
|
|
Instructor *string `json:"instructor"`
|
|
Category *string `json:"category"`
|
|
Level *education.CourseLevel `json:"level"`
|
|
Duration *time.Duration `json:"duration"`
|
|
Price *float64 `json:"price"`
|
|
Language *string `json:"language"`
|
|
IsPublished *bool `json:"is_published"`
|
|
Tags []string `json:"tags"`
|
|
}
|
|
|
|
type CreateTutorialRequest struct {
|
|
Title string `json:"title" binding:"required"`
|
|
Description string `json:"description" binding:"required"`
|
|
Author string `json:"author" binding:"required"`
|
|
Category string `json:"category" binding:"required"`
|
|
VideoURL string `json:"video_url" binding:"required"`
|
|
Thumbnail string `json:"thumbnail"`
|
|
Duration time.Duration `json:"duration" binding:"required"`
|
|
Quality education.VideoQuality `json:"quality" binding:"required"`
|
|
Language string `json:"language" binding:"required"`
|
|
IsFree bool `json:"is_free"`
|
|
Tags []string `json:"tags"`
|
|
}
|
|
|
|
type UpdateTutorialRequest struct {
|
|
Title *string `json:"title"`
|
|
Description *string `json:"description"`
|
|
Author *string `json:"author"`
|
|
Category *string `json:"category"`
|
|
VideoURL *string `json:"video_url"`
|
|
Thumbnail *string `json:"thumbnail"`
|
|
Duration *time.Duration `json:"duration"`
|
|
Quality *education.VideoQuality `json:"quality"`
|
|
IsPublished *bool `json:"is_published"`
|
|
Tags []string `json:"tags"`
|
|
}
|
|
|
|
type AddLessonRequest struct {
|
|
Title string `json:"title" binding:"required"`
|
|
Description string `json:"description" binding:"required"`
|
|
Content string `json:"content" binding:"required"`
|
|
VideoURL string `json:"video_url"`
|
|
Duration time.Duration `json:"duration" binding:"required"`
|
|
Order int `json:"order" binding:"required"`
|
|
IsFree bool `json:"is_free"`
|
|
}
|
|
|
|
type AddExerciseRequest struct {
|
|
Title string `json:"title" binding:"required"`
|
|
Description string `json:"description" binding:"required"`
|
|
Content string `json:"content" binding:"required"`
|
|
Solution string `json:"solution" binding:"required"`
|
|
Type education.ExerciseType `json:"type" binding:"required"`
|
|
Points int `json:"points" binding:"required"`
|
|
TimeLimit time.Duration `json:"time_limit"`
|
|
IsRequired bool `json:"is_required"`
|
|
}
|
|
|
|
type UpdateProgressRequest struct {
|
|
Progress float64 `json:"progress" binding:"required"`
|
|
CompletedLessons []string `json:"completed_lessons"`
|
|
CurrentLesson string `json:"current_lesson"`
|
|
Score float64 `json:"score"`
|
|
TimeSpent time.Duration `json:"time_spent"`
|
|
}
|
|
|
|
type AddTutorialStepRequest struct {
|
|
Title string `json:"title" binding:"required"`
|
|
Description string `json:"description" binding:"required"`
|
|
Content string `json:"content" binding:"required"`
|
|
Order int `json:"order" binding:"required"`
|
|
Timestamp time.Duration `json:"timestamp"`
|
|
IsFree bool `json:"is_free"`
|
|
}
|
|
|
|
type AddTutorialCommentRequest struct {
|
|
Content string `json:"content" binding:"required"`
|
|
Rating int `json:"rating" binding:"min=1,max=5"`
|
|
}
|
|
|
|
// COURSES HANDLERS
|
|
|
|
// CreateCourse crée un nouveau cours
|
|
func (h *Handler) CreateCourse(c *gin.Context) {
|
|
_, exists := common.GetUserIDFromContext(c)
|
|
if !exists {
|
|
response.Error(c, http.StatusUnauthorized, "Utilisateur non authentifié")
|
|
return
|
|
}
|
|
|
|
var req CreateCourseRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, http.StatusBadRequest, "Données de requête invalides")
|
|
return
|
|
}
|
|
|
|
course, err := h.courseManager.CreateCourse(
|
|
c.Request.Context(),
|
|
req.Title,
|
|
req.Description,
|
|
req.Instructor,
|
|
req.Category,
|
|
req.Level,
|
|
req.Duration,
|
|
req.Price,
|
|
req.Language,
|
|
)
|
|
if err != nil {
|
|
h.logger.Error("Échec de création du cours", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec de création du cours")
|
|
return
|
|
}
|
|
|
|
response.Success(c, course, "Cours créé avec succès")
|
|
}
|
|
|
|
// GetCourse récupère un cours par son ID
|
|
func (h *Handler) GetCourse(c *gin.Context) {
|
|
courseID := c.Param("course_id")
|
|
if courseID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de cours requis")
|
|
return
|
|
}
|
|
|
|
course, err := h.courseManager.GetCourse(c.Request.Context(), courseID)
|
|
if err != nil {
|
|
h.logger.Error("Échec de récupération du cours", zap.Error(err))
|
|
response.Error(c, http.StatusNotFound, "Cours non trouvé")
|
|
return
|
|
}
|
|
|
|
response.Success(c, course, "Cours récupéré avec succès")
|
|
}
|
|
|
|
// ListCourses liste tous les cours disponibles
|
|
func (h *Handler) ListCourses(c *gin.Context) {
|
|
filters := make(map[string]interface{})
|
|
|
|
if category := c.Query("category"); category != "" {
|
|
filters["category"] = category
|
|
}
|
|
if level := c.Query("level"); level != "" {
|
|
filters["level"] = education.CourseLevel(level)
|
|
}
|
|
if isPublished := c.Query("is_published"); isPublished != "" {
|
|
if published, err := strconv.ParseBool(isPublished); err == nil {
|
|
filters["is_published"] = published
|
|
}
|
|
}
|
|
if isFree := c.Query("is_free"); isFree != "" {
|
|
if free, err := strconv.ParseBool(isFree); err == nil {
|
|
filters["is_free"] = free
|
|
}
|
|
}
|
|
|
|
courses, err := h.courseManager.ListCourses(c.Request.Context(), filters)
|
|
if err != nil {
|
|
h.logger.Error("Échec de récupération des cours", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec de récupération des cours")
|
|
return
|
|
}
|
|
|
|
response.Success(c, courses, "Cours récupérés avec succès")
|
|
}
|
|
|
|
// UpdateCourse met à jour un cours
|
|
func (h *Handler) UpdateCourse(c *gin.Context) {
|
|
_, exists := common.GetUserIDFromContext(c)
|
|
if !exists {
|
|
response.Error(c, http.StatusUnauthorized, "Utilisateur non authentifié")
|
|
return
|
|
}
|
|
|
|
courseID := c.Param("course_id")
|
|
if courseID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de cours requis")
|
|
return
|
|
}
|
|
|
|
var req UpdateCourseRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, http.StatusBadRequest, "Données de requête invalides")
|
|
return
|
|
}
|
|
|
|
updates := make(map[string]interface{})
|
|
if req.Title != nil {
|
|
updates["title"] = *req.Title
|
|
}
|
|
if req.Description != nil {
|
|
updates["description"] = *req.Description
|
|
}
|
|
if req.Instructor != nil {
|
|
updates["instructor"] = *req.Instructor
|
|
}
|
|
if req.Category != nil {
|
|
updates["category"] = *req.Category
|
|
}
|
|
if req.Level != nil {
|
|
updates["level"] = *req.Level
|
|
}
|
|
if req.Duration != nil {
|
|
updates["duration"] = *req.Duration
|
|
}
|
|
if req.Price != nil {
|
|
updates["price"] = *req.Price
|
|
}
|
|
if req.Language != nil {
|
|
updates["language"] = *req.Language
|
|
}
|
|
if req.IsPublished != nil {
|
|
updates["is_published"] = *req.IsPublished
|
|
}
|
|
if req.Tags != nil {
|
|
updates["tags"] = req.Tags
|
|
}
|
|
|
|
course, err := h.courseManager.UpdateCourse(c.Request.Context(), courseID, updates)
|
|
if err != nil {
|
|
h.logger.Error("Échec de mise à jour du cours", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec de mise à jour du cours")
|
|
return
|
|
}
|
|
|
|
response.Success(c, course, "Cours mis à jour avec succès")
|
|
}
|
|
|
|
// DeleteCourse supprime un cours
|
|
func (h *Handler) DeleteCourse(c *gin.Context) {
|
|
_, exists := common.GetUserIDFromContext(c)
|
|
if !exists {
|
|
response.Error(c, http.StatusUnauthorized, "Utilisateur non authentifié")
|
|
return
|
|
}
|
|
|
|
courseID := c.Param("course_id")
|
|
if courseID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de cours requis")
|
|
return
|
|
}
|
|
|
|
err := h.courseManager.DeleteCourse(c.Request.Context(), courseID)
|
|
if err != nil {
|
|
h.logger.Error("Échec de suppression du cours", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec de suppression du cours")
|
|
return
|
|
}
|
|
|
|
response.Success(c, nil, "Cours supprimé avec succès")
|
|
}
|
|
|
|
// AddLesson ajoute une leçon à un cours
|
|
func (h *Handler) AddLesson(c *gin.Context) {
|
|
_, exists := common.GetUserIDFromContext(c)
|
|
if !exists {
|
|
response.Error(c, http.StatusUnauthorized, "Utilisateur non authentifié")
|
|
return
|
|
}
|
|
|
|
courseID := c.Param("course_id")
|
|
if courseID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de cours requis")
|
|
return
|
|
}
|
|
|
|
var req AddLessonRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, http.StatusBadRequest, "Données de requête invalides")
|
|
return
|
|
}
|
|
|
|
lesson, err := h.courseManager.AddLesson(
|
|
c.Request.Context(),
|
|
courseID,
|
|
req.Title,
|
|
req.Description,
|
|
req.Content,
|
|
req.VideoURL,
|
|
req.Duration,
|
|
req.Order,
|
|
req.IsFree,
|
|
)
|
|
if err != nil {
|
|
h.logger.Error("Échec d'ajout de leçon", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec d'ajout de leçon")
|
|
return
|
|
}
|
|
|
|
response.Success(c, lesson, "Leçon ajoutée avec succès")
|
|
}
|
|
|
|
// AddExercise ajoute un exercice à un cours
|
|
func (h *Handler) AddExercise(c *gin.Context) {
|
|
_, exists := common.GetUserIDFromContext(c)
|
|
if !exists {
|
|
response.Error(c, http.StatusUnauthorized, "Utilisateur non authentifié")
|
|
return
|
|
}
|
|
|
|
courseID := c.Param("course_id")
|
|
lessonID := c.Param("lesson_id")
|
|
if courseID == "" || lessonID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de cours et de leçon requis")
|
|
return
|
|
}
|
|
|
|
var req AddExerciseRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, http.StatusBadRequest, "Données de requête invalides")
|
|
return
|
|
}
|
|
|
|
exercise, err := h.courseManager.AddExercise(
|
|
c.Request.Context(),
|
|
courseID,
|
|
lessonID,
|
|
req.Title,
|
|
req.Description,
|
|
req.Content,
|
|
req.Solution,
|
|
req.Type,
|
|
req.Points,
|
|
req.TimeLimit,
|
|
req.IsRequired,
|
|
)
|
|
if err != nil {
|
|
h.logger.Error("Échec d'ajout d'exercice", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec d'ajout d'exercice")
|
|
return
|
|
}
|
|
|
|
response.Success(c, exercise, "Exercice ajouté avec succès")
|
|
}
|
|
|
|
// GetUserProgress récupère la progression d'un utilisateur
|
|
func (h *Handler) GetUserProgress(c *gin.Context) {
|
|
userID, exists := common.GetUserIDFromContext(c)
|
|
if !exists {
|
|
response.Error(c, http.StatusUnauthorized, "Utilisateur non authentifié")
|
|
return
|
|
}
|
|
|
|
courseID := c.Param("course_id")
|
|
if courseID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de cours requis")
|
|
return
|
|
}
|
|
|
|
progress, err := h.courseManager.GetUserProgress(c.Request.Context(), userID, courseID)
|
|
if err != nil {
|
|
h.logger.Error("Échec de récupération de la progression", zap.Error(err))
|
|
response.Error(c, http.StatusNotFound, "Progression non trouvée")
|
|
return
|
|
}
|
|
|
|
response.Success(c, progress, "Progression récupérée avec succès")
|
|
}
|
|
|
|
// UpdateUserProgress met à jour la progression d'un utilisateur
|
|
func (h *Handler) UpdateUserProgress(c *gin.Context) {
|
|
userID, exists := common.GetUserIDFromContext(c)
|
|
if !exists {
|
|
response.Error(c, http.StatusUnauthorized, "Utilisateur non authentifié")
|
|
return
|
|
}
|
|
|
|
courseID := c.Param("course_id")
|
|
if courseID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de cours requis")
|
|
return
|
|
}
|
|
|
|
var req UpdateProgressRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, http.StatusBadRequest, "Données de requête invalides")
|
|
return
|
|
}
|
|
|
|
progress, err := h.courseManager.UpdateUserProgress(
|
|
c.Request.Context(),
|
|
userID,
|
|
courseID,
|
|
req.Progress,
|
|
req.CompletedLessons,
|
|
req.CurrentLesson,
|
|
req.Score,
|
|
req.TimeSpent,
|
|
)
|
|
if err != nil {
|
|
h.logger.Error("Échec de mise à jour de la progression", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec de mise à jour de la progression")
|
|
return
|
|
}
|
|
|
|
response.Success(c, progress, "Progression mise à jour avec succès")
|
|
}
|
|
|
|
// IssueCertificate émet un certificat
|
|
func (h *Handler) IssueCertificate(c *gin.Context) {
|
|
userID, exists := common.GetUserIDFromContext(c)
|
|
if !exists {
|
|
response.Error(c, http.StatusUnauthorized, "Utilisateur non authentifié")
|
|
return
|
|
}
|
|
|
|
courseID := c.Param("course_id")
|
|
if courseID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de cours requis")
|
|
return
|
|
}
|
|
|
|
// Récupérer les paramètres de la requête
|
|
title := c.Query("title")
|
|
description := c.Query("description")
|
|
scoreStr := c.Query("score")
|
|
maxScoreStr := c.Query("max_score")
|
|
|
|
if title == "" || description == "" || scoreStr == "" || maxScoreStr == "" {
|
|
response.Error(c, http.StatusBadRequest, "Tous les paramètres sont requis")
|
|
return
|
|
}
|
|
|
|
score, err := strconv.ParseFloat(scoreStr, 64)
|
|
if err != nil {
|
|
response.Error(c, http.StatusBadRequest, "Score invalide")
|
|
return
|
|
}
|
|
|
|
maxScore, err := strconv.ParseFloat(maxScoreStr, 64)
|
|
if err != nil {
|
|
response.Error(c, http.StatusBadRequest, "Score maximum invalide")
|
|
return
|
|
}
|
|
|
|
certificate, err := h.courseManager.IssueCertificate(
|
|
c.Request.Context(),
|
|
courseID,
|
|
userID,
|
|
title,
|
|
description,
|
|
score,
|
|
maxScore,
|
|
)
|
|
if err != nil {
|
|
h.logger.Error("Échec d'émission du certificat", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec d'émission du certificat")
|
|
return
|
|
}
|
|
|
|
response.Success(c, certificate, "Certificat émis avec succès")
|
|
}
|
|
|
|
// TUTORIALS HANDLERS
|
|
|
|
// CreateTutorial crée un nouveau tutoriel
|
|
func (h *Handler) CreateTutorial(c *gin.Context) {
|
|
_, exists := common.GetUserIDFromContext(c)
|
|
if !exists {
|
|
response.Error(c, http.StatusUnauthorized, "Utilisateur non authentifié")
|
|
return
|
|
}
|
|
|
|
var req CreateTutorialRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, http.StatusBadRequest, "Données de requête invalides")
|
|
return
|
|
}
|
|
|
|
tutorial, err := h.tutorialManager.CreateTutorial(
|
|
c.Request.Context(),
|
|
req.Title,
|
|
req.Description,
|
|
req.Author,
|
|
req.Category,
|
|
req.VideoURL,
|
|
req.Thumbnail,
|
|
req.Language,
|
|
req.Duration,
|
|
req.Quality,
|
|
req.IsFree,
|
|
req.Tags,
|
|
)
|
|
if err != nil {
|
|
h.logger.Error("Échec de création du tutoriel", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec de création du tutoriel")
|
|
return
|
|
}
|
|
|
|
response.Success(c, tutorial, "Tutoriel créé avec succès")
|
|
}
|
|
|
|
// GetTutorial récupère un tutoriel par son ID
|
|
func (h *Handler) GetTutorial(c *gin.Context) {
|
|
tutorialID := c.Param("tutorial_id")
|
|
if tutorialID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de tutoriel requis")
|
|
return
|
|
}
|
|
|
|
tutorial, err := h.tutorialManager.GetTutorial(c.Request.Context(), tutorialID)
|
|
if err != nil {
|
|
h.logger.Error("Échec de récupération du tutoriel", zap.Error(err))
|
|
response.Error(c, http.StatusNotFound, "Tutoriel non trouvé")
|
|
return
|
|
}
|
|
|
|
// Incrémenter les vues
|
|
go func() {
|
|
if err := h.tutorialManager.IncrementViews(c.Request.Context(), tutorialID); err != nil {
|
|
h.logger.Error("Échec d'incrémentation des vues", zap.Error(err))
|
|
}
|
|
}()
|
|
|
|
response.Success(c, tutorial, "Tutoriel récupéré avec succès")
|
|
}
|
|
|
|
// ListTutorials liste tous les tutoriels disponibles
|
|
func (h *Handler) ListTutorials(c *gin.Context) {
|
|
filters := make(map[string]interface{})
|
|
|
|
if category := c.Query("category"); category != "" {
|
|
filters["category"] = category
|
|
}
|
|
if isPublished := c.Query("is_published"); isPublished != "" {
|
|
if published, err := strconv.ParseBool(isPublished); err == nil {
|
|
filters["is_published"] = published
|
|
}
|
|
}
|
|
if isFree := c.Query("is_free"); isFree != "" {
|
|
if free, err := strconv.ParseBool(isFree); err == nil {
|
|
filters["is_free"] = free
|
|
}
|
|
}
|
|
if language := c.Query("language"); language != "" {
|
|
filters["language"] = language
|
|
}
|
|
if author := c.Query("author"); author != "" {
|
|
filters["author"] = author
|
|
}
|
|
|
|
tutorials, err := h.tutorialManager.ListTutorials(c.Request.Context(), filters)
|
|
if err != nil {
|
|
h.logger.Error("Échec de récupération des tutoriels", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec de récupération des tutoriels")
|
|
return
|
|
}
|
|
|
|
response.Success(c, tutorials, "Tutoriels récupérés avec succès")
|
|
}
|
|
|
|
// SearchTutorials recherche des tutoriels
|
|
func (h *Handler) SearchTutorials(c *gin.Context) {
|
|
query := c.Query("q")
|
|
if query == "" {
|
|
response.Error(c, http.StatusBadRequest, "Terme de recherche requis")
|
|
return
|
|
}
|
|
|
|
filters := make(map[string]interface{})
|
|
if category := c.Query("category"); category != "" {
|
|
filters["category"] = category
|
|
}
|
|
if isPublished := c.Query("is_published"); isPublished != "" {
|
|
if published, err := strconv.ParseBool(isPublished); err == nil {
|
|
filters["is_published"] = published
|
|
}
|
|
}
|
|
if isFree := c.Query("is_free"); isFree != "" {
|
|
if free, err := strconv.ParseBool(isFree); err == nil {
|
|
filters["is_free"] = free
|
|
}
|
|
}
|
|
|
|
tutorials, err := h.tutorialManager.SearchTutorials(c.Request.Context(), query, filters)
|
|
if err != nil {
|
|
h.logger.Error("Échec de recherche des tutoriels", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec de recherche des tutoriels")
|
|
return
|
|
}
|
|
|
|
response.Success(c, tutorials, "Recherche de tutoriels terminée")
|
|
}
|
|
|
|
// UpdateTutorial met à jour un tutoriel
|
|
func (h *Handler) UpdateTutorial(c *gin.Context) {
|
|
_, exists := common.GetUserIDFromContext(c)
|
|
if !exists {
|
|
response.Error(c, http.StatusUnauthorized, "Utilisateur non authentifié")
|
|
return
|
|
}
|
|
|
|
tutorialID := c.Param("tutorial_id")
|
|
if tutorialID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de tutoriel requis")
|
|
return
|
|
}
|
|
|
|
var req UpdateTutorialRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, http.StatusBadRequest, "Données de requête invalides")
|
|
return
|
|
}
|
|
|
|
updates := make(map[string]interface{})
|
|
if req.Title != nil {
|
|
updates["title"] = *req.Title
|
|
}
|
|
if req.Description != nil {
|
|
updates["description"] = *req.Description
|
|
}
|
|
if req.Author != nil {
|
|
updates["author"] = *req.Author
|
|
}
|
|
if req.Category != nil {
|
|
updates["category"] = *req.Category
|
|
}
|
|
if req.VideoURL != nil {
|
|
updates["video_url"] = *req.VideoURL
|
|
}
|
|
if req.Thumbnail != nil {
|
|
updates["thumbnail"] = *req.Thumbnail
|
|
}
|
|
if req.Duration != nil {
|
|
updates["duration"] = *req.Duration
|
|
}
|
|
if req.Quality != nil {
|
|
updates["quality"] = *req.Quality
|
|
}
|
|
if req.IsPublished != nil {
|
|
updates["is_published"] = *req.IsPublished
|
|
}
|
|
if req.Tags != nil {
|
|
updates["tags"] = req.Tags
|
|
}
|
|
|
|
tutorial, err := h.tutorialManager.UpdateTutorial(c.Request.Context(), tutorialID, updates)
|
|
if err != nil {
|
|
h.logger.Error("Échec de mise à jour du tutoriel", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec de mise à jour du tutoriel")
|
|
return
|
|
}
|
|
|
|
response.Success(c, tutorial, "Tutoriel mis à jour avec succès")
|
|
}
|
|
|
|
// DeleteTutorial supprime un tutoriel
|
|
func (h *Handler) DeleteTutorial(c *gin.Context) {
|
|
_, exists := common.GetUserIDFromContext(c)
|
|
if !exists {
|
|
response.Error(c, http.StatusUnauthorized, "Utilisateur non authentifié")
|
|
return
|
|
}
|
|
|
|
tutorialID := c.Param("tutorial_id")
|
|
if tutorialID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de tutoriel requis")
|
|
return
|
|
}
|
|
|
|
err := h.tutorialManager.DeleteTutorial(c.Request.Context(), tutorialID)
|
|
if err != nil {
|
|
h.logger.Error("Échec de suppression du tutoriel", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec de suppression du tutoriel")
|
|
return
|
|
}
|
|
|
|
response.Success(c, nil, "Tutoriel supprimé avec succès")
|
|
}
|
|
|
|
// AddTutorialStep ajoute une étape à un tutoriel
|
|
func (h *Handler) AddTutorialStep(c *gin.Context) {
|
|
_, exists := common.GetUserIDFromContext(c)
|
|
if !exists {
|
|
response.Error(c, http.StatusUnauthorized, "Utilisateur non authentifié")
|
|
return
|
|
}
|
|
|
|
tutorialID := c.Param("tutorial_id")
|
|
if tutorialID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de tutoriel requis")
|
|
return
|
|
}
|
|
|
|
var req AddTutorialStepRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, http.StatusBadRequest, "Données de requête invalides")
|
|
return
|
|
}
|
|
|
|
step, err := h.tutorialManager.AddTutorialStep(
|
|
c.Request.Context(),
|
|
tutorialID,
|
|
req.Title,
|
|
req.Description,
|
|
req.Content,
|
|
req.Order,
|
|
req.Timestamp,
|
|
req.IsFree,
|
|
)
|
|
if err != nil {
|
|
h.logger.Error("Échec d'ajout d'étape de tutoriel", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec d'ajout d'étape de tutoriel")
|
|
return
|
|
}
|
|
|
|
response.Success(c, step, "Étape de tutoriel ajoutée avec succès")
|
|
}
|
|
|
|
// GetTutorialSteps récupère les étapes d'un tutoriel
|
|
func (h *Handler) GetTutorialSteps(c *gin.Context) {
|
|
tutorialID := c.Param("tutorial_id")
|
|
if tutorialID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de tutoriel requis")
|
|
return
|
|
}
|
|
|
|
steps, err := h.tutorialManager.GetTutorialSteps(c.Request.Context(), tutorialID)
|
|
if err != nil {
|
|
h.logger.Error("Échec de récupération des étapes", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec de récupération des étapes")
|
|
return
|
|
}
|
|
|
|
response.Success(c, steps, "Étapes récupérées avec succès")
|
|
}
|
|
|
|
// AddTutorialComment ajoute un commentaire à un tutoriel
|
|
func (h *Handler) AddTutorialComment(c *gin.Context) {
|
|
userID, exists := common.GetUserIDFromContext(c)
|
|
if !exists {
|
|
response.Error(c, http.StatusUnauthorized, "Utilisateur non authentifié")
|
|
return
|
|
}
|
|
|
|
tutorialID := c.Param("tutorial_id")
|
|
if tutorialID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de tutoriel requis")
|
|
return
|
|
}
|
|
|
|
var req AddTutorialCommentRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, http.StatusBadRequest, "Données de requête invalides")
|
|
return
|
|
}
|
|
|
|
username, _ := common.GetUsernameFromContext(c)
|
|
if username == "" {
|
|
username = "Utilisateur anonyme"
|
|
}
|
|
|
|
comment, err := h.tutorialManager.AddTutorialComment(
|
|
c.Request.Context(),
|
|
tutorialID,
|
|
userID.String(),
|
|
username,
|
|
req.Content,
|
|
req.Rating,
|
|
)
|
|
if err != nil {
|
|
h.logger.Error("Échec d'ajout de commentaire", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec d'ajout de commentaire")
|
|
return
|
|
}
|
|
|
|
response.Success(c, comment, "Commentaire ajouté avec succès")
|
|
}
|
|
|
|
// GetTutorialComments récupère les commentaires d'un tutoriel
|
|
func (h *Handler) GetTutorialComments(c *gin.Context) {
|
|
tutorialID := c.Param("tutorial_id")
|
|
if tutorialID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de tutoriel requis")
|
|
return
|
|
}
|
|
|
|
comments, err := h.tutorialManager.GetTutorialComments(c.Request.Context(), tutorialID)
|
|
if err != nil {
|
|
h.logger.Error("Échec de récupération des commentaires", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec de récupération des commentaires")
|
|
return
|
|
}
|
|
|
|
response.Success(c, comments, "Commentaires récupérés avec succès")
|
|
}
|
|
|
|
// LikeTutorial ajoute un like à un tutoriel
|
|
func (h *Handler) LikeTutorial(c *gin.Context) {
|
|
tutorialID := c.Param("tutorial_id")
|
|
if tutorialID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de tutoriel requis")
|
|
return
|
|
}
|
|
|
|
err := h.tutorialManager.LikeTutorial(c.Request.Context(), tutorialID)
|
|
if err != nil {
|
|
h.logger.Error("Échec d'ajout de like", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec d'ajout de like")
|
|
return
|
|
}
|
|
|
|
response.Success(c, nil, "Like ajouté avec succès")
|
|
}
|
|
|
|
// DislikeTutorial ajoute un dislike à un tutoriel
|
|
func (h *Handler) DislikeTutorial(c *gin.Context) {
|
|
tutorialID := c.Param("tutorial_id")
|
|
if tutorialID == "" {
|
|
response.Error(c, http.StatusBadRequest, "ID de tutoriel requis")
|
|
return
|
|
}
|
|
|
|
err := h.tutorialManager.DislikeTutorial(c.Request.Context(), tutorialID)
|
|
if err != nil {
|
|
h.logger.Error("Échec d'ajout de dislike", zap.Error(err))
|
|
response.Error(c, http.StatusInternalServerError, "Échec d'ajout de dislike")
|
|
return
|
|
}
|
|
|
|
response.Success(c, nil, "Dislike ajouté avec succès")
|
|
}
|