Skip to content
Merged
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ dependencies {
// bucket4j
implementation 'com.bucket4j:bucket4j_jdk17-core:8.14.0'

// commons-lang3
implementation 'org.apache.commons:commons-lang3:3.18.0'
// caffeine
implementation 'com.github.ben-manes.caffeine:caffeine'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.oronaminc.join.answer.dto;

import com.oronaminc.join.global.dto.WriterDto;
import com.oronaminc.join.websocket.common.EventType;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
Expand All @@ -13,7 +14,7 @@ public record AnswerCreateResponse(
@Schema(description = "답변이 생성될 질문 ID")
Long questionId,
@Schema(description = "답변 생성/삭제/수정 상태", example = "CREATE")
String event,
EventType event,
@Schema(description = "답변 ID", example = "11")
Long answerId,
@Schema(description = "답변 내용", example = "답변입니다.")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.oronaminc.join.answer.dto;

import com.oronaminc.join.websocket.common.EventType;
import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "답변 삭제 응답 DTO")
public record AnswerDeleteResponse(
Long answerId,
@Schema(description = "삭제 이벤트", example = "DELETE")
String event
EventType event
) {

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.oronaminc.join.answer.dto;

import com.oronaminc.join.websocket.common.EventType;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

Expand All @@ -8,7 +9,7 @@
public record AnswerUpdateResponse(
Long answerId,
@Schema(description = "수정 이벤트", example = "UPDATE")
String event,
EventType event,
@Schema(description = "수정된 내용", example = "수정된 답변입니다.")
String content

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.oronaminc.join.global.dto.WriterDto;
import com.oronaminc.join.member.domain.Member;
import com.oronaminc.join.question.domain.Question;
import com.oronaminc.join.websocket.common.EventType;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

Expand All @@ -17,7 +18,7 @@ public class AnswerMapper {
public static AnswerCreateResponse toAnswerCreateResponse(Answer answer) {
return AnswerCreateResponse.builder()
.questionId(answer.getQuestion().getId())
.event("CREATE")
.event(EventType.CREATE)
.answerId(answer.getId())
.content(answer.getContent())
.emojiCount(0)
Expand Down Expand Up @@ -51,7 +52,7 @@ public static Answer toEntity(Question question, Member member, AnswerRequest re
public static AnswerUpdateResponse toAnswerUpdateResponse(Answer answer) {
return AnswerUpdateResponse.builder()
.answerId(answer.getId())
.event("UPDATE")
.event(EventType.UPDATE)
.content(answer.getContent())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package com.oronaminc.join.emoji.dto;

import com.oronaminc.join.emoji.domain.TargetType;
import com.oronaminc.join.websocket.common.EventType;
import io.swagger.v3.oas.annotations.media.Schema;

@Schema(description = "발표방/질문/답변 공감 생성/삭제 응답 DTO")
public record EmojiResponse(
@Schema(description = "이벤트 타입 (CREATE, DELETE)", example = "CREATE")
String event,
EventType event,
@Schema(description = "공감 대상 타입 (ROOM, QUESTION, ANSWER)", example = "ROOM")
TargetType targetType,
@Schema(description = "공감 대상 ID", example = "1")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.oronaminc.join.member.service.MemberReader;
import com.oronaminc.join.question.service.QuestionReader;
import com.oronaminc.join.room.service.RoomReader;
import com.oronaminc.join.websocket.common.EventType;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -46,7 +47,7 @@ public EmojiResponse createEmoji(Long memberId, EmojiRequest emojiRequest) {

emojiCount = incrementEmojiCount(targetType, targetId);

return new EmojiResponse("CREATE", targetType, targetId, emojiCount);
return new EmojiResponse(EventType.CREATE, targetType, targetId, emojiCount);

}

Expand All @@ -62,7 +63,7 @@ public EmojiResponse deleteEmoji(Long memberId, EmojiRequest emojiRequest) {
);
emojiCount = decrementEmojiCount(targetType, targetId);

return new EmojiResponse("DELETE", targetType, targetId, emojiCount);
return new EmojiResponse(EventType.DELETE, targetType, targetId, emojiCount);

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ public enum ErrorCode {
UNAUTHORIZED_LIMIT_PARTICIPANT("PARTICIPANT-005", "인원이 가득 차 참가할 수 없습니다.", UNAUTHORIZED),
UNAUTHORIZED_NOT_JOIN_ROOM("PARTICIPANT-005", "발표방에 참여하지 않았습니다. 먼저 참여해주세요.", UNAUTHORIZED),


FILE_UPLOAD_FAILED("FILE-001", "파일 업로드에 실패하였습니다.", INTERNAL_SERVER_ERROR),
NOT_FOUND_FILE("FILE-002", "존재하지 않는 파일입니다.", NOT_FOUND),
MOVEMENT_FILE_FAILED("FILE-003", "파일 이동이 실패하였습니다.", INTERNAL_SERVER_ERROR),
Expand All @@ -51,7 +50,6 @@ public enum ErrorCode {
UNAUTHORIZED_DELETE_ANSWER("ANSWER-005", "작성자 혹은 팀원, 발표자가 아니면 해당 댓글을 삭제할 수 없습니다.", UNAUTHORIZED),
TOO_MANY_REQUESTS_ANSWER("ANSWER-006", "잠시 후 다시 시도해주세요.", UNAUTHORIZED),


ACCESS_DENIED_SESSION("SESSION-1201", "접근 권한이 없습니다.", FORBIDDEN),
NOT_FOUND_SESSION("SESSION-1202", "세션이 유효하지 않습니다.", UNAUTHORIZED),
EXPIRED_SESSION("SESSION-1203", "세션이 만료되었습니다.", UNAUTHORIZED),
Expand All @@ -62,6 +60,7 @@ public enum ErrorCode {
SOCKET_BAD_REQUEST_PATH("SOCKET-1002", "경로가 유효하지 않습니다.", BAD_REQUEST),
SOCKET_BAD_REQUEST_MEMBER("SOCKET-1003", "회원이 유효하지 않습니다.", BAD_REQUEST),

STOMP_INVALID_DESTINATION("STOMP-001", "경로가 유효하지 않습니다.", BAD_REQUEST),

CONFLICT_EMOJI("EMOJI-001", "공감 처리 중 충돌이 발생했습니다.", CONFLICT),
NOT_FOUND_EMOJI("EMOJI-002", "해당 이모지가 존재하지 않습니다.", NOT_FOUND),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.oronaminc.join.global.exception;

import com.oronaminc.join.global.util.StringUtil;
import java.text.MessageFormat;
import lombok.AllArgsConstructor;
import lombok.Getter;

Expand All @@ -8,5 +10,19 @@
public class ErrorException extends RuntimeException {

private final ErrorCode errorCode;
private final String errorMessage;

}
public ErrorException(ErrorCode errorCode) {
this(errorCode, null);
}

public static ErrorException of(ErrorCode errorCode, String message, Object... args) {
String errorMessage = createMessage(message, args);
return new ErrorException(errorCode, errorMessage);
}

public static String createMessage(String message, Object... args) {
return StringUtil.format(message, args);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class ExceptionAdvice {
public ResponseEntity<ErrorResponse> handleErrorException(ErrorException ex) {
ErrorCode errorCode = ex.getErrorCode();

log.error(errorCode.getMessage(), ex);
log.error("ErrorCode: {}, ErrorMessage: {}", ex.getErrorCode(), ex.getErrorMessage());

HttpStatus httpStatus = switch (errorCode.getErrorStatus()) {
case NOT_FOUND -> HttpStatus.NOT_FOUND;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ public class MemberController {
@GetMapping("/exists")
@ResponseStatus(HttpStatus.OK)
public ExistsMemberResponse existsMemberByEmail(
@Valid ExistsMemberRequest existsMemberRequest) {
boolean exists = memberService.existsMemberByEmail(existsMemberRequest.email());
@RequestParam String email) {
boolean exists = memberService.existsMemberByEmail(email);
return new ExistsMemberResponse(exists);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.oronaminc.join.member.security;


import static com.oronaminc.join.member.util.MemberMapper.toSessionInfoResponse;

import java.util.List;

import org.springframework.http.HttpStatus;
Expand Down Expand Up @@ -46,7 +49,7 @@ public class AuthController {
)
@PostMapping("/kakao")
@ResponseStatus(HttpStatus.OK)
public KakaoLoginResponse kakaoLogin(
public SessionInfoResponse kakaoLogin(
@RequestBody KakaoLoginRequest kakaoLoginRequest,
HttpServletRequest request
) {
Expand All @@ -64,7 +67,7 @@ public KakaoLoginResponse kakaoLogin(

request.getSession(true).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);

return new KakaoLoginResponse(memberDetails.getId());
return toSessionInfoResponse(memberDetails);
}

@Operation(
Expand All @@ -77,7 +80,7 @@ public KakaoLoginResponse kakaoLogin(
)
@PostMapping("/guest")
@ResponseStatus(HttpStatus.CREATED)
public GuestLoginResponse guestLogin(@RequestBody @Valid GuestLoginRequest guestLoginRequest, HttpServletRequest request) {
public SessionInfoResponse guestLogin(@RequestBody @Valid GuestLoginRequest guestLoginRequest, HttpServletRequest request) {
MemberDetails guest = authService.loadGuest(guestLoginRequest);

Authentication authentication = new UsernamePasswordAuthenticationToken(
Expand All @@ -90,7 +93,7 @@ public GuestLoginResponse guestLogin(@RequestBody @Valid GuestLoginRequest guest

request.getSession(true).setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, context);

return new GuestLoginResponse(guest.getId());
return toSessionInfoResponse(guest);
}

@Operation(
Expand All @@ -106,12 +109,7 @@ public GuestLoginResponse guestLogin(@RequestBody @Valid GuestLoginRequest guest
@ResponseStatus(HttpStatus.OK)
public SessionInfoResponse getSessionInfo(@AuthenticationPrincipal MemberDetails memberDetails) {

return new SessionInfoResponse(
memberDetails.getId(),
memberDetails.getName(),
memberDetails.getNickname(),
memberDetails.getRole()
);
return toSessionInfoResponse(memberDetails);
}

@Operation(
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/oronaminc/join/member/util/MemberMapper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.oronaminc.join.member.util;

import com.oronaminc.join.member.dto.SessionInfoResponse;
import java.util.Map;

import com.oronaminc.join.member.domain.Member;
Expand Down Expand Up @@ -65,4 +66,13 @@ public static KakaoUserResponse toKakaoUserResponse(Map<String, Object> kakaoAcc
.profileImageUrl((String) profile.get("profile_image_url"))
.build();
}

public static SessionInfoResponse toSessionInfoResponse(MemberDetails memberDetails) {
return new SessionInfoResponse(
memberDetails.getId(),
memberDetails.getName(),
memberDetails.getNickname(),
memberDetails.getRole()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@

public interface ParticipantRepository extends JpaRepository<Participant, Long> {

@Query(
"SELECT COUNT(p) > 0 " +
"FROM Participant p " +
"WHERE p.room.id = :roomId AND p.member.id = :memberId"
)
@Query("""
select COUNT(p) > 0
from Participant p
where p.room.id = :roomId and p.member.id = :memberId
""")
boolean existsByRoomIdAndMemberId(@Param("roomId") Long roomId, @Param("memberId") Long memberId);

Optional<Participant> findByRoomIdAndParticipantType(Long roomId, ParticipantType participantType);
Expand All @@ -30,23 +30,23 @@ public interface ParticipantRepository extends JpaRepository<Participant, Long>
from Participant p
where p.member.id = :memberId
group by p.participantType
""")
""")
List<ParticipantCountDto> countByMemberIdGroupByParticipantType(Long memberId);

@Query("""
select p
from Participant p
join fetch p.room
where p.member.id = :memberId
""")
""")
Page<Participant> findByMemberId(Long memberId, Pageable pageable);

@Query("""
select p
from Participant p
join fetch p.room
where p.member.id = :memberId and p.participantType = :pType
""")
""")
Page<Participant> findByMemberIdAndParticipantType(Long memberId, ParticipantType pType,
Pageable pageable);

Expand All @@ -55,30 +55,30 @@ Page<Participant> findByMemberIdAndParticipantType(Long memberId, ParticipantTyp
from Participant p
join fetch p.room
where p.member.id = :memberId and p.participantType != :pType
""")
""")
Page<Participant> findByMemberIdAndParticipantTypeNot(Long memberId, ParticipantType pType,
Pageable pageable);

Optional<Participant> findByRoomIdAndMemberId( @Param("roomId") Long roomId, @Param("memberId") Long memberId );

@Query("""
SELECT CASE WHEN COUNT(p) > 0 THEN true ELSE false END
FROM Participant p
WHERE p.room.id = :roomId
AND p.member.id = :memberId
AND (p.participantType = com.oronaminc.join.participant.domain.ParticipantType.PRESENTER
OR p.participantType = com.oronaminc.join.participant.domain.ParticipantType.TEAM)
select case when COUNT(p) > 0 then true else false end
from Participant p
where p.room.id = :roomId
and p.member.id = :memberId
and (p.participantType = com.oronaminc.join.participant.domain.ParticipantType.PRESENTER
or p.participantType = com.oronaminc.join.participant.domain.ParticipantType.TEAM)
""")
boolean existsPresenterOrTeamByMemberId(@Param("roomId") Long roomId, @Param("memberId") Long memberId);

void deleteByRoomId(Long roomId);

@Query(value = """
SELECT COUNT(*)
FROM participant
WHERE room_id = :roomId
AND exited_at IS NOT NULL
AND TIMESTAMPDIFF(SECOND, created_at, exited_at) >= 30
select COUNT(*)
from participant
where room_id = :roomId
and exited_at is not null
and TIMESTAMPDIFF(SECOND, created_at, exited_at) >= 30
""", nativeQuery = true)
Long countParticipantsStayedOver30Seconds(@Param("roomId") Long roomId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
Expand All @@ -30,7 +31,7 @@ public ResponseEntity<QuestionListResponse> getQuestions(
@RequestParam(required = false) Long lastEmojiCount,
@RequestParam(defaultValue = "10") int size,
@RequestParam Long memberId,
@RequestParam Long roomId
@PathVariable Long roomId
) {
Slice<QuestionAssembleResponse> result = questionService.getQuestions(
sort, lastId, lastEmojiCount, size, memberId, roomId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


import com.oronaminc.join.global.dto.WriterDto;
import com.oronaminc.join.websocket.common.EventType;
import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDateTime;
import lombok.Builder;
Expand All @@ -10,7 +11,7 @@
@Schema(description = "질문 생성 응답 DTO")
public record QuestionCreateResponse(
@Schema(description = "", example = "CREATE")
String event,
EventType event,
@Schema(description = "질문 ID", example = "11")
Long questionId,
@Schema(description = "질문 내용", example = "질문있습니다. 질문생성DTO가 맞나요?")
Expand Down
Loading