Skip to content

Commit 4d0613a

Browse files
milowonyyytir777marshmallowing
authored
Release : 마이페이지에서 참여중인 파티 조회 (#57)
* feat : 닉네임 중복 체크 (nickname unique), 인증코드 검사 예외처리 추가 * fix : user_id IDENTITY strategy & dev redis host 이름변경 (localhost -> redis) * test코드 생성 & swagger url 삭제 & 환경변수 중복 삭제 * fix : 엔드포인트 추가 * Feature/26 notification (#29) * feat : google login 구현 완료 * feat : google login 구현 완료 (ios 구현 중) * fix : main push 시에만 workflow trigger * Feature/#28 google apple login * feat : google login 구현 완료 * fix : user hard delete * feat : apple 로그인 구현 및 ddl-auto -> update 변경 * workflow 줄바꿈 에러 수정 * hotifx : 에러 핸들링 수정 및 무중단 배포 삭제 (리소스 너무 많이 먹음) * 수정사항 반영 (API 인증 관련, db schema, 예외 처리 등..) * Feature/35 term (#38) * 약관 엔티티 생성 및 연관관계 설정 * 회원가입에 약관 저장 로직 추가 * 서버에서 idToken을 받아올 수 없으므로 단순히 이메일로 accessToken을 받아오는 test API 추가 * fix : docker compose 명령어 수정 * Feature : 파티 기능 (#42) * feat : 파티 엔티티 정의 * feat : 파티 dto * feat : party dto 정의 * feat : party entity 정의 * feat : 파티 생성,수정,삭제, 조회 partycontroller partyservice partyrepository * feat : 거리 계산 클래스 * refactor : 불필요한 코드 삭제 * refactor : token provider로 유저 아이디 추출하도록 변경 * Fix: 파티 기능 버그 수정 * docs : 파티 swagger 문서 추가 * hotfix : url parser 경로 제거 * hotfix : 파티 거리 계산 로직 임시 주석 처리 * hotfix : 파티 수정, 삭제 controller 추가 * hotfix : 선택 값들이 존재할때만 넣도록 수정 * hotfix : 위도, 경도 로직 삭제 * Feat : 마이페이지 참여중인 파티 조회 (#50) * feat : 마이페이지 활성 파티 조회 * docs : 유저 기능 swagger 문서화 --------- Co-authored-by: Wonjae Lim <[email protected]> Co-authored-by: Youjin <[email protected]>
1 parent 47556ca commit 4d0613a

File tree

5 files changed

+159
-3
lines changed

5 files changed

+159
-3
lines changed

src/main/java/ita/tinybite/domain/party/repository/PartyParticipantRepository.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import ita.tinybite.domain.party.entity.Party;
77
import ita.tinybite.domain.party.entity.PartyParticipant;
88
import ita.tinybite.domain.party.enums.ParticipantStatus;
9+
import ita.tinybite.domain.party.enums.PartyStatus;
910
import ita.tinybite.domain.user.entity.User;
1011
import org.springframework.data.jpa.repository.JpaRepository;
1112
import org.springframework.data.jpa.repository.Query;
@@ -24,4 +25,23 @@ public interface PartyParticipantRepository extends JpaRepository<PartyParticipa
2425
List<PartyParticipant> findByPartyAndStatus(Party party, ParticipantStatus status);
2526

2627
boolean existsByPartyAndUserAndStatus(Party party, User user, ParticipantStatus status);
28+
29+
@Query("SELECT pp FROM PartyParticipant pp " +
30+
"WHERE pp.user.id = :userId " +
31+
"AND pp.party.status =:partyStatus " +
32+
"AND pp.status = :participantStatus")
33+
List<PartyParticipant> findActivePartiesByUserId(
34+
@Param("userId") Long userId,
35+
@Param("partyStatuses") PartyStatus partyStatus,
36+
@Param("participantStatus") ParticipantStatus participantStatus
37+
);
38+
39+
@Query("SELECT COUNT(pp) FROM PartyParticipant pp " +
40+
"WHERE pp.party.id = :partyId " +
41+
"AND pp.status = :status")
42+
int countByPartyIdAndStatus(
43+
@Param("partyId") Long partyId,
44+
@Param("status") ParticipantStatus status
45+
);
46+
2747
}

src/main/java/ita/tinybite/domain/user/controller/UserController.java

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
11
package ita.tinybite.domain.user.controller;
22

3+
import io.swagger.v3.oas.annotations.Operation;
4+
import io.swagger.v3.oas.annotations.media.ArraySchema;
5+
import io.swagger.v3.oas.annotations.media.Content;
6+
import io.swagger.v3.oas.annotations.media.Schema;
7+
import io.swagger.v3.oas.annotations.responses.ApiResponse;
8+
import io.swagger.v3.oas.annotations.responses.ApiResponses;
9+
import ita.tinybite.domain.auth.dto.response.UserDto;
310
import ita.tinybite.domain.user.dto.req.UpdateUserReqDto;
11+
import ita.tinybite.domain.user.dto.res.PartyResponse;
12+
import ita.tinybite.domain.user.dto.res.UserResDto;
413
import ita.tinybite.domain.user.service.UserService;
514
import ita.tinybite.global.response.APIResponse;
615
import jakarta.validation.Valid;
16+
import org.springframework.http.ResponseEntity;
17+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
718
import org.springframework.web.bind.annotation.*;
819

20+
import java.util.List;
21+
922
import static ita.tinybite.global.response.APIResponse.success;
1023

1124
@RestController
@@ -18,30 +31,73 @@ public UserController(UserService userService) {
1831
this.userService = userService;
1932
}
2033

34+
@Operation(summary = "내 정보 조회", description = "현재 로그인한 사용자의 정보를 조회합니다.")
35+
@ApiResponses({
36+
@ApiResponse(responseCode = "200", description = "조회 성공",
37+
content = @Content(schema = @Schema(implementation = UserResDto.class))),
38+
@ApiResponse(responseCode = "401", description = "인증 실패",
39+
content = @Content(schema = @Schema(implementation = APIResponse.class)))
40+
})
2141
@GetMapping("/me")
22-
public APIResponse<?> getUser() {
42+
public APIResponse<UserResDto> getUser() {
2343
return success(userService.getUser());
2444
}
2545

46+
@Operation(summary = "내 정보 수정", description = "현재 로그인한 사용자의 정보를 수정합니다.")
47+
@ApiResponses({
48+
@ApiResponse(responseCode = "200", description = "수정 성공"),
49+
@ApiResponse(responseCode = "400", description = "잘못된 요청",
50+
content = @Content(schema = @Schema(implementation = APIResponse.class))),
51+
@ApiResponse(responseCode = "401", description = "인증 실패")
52+
})
2653
@PatchMapping("/me")
2754
public APIResponse<?> updateUser(@Valid @RequestBody UpdateUserReqDto req) {
2855
userService.updateUser(req);
2956
return success();
3057
}
3158

59+
@Operation(summary = "위치 정보 수정", description = "사용자의 현재 위치(위도, 경도)를 업데이트합니다.")
60+
@ApiResponses({
61+
@ApiResponse(responseCode = "200", description = "위치 업데이트 성공"),
62+
@ApiResponse(responseCode = "401", description = "인증 실패")
63+
})
3264
@PatchMapping("/me/location")
3365
public APIResponse<?> updateLocation(@RequestParam(defaultValue = "37.3623504988728") String latitude,
3466
@RequestParam(defaultValue = "127.117057453619") String longitude) {
3567
userService.updateLocation(latitude, longitude);
3668
return success();
3769
}
3870

71+
@Operation(summary = "회원 탈퇴", description = "현재 로그인한 사용자를 삭제합니다.")
72+
@ApiResponses({
73+
@ApiResponse(responseCode = "200", description = "탈퇴 성공"),
74+
@ApiResponse(responseCode = "401", description = "인증 실패")
75+
})
3976
@DeleteMapping("/me")
4077
public APIResponse<?> deleteUser() {
4178
userService.deleteUser();
4279
return success();
4380
}
4481

82+
@Operation(summary = "활성 파티 목록 조회", description = "사용자가 참여 중인 활성 파티 목록을 조회합니다.")
83+
@ApiResponses({
84+
@ApiResponse(responseCode = "200", description = "조회 성공",
85+
content = @Content(array = @ArraySchema(schema = @Schema(implementation = PartyResponse.class)))),
86+
@ApiResponse(responseCode = "401", description = "인증 실패")
87+
})
88+
@GetMapping("/parties/active")
89+
public ResponseEntity<List<PartyResponse>> getActiveParties(
90+
@AuthenticationPrincipal Long userId) {
91+
List<PartyResponse> response = userService.getActiveParties(userId);
92+
return ResponseEntity.ok(response);
93+
}
94+
95+
@Operation(summary = "닉네임 중복 확인", description = "닉네임 사용 가능 여부를 확인합니다.")
96+
@ApiResponses({
97+
@ApiResponse(responseCode = "200", description = "사용 가능한 닉네임"),
98+
@ApiResponse(responseCode = "400", description = "이미 사용 중인 닉네임",
99+
content = @Content(schema = @Schema(implementation = APIResponse.class)))
100+
})
45101
@GetMapping("/nickname/check")
46102
public APIResponse<?> validateNickname(@RequestParam String nickname) {
47103
userService.validateNickname(nickname);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package ita.tinybite.domain.user.dto.res;
2+
3+
import ita.tinybite.domain.party.entity.Party;
4+
import ita.tinybite.domain.party.enums.ParticipantStatus;
5+
import ita.tinybite.domain.party.enums.PartyStatus;
6+
import lombok.AllArgsConstructor;
7+
import lombok.Builder;
8+
import lombok.Getter;
9+
10+
import java.time.LocalDateTime;
11+
12+
@Getter
13+
@AllArgsConstructor
14+
@Builder
15+
public class PartyResponse {
16+
private Long id;
17+
private String title;
18+
private String description;
19+
private Integer maxParticipants;
20+
private Integer currentParticipants;
21+
private PartyStatus status;
22+
private String hostUsername;
23+
private LocalDateTime startDate;
24+
private LocalDateTime endDate;
25+
private LocalDateTime createdAt;
26+
private boolean isHost;
27+
private ParticipantStatus participantStatus;
28+
29+
public static PartyResponse from(Party party, int currentParticipants, boolean isHost, ParticipantStatus participantStatus) {
30+
return PartyResponse.builder()
31+
.id(party.getId())
32+
.title(party.getTitle())
33+
.description(party.getDescription())
34+
.maxParticipants(party.getMaxParticipants())
35+
.currentParticipants(currentParticipants)
36+
.status(party.getStatus())
37+
.hostUsername(party.getHost().getNickname())
38+
.startDate(party.getCreatedAt())
39+
.endDate(party.getClosedAt())
40+
.createdAt(party.getCreatedAt())
41+
.isHost(isHost)
42+
.participantStatus(participantStatus)
43+
.build();
44+
}
45+
}

src/main/java/ita/tinybite/domain/user/service/UserService.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
package ita.tinybite.domain.user.service;
22

33
import ita.tinybite.domain.auth.service.SecurityProvider;
4+
import ita.tinybite.domain.party.entity.Party;
5+
import ita.tinybite.domain.party.entity.PartyParticipant;
6+
import ita.tinybite.domain.party.enums.ParticipantStatus;
7+
import ita.tinybite.domain.party.enums.PartyStatus;
8+
import ita.tinybite.domain.party.repository.PartyParticipantRepository;
49
import ita.tinybite.domain.user.constant.UserStatus;
510
import ita.tinybite.domain.user.dto.req.UpdateUserReqDto;
11+
import ita.tinybite.domain.user.dto.res.PartyResponse;
612
import ita.tinybite.domain.user.dto.res.UserResDto;
713
import ita.tinybite.domain.user.entity.User;
814
import ita.tinybite.domain.user.repository.UserRepository;
@@ -11,19 +17,25 @@
1117
import ita.tinybite.global.location.LocationService;
1218
import org.springframework.stereotype.Service;
1319

20+
import java.util.List;
21+
import java.util.stream.Collectors;
22+
1423
@Service
1524
public class UserService {
1625

1726
private final SecurityProvider securityProvider;
1827
private final UserRepository userRepository;
1928
private final LocationService locationService;
29+
private final PartyParticipantRepository participantRepository;
2030

2131
public UserService(SecurityProvider securityProvider,
2232
UserRepository userRepository,
23-
LocationService locationService) {
33+
LocationService locationService,
34+
PartyParticipantRepository participantRepository) {
2435
this.securityProvider = securityProvider;
2536
this.userRepository = userRepository;
2637
this.locationService = locationService;
38+
this.participantRepository = participantRepository;
2739
}
2840

2941
public UserResDto getUser() {
@@ -50,4 +62,23 @@ public void validateNickname(String nickname) {
5062
if(userRepository.existsByNickname(nickname))
5163
throw BusinessException.of(AuthErrorCode.DUPLICATED_NICKNAME);
5264
}
65+
66+
public List<PartyResponse> getActiveParties(Long userId) {
67+
List<PartyParticipant> participants = participantRepository
68+
.findActivePartiesByUserId(
69+
userId,
70+
PartyStatus.RECRUITING,
71+
ParticipantStatus.APPROVED
72+
);
73+
74+
return participants.stream()
75+
.map(pp -> {
76+
Party party = pp.getParty();
77+
int currentParticipants = participantRepository
78+
.countByPartyIdAndStatus(party.getId(), ParticipantStatus.APPROVED);
79+
boolean isHost = party.getHost().getUserId().equals(userId);
80+
return PartyResponse.from(party, currentParticipants, isHost,pp.getStatus());
81+
})
82+
.collect(Collectors.toList());
83+
}
5384
}

src/test/java/ita/tinybite/domain/user/service/UserServiceTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package ita.tinybite.domain.user.service;
22

33
import ita.tinybite.domain.auth.service.AuthService;
4+
import ita.tinybite.domain.party.entity.Party;
5+
import ita.tinybite.domain.party.repository.PartyParticipantRepository;
46
import ita.tinybite.domain.user.constant.LoginType;
57
import ita.tinybite.domain.user.constant.UserStatus;
68
import ita.tinybite.domain.user.dto.req.UpdateUserReqDto;
@@ -23,6 +25,8 @@ class UserServiceTest {
2325
@Autowired
2426
private UserRepository userRepository;
2527

28+
private PartyParticipantRepository participantRepository;
29+
2630
@Autowired
2731
private AuthService authService;
2832

@@ -37,7 +41,7 @@ class UserServiceTest {
3741
void setUp() {
3842
securityProvider = new FakeSecurityProvider(userRepository);
3943
locationService = new FakeLocationService();
40-
userService = new UserService(securityProvider, userRepository, locationService);
44+
userService = new UserService(securityProvider, userRepository, locationService,participantRepository);
4145

4246
User user = User.builder()
4347

0 commit comments

Comments
 (0)