diff --git a/typing-server/api/handler/score.go b/typing-server/api/handler/score.go index 543e75c..07ddd13 100644 --- a/typing-server/api/handler/score.go +++ b/typing-server/api/handler/score.go @@ -5,6 +5,7 @@ import ( "net/http" "strconv" + "github.com/go-chi/chi/v5" "github.com/google/uuid" "github.com/su-its/typing/typing-server/api/service" "github.com/su-its/typing/typing-server/domain/model" @@ -79,3 +80,43 @@ func PostScore(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusCreated) } + +func GetMyScoreRanking(w http.ResponseWriter, r *http.Request) { + ctx := r.Context() + + userID := chi.URLParam(r, "user-id") + if userID == "" { + http.Error(w, "user-id is required", http.StatusBadRequest) + return + } + + userUUID, err := uuid.Parse(userID) + if err != nil { + http.Error(w, "Invalid user-id", http.StatusBadRequest) + return + } + + sortBy := r.URL.Query().Get("sort_by") + if sortBy == "" { + sortBy = "keystrokes" + } + + currentRank, err := service.GetMyScoreRanking(ctx, entClient, userUUID, sortBy) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + response := struct { + CurrentRank int `json:"current-rank"` + }{ + CurrentRank: currentRank, + } + + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(response) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } +} diff --git a/typing-server/api/handler/user.go b/typing-server/api/handler/user.go index 79cca14..2fb0c88 100644 --- a/typing-server/api/handler/user.go +++ b/typing-server/api/handler/user.go @@ -4,8 +4,6 @@ import ( "encoding/json" "net/http" - "github.com/go-chi/chi/v5" - "github.com/google/uuid" "github.com/su-its/typing/typing-server/api/service" ) @@ -31,43 +29,3 @@ func GetUser(w http.ResponseWriter, r *http.Request) { return } } - -func GetMyScoreRanking(w http.ResponseWriter, r *http.Request) { - ctx := r.Context() - - userID := chi.URLParam(r, "user-id") - if userID == "" { - http.Error(w, "user-id is required", http.StatusBadRequest) - return - } - - userUUID, err := uuid.Parse(userID) - if err != nil { - http.Error(w, "Invalid user-id", http.StatusBadRequest) - return - } - - sortBy := r.URL.Query().Get("sort_by") - if sortBy == "" { - sortBy = "keystrokes" - } - - currentRank, err := service.GetMyScoreRanking(ctx, entClient, userUUID, sortBy) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - response := struct { - CurrentRank int `json:"current-rank"` - }{ - CurrentRank: currentRank, - } - - w.Header().Set("Content-Type", "application/json") - err = json.NewEncoder(w).Encode(response) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } -} diff --git a/typing-server/api/repository/score.go b/typing-server/api/repository/score.go index 47ce77d..3d151be 100644 --- a/typing-server/api/repository/score.go +++ b/typing-server/api/repository/score.go @@ -199,3 +199,32 @@ func updateMaxScoreFlags(ctx context.Context, maxKeystrokeScore, maxAccuracyScor return nil } + +func GetMaxScoreByUserID(ctx context.Context, client *ent.Client, userID uuid.UUID, sortBy string) (*ent.Score, error) { + query := client.Score.Query(). + WithUser(). + Where( + score.And( + score.UserID(userID), + score.KeystrokesGTE(120), + score.AccuracyGTE(0.95), + ), + ). + Order(ent.Desc(sortBy)) + + switch sortBy { + case "accuracy": + query = query.Where(score.IsMaxAccuracy(true)) + case "keystrokes": + query = query.Where(score.IsMaxKeystrokes(true)) + default: + return nil, fmt.Errorf("invalid sort by parameter: %s", sortBy) + } + + maxScore, err := query.First(ctx) + if err != nil { + return nil, err + } + + return maxScore, nil +} diff --git a/typing-server/api/repository/user.go b/typing-server/api/repository/user.go index f3c0876..9209796 100644 --- a/typing-server/api/repository/user.go +++ b/typing-server/api/repository/user.go @@ -2,10 +2,8 @@ package repository import ( "context" - "fmt" - "github.com/google/uuid" + "github.com/su-its/typing/typing-server/domain/repository/ent" - "github.com/su-its/typing/typing-server/domain/repository/ent/score" "github.com/su-its/typing/typing-server/domain/repository/ent/user" ) @@ -27,41 +25,3 @@ func GetUserByStudentNumber(ctx context.Context, client *ent.Client, studentNumb Edges: entUser.Edges, }, nil } - -func GetMaxScoreByUserID(ctx context.Context, client *ent.Client, userID uuid.UUID, sortBy string) (*ent.Score, error) { - var maxScore *ent.Score - var err error - - switch sortBy { - case "accuracy": - maxScore, err = client.Score.Query(). - Where( - score.And( - score.UserID(userID), - score.KeystrokesGTE(120), - score.AccuracyGTE(0.95), - ), - ). - Order(ent.Desc(score.FieldAccuracy)). - First(ctx) - case "keystrokes": - maxScore, err = client.Score.Query(). - Where( - score.And( - score.UserID(userID), - score.KeystrokesGTE(120), - score.AccuracyGTE(0.95), - ), - ). - Order(ent.Desc(score.FieldKeystrokes)). - First(ctx) - default: - return nil, fmt.Errorf("invalid sort by parameter: %s", sortBy) - } - - if err != nil { - return nil, err - } - - return maxScore, nil -} diff --git a/typing-server/api/service/score.go b/typing-server/api/service/score.go index e3a5248..d9951ac 100644 --- a/typing-server/api/service/score.go +++ b/typing-server/api/service/score.go @@ -3,10 +3,13 @@ package service import ( "context" + "fmt" + "github.com/google/uuid" "github.com/su-its/typing/typing-server/api/repository" "github.com/su-its/typing/typing-server/domain/model" "github.com/su-its/typing/typing-server/domain/repository/ent" + "github.com/su-its/typing/typing-server/domain/repository/ent/score" ) func GetScoresRanking(ctx context.Context, client *ent.Client, request *model.GetScoresRankingRequest) (*model.GetScoresRankingResponse, error) { @@ -24,3 +27,62 @@ func CreateScore(ctx context.Context, client *ent.Client, userID uuid.UUID, keys return nil } + +func GetMyScoreRanking(ctx context.Context, client *ent.Client, userID uuid.UUID, sortBy string) (int, error) { + // ユーザーの最大スコアを取得 + userMaxScore, err := repository.GetMaxScoreByUserID(ctx, client, userID, sortBy) + if err != nil { + if ent.IsNotFound(err) { + // ユーザーのスコアが存在しない場合は、ランキング外として0を返す + return 0, nil + } + return 0, err + } + + // ユーザーの最大スコアより上位のスコアをカウント + var rank int + + switch sortBy { + case "accuracy": + rank, err = client.Score.Query(). + Where( + score.And( + score.KeystrokesGTE(120), + score.AccuracyGTE(0.95), + score.Or( + score.AccuracyGT(userMaxScore.Accuracy), + score.And( + score.AccuracyEQ(userMaxScore.Accuracy), + score.KeystrokesGT(userMaxScore.Keystrokes), + ), + ), + ), + ). + Count(ctx) + case "keystrokes": + rank, err = client.Score.Query(). + Where( + score.And( + score.KeystrokesGTE(120), + score.AccuracyGTE(0.95), + score.Or( + score.KeystrokesGT(userMaxScore.Keystrokes), + score.And( + score.KeystrokesEQ(userMaxScore.Keystrokes), + score.AccuracyGT(userMaxScore.Accuracy), + ), + ), + ), + ). + Count(ctx) + default: + return 0, fmt.Errorf("invalid sort by parameter: %s", sortBy) + } + + if err != nil { + return 0, err + } + + // ランキングを返す + return rank + 1, nil +} diff --git a/typing-server/api/service/user.go b/typing-server/api/service/user.go index 5a17619..6c95f3f 100644 --- a/typing-server/api/service/user.go +++ b/typing-server/api/service/user.go @@ -2,12 +2,9 @@ package service import ( "context" - "fmt" - "github.com/google/uuid" "github.com/su-its/typing/typing-server/api/repository" "github.com/su-its/typing/typing-server/domain/repository/ent" - "github.com/su-its/typing/typing-server/domain/repository/ent/score" ) func GetUserByStudentNumber(ctx context.Context, client *ent.Client, studentNumber string) (*ent.User, error) { @@ -18,62 +15,3 @@ func GetUserByStudentNumber(ctx context.Context, client *ent.Client, studentNumb return user, nil } - -func GetMyScoreRanking(ctx context.Context, client *ent.Client, userID uuid.UUID, sortBy string) (int, error) { - // ユーザーの最大スコアを取得 - userMaxScore, err := repository.GetMaxScoreByUserID(ctx, client, userID, sortBy) - if err != nil { - if ent.IsNotFound(err) { - // ユーザーのスコアが存在しない場合は、ランキング外として0を返す - return 0, nil - } - return 0, err - } - - // ユーザーの最大スコアより上位のスコアをカウント - var rank int - - switch sortBy { - case "accuracy": - rank, err = client.Score.Query(). - Where( - score.And( - score.KeystrokesGTE(120), - score.AccuracyGTE(0.95), - score.Or( - score.AccuracyGT(userMaxScore.Accuracy), - score.And( - score.AccuracyEQ(userMaxScore.Accuracy), - score.KeystrokesGT(userMaxScore.Keystrokes), - ), - ), - ), - ). - Count(ctx) - case "keystrokes": - rank, err = client.Score.Query(). - Where( - score.And( - score.KeystrokesGTE(120), - score.AccuracyGTE(0.95), - score.Or( - score.KeystrokesGT(userMaxScore.Keystrokes), - score.And( - score.KeystrokesEQ(userMaxScore.Keystrokes), - score.AccuracyGT(userMaxScore.Accuracy), - ), - ), - ), - ). - Count(ctx) - default: - return 0, fmt.Errorf("invalid sort by parameter: %s", sortBy) - } - - if err != nil { - return 0, err - } - - // ランキングを返す - return rank + 1, nil -}