Skip to content

Commit 9d3d36b

Browse files
authored
Merge pull request #305 from CSE-Shaco/develop
feat(server): MBTI 결과 저장/통계 API 추가
2 parents d47ec7b + 2a7aa35 commit 9d3d36b

11 files changed

Lines changed: 208 additions & 0 deletions

File tree

src/main/java/inha/gdgoc/domain/game/controller/GameUserController.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22

33
import static inha.gdgoc.domain.game.controller.message.GameUserMessage.GAME_RANK_RETRIEVED_SUCCESS;
44
import static inha.gdgoc.domain.game.controller.message.GameUserMessage.GAME_RANK_SAVE_SUCCESS;
5+
import static inha.gdgoc.domain.game.controller.message.GameUserMessage.MBTI_RESULT_UPSERT_SUCCESS;
6+
import static inha.gdgoc.domain.game.controller.message.GameUserMessage.MBTI_RESULT_STATS_RETRIEVED_SUCCESS;
57

68
import inha.gdgoc.domain.game.dto.request.GameUserRequest;
9+
import inha.gdgoc.domain.game.dto.request.MbtiResultRequest;
710
import inha.gdgoc.domain.game.dto.response.GameUserResponse;
11+
import inha.gdgoc.domain.game.dto.response.MbtiResultResponse;
12+
import inha.gdgoc.domain.game.dto.response.MbtiStatsResponse;
813
import inha.gdgoc.domain.game.service.GameUserService;
14+
import inha.gdgoc.domain.game.service.MbtiResultService;
915
import inha.gdgoc.global.dto.response.ApiResponse;
1016
import java.util.List;
1117
import lombok.RequiredArgsConstructor;
@@ -22,6 +28,7 @@
2228
public class GameUserController {
2329

2430
private final GameUserService gameUserService;
31+
private final MbtiResultService mbtiResultService;
2532

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

3946
return ResponseEntity.ok(ApiResponse.ok(GAME_RANK_RETRIEVED_SUCCESS, response));
4047
}
48+
49+
@PostMapping("/mbti/result")
50+
public ResponseEntity<ApiResponse<MbtiResultResponse, Void>> upsertMbtiResult(
51+
@RequestBody MbtiResultRequest request
52+
) {
53+
MbtiResultResponse response = mbtiResultService.upsertMbtiResult(request);
54+
return ResponseEntity.ok(ApiResponse.ok(MBTI_RESULT_UPSERT_SUCCESS, response));
55+
}
56+
57+
@GetMapping("/mbti/result/stats")
58+
public ResponseEntity<ApiResponse<MbtiStatsResponse, Void>> getMbtiStats() {
59+
MbtiStatsResponse response = mbtiResultService.getMbtiStats();
60+
return ResponseEntity.ok(ApiResponse.ok(MBTI_RESULT_STATS_RETRIEVED_SUCCESS, response));
61+
}
4162
}

src/main/java/inha/gdgoc/domain/game/controller/message/GameUserMessage.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@
33
public class GameUserMessage {
44
public static final String GAME_RANK_SAVE_SUCCESS = "성공적으로 유저 랭킹 정보를 저장했습니다.";
55
public static final String GAME_RANK_RETRIEVED_SUCCESS = "성공적으로 랭킹 정보를 반환했습니다.";
6+
public static final String MBTI_RESULT_UPSERT_SUCCESS = "성공적으로 MBTI 결과를 저장했습니다.";
7+
public static final String MBTI_RESULT_STATS_RETRIEVED_SUCCESS = "성공적으로 MBTI 통계를 반환했습니다.";
68
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package inha.gdgoc.domain.game.dto.request;
2+
3+
import inha.gdgoc.domain.game.entity.MbtiResult;
4+
import lombok.Getter;
5+
import lombok.NoArgsConstructor;
6+
7+
@Getter
8+
@NoArgsConstructor
9+
public class MbtiResultRequest {
10+
private String name;
11+
private String studentId;
12+
private String mbtiType;
13+
14+
public MbtiResult toEntity() {
15+
return MbtiResult.builder()
16+
.name(name)
17+
.studentId(studentId)
18+
.mbtiType(mbtiType)
19+
.build();
20+
}
21+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package inha.gdgoc.domain.game.dto.response;
2+
3+
import inha.gdgoc.domain.game.entity.MbtiResult;
4+
import lombok.Getter;
5+
6+
@Getter
7+
public class MbtiResultResponse {
8+
private final Long id;
9+
private final String name;
10+
private final String studentId;
11+
private final String mbtiType;
12+
13+
public MbtiResultResponse(MbtiResult mbtiResult) {
14+
this.id = mbtiResult.getId();
15+
this.name = mbtiResult.getName();
16+
this.studentId = mbtiResult.getStudentId();
17+
this.mbtiType = mbtiResult.getMbtiType();
18+
}
19+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package inha.gdgoc.domain.game.dto.response;
2+
3+
import java.util.List;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Getter;
6+
7+
@Getter
8+
@AllArgsConstructor
9+
public class MbtiStatsResponse {
10+
private final long totalCount;
11+
private final List<MbtiTypeCountResponse> typeCounts;
12+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package inha.gdgoc.domain.game.dto.response;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Getter;
5+
6+
@Getter
7+
@AllArgsConstructor
8+
public class MbtiTypeCountResponse {
9+
private final String mbtiType;
10+
private final long count;
11+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package inha.gdgoc.domain.game.entity;
2+
3+
import inha.gdgoc.global.entity.BaseEntity;
4+
import jakarta.persistence.Column;
5+
import jakarta.persistence.Entity;
6+
import jakarta.persistence.GeneratedValue;
7+
import jakarta.persistence.GenerationType;
8+
import jakarta.persistence.Id;
9+
import jakarta.persistence.Table;
10+
import jakarta.persistence.UniqueConstraint;
11+
import lombok.AccessLevel;
12+
import lombok.AllArgsConstructor;
13+
import lombok.Builder;
14+
import lombok.Getter;
15+
import lombok.NoArgsConstructor;
16+
17+
@Entity
18+
@Table(
19+
name = "mbti_result",
20+
uniqueConstraints = {
21+
@UniqueConstraint(name = "uk_mbti_result_name_student_id", columnNames = {"name", "student_id"})
22+
}
23+
)
24+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
25+
@AllArgsConstructor
26+
@Getter
27+
@Builder
28+
public class MbtiResult extends BaseEntity {
29+
30+
@Id
31+
@GeneratedValue(strategy = GenerationType.IDENTITY)
32+
@Column(name = "id")
33+
private Long id;
34+
35+
@Column(name = "name", nullable = false, length = 100)
36+
private String name;
37+
38+
@Column(name = "student_id", nullable = false, length = 20)
39+
private String studentId;
40+
41+
@Column(name = "mbti_type", nullable = false, length = 4)
42+
private String mbtiType;
43+
44+
public void updateMbtiType(String mbtiType) {
45+
this.mbtiType = mbtiType;
46+
}
47+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package inha.gdgoc.domain.game.repository;
2+
3+
import inha.gdgoc.domain.game.entity.MbtiResult;
4+
import inha.gdgoc.domain.game.repository.projection.MbtiTypeCountProjection;
5+
import java.util.List;
6+
import java.util.Optional;
7+
import org.springframework.data.jpa.repository.JpaRepository;
8+
import org.springframework.data.jpa.repository.Query;
9+
10+
public interface MbtiResultRepository extends JpaRepository<MbtiResult, Long> {
11+
Optional<MbtiResult> findByNameAndStudentId(String name, String studentId);
12+
13+
@Query("select m.mbtiType as mbtiType, count(m) as count from MbtiResult m group by m.mbtiType")
14+
List<MbtiTypeCountProjection> countByMbtiType();
15+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package inha.gdgoc.domain.game.repository.projection;
2+
3+
public interface MbtiTypeCountProjection {
4+
String getMbtiType();
5+
long getCount();
6+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package inha.gdgoc.domain.game.service;
2+
3+
import inha.gdgoc.domain.game.dto.request.MbtiResultRequest;
4+
import inha.gdgoc.domain.game.dto.response.MbtiResultResponse;
5+
import inha.gdgoc.domain.game.dto.response.MbtiStatsResponse;
6+
import inha.gdgoc.domain.game.dto.response.MbtiTypeCountResponse;
7+
import inha.gdgoc.domain.game.entity.MbtiResult;
8+
import inha.gdgoc.domain.game.repository.MbtiResultRepository;
9+
import java.util.Comparator;
10+
import java.util.List;
11+
import jakarta.transaction.Transactional;
12+
import lombok.RequiredArgsConstructor;
13+
import org.springframework.stereotype.Service;
14+
15+
@RequiredArgsConstructor
16+
@Service
17+
public class MbtiResultService {
18+
19+
private final MbtiResultRepository mbtiResultRepository;
20+
21+
@Transactional
22+
public MbtiResultResponse upsertMbtiResult(MbtiResultRequest request) {
23+
MbtiResult result = mbtiResultRepository.findByNameAndStudentId(request.getName(), request.getStudentId())
24+
.map(existing -> {
25+
existing.updateMbtiType(request.getMbtiType());
26+
return existing;
27+
})
28+
.orElseGet(() -> mbtiResultRepository.save(request.toEntity()));
29+
30+
return new MbtiResultResponse(result);
31+
}
32+
33+
public MbtiStatsResponse getMbtiStats() {
34+
long totalCount = mbtiResultRepository.count();
35+
List<MbtiTypeCountResponse> typeCounts = mbtiResultRepository.countByMbtiType().stream()
36+
.map(item -> new MbtiTypeCountResponse(item.getMbtiType(), item.getCount()))
37+
.sorted(Comparator.comparingLong(MbtiTypeCountResponse::getCount).reversed()
38+
.thenComparing(MbtiTypeCountResponse::getMbtiType))
39+
.toList();
40+
41+
return new MbtiStatsResponse(totalCount, typeCounts);
42+
}
43+
}

0 commit comments

Comments
 (0)