diff --git a/typing-server/api/handler/score.go b/typing-server/api/handler/score.go index b061ab3..063d7d8 100644 --- a/typing-server/api/handler/score.go +++ b/typing-server/api/handler/score.go @@ -46,28 +46,26 @@ func GetScoresRanking(w http.ResponseWriter, r *http.Request) { func PostScore(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - userIDStr := r.URL.Query().Get("user_id") - userID, err := uuid.Parse(userIDStr) - if err != nil { - http.Error(w, "Invalid user_id", http.StatusBadRequest) - return + // リクエストボディから値を取得 + var requestBody struct { + UserID string `json:"user_id"` + Keystrokes int `json:"keystrokes"` + Accuracy float64 `json:"accuracy"` } - - keystrokesStr := r.URL.Query().Get("keystrokes") - keystrokes, err := strconv.Atoi(keystrokesStr) - if err != nil { - http.Error(w, "Invalid keystrokes", http.StatusBadRequest) + if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil { + http.Error(w, "Invalid request body", http.StatusBadRequest) return } - accuracyStr := r.URL.Query().Get("accuracy") - accuracy, err := strconv.ParseFloat(accuracyStr, 64) + // user_idをUUIDに変換 + userID, err := uuid.Parse(requestBody.UserID) if err != nil { - http.Error(w, "Invalid accuracy", http.StatusBadRequest) + http.Error(w, "Invalid user_id", http.StatusBadRequest) return } - if err := service.CreateScore(ctx, entClient, userID, keystrokes, accuracy); err != nil { + // スコアを作成 + if err := service.CreateScore(ctx, entClient, userID, requestBody.Keystrokes, requestBody.Accuracy); 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 aa6ff5a..fadff4b 100644 --- a/typing-server/api/repository/score.go +++ b/typing-server/api/repository/score.go @@ -2,6 +2,7 @@ package repository import ( "context" + "database/sql" "fmt" "github.com/google/uuid" @@ -90,10 +91,27 @@ func GetScoresRanking(ctx context.Context, client *ent.Client, sortBy string, st return rankings, nil } - func CreateScore(ctx context.Context, client *ent.Client, userID uuid.UUID, keystrokes int, accuracy float64) error { - // Create a new score - createdScore, err := client.Score.Create(). + // トランザクションを開始 + tx, err := client.Tx(ctx) + if err != nil { + return err + } + defer func() { + // txがコミット済みの場合はロールバックしない + if tx == nil { + return + } + if err := tx.Rollback(); err != nil { + if err != sql.ErrTxDone { + return + } + return + } + }() + + // 新しいスコアを作成 + createdScore, err := tx.Score.Create(). SetKeystrokes(keystrokes). SetAccuracy(accuracy). SetUserID(userID). @@ -102,46 +120,70 @@ func CreateScore(ctx context.Context, client *ent.Client, userID uuid.UUID, keys return err } + // キーストロークと精度のチェック if keystrokes < 120 || accuracy < 0.95 { - return nil + return tx.Commit() } - // Get the user - user, err := client.User.Query().Where(user.ID(userID)).Only(ctx) + // ユーザーを取得 + user, err := tx.User.Query().Where(user.ID(userID)).Only(ctx) if err != nil { return err } - // Check if the new score has the maximum keystrokes + // 最大キーストロークと最大精度のスコアを取得 + maxKeystrokeScore, maxAccuracyScore, err := getMaxScores(ctx, user) + if err != nil { + return err + } + + // 以前の最大スコアのフラグを更新 + err = updateMaxScoreFlags(ctx, maxKeystrokeScore, maxAccuracyScore, createdScore) + if err != nil { + return err + } + + // トランザクションをコミット + return tx.Commit() +} + +func getMaxScores(ctx context.Context, user *ent.User) (*ent.Score, *ent.Score, error) { + // 最大キーストロークのスコアを取得 maxKeystrokeScore, err := user.QueryScores().Where(score.IsMaxKeystrokes(true)).Only(ctx) if err != nil && !ent.IsNotFound(err) { - return err + return nil, nil, err } - isMaxKeystrokes := maxKeystrokeScore == nil || createdScore.Keystrokes >= maxKeystrokeScore.Keystrokes - // Check if the new score has the maximum accuracy + // 最大精度のスコアを取得 maxAccuracyScore, err := user.QueryScores().Where(score.IsMaxAccuracy(true)).Only(ctx) if err != nil && !ent.IsNotFound(err) { - return err + return nil, nil, err } + + return maxKeystrokeScore, maxAccuracyScore, nil +} + +func updateMaxScoreFlags(ctx context.Context, maxKeystrokeScore, maxAccuracyScore, createdScore *ent.Score) error { + isMaxKeystrokes := maxKeystrokeScore == nil || createdScore.Keystrokes >= maxKeystrokeScore.Keystrokes isMaxAccuracy := maxAccuracyScore == nil || createdScore.Accuracy >= maxAccuracyScore.Accuracy - // Update the flags of the previous maximum scores - if maxKeystrokeScore != nil && !isMaxKeystrokes { - err = maxKeystrokeScore.Update().SetIsMaxKeystrokes(false).Exec(ctx) + // 以前の最大スコアのフラグを更新 + if maxKeystrokeScore != nil && isMaxKeystrokes { + err := maxKeystrokeScore.Update().SetIsMaxKeystrokes(false).Exec(ctx) if err != nil { return err } } - if maxAccuracyScore != nil && !isMaxAccuracy { - err = maxAccuracyScore.Update().SetIsMaxAccuracy(false).Exec(ctx) + + if maxAccuracyScore != nil && isMaxAccuracy { + err := maxAccuracyScore.Update().SetIsMaxAccuracy(false).Exec(ctx) if err != nil { return err } } - // Update the score with the maximum flags - err = createdScore.Update(). + // 作成したスコアの最大フラグを更新 + err := createdScore.Update(). SetIsMaxKeystrokes(isMaxKeystrokes). SetIsMaxAccuracy(isMaxAccuracy). Exec(ctx)