Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

import static inha.gdgoc.domain.game.controller.message.GameUserMessage.GAME_RANK_RETRIEVED_SUCCESS;
import static inha.gdgoc.domain.game.controller.message.GameUserMessage.GAME_RANK_SAVE_SUCCESS;
import static inha.gdgoc.domain.game.controller.message.GameUserMessage.MBTI_RESULT_UPSERT_SUCCESS;
import static inha.gdgoc.domain.game.controller.message.GameUserMessage.MBTI_RESULT_STATS_RETRIEVED_SUCCESS;

import inha.gdgoc.domain.game.dto.request.GameUserRequest;
import inha.gdgoc.domain.game.dto.request.MbtiResultRequest;
import inha.gdgoc.domain.game.dto.response.GameUserResponse;
import inha.gdgoc.domain.game.dto.response.MbtiResultResponse;
import inha.gdgoc.domain.game.dto.response.MbtiStatsResponse;
import inha.gdgoc.domain.game.service.GameUserService;
import inha.gdgoc.domain.game.service.MbtiResultService;
import inha.gdgoc.global.dto.response.ApiResponse;
import java.util.List;
import lombok.RequiredArgsConstructor;
Expand All @@ -22,6 +28,7 @@
public class GameUserController {

private final GameUserService gameUserService;
private final MbtiResultService mbtiResultService;

@PostMapping("/result")
public ResponseEntity<ApiResponse<List<GameUserResponse>, Void>> saveGameResult(
Expand All @@ -38,4 +45,18 @@ public ResponseEntity<ApiResponse<List<GameUserResponse>, Void>> getUserRankings

return ResponseEntity.ok(ApiResponse.ok(GAME_RANK_RETRIEVED_SUCCESS, response));
}

@PostMapping("/mbti/result")
public ResponseEntity<ApiResponse<MbtiResultResponse, Void>> upsertMbtiResult(
@RequestBody MbtiResultRequest request
) {
MbtiResultResponse response = mbtiResultService.upsertMbtiResult(request);
return ResponseEntity.ok(ApiResponse.ok(MBTI_RESULT_UPSERT_SUCCESS, response));
}

@GetMapping("/mbti/result/stats")
public ResponseEntity<ApiResponse<MbtiStatsResponse, Void>> getMbtiStats() {
MbtiStatsResponse response = mbtiResultService.getMbtiStats();
return ResponseEntity.ok(ApiResponse.ok(MBTI_RESULT_STATS_RETRIEVED_SUCCESS, response));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
public class GameUserMessage {
public static final String GAME_RANK_SAVE_SUCCESS = "성공적으로 유저 랭킹 정보를 저장했습니다.";
public static final String GAME_RANK_RETRIEVED_SUCCESS = "성공적으로 랭킹 정보를 반환했습니다.";
public static final String MBTI_RESULT_UPSERT_SUCCESS = "성공적으로 MBTI 결과를 저장했습니다.";
public static final String MBTI_RESULT_STATS_RETRIEVED_SUCCESS = "성공적으로 MBTI 통계를 반환했습니다.";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package inha.gdgoc.domain.game.dto.request;

import inha.gdgoc.domain.game.entity.MbtiResult;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
public class MbtiResultRequest {
private String name;
private String studentId;
private String mbtiType;

public MbtiResult toEntity() {
return MbtiResult.builder()
.name(name)
.studentId(studentId)
.mbtiType(mbtiType)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package inha.gdgoc.domain.game.dto.response;

import inha.gdgoc.domain.game.entity.MbtiResult;
import lombok.Getter;

@Getter
public class MbtiResultResponse {
private final Long id;
private final String name;
private final String studentId;
private final String mbtiType;

public MbtiResultResponse(MbtiResult mbtiResult) {
this.id = mbtiResult.getId();
this.name = mbtiResult.getName();
this.studentId = mbtiResult.getStudentId();
this.mbtiType = mbtiResult.getMbtiType();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package inha.gdgoc.domain.game.dto.response;

import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class MbtiStatsResponse {
private final long totalCount;
private final List<MbtiTypeCountResponse> typeCounts;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package inha.gdgoc.domain.game.dto.response;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class MbtiTypeCountResponse {
private final String mbtiType;
private final long count;
}
47 changes: 47 additions & 0 deletions src/main/java/inha/gdgoc/domain/game/entity/MbtiResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package inha.gdgoc.domain.game.entity;

import inha.gdgoc.global.entity.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(
name = "mbti_result",
uniqueConstraints = {
@UniqueConstraint(name = "uk_mbti_result_name_student_id", columnNames = {"name", "student_id"})
}
)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Getter
@Builder
public class MbtiResult extends BaseEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;

@Column(name = "name", nullable = false, length = 100)
private String name;

@Column(name = "student_id", nullable = false, length = 20)
private String studentId;

@Column(name = "mbti_type", nullable = false, length = 4)
private String mbtiType;

public void updateMbtiType(String mbtiType) {
this.mbtiType = mbtiType;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package inha.gdgoc.domain.game.repository;

import inha.gdgoc.domain.game.entity.MbtiResult;
import inha.gdgoc.domain.game.repository.projection.MbtiTypeCountProjection;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface MbtiResultRepository extends JpaRepository<MbtiResult, Long> {
Optional<MbtiResult> findByNameAndStudentId(String name, String studentId);

@Query("select m.mbtiType as mbtiType, count(m) as count from MbtiResult m group by m.mbtiType")
List<MbtiTypeCountProjection> countByMbtiType();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package inha.gdgoc.domain.game.repository.projection;

public interface MbtiTypeCountProjection {
String getMbtiType();
long getCount();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package inha.gdgoc.domain.game.service;

import inha.gdgoc.domain.game.dto.request.MbtiResultRequest;
import inha.gdgoc.domain.game.dto.response.MbtiResultResponse;
import inha.gdgoc.domain.game.dto.response.MbtiStatsResponse;
import inha.gdgoc.domain.game.dto.response.MbtiTypeCountResponse;
import inha.gdgoc.domain.game.entity.MbtiResult;
import inha.gdgoc.domain.game.repository.MbtiResultRepository;
import java.util.Comparator;
import java.util.List;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@RequiredArgsConstructor
@Service
public class MbtiResultService {

private final MbtiResultRepository mbtiResultRepository;

@Transactional
public MbtiResultResponse upsertMbtiResult(MbtiResultRequest request) {
MbtiResult result = mbtiResultRepository.findByNameAndStudentId(request.getName(), request.getStudentId())
.map(existing -> {
existing.updateMbtiType(request.getMbtiType());
return existing;
})
.orElseGet(() -> mbtiResultRepository.save(request.toEntity()));

return new MbtiResultResponse(result);
}

public MbtiStatsResponse getMbtiStats() {
long totalCount = mbtiResultRepository.count();
List<MbtiTypeCountResponse> typeCounts = mbtiResultRepository.countByMbtiType().stream()
.map(item -> new MbtiTypeCountResponse(item.getMbtiType(), item.getCount()))
.sorted(Comparator.comparingLong(MbtiTypeCountResponse::getCount).reversed()
.thenComparing(MbtiTypeCountResponse::getMbtiType))
.toList();

return new MbtiStatsResponse(totalCount, typeCounts);
}
}
11 changes: 11 additions & 0 deletions src/main/resources/db/migration/V20260306__create_mbti_result.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CREATE TABLE IF NOT EXISTS mbti_result (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
student_id VARCHAR(20) NOT NULL,
mbti_type VARCHAR(4) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
);

CREATE UNIQUE INDEX IF NOT EXISTS uk_mbti_result_name_student_id
ON mbti_result (name, student_id);
Loading