Skip to content
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.run_us.server.domains.crew.controller;

import com.run_us.server.domains.crew.controller.model.enums.CrewHttpResponseCode;
import com.run_us.server.domains.crew.controller.model.request.CreateJoinRequest;
import com.run_us.server.domains.crew.controller.model.request.ReviewJoinRequest;
import com.run_us.server.domains.crew.controller.model.response.*;
Expand All @@ -9,10 +8,10 @@
import com.run_us.server.domains.crew.controller.model.request.CreateCrewRequest;
import com.run_us.server.domains.crew.controller.model.response.CreateCrewResponse;
import com.run_us.server.domains.crew.service.usecase.CreateCrewUseCase;
import com.run_us.server.domains.crew.service.usecase.CrewMemberUseCase;
import com.run_us.server.global.common.SuccessResponse;

import com.run_us.server.global.security.annotation.CurrentUser;
import com.run_us.server.global.security.principal.UserPrincipal;
import jakarta.validation.Valid;
import lombok.extern.slf4j.Slf4j;
import lombok.RequiredArgsConstructor;
Expand All @@ -30,6 +29,7 @@
public class CrewController {
private final CrewJoinUseCase crewJoinUseCase;
private final CreateCrewUseCase createCrewUseCase;
private final CrewMemberUseCase crewMemberUseCase;


@PostMapping
Expand All @@ -47,81 +47,98 @@ public ResponseEntity<SuccessResponse<CreateCrewResponse>> createCrew(
@PostMapping("/{crewPublicId}/join-requests")
public ResponseEntity<SuccessResponse<CreateJoinRequestResponse>> requestJoin(
@PathVariable String crewPublicId,
@CurrentUser UserPrincipal userPrincipal,
@CurrentUser String currentUserPublicId,
@Valid @RequestBody CreateJoinRequest request
) {
log.info("action=request_join crewPublicId={} userPublicId={}", crewPublicId, userPrincipal.getPublicId());
CrewJoinRequestInternalResponse response = crewJoinUseCase.createJoinRequest(crewPublicId, userPrincipal.getInternalId(), request);
return ResponseEntity.ok(
SuccessResponse.of(
CrewHttpResponseCode.JOIN_REQUEST_CREATED,
response.toPublicCreateResponse(userPrincipal.getPublicId())));
log.info("action=request_join crewPublicId={} userPublicId={}", crewPublicId, currentUserPublicId);
SuccessResponse<CreateJoinRequestResponse> response = crewJoinUseCase.createJoinRequest(crewPublicId, currentUserPublicId, request);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}

@DeleteMapping("/{crewPublicId}/join-requests")
public ResponseEntity<SuccessResponse<CancelJoinRequestResponse>> cancelJoinRequest(
@PathVariable String crewPublicId,
@CurrentUser UserPrincipal userPrincipal
@CurrentUser String currentUserPublicId
) {
log.info("action=cancel_join_request crewPublicId={} userPublicId={}", crewPublicId, userPrincipal.getPublicId());
CrewJoinRequestInternalResponse response = crewJoinUseCase.cancelJoinRequest(crewPublicId, userPrincipal.getInternalId());
return ResponseEntity.ok(
SuccessResponse.of(
CrewHttpResponseCode.JOIN_REQUEST_CANCELLED,
response.toPublicCancelResponse()
)
);
log.info("action=cancel_join_request crewPublicId={} userPublicId={}", crewPublicId, currentUserPublicId);
SuccessResponse<CancelJoinRequestResponse> response = crewJoinUseCase.cancelJoinRequest(crewPublicId, currentUserPublicId);
return ResponseEntity.status(HttpStatus.OK).body(response);
}

@GetMapping("/{crewPublicId}/join-requests")
public ResponseEntity<SuccessResponse<List<FetchJoinRequestResponse>>> getJoinRequests(
@PathVariable String crewPublicId,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int limit,
@CurrentUser UserPrincipal userPrincipal
@CurrentUser String currentUserPublicId
) {
log.info("action=get_join_requests_start crewPublicId={} page={} limit={}", crewPublicId, page, limit);

List<FetchJoinRequestResponse> responses = crewJoinUseCase.getJoinRequests(
SuccessResponse<List<FetchJoinRequestResponse>> responses = crewJoinUseCase.getJoinRequests(
crewPublicId,
PageRequest.of(page, limit),
userPrincipal.getInternalId()
currentUserPublicId
);

log.info("action=get_join_requests_complete crewPublicId={} page={} limit={} result_count={}",
crewPublicId, page, limit, responses.size());
return ResponseEntity.ok(
SuccessResponse.of(
CrewHttpResponseCode.JOIN_REQUEST_FETCHED,
responses
)
);
crewPublicId, page, limit, responses.getPayload().size());
return ResponseEntity.status(HttpStatus.OK).body(responses);
}

@PutMapping("/{crewPublicId}/join-requests/{requestId}")
public ResponseEntity<SuccessResponse<ReviewJoinRequestResponse>> reviewJoinRequest(
@PathVariable String crewPublicId,
@PathVariable Integer requestId,
@CurrentUser UserPrincipal userPrincipal,
@CurrentUser String currentUserPublicId,
@Valid @RequestBody ReviewJoinRequest request
) {
log.info("action=process_join_request_start crewPublicId={} requestId={} status={}",
crewPublicId, requestId, request.getStatus());

ReviewJoinRequestResponse response = crewJoinUseCase.reviewJoinRequest(
SuccessResponse<ReviewJoinRequestResponse> response = crewJoinUseCase.reviewJoinRequest(
crewPublicId,
requestId,
CrewJoinRequestStatus.valueOf(request.getStatus()),
userPrincipal.getInternalId()
currentUserPublicId
);

log.info("action=process_join_request_complete crewPublicId={} requestId={}",
crewPublicId, requestId);
return ResponseEntity.ok(
SuccessResponse.of(
CrewHttpResponseCode.JOIN_REQUEST_REVIEWED,
response
)
);
return ResponseEntity.status(HttpStatus.OK).body(response);
}

@GetMapping("/{crewPublicId}/members")
public ResponseEntity<SuccessResponse<List<FetchMemberResponse>>> getMembers(
@PathVariable String crewPublicId,
@CurrentUser String currentUserPublicId,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int limit
) {
log.info("action=get_members_request_start crewPublicId={} currentUserPublicId={}",
crewPublicId, currentUserPublicId);

SuccessResponse<List<FetchMemberResponse>> response =
crewMemberUseCase.getMembers(crewPublicId, currentUserPublicId, PageRequest.of(page, limit));

log.info("action=get_members_request_end crewPublicId={} currentUserPublicId={}",
crewPublicId, currentUserPublicId);
return ResponseEntity.status(HttpStatus.OK).body(response);
}

@DeleteMapping("/{crewPublicId}/members/{userPublicId}")
public ResponseEntity<SuccessResponse<KickMemberResponse>> kickMember(
@PathVariable String crewPublicId,
@PathVariable String userPublicId,
@CurrentUser String currentUserPublicId
) {
log.info("action=remove_member_request_start crewPublicId={} userPublicId={} currentUserPublicId={}",
crewPublicId, userPublicId, currentUserPublicId);

SuccessResponse<KickMemberResponse> response =
crewMemberUseCase.kickMember(crewPublicId, currentUserPublicId, userPublicId);

log.info("action=remove_member_request_end crewPublicId={} userPublicId={} currentUserPublicId={}",
crewPublicId, userPublicId, currentUserPublicId);
return ResponseEntity.status(HttpStatus.OK).body(response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public enum CrewErrorCode implements CustomResponseCode {
SUSPENDED_CREW("CEH4004", "Suspended crew", "Suspended crew", HttpStatus.BAD_REQUEST),
INVALID_JOIN_REQUEST_STATUS("CEH4005", "Invalid join request status", "Invalid join request status", HttpStatus.BAD_REQUEST),
JOIN_REQUEST_ALREADY_PROCESSED("CEH4006", "Join request already processed", "Join request already processed", HttpStatus.BAD_REQUEST),
NOT_CREW_MEMBER("CEH4007", "User is not a crew member", "User is not a crew member", HttpStatus.BAD_REQUEST),


// 403
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ public enum CrewHttpResponseCode implements CustomResponseCode {
JOIN_REQUEST_CANCELLED("CSH2008", HttpStatus.OK, "크루 가입 요청 취소 성공", "크루 가입 요청 취소 성공"),
JOIN_REQUEST_FETCHED("CSH2009", HttpStatus.OK, "크루 가입 요청 조회 성공", "크루 가입 요청 조회 성공"),
JOIN_REQUEST_REVIEWED("CSH2010", HttpStatus.OK, "크루 가입 요청 처리 성공", "크루 가입 요청 처리 성공"),

KICK_MEMBER_SUCCESS("CSH2031", HttpStatus.OK, "크루 멤버 추방 성공", "크루 멤버 추방 성공"),
GET_MEMBERS_SUCCESS("CSH2032", HttpStatus.OK, "크루 멤버 리스트 조회 성공", "크루 멤버 리스트 조회 성공"),
;

private final String code;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.run_us.server.domains.crew.controller.model.response;

import com.run_us.server.domains.crew.domain.CrewMembership;
import com.run_us.server.domains.user.domain.User;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class FetchMemberResponse {
private String nickname;
private String publicId;
private String profileImg;
private String role;
private int runningDistance;

public static FetchMemberResponse from(User user, CrewMembership crewMembership, int runningDistance) {
return new FetchMemberResponse(
user.getProfile().getNickname(),
user.getPublicId(),
user.getProfile().getImgUrl(),
crewMembership.getRole().name(),
runningDistance
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.run_us.server.domains.crew.controller.model.response;

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class KickMemberResponse {
private String userPublicId;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.run_us.server.domains.crew.controller.model.response;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Getter
@AllArgsConstructor
@Builder
public class ReviewJoinRequestResponse {
private Integer requestId;
}
5 changes: 5 additions & 0 deletions src/main/java/com/run_us/server/domains/crew/domain/Crew.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ public void addMember(Integer userId) {
this.memberCount++;
}

public void removeMember(Integer userId) {
this.crewMemberships.removeIf(membership -> membership.getUserId().equals(userId));
this.memberCount--;
}

@Override
public void prePersist() {
this.publicId = TSID.Factory.getTsid().toString();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.run_us.server.domains.crew.domain;

import com.run_us.server.global.common.resolver.DomainPrincipal;

public class CrewPrincipal extends DomainPrincipal {
public CrewPrincipal(String publicId, Integer internalId) {
super(publicId, internalId);
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package com.run_us.server.domains.crew.repository;

import com.run_us.server.domains.crew.domain.Crew;
import com.run_us.server.domains.crew.domain.CrewJoinRequest;
import com.run_us.server.domains.crew.domain.CrewMembership;

import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

Expand All @@ -17,4 +16,12 @@ public interface CrewRepository extends JpaRepository<Crew, Integer> {
@Query("SELECT EXISTS (SELECT 1 FROM Crew c JOIN c.crewMemberships m " +
"WHERE c.id = :crewId AND m.userId = :userId)")
boolean existsMembershipByCrewIdAndUserId(Integer crewId, Integer userId);

@Query("SELECT m FROM Crew c JOIN c.crewMemberships m " +
"WHERE c.id = :crewId " +
"ORDER BY m.joinedAt DESC")
List<CrewMembership> findMembershipsByCrewId(
Integer crewId,
PageRequest pageRequest
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.run_us.server.domains.crew.controller.model.enums.CrewErrorCode;
import com.run_us.server.domains.crew.domain.Crew;
import com.run_us.server.domains.crew.domain.CrewJoinRequest;
import com.run_us.server.domains.crew.domain.CrewMembership;
import com.run_us.server.domains.crew.domain.enums.CrewJoinRequestStatus;
import com.run_us.server.domains.crew.domain.enums.CrewJoinType;
import com.run_us.server.domains.crew.repository.CrewJoinRequestRepository;
Expand Down Expand Up @@ -36,6 +37,12 @@ public List<CrewJoinRequest> getJoinRequests(Crew crew, PageRequest pageRequest)
return crewJoinRequestRepository.findAllByCrewId(crew.getId(), pageRequest).getContent();
}

@Transactional(readOnly = true)
public List<CrewMembership> getMemberships(Crew crew, PageRequest pageRequest) {
log.debug("action=get_memberships_start crewId={}", crew.getPublicId());
return crewRepository.findMembershipsByCrewId(crew.getId(), pageRequest);
}

@Transactional
public CrewJoinRequest createJoinRequest(Crew crew, Integer userInternalId, String answer) {
log.debug("action=create_join_request crewPublicId={} userInternalId={}", crew.getPublicId(), userInternalId);
Expand Down Expand Up @@ -77,4 +84,14 @@ public CrewJoinRequest reviewJoinRequest(Crew crew, Integer requestId, CrewJoinR
crew.getPublicId(), requestId);
return request;
}

@Transactional
public void removeMember(Crew crew, Integer targetUserId) {
log.debug("action=remove_member_start crewPublicId={} targetUserId={}", crew.getPublicId(), targetUserId);

crew.removeMember(targetUserId);
crewRepository.save(crew);

log.debug("action=remove_member_end crewPublicId={} targetUserId={}", crew.getPublicId(), targetUserId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,28 @@ public void validateCanReviewJoinRequest(Integer userId, CrewJoinRequestStatus s
throw new CrewException(CrewErrorCode.SUSPENDED_CREW);
}
}

public void validateCanKickMember(Integer userId, Integer targetMemberId, Crew crew) {
if(!crew.getOwner().getId().equals(userId)) {
throw new CrewException(CrewErrorCode.CREW_NOT_FOUND);
}

if (!crewRepository.existsMembershipByCrewIdAndUserId(crew.getId(), targetMemberId)) {
throw new CrewException(CrewErrorCode.NOT_CREW_MEMBER);
}

if(!crew.isActive()) {
throw new CrewException(CrewErrorCode.SUSPENDED_CREW);
}
}

public void validateCanFetchMembers(Integer userId, Crew crew) {
if(!crew.getOwner().getId().equals(userId)) {
throw new CrewException(CrewErrorCode.CREW_NOT_FOUND);
}

if(!crew.isActive()) {
throw new CrewException(CrewErrorCode.SUSPENDED_CREW);
}
}
}
Loading