95 lines
3.0 KiB
Go
95 lines
3.0 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
|
|
"cmr-backend/internal/apperr"
|
|
"cmr-backend/internal/store/postgres"
|
|
)
|
|
|
|
type ResultService struct {
|
|
store *postgres.Store
|
|
}
|
|
|
|
type SessionResultView struct {
|
|
Session EntrySessionSummary `json:"session"`
|
|
Result ResultSummaryPayload `json:"result"`
|
|
}
|
|
|
|
type ResultSummaryPayload struct {
|
|
Status string `json:"status"`
|
|
FinalDurationSec *int `json:"finalDurationSec,omitempty"`
|
|
FinalScore *int `json:"finalScore,omitempty"`
|
|
CompletedControls *int `json:"completedControls,omitempty"`
|
|
TotalControls *int `json:"totalControls,omitempty"`
|
|
DistanceMeters *float64 `json:"distanceMeters,omitempty"`
|
|
AverageSpeedKmh *float64 `json:"averageSpeedKmh,omitempty"`
|
|
MaxHeartRateBpm *int `json:"maxHeartRateBpm,omitempty"`
|
|
Summary map[string]any `json:"summary,omitempty"`
|
|
}
|
|
|
|
func NewResultService(store *postgres.Store) *ResultService {
|
|
return &ResultService{store: store}
|
|
}
|
|
|
|
func (s *ResultService) GetSessionResult(ctx context.Context, sessionPublicID, userID string) (*SessionResultView, error) {
|
|
record, err := s.store.GetSessionResultByPublicID(ctx, sessionPublicID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if record == nil {
|
|
return nil, apperr.New(http.StatusNotFound, "session_not_found", "session not found")
|
|
}
|
|
if userID != "" && record.UserID != userID {
|
|
return nil, apperr.New(http.StatusForbidden, "session_forbidden", "session does not belong to current user")
|
|
}
|
|
return buildSessionResultView(record), nil
|
|
}
|
|
|
|
func (s *ResultService) ListMyResults(ctx context.Context, userID string, limit int) ([]SessionResultView, error) {
|
|
if userID == "" {
|
|
return nil, apperr.New(http.StatusUnauthorized, "unauthorized", "user is required")
|
|
}
|
|
|
|
records, err := s.store.ListSessionResultsByUserID(ctx, userID, limit)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
results := make([]SessionResultView, 0, len(records))
|
|
for i := range records {
|
|
results = append(results, *buildSessionResultView(&records[i]))
|
|
}
|
|
return results, nil
|
|
}
|
|
|
|
func buildSessionResultView(record *postgres.SessionResultRecord) *SessionResultView {
|
|
view := &SessionResultView{
|
|
Session: buildEntrySessionSummary(&record.Session),
|
|
Result: ResultSummaryPayload{
|
|
Status: record.Status,
|
|
},
|
|
}
|
|
|
|
if record.Result != nil {
|
|
view.Result.Status = record.Result.ResultStatus
|
|
view.Result.FinalDurationSec = record.Result.FinalDurationSec
|
|
view.Result.FinalScore = record.Result.FinalScore
|
|
view.Result.CompletedControls = record.Result.CompletedControls
|
|
view.Result.TotalControls = record.Result.TotalControls
|
|
view.Result.DistanceMeters = record.Result.DistanceMeters
|
|
view.Result.AverageSpeedKmh = record.Result.AverageSpeedKmh
|
|
view.Result.MaxHeartRateBpm = record.Result.MaxHeartRateBpm
|
|
if record.Result.SummaryJSON != "" {
|
|
summary := map[string]any{}
|
|
if err := json.Unmarshal([]byte(record.Result.SummaryJSON), &summary); err == nil && len(summary) > 0 {
|
|
view.Result.Summary = summary
|
|
}
|
|
}
|
|
}
|
|
|
|
return view
|
|
}
|