[BE-API-036] be-api: Implement track analytics dashboard endpoint

This commit is contained in:
senke 2025-12-24 14:48:28 +01:00
parent eb92dfeb11
commit 0ed89e62fa
3 changed files with 75 additions and 2 deletions

View file

@ -2745,7 +2745,7 @@
"description": "GET /api/v1/analytics/tracks/:id returns comprehensive track analytics",
"owner": "backend",
"estimated_hours": 6,
"status": "todo",
"status": "completed",
"files_involved": [],
"implementation_steps": [
{
@ -2766,7 +2766,9 @@
"Unit tests",
"Integration tests"
],
"notes": ""
"notes": "",
"completed_at": "2025-12-24T14:48:27.682601",
"implementation_notes": "Implemented GET /api/v1/analytics/tracks/:id endpoint. Added GetTrackAnalyticsDashboard method to AnalyticsHandler that returns comprehensive track analytics including stats (total plays, unique listeners, average duration, completion rate) and plays over time (30 days). Route registered in setupAnalyticsRoutes."
},
{
"id": "BE-API-037",

View file

@ -852,6 +852,8 @@ func (r *APIRouter) setupAnalyticsRoutes(router *gin.RouterGroup) {
{
// BE-API-035: Analytics events endpoint
analytics.POST("/events", analyticsHandler.RecordEvent)
// BE-API-036: Track analytics dashboard endpoint
analytics.GET("/tracks/:id", analyticsHandler.GetTrackAnalyticsDashboard)
}
}

View file

@ -256,6 +256,75 @@ func (h *AnalyticsHandler) GetUserStats(c *gin.Context) {
RespondSuccess(c, http.StatusOK, gin.H{"stats": stats})
}
// GetTrackAnalyticsDashboard gère la récupération du dashboard d'analytics complet pour un track
// BE-API-036: GET /api/v1/analytics/tracks/:id returns comprehensive track analytics
// @Summary Get Track Analytics Dashboard
// @Description Get comprehensive analytics dashboard for a track
// @Tags Analytics
// @Accept json
// @Produce json
// @Security BearerAuth
// @Param id path string true "Track ID"
// @Success 200 {object} APIResponse{data=object{dashboard=object}}
// @Failure 400 {object} APIResponse "Validation Error"
// @Failure 404 {object} APIResponse "Track not found"
// @Failure 500 {object} APIResponse "Internal Error"
// @Router /analytics/tracks/{id} [get]
func (h *AnalyticsHandler) GetTrackAnalyticsDashboard(c *gin.Context) {
trackIDStr := c.Param("id")
if trackIDStr == "" {
RespondWithAppError(c, apperrors.NewValidationError("track id is required"))
return
}
trackID, err := uuid.Parse(trackIDStr)
if err != nil {
RespondWithAppError(c, apperrors.NewValidationError("invalid track id"))
return
}
// Récupérer les statistiques de base
stats, err := h.analyticsService.GetTrackStats(c.Request.Context(), trackID)
if err != nil {
if err.Error() == "track not found" {
RespondWithAppError(c, apperrors.NewNotFoundError("track"))
return
}
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to get track stats", err))
return
}
// Récupérer les lectures sur une période (30 derniers jours)
startDate := time.Now().AddDate(0, 0, -30)
endDate := time.Now()
playsOverTime, err := h.analyticsService.GetPlaysOverTime(c.Request.Context(), trackID, startDate, endDate, "day")
if err != nil {
// Ne pas échouer si on ne peut pas récupérer les données temporelles
playsOverTime = []services.PlayTimePoint{}
}
// Construire le dashboard complet
dashboard := gin.H{
"track_id": trackID.String(),
"stats": gin.H{
"total_plays": stats.TotalPlays,
"unique_listeners": stats.UniqueListeners,
"average_duration": stats.AverageDuration,
"completion_rate": stats.CompletionRate,
},
"plays_over_time": playsOverTime,
"period": gin.H{
"start_date": startDate.Format(time.RFC3339),
"end_date": endDate.Format(time.RFC3339),
"days": 30,
},
}
RespondSuccess(c, http.StatusOK, gin.H{
"dashboard": dashboard,
})
}
// RecordEventRequest représente la requête pour enregistrer un événement analytics personnalisé
// BE-API-035: POST /api/v1/analytics/events to record custom analytics events
type RecordEventRequest struct {