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
@@ -0,0 +1,6 @@
package in.koreatech.koin._common.event;

public record ClubCreateEvent(
String clubName
) {
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package in.koreatech.koin.admin.club.service;

import static in.koreatech.koin.domain.club.enums.SNSType.*;

import java.util.AbstractMap;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.AbstractMap;
import java.util.List;
import java.util.stream.Stream;

import static in.koreatech.koin.domain.club.enums.SNSType.*;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
Expand All @@ -36,16 +35,14 @@
import in.koreatech.koin.domain.club.model.Club;
import in.koreatech.koin.domain.club.model.ClubAdmin;
import in.koreatech.koin.domain.club.model.ClubCategory;

import in.koreatech.koin.domain.club.model.ClubSNS;
import in.koreatech.koin.domain.club.model.redis.ClubCreateRedis;
import in.koreatech.koin.domain.club.repository.ClubAdminRepository;
import in.koreatech.koin.domain.club.repository.ClubCategoryRepository;
import in.koreatech.koin.domain.club.repository.ClubRepository;
import in.koreatech.koin.domain.club.repository.redis.ClubCreateRedisRepository;
import in.koreatech.koin.domain.user.model.User;
import in.koreatech.koin.domain.user.repository.UserRepository;
import in.koreatech.koin.domain.club.model.ClubSNS;

import lombok.RequiredArgsConstructor;

@Service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
import org.springframework.web.bind.annotation.RequestParam;

import in.koreatech.koin._common.auth.Auth;
import in.koreatech.koin._common.auth.UserId;
import in.koreatech.koin.domain.club.dto.request.CreateClubRequest;
import in.koreatech.koin.domain.club.dto.request.CreateQnaRequest;
import in.koreatech.koin.domain.club.dto.request.EmpowermentClubManagerRequest;
import in.koreatech.koin.domain.club.dto.request.UpdateClubIntroductionRequest;
import in.koreatech.koin.domain.club.dto.request.UpdateClubRequest;
import in.koreatech.koin.domain.club.dto.response.ClubHotResponse;
Expand Down Expand Up @@ -94,7 +96,8 @@ ResponseEntity<ClubResponse> updateClubIntroduction(
@Operation(summary = "동아리를 상세조회한다")
@PostMapping("/{clubId}")
ResponseEntity<ClubResponse> getClub(
@Parameter(in = PATH) @PathVariable Integer clubId
@Parameter(in = PATH) @PathVariable Integer clubId,
@UserId Integer userId
);

@ApiResponses(
Expand Down Expand Up @@ -221,4 +224,20 @@ ResponseEntity<Void> deleteQna(
@Parameter(in = PATH) @PathVariable Integer qnaId,
@Auth(permit = {STUDENT}) Integer studentId
);

@ApiResponses(
value = {
@ApiResponse(responseCode = "204"),
@ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))),
}
)
@Operation(summary = "동아리 관리자 권한을 위임한다")
@PutMapping("/empowerment")
ResponseEntity<Void> empowermentClubManager(
@RequestBody @Valid EmpowermentClubManagerRequest request,
@Auth(permit = {STUDENT}) Integer studentId
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
import org.springframework.web.bind.annotation.RestController;

import in.koreatech.koin._common.auth.Auth;
import in.koreatech.koin._common.auth.UserId;
import in.koreatech.koin.domain.club.dto.request.CreateClubRequest;
import in.koreatech.koin.domain.club.dto.request.CreateQnaRequest;
import in.koreatech.koin.domain.club.dto.request.EmpowermentClubManagerRequest;
import in.koreatech.koin.domain.club.dto.request.UpdateClubIntroductionRequest;
import in.koreatech.koin.domain.club.dto.request.UpdateClubRequest;
import in.koreatech.koin.domain.club.dto.response.ClubHotResponse;
Expand Down Expand Up @@ -76,9 +78,10 @@ public ResponseEntity<ClubsByCategoryResponse> getClubByCategory(

@GetMapping("/{clubId}")
public ResponseEntity<ClubResponse> getClub(
@Parameter(in = PATH) @PathVariable Integer clubId
@Parameter(in = PATH) @PathVariable Integer clubId,
@UserId Integer userId
) {
ClubResponse response = clubService.getClub(clubId);
ClubResponse response = clubService.getClub(clubId, userId);
return ResponseEntity.ok(response);
}

Expand Down Expand Up @@ -133,4 +136,13 @@ public ResponseEntity<Void> deleteQna(
clubService.deleteQna(clubId, qnaId, studentId);
return ResponseEntity.noContent().build();
}

@PutMapping("/empowerment")
public ResponseEntity<Void> empowermentClubManager(
@RequestBody @Valid EmpowermentClubManagerRequest request,
@Auth(permit = {STUDENT}) Integer studentId
) {
clubService.empowermentClubManager(request, studentId);
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public record CreateClubRequest(

@Schema(description = "동아리 관리자 ID 리스트", requiredMode = REQUIRED)
@NotEmpty(message = "동아리 관리자는 필수 입력 사항입니다.")
List<InnerClubAdminRequest> clubAdmins,
List<InnerClubManagerRequest> clubManagers,

@Schema(description = "동아리 분과 카테고리 ID", example = "1", requiredMode = REQUIRED)
@NotNull(message = "동아리 분과 카테고리 ID는 필수 입력 사항입니다.")
Expand All @@ -54,7 +54,7 @@ public record CreateClubRequest(
String phoneNumber
) {
@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
public record InnerClubAdminRequest(
public record InnerClubManagerRequest(
@Schema(description = "동아리 관리자 id", example = "bcsdlab", requiredMode = REQUIRED)
String userid
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package in.koreatech.koin.domain.club.dto.request;

import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;

public record EmpowermentClubManagerRequest(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C

권한 위임 정의서에 현 관리자 아이디, 향후 관리자 아이디를 입력받고 있는 거 같은데 다른 부분일까요??

@Schema(description = "동아리 아이디", example = "1", requiredMode = REQUIRED)
Integer clubId,

@Schema(description = "위임받는 사용자의 아이디", example = "example", requiredMode = REQUIRED)
@NotEmpty(message = "위임받는 사용자의 아이디를 입력해주세요.")
String changedManagerId
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,12 @@ public record ClubResponse(
Optional<String> openChat,

@Schema(description = "전화번호", example = "010-1234-5678")
Optional<String> phoneNumber
Optional<String> phoneNumber,

@Schema(description = "동아리 관리자 여부", example = "true")
Boolean manager
) {
public static ClubResponse from(Club club, List<ClubSNS> clubSNSs) {
public static ClubResponse from(Club club, List<ClubSNS> clubSNSs, Boolean manager) {
Optional<String> instagram = Optional.empty();
Optional<String> googleForm = Optional.empty();
Optional<String> openChat = Optional.empty();
Expand All @@ -73,7 +76,8 @@ public static ClubResponse from(Club club, List<ClubSNS> clubSNSs) {
instagram,
googleForm,
openChat,
phoneNumber
phoneNumber,
manager
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package in.koreatech.koin.domain.club.exception;

import in.koreatech.koin._common.exception.custom.KoinException;

public class AlreadyManagerException extends KoinException {
private static final String DEFAULT_MESSAGE = "이미 동아리의 관리자입니다.";

public AlreadyManagerException(String message) {
super(message);
}

public AlreadyManagerException(String message, String detail) {
super(message, detail);
}

public static AlreadyManagerException withDetail(String detail) {
return new AlreadyManagerException(DEFAULT_MESSAGE, detail);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package in.koreatech.koin.domain.club.model.redis;

import static in.koreatech.koin.domain.club.dto.request.CreateClubRequest.InnerClubManagerRequest;

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

Expand All @@ -25,7 +27,7 @@ public class ClubCreateRedis {

private String imageUrl;

private List<CreateClubRequest.InnerClubAdminRequest> clubAdmins;
private List<InnerClubManagerRequest> clubAdmins;

private Integer clubCategoryId;

Expand All @@ -50,7 +52,7 @@ private ClubCreateRedis(
String id,
String name,
String imageUrl,
List<CreateClubRequest.InnerClubAdminRequest> clubAdmins,
List<InnerClubManagerRequest> clubAdmins,
Integer clubCategoryId,
String location,
String description,
Expand Down Expand Up @@ -81,7 +83,7 @@ public static ClubCreateRedis of(CreateClubRequest request, Integer requesterId)
.id(request.name())
.name(request.name())
.imageUrl(request.imageUrl())
.clubAdmins(request.clubAdmins())
.clubAdmins(request.clubManagers())
.clubCategoryId(request.clubCategoryId())
.location(request.location())
.description(request.description())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;

import in.koreatech.koin.domain.club.model.Club;
import in.koreatech.koin.domain.club.model.ClubAdmin;
import in.koreatech.koin.domain.user.model.User;

public interface ClubAdminRepository extends Repository<ClubAdmin, Integer> {

Expand All @@ -26,4 +28,8 @@ SELECT COUNT(ca)
int countAll();

boolean existsByClubIdAndUserId(Integer clubId, Integer studentId);

boolean existsByClubAndUser(Club club, User user);

void deleteByClubAndUser(Club club, User user);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,28 @@
import java.util.List;
import java.util.Objects;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import in.koreatech.koin._common.auth.exception.AuthorizationException;
import in.koreatech.koin._common.event.ClubCreateEvent;
import in.koreatech.koin.domain.club.dto.request.CreateClubRequest;
import in.koreatech.koin.domain.club.dto.request.CreateQnaRequest;
import in.koreatech.koin.domain.club.dto.request.EmpowermentClubManagerRequest;
import in.koreatech.koin.domain.club.dto.request.UpdateClubIntroductionRequest;
import in.koreatech.koin.domain.club.dto.request.UpdateClubRequest;
import in.koreatech.koin.domain.club.dto.response.ClubHotResponse;
import in.koreatech.koin.domain.club.dto.response.ClubResponse;
import in.koreatech.koin.domain.club.dto.response.ClubsByCategoryResponse;
import in.koreatech.koin.domain.club.dto.response.QnasResponse;
import in.koreatech.koin.domain.club.enums.SNSType;
import in.koreatech.koin.domain.club.exception.AlreadyManagerException;
import in.koreatech.koin.domain.club.exception.ClubHotNotFoundException;
import in.koreatech.koin.domain.club.exception.ClubLikeNotFoundException;
import in.koreatech.koin.domain.club.exception.DuplicateClubLikiException;
import in.koreatech.koin.domain.club.model.Club;
import in.koreatech.koin.domain.club.model.ClubAdmin;
import in.koreatech.koin.domain.club.model.ClubCategory;
import in.koreatech.koin.domain.club.model.ClubLike;
import in.koreatech.koin.domain.club.model.ClubQna;
Expand Down Expand Up @@ -57,11 +62,14 @@ public class ClubService {
private final ClubLikeRepository clubLikeRepository;
private final UserRepository userRepository;
private final ClubCreateRedisRepository clubCreateRedisRepository;
private final ApplicationEventPublisher eventPublisher;

@Transactional
public void createClubRequest(CreateClubRequest request, Integer studentId) {
ClubCreateRedis createRedis = ClubCreateRedis.of(request, studentId);
clubCreateRedisRepository.save(createRedis);

eventPublisher.publishEvent(new ClubCreateEvent(request.name()));
}

@Transactional
Expand All @@ -73,8 +81,9 @@ public ClubResponse updateClub(Integer clubId, UpdateClubRequest request, Intege
club.update(request.name(), request.imageUrl(), clubCategory, request.location(), request.description());

List<ClubSNS> newSNS = updateClubSNS(request, club);
Boolean manager = clubAdminRepository.existsByClubIdAndUserId(clubId, studentId);

return ClubResponse.from(club, newSNS);
return ClubResponse.from(club, newSNS, manager);
}

private List<ClubSNS> updateClubSNS(UpdateClubRequest request, Club club) {
Expand Down Expand Up @@ -103,8 +112,9 @@ public ClubResponse updateClubIntroduction(

club.updateIntroduction(request.introduction());
List<ClubSNS> clubSNSs = club.getClubSNSs();
Boolean manager = clubAdminRepository.existsByClubIdAndUserId(clubId, studentId);

return ClubResponse.from(club, clubSNSs);
return ClubResponse.from(club, clubSNSs, manager);
}

private void isClubAdmin(Integer clubId, Integer studentId) {
Expand All @@ -114,12 +124,13 @@ private void isClubAdmin(Integer clubId, Integer studentId) {
}

@Transactional
public ClubResponse getClub(Integer clubId) {
public ClubResponse getClub(Integer clubId, Integer userId) {
Club club = clubRepository.getByIdWithPessimisticLock(clubId);
club.increaseHits();
List<ClubSNS> clubSNSs = clubSNSRepository.findAllByClub(club);
Boolean manager = clubAdminRepository.existsByClubIdAndUserId(clubId, userId);

return ClubResponse.from(club, clubSNSs);
return ClubResponse.from(club, clubSNSs, manager);
}

public ClubsByCategoryResponse getClubByCategory(Integer categoryId, Boolean hitSort) {
Expand Down Expand Up @@ -217,4 +228,24 @@ private void validateQnaDeleteAuthorization(Integer clubId, ClubQna qna, Integer
return;
throw AuthorizationException.withDetail("studentId: " + studentId);
}

@Transactional
public void empowermentClubManager(EmpowermentClubManagerRequest request, Integer studentId) {
Club club = clubRepository.getById(request.clubId());
User currentManager = userRepository.getById(studentId);
User changedManager = userRepository.getByUserId(request.changedManagerId());

isClubAdmin(request.clubId(), studentId);
if (clubAdminRepository.existsByClubAndUser(club, changedManager)) {
throw AlreadyManagerException.withDetail("");
}
clubAdminRepository.deleteByClubAndUser(club, currentManager);

ClubAdmin newClubManager = ClubAdmin.builder()
.club(club)
.user(changedManager)
.build();

clubAdminRepository.save(newClubManager);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package in.koreatech.koin.infrastructure.slack.eventlistener;

import static org.springframework.transaction.event.TransactionPhase.AFTER_COMMIT;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionalEventListener;

import in.koreatech.koin._common.event.ClubCreateEvent;
import in.koreatech.koin.infrastructure.slack.client.SlackClient;
import in.koreatech.koin.infrastructure.slack.model.SlackNotificationFactory;
import lombok.RequiredArgsConstructor;

@Component
@RequiredArgsConstructor
@Transactional(propagation = Propagation.REQUIRES_NEW)
public class ClubEventListener {

private final SlackClient slackClient;
private final SlackNotificationFactory slackNotificationFactory;

@Async
@TransactionalEventListener(phase = AFTER_COMMIT)
public void onClubCreateEvent(ClubCreateEvent event){
var notification = slackNotificationFactory.generateClubCreateSendNotification(event.clubName());
slackClient.sendMessage(notification);
}
}
Loading