Skip to content

Commit cfdd7cd

Browse files
committed
♻️ Refactor: 로직 수정
1 parent a76b869 commit cfdd7cd

File tree

10 files changed

+136
-28
lines changed

10 files changed

+136
-28
lines changed

src/main/java/com/pbl/insaroad/domain/game/controller/GameController.java

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,8 @@
1313
import org.springframework.web.bind.annotation.RequestParam;
1414

1515
import com.pbl.insaroad.domain.game.dto.request.GameRequest.CompleteRequest;
16-
import com.pbl.insaroad.domain.game.dto.request.GameRequest.UnvisitedRequest;
1716
import com.pbl.insaroad.domain.game.dto.response.GameResponse.GameProgressResponse;
1817
import com.pbl.insaroad.domain.game.dto.response.GameResponse.StartResponse;
19-
import com.pbl.insaroad.domain.game.dto.response.GameResponse.UnvisitedResponse;
2018
import com.pbl.insaroad.global.response.BaseResponse;
2119

2220
import io.swagger.v3.oas.annotations.Operation;
@@ -36,16 +34,9 @@ public interface GameController {
3634
@GetMapping("/start")
3735
BaseResponse<StartResponse> resume(@RequestParam("userCode") String userCode);
3836

39-
@PostMapping("/unvisited")
40-
@Operation(
41-
summary = "미방문 Location 전체 조회",
42-
description = "사용자 기준으로 아직 방문하지 않은 Location 목록 전체를 반환합니다.")
43-
ResponseEntity<BaseResponse<UnvisitedResponse>> unvisited(
44-
@RequestBody @Valid UnvisitedRequest request);
45-
4637
@PostMapping("/complete")
4738
@Operation(
48-
summary = "게임 진행/완료 처리",
39+
summary = "[개발자] 게임 진행/완료 처리",
4940
description =
5041
"""
5142
현재 Location 방문을 처리합니다.
@@ -58,4 +49,8 @@ ResponseEntity<BaseResponse<UnvisitedResponse>> unvisited(
5849
""")
5950
ResponseEntity<BaseResponse<GameProgressResponse>> complete(
6051
@RequestBody @Valid CompleteRequest request);
52+
53+
@Operation(summary = "[Web] 정보 조회", description = "사용자 코드로 완주 여부에 따라 다른 화면 정보를 반환합니다.")
54+
@GetMapping("/progress")
55+
BaseResponse<GameProgressResponse> getProgress(@RequestParam String userCode);
6156
}

src/main/java/com/pbl/insaroad/domain/game/controller/GameControllerImpl.java

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,12 @@
77

88
import org.springframework.http.ResponseEntity;
99
import org.springframework.web.bind.annotation.RequestBody;
10+
import org.springframework.web.bind.annotation.RequestParam;
1011
import org.springframework.web.bind.annotation.RestController;
1112

1213
import com.pbl.insaroad.domain.game.dto.request.GameRequest.CompleteRequest;
13-
import com.pbl.insaroad.domain.game.dto.request.GameRequest.UnvisitedRequest;
1414
import com.pbl.insaroad.domain.game.dto.response.GameResponse.GameProgressResponse;
1515
import com.pbl.insaroad.domain.game.dto.response.GameResponse.StartResponse;
16-
import com.pbl.insaroad.domain.game.dto.response.GameResponse.UnvisitedResponse;
1716
import com.pbl.insaroad.domain.user.service.UserService;
1817
import com.pbl.insaroad.global.response.BaseResponse;
1918

@@ -35,16 +34,14 @@ public BaseResponse<StartResponse> resume(String userCode) {
3534
return BaseResponse.success(userService.startResumeGame(userCode));
3635
}
3736

38-
@Override
39-
public ResponseEntity<BaseResponse<UnvisitedResponse>> unvisited(
40-
@RequestBody @Valid UnvisitedRequest request) {
41-
return ResponseEntity.ok(
42-
BaseResponse.success(userService.getUnvisitedLocations(request.getUserCode())));
43-
}
44-
4537
@Override
4638
public ResponseEntity<BaseResponse<GameProgressResponse>> complete(
4739
@RequestBody @Valid CompleteRequest request) {
4840
return ResponseEntity.ok(BaseResponse.success(userService.completeGame(request)));
4941
}
42+
43+
@Override
44+
public BaseResponse<GameProgressResponse> getProgress(@RequestParam String userCode) {
45+
return BaseResponse.success(userService.getGameProgress(userCode));
46+
}
5047
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright (c) SKU PBL Team4
3+
*/
4+
package com.pbl.insaroad.domain.game.mapper;
5+
6+
import java.util.List;
7+
8+
import com.pbl.insaroad.domain.game.dto.response.GameResponse.CompleteResponse;
9+
import com.pbl.insaroad.domain.game.dto.response.GameResponse.FinishResponse;
10+
import com.pbl.insaroad.domain.game.dto.response.GameResponse.GameProgressResponse;
11+
import com.pbl.insaroad.domain.location.dto.LocationResponse;
12+
import com.pbl.insaroad.domain.location.entity.Location;
13+
import com.pbl.insaroad.domain.ticket.entity.Ticket;
14+
import com.pbl.insaroad.domain.user.entity.User;
15+
16+
public final class GameMapper {
17+
18+
private GameMapper() {}
19+
20+
/* ---------- 미완주 ---------- */
21+
22+
public static GameProgressResponse toInProgress(User user, List<Location> unvisited) {
23+
return GameProgressResponse.builder()
24+
.completed(false)
25+
.complete(
26+
CompleteResponse.builder()
27+
.userCode(user.getCode())
28+
.unvisitedLocations(unvisited.stream().map(LocationResponse::from).toList())
29+
.build())
30+
.finish(null)
31+
.build();
32+
}
33+
34+
/* ---------- 완주 + 티켓 있음 ---------- */
35+
36+
public static GameProgressResponse toCompletedWithTicket(User user, Ticket ticket, String qrUrl) {
37+
return GameProgressResponse.builder()
38+
.completed(true)
39+
.complete(null)
40+
.finish(FinishResponse.builder().exchangeUrl(qrUrl).issuedAt(ticket.getCreatedAt()).build())
41+
.build();
42+
}
43+
44+
/* ---------- 완주 + 티켓 없음 ---------- */
45+
46+
public static GameProgressResponse toCompletedWithoutTicket() {
47+
return GameProgressResponse.builder().completed(true).complete(null).finish(null).build();
48+
}
49+
}

src/main/java/com/pbl/insaroad/domain/koreanname/service/KoreanNameService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public class KoreanNameService {
2727
private final KoreanNameAiClient koreanNameAiClient;
2828
private final UserService userService;
2929

30-
@Transactional(readOnly = true)
30+
@Transactional
3131
public KoreanNameResponse recommendName(KoreanNameRequest request) {
3232
String systemPrompt =
3333
"""

src/main/java/com/pbl/insaroad/domain/location/controller/LocationController.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
import org.springframework.web.bind.annotation.RequestBody;
1313
import org.springframework.web.bind.annotation.RequestMapping;
1414

15+
import com.pbl.insaroad.domain.game.dto.request.GameRequest;
16+
import com.pbl.insaroad.domain.game.dto.response.GameResponse.UnvisitedResponse;
1517
import com.pbl.insaroad.domain.location.dto.LocationResponse;
1618
import com.pbl.insaroad.domain.location.dto.request.LocationRequest;
1719
import com.pbl.insaroad.global.response.BaseResponse;
@@ -31,4 +33,11 @@ ResponseEntity<BaseResponse<LocationResponse>> create(
3133
@DeleteMapping("/{id}")
3234
@Operation(summary = "Location 삭제", description = "Location을 삭제합니다.")
3335
ResponseEntity<BaseResponse<Void>> deleteById(@PathVariable("id") Long locationId);
36+
37+
@PostMapping("/unvisited")
38+
@Operation(
39+
summary = "미방문 Location 전체 조회",
40+
description = "사용자 기준으로 아직 방문하지 않은 Location 목록 전체를 반환합니다.")
41+
ResponseEntity<BaseResponse<UnvisitedResponse>> unvisited(
42+
@RequestBody @Valid GameRequest.UnvisitedRequest request);
3443
}

src/main/java/com/pbl/insaroad/domain/location/controller/LocationControllerImpl.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
import org.springframework.web.bind.annotation.RequestBody;
1111
import org.springframework.web.bind.annotation.RestController;
1212

13+
import com.pbl.insaroad.domain.game.dto.request.GameRequest;
14+
import com.pbl.insaroad.domain.game.dto.response.GameResponse.UnvisitedResponse;
1315
import com.pbl.insaroad.domain.location.dto.LocationResponse;
1416
import com.pbl.insaroad.domain.location.dto.request.LocationRequest.CreateLocationRequest;
1517
import com.pbl.insaroad.domain.location.service.LocationService;
18+
import com.pbl.insaroad.domain.user.service.UserService;
1619
import com.pbl.insaroad.global.response.BaseResponse;
1720

1821
import lombok.RequiredArgsConstructor;
@@ -22,13 +25,21 @@
2225
public class LocationControllerImpl implements LocationController {
2326

2427
private final LocationService locationService;
28+
private final UserService userService;
2529

2630
@Override
2731
public ResponseEntity<BaseResponse<LocationResponse>> create(
2832
@RequestBody @Valid CreateLocationRequest request) {
2933
return ResponseEntity.ok(BaseResponse.success(locationService.create(request)));
3034
}
3135

36+
@Override
37+
public ResponseEntity<BaseResponse<UnvisitedResponse>> unvisited(
38+
@RequestBody @Valid GameRequest.UnvisitedRequest request) {
39+
return ResponseEntity.ok(
40+
BaseResponse.success(userService.getUnvisitedLocations(request.getUserCode())));
41+
}
42+
3243
@Override
3344
public ResponseEntity<BaseResponse<Void>> deleteById(@PathVariable("id") Long locationId) {
3445
locationService.deleteById(locationId);

src/main/java/com/pbl/insaroad/domain/ticket/repository/TicketRepository.java

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,26 @@ public interface TicketRepository extends JpaRepository<Ticket, Long> {
3030
@Lock(LockModeType.PESSIMISTIC_WRITE)
3131
@Query(
3232
"""
33-
select t
34-
from Ticket t
35-
where t.userId = :userId
36-
and t.usedAt is null
37-
and (t.expiresAt is null or t.expiresAt >= :today)
38-
order by t.createdAt desc
39-
""")
33+
select t
34+
from Ticket t
35+
where t.userId = :userId
36+
and t.usedAt is null
37+
and (t.expiresAt is null or t.expiresAt >= :today)
38+
order by t.createdAt desc
39+
""")
4040
List<Ticket> findLatestValidTicketForUpdate(
4141
@Param("userId") Long userId, @Param("today") LocalDate today, Pageable pageable);
4242

4343
Optional<Ticket> findTop1ByUserIdOrderByCreatedAtDesc(Long userId);
44+
45+
@Query(
46+
"""
47+
SELECT t
48+
FROM Ticket t
49+
WHERE t.userId = :userId
50+
AND t.expiresAt >= :today
51+
ORDER BY t.createdAt DESC
52+
""")
53+
List<Ticket> findLatestValidTicket(
54+
@Param("userId") Long userId, @Param("today") LocalDate today, Pageable pageable);
4455
}

src/main/java/com/pbl/insaroad/domain/ticket/service/TicketService.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package com.pbl.insaroad.domain.ticket.service;
55

66
import java.util.List;
7+
import java.util.Optional;
78

89
import com.pbl.insaroad.domain.ticket.dto.request.TicketRequest.ConsumeTicketRequest;
910
import com.pbl.insaroad.domain.ticket.dto.request.TicketRequest.VerifyTicketRequest;
@@ -45,4 +46,6 @@ public interface TicketService {
4546
Ticket issueNewTicket(Long userId);
4647

4748
List<TicketItemResponse> getTicketsByUserCode(String userCode);
49+
50+
Optional<Ticket> findLatestValidTicket(Long userId);
4851
}

src/main/java/com/pbl/insaroad/domain/ticket/service/TicketServiceImpl.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import java.time.LocalDate;
77
import java.time.LocalDateTime;
88
import java.util.List;
9+
import java.util.Optional;
910

1011
import org.springframework.data.domain.PageRequest;
1112
import org.springframework.stereotype.Service;
@@ -118,4 +119,13 @@ private User getUserByCodeOrThrow(String userCode) {
118119
.findByCode(userCode)
119120
.orElseThrow(() -> new CustomException(GameErrorCode.USER_NOT_FOUND));
120121
}
122+
123+
@Override
124+
public Optional<Ticket> findLatestValidTicket(Long userId) {
125+
126+
List<Ticket> tickets =
127+
ticketRepository.findLatestValidTicket(userId, LocalDate.now(), PageRequest.of(0, 1));
128+
129+
return tickets.isEmpty() ? Optional.empty() : Optional.of(tickets.getFirst());
130+
}
121131
}

src/main/java/com/pbl/insaroad/domain/user/service/UserService.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.pbl.insaroad.domain.game.dto.response.GameResponse.StartResponse;
2020
import com.pbl.insaroad.domain.game.dto.response.GameResponse.UnvisitedResponse;
2121
import com.pbl.insaroad.domain.game.exception.GameErrorCode;
22+
import com.pbl.insaroad.domain.game.mapper.GameMapper;
2223
import com.pbl.insaroad.domain.location.dto.LocationResponse;
2324
import com.pbl.insaroad.domain.location.entity.Location;
2425
import com.pbl.insaroad.domain.location.repository.LocationRepository;
@@ -128,7 +129,11 @@ public GameProgressResponse completeGame(CompleteRequest request) {
128129
int currentStage = user.getStage();
129130

130131
if (currentStage >= 3) {
131-
// stage3 완료로 간주하고 finish 응답(티켓은 issueNewTicket이 막아줌)
132+
133+
if (!user.isCompleted()) {
134+
user.completeMission();
135+
}
136+
132137
Ticket ticket = ticketService.issueNewTicket(user.getId());
133138
FinishResponse finish =
134139
FinishResponse.builder()
@@ -153,6 +158,24 @@ public GameProgressResponse completeGame(CompleteRequest request) {
153158
return GameProgressResponse.builder().completed(false).complete(complete).finish(null).build();
154159
}
155160

161+
@Transactional(readOnly = true)
162+
public GameProgressResponse getGameProgress(String userCode) {
163+
164+
User user = getUserByCodeOrThrow(userCode);
165+
166+
if (!user.isCompleted()) {
167+
return GameMapper.toInProgress(user, findUnvisitedLocations(user));
168+
}
169+
170+
return ticketService
171+
.findLatestValidTicket(user.getId())
172+
.map(
173+
ticket ->
174+
GameMapper.toCompletedWithTicket(
175+
user, ticket, qrPayloadFactory.create(ticket.getToken())))
176+
.orElseGet(GameMapper::toCompletedWithoutTicket);
177+
}
178+
156179
/* =========================
157180
* Private Helpers
158181
* ========================= */

0 commit comments

Comments
 (0)