diff --git a/src/main/java/doldol_server/doldol/auth/service/AuthService.java b/src/main/java/doldol_server/doldol/auth/service/AuthService.java index cf9adde..2f0b04a 100644 --- a/src/main/java/doldol_server/doldol/auth/service/AuthService.java +++ b/src/main/java/doldol_server/doldol/auth/service/AuthService.java @@ -92,6 +92,10 @@ public void validateVerificationCode(String email, String code) { @Transactional public void register(RegisterRequest registerRequest) { + checkEmailDuplicate(registerRequest.email()); + checkPhoneDuplicate(registerRequest.phone()); + checkIdDuplicate(registerRequest.id()); + validateAndDeleteEmailVerification(registerRequest.email()); User user = User.builder() @@ -107,6 +111,8 @@ public void register(RegisterRequest registerRequest) { @Transactional public void oauthRegister(OAuthRegisterRequest oAuthRegisterRequest) { + checkEmailDuplicate(oAuthRegisterRequest.email()); + checkPhoneDuplicate(oAuthRegisterRequest.phone()); validateAndDeleteEmailVerification(oAuthRegisterRequest.email()); User user = User.builder() diff --git a/src/main/java/doldol_server/doldol/common/config/SecurityConfig.java b/src/main/java/doldol_server/doldol/common/config/SecurityConfig.java index e2039c7..76908ba 100644 --- a/src/main/java/doldol_server/doldol/common/config/SecurityConfig.java +++ b/src/main/java/doldol_server/doldol/common/config/SecurityConfig.java @@ -51,7 +51,8 @@ public class SecurityConfig { "/swagger-resources/**", "/webjars/**", "/actuator/**", - "/papers/invite" + "/papers/invite", + "/invites/**/comments", }; private static final String[] BLACKLIST = { @@ -90,8 +91,6 @@ public SecurityFilterChain filterChain(HttpSecurity http, AuthenticationManager .requestMatchers(WHITELIST).permitAll() .requestMatchers(HttpMethod.POST, "/reports").hasAuthority(Role.USER.getRole()) .requestMatchers(HttpMethod.GET, "/invites/**").permitAll() - .requestMatchers(HttpMethod.POST, "/invites").hasAuthority(Role.USER.getRole()) - .requestMatchers(HttpMethod.POST, "/invites/**/comments").hasAuthority(Role.USER.getRole()) .requestMatchers(BLACKLIST).authenticated() .anyRequest().authenticated()) .addFilterAt(new CustomUserLoginFilter(authenticationManager, tokenProvider, objectMapper), diff --git a/src/main/java/doldol_server/doldol/invite/controller/InviteController.java b/src/main/java/doldol_server/doldol/invite/controller/InviteController.java index 277d8ae..50f2e5a 100644 --- a/src/main/java/doldol_server/doldol/invite/controller/InviteController.java +++ b/src/main/java/doldol_server/doldol/invite/controller/InviteController.java @@ -1,9 +1,10 @@ package doldol_server.doldol.invite.controller; -import doldol_server.doldol.auth.dto.CustomUserDetails; import doldol_server.doldol.common.response.ApiResponse; +import doldol_server.doldol.auth.dto.CustomUserDetails; import doldol_server.doldol.invite.dto.request.InviteCommentCreateRequest; import doldol_server.doldol.invite.dto.request.InviteCreateRequest; +import doldol_server.doldol.invite.dto.request.InviteUpdateRequest; import doldol_server.doldol.invite.dto.response.InviteCommentResponse; import doldol_server.doldol.invite.dto.response.InviteResponse; import doldol_server.doldol.invite.service.InviteService; @@ -17,7 +18,7 @@ import java.util.List; -@Tag(name = "Invite", description = "파티/모임 초대장 API") +@Tag(name = "초대장", description = "파티/모임 초대장 API") @RestController @RequestMapping("/invites") @RequiredArgsConstructor @@ -43,17 +44,13 @@ public ApiResponse getInvite(@PathVariable Long inviteId) { return ApiResponse.ok(inviteService.getInvite(inviteId)); } - @Operation( - summary = "초대장 댓글 등록", - security = {@SecurityRequirement(name = "jwt")} - ) + @Operation(summary = "초대장 댓글 등록") @PostMapping("/{inviteId}/comments") public ApiResponse addComment( @PathVariable Long inviteId, - @Valid @RequestBody InviteCommentCreateRequest request, - @AuthenticationPrincipal CustomUserDetails userDetails + @Valid @RequestBody InviteCommentCreateRequest request ) { - return ApiResponse.created(inviteService.addComment(inviteId, request, userDetails.getUserId())); + return ApiResponse.created(inviteService.addComment(inviteId, request)); } @Operation(summary = "초대장 댓글 목록 조회") @@ -61,5 +58,32 @@ public ApiResponse addComment( public ApiResponse> getComments(@PathVariable Long inviteId) { return ApiResponse.ok(inviteService.getComments(inviteId)); } + + @Operation( + summary = "초대장 수정", + security = {@SecurityRequirement(name = "jwt")} + ) + @PutMapping("/{inviteId}") + public ApiResponse updateInvite( + @PathVariable Long inviteId, + @Valid @RequestBody InviteUpdateRequest request, + @AuthenticationPrincipal CustomUserDetails userDetails + ) { + inviteService.updateInvite(inviteId, request, userDetails.getUserId()); + return ApiResponse.noContent(); + } + + @Operation( + summary = "초대장 삭제", + security = {@SecurityRequirement(name = "jwt")} + ) + @DeleteMapping("/{inviteId}") + public ApiResponse deleteInvite( + @PathVariable Long inviteId, + @AuthenticationPrincipal CustomUserDetails userDetails + ) { + inviteService.deleteInvite(inviteId, userDetails.getUserId()); + return ApiResponse.noContent(); + } } diff --git a/src/main/java/doldol_server/doldol/invite/dto/request/InviteCreateRequest.java b/src/main/java/doldol_server/doldol/invite/dto/request/InviteCreateRequest.java index a2bc89d..83ce7d5 100644 --- a/src/main/java/doldol_server/doldol/invite/dto/request/InviteCreateRequest.java +++ b/src/main/java/doldol_server/doldol/invite/dto/request/InviteCreateRequest.java @@ -1,7 +1,6 @@ package doldol_server.doldol.invite.dto.request; import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.FutureOrPresent; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; @@ -21,7 +20,6 @@ public class InviteCreateRequest { @Schema(description = "행사 일시", example = "2025-12-31T18:30:00") @NotNull(message = "일시는 필수입니다.") - @FutureOrPresent(message = "과거 일시는 입력할 수 없습니다.") private LocalDateTime eventDateTime; @Schema(description = "행사 장소", example = "서울시 강남구 역삼동 123-45") @@ -35,12 +33,10 @@ public class InviteCreateRequest { @Schema(description = "초대장 본문 문구", example = "함께 모여 즐거운 시간을 보내요!") @NotBlank(message = "문구는 필수입니다.") - @Size(max = 1000, message = "문구는 1000자를 초과할 수 없습니다.") private String content; @Schema(description = "보내는 사람 또는 단체 명", example = "돌돌팀") @NotBlank(message = "보내는 사람은 필수입니다.") - @Size(max = 60, message = "보내는 사람은 60자를 초과할 수 없습니다.") private String sender; @Schema(description = "초대장 테마", example = "retro") diff --git a/src/main/java/doldol_server/doldol/invite/dto/request/InviteUpdateRequest.java b/src/main/java/doldol_server/doldol/invite/dto/request/InviteUpdateRequest.java new file mode 100644 index 0000000..5df108e --- /dev/null +++ b/src/main/java/doldol_server/doldol/invite/dto/request/InviteUpdateRequest.java @@ -0,0 +1,51 @@ +package doldol_server.doldol.invite.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +public class InviteUpdateRequest { + + private static final String TITLE_MESSAGE = "제목은 1자 이상 40자 이하여야 합니다."; + + @Schema(description = "초대장 제목", example = "돌돌이의 홈파티") + @NotBlank(message = TITLE_MESSAGE) + @Size(max = 40, message = TITLE_MESSAGE) + private String title; + + @Schema(description = "행사 일시", example = "2025-12-31T18:30:00") + @NotNull(message = "일시는 필수입니다.") + private LocalDateTime eventDateTime; + + @Schema(description = "행사 장소", example = "서울시 강남구 역삼동 123-45") + @NotBlank(message = "장소는 필수입니다.") + @Size(max = 120, message = "장소는 120자를 초과할 수 없습니다.") + private String location; + + @Schema(description = "장소 링크", example = "https://maps.google.com/?q=서울시+강남구") + private String locationLink; + + @Schema(description = "초대장 본문 문구", example = "함께 모여 즐거운 시간을 보내요!") + @NotBlank(message = "문구는 필수입니다.") + private String content; + + @Schema(description = "보내는 사람 또는 단체 명", example = "돌돌팀") + @NotBlank(message = "보내는 사람은 필수입니다.") + @Size(max = 60, message = "보내는 사람은 60자를 초과할 수 없습니다.") + private String sender; + + @Schema(description = "초대장 테마", example = "retro") + @Size(max = 30, message = "테마는 30자를 초과할 수 없습니다.") + private String theme; + + @Schema(description = "폰트 스타일", example = "Arial") + @NotBlank(message = "폰트 스타일은 필수입니다.") + private String fontStyle; + +} + diff --git a/src/main/java/doldol_server/doldol/invite/dto/response/InviteCommentResponse.java b/src/main/java/doldol_server/doldol/invite/dto/response/InviteCommentResponse.java index a260532..fa8db59 100644 --- a/src/main/java/doldol_server/doldol/invite/dto/response/InviteCommentResponse.java +++ b/src/main/java/doldol_server/doldol/invite/dto/response/InviteCommentResponse.java @@ -17,12 +17,6 @@ public class InviteCommentResponse { @Schema(description = "댓글 작성자", example = "돌돌이 친구들") private final String author; - @Schema(description = "댓글 작성자 ID", example = "1") - private final Long userId; - - @Schema(description = "댓글 작성자 이름", example = "홍길동") - private final String userName; - @Schema(description = "댓글 내용", example = "꼭 참석할게요!") private final String content; @@ -33,8 +27,6 @@ public static InviteCommentResponse from(InviteComment comment) { return InviteCommentResponse.builder() .commentId(comment.getCommentId()) .author(comment.getAuthor()) - .userId(comment.getUser().getId()) - .userName(comment.getUser().getName()) .content(comment.getContent()) .createdAt(comment.getCreatedAt()) .build(); diff --git a/src/main/java/doldol_server/doldol/invite/entity/Invite.java b/src/main/java/doldol_server/doldol/invite/entity/Invite.java index 22f5806..f6e45aa 100644 --- a/src/main/java/doldol_server/doldol/invite/entity/Invite.java +++ b/src/main/java/doldol_server/doldol/invite/entity/Invite.java @@ -18,12 +18,6 @@ @Entity public class Invite extends BaseEntity { - private static final int TITLE_MAX_LENGTH = 40; - private static final int LOCATION_MAX_LENGTH = 120; - private static final int SENDER_MAX_LENGTH = 60; - private static final int CODE_MAX_LENGTH = 36; - private static final int THEME_MAX_LENGTH = 30; - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long inviteId; @@ -31,25 +25,24 @@ public class Invite extends BaseEntity { @Column(nullable = false) private LocalDateTime eventDateTime; - @Column(nullable = false, length = LOCATION_MAX_LENGTH) + @Column(nullable = false) private String location; - @Column(name = "location_link", length = 500) + @Column(name = "location_link") private String locationLink; @Column(nullable = false, length = 1000) private String content; - @Column(nullable = false, length = TITLE_MAX_LENGTH) + @Column(nullable = false) private String title; - @Column(nullable = false, length = SENDER_MAX_LENGTH) + @Column(nullable = false) private String sender; - @Column(nullable = false, unique = true, length = CODE_MAX_LENGTH) + @Column(nullable = false, unique = true) private String inviteCode; - @Column(length = THEME_MAX_LENGTH) private String theme; @Column(name = "font_style", nullable = false) @@ -81,5 +74,17 @@ public void addComment(InviteComment comment) { comments.add(comment); comment.assignInvite(this); } + + public void update(String title, LocalDateTime eventDateTime, String location, String locationLink, + String content, String sender, String theme, String fontStyle) { + this.title = title; + this.eventDateTime = eventDateTime; + this.location = location; + this.locationLink = locationLink; + this.content = content; + this.sender = sender; + this.theme = theme; + this.fontStyle = fontStyle; + } } diff --git a/src/main/java/doldol_server/doldol/invite/entity/InviteComment.java b/src/main/java/doldol_server/doldol/invite/entity/InviteComment.java index d142440..a9339f6 100644 --- a/src/main/java/doldol_server/doldol/invite/entity/InviteComment.java +++ b/src/main/java/doldol_server/doldol/invite/entity/InviteComment.java @@ -1,7 +1,6 @@ package doldol_server.doldol.invite.entity; import doldol_server.doldol.common.entity.BaseEntity; -import doldol_server.doldol.user.entity.User; import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Builder; @@ -14,7 +13,6 @@ @Entity public class InviteComment extends BaseEntity { - private static final int AUTHOR_MAX_LENGTH = 40; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -24,21 +22,14 @@ public class InviteComment extends BaseEntity { @JoinColumn(name = "invite_id", nullable = false) private Invite invite; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id", referencedColumnName = "user_id", nullable = false) - private User user; - - @Column(length = AUTHOR_MAX_LENGTH) private String author; - @Column(nullable = false, length = 500) private String content; @Builder - private InviteComment(String author, String content, User user) { + private InviteComment(String author, String content) { this.author = author; this.content = content; - this.user = user; } void assignInvite(Invite invite) { diff --git a/src/main/java/doldol_server/doldol/invite/errorCode/InviteErrorCode.java b/src/main/java/doldol_server/doldol/invite/errorCode/InviteErrorCode.java index c142993..f245297 100644 --- a/src/main/java/doldol_server/doldol/invite/errorCode/InviteErrorCode.java +++ b/src/main/java/doldol_server/doldol/invite/errorCode/InviteErrorCode.java @@ -9,7 +9,8 @@ @AllArgsConstructor public enum InviteErrorCode implements ErrorCode { - INVITE_NOT_FOUND(HttpStatus.NOT_FOUND, "IV-001", "초대장을 찾을 수 없습니다."); + INVITE_NOT_FOUND(HttpStatus.NOT_FOUND, "IV-001", "초대장을 찾을 수 없습니다."), + INVITE_FORBIDDEN(HttpStatus.FORBIDDEN, "IV-002", "초대장을 수정하거나 삭제할 권한이 없습니다."); private final HttpStatus httpStatus; private final String code; diff --git a/src/main/java/doldol_server/doldol/invite/service/InviteService.java b/src/main/java/doldol_server/doldol/invite/service/InviteService.java index 1979a60..0d307a6 100644 --- a/src/main/java/doldol_server/doldol/invite/service/InviteService.java +++ b/src/main/java/doldol_server/doldol/invite/service/InviteService.java @@ -3,6 +3,7 @@ import doldol_server.doldol.common.exception.CustomException; import doldol_server.doldol.invite.dto.request.InviteCommentCreateRequest; import doldol_server.doldol.invite.dto.request.InviteCreateRequest; +import doldol_server.doldol.invite.dto.request.InviteUpdateRequest; import doldol_server.doldol.invite.dto.response.InviteCommentResponse; import doldol_server.doldol.invite.dto.response.InviteResponse; import doldol_server.doldol.invite.entity.Invite; @@ -58,17 +59,13 @@ public InviteResponse getInvite(Long inviteId) { } @Transactional - public InviteCommentResponse addComment(Long inviteId, InviteCommentCreateRequest request, Long userId) { + public InviteCommentResponse addComment(Long inviteId, InviteCommentCreateRequest request) { Invite invite = inviteRepository.findById(inviteId) .orElseThrow(() -> new CustomException(InviteErrorCode.INVITE_NOT_FOUND)); - User user = userRepository.findById(userId) - .orElseThrow(() -> new CustomException(UserErrorCode.USER_NOT_FOUND)); - InviteComment comment = InviteComment.builder() .author(request.getAuthor()) .content(request.getContent()) - .user(user) .build(); invite.addComment(comment); inviteCommentRepository.save(comment); @@ -85,5 +82,38 @@ public List getComments(Long inviteId) { .map(InviteCommentResponse::from) .toList(); } + + @Transactional + public void updateInvite(Long inviteId, InviteUpdateRequest request, Long userId) { + Invite invite = inviteRepository.findById(inviteId) + .orElseThrow(() -> new CustomException(InviteErrorCode.INVITE_NOT_FOUND)); + + if (!invite.getUser().getId().equals(userId)) { + throw new CustomException(InviteErrorCode.INVITE_FORBIDDEN); + } + + invite.update( + request.getTitle(), + request.getEventDateTime(), + request.getLocation(), + request.getLocationLink(), + request.getContent(), + request.getSender(), + request.getTheme(), + request.getFontStyle() + ); + } + + @Transactional + public void deleteInvite(Long inviteId, Long userId) { + Invite invite = inviteRepository.findById(inviteId) + .orElseThrow(() -> new CustomException(InviteErrorCode.INVITE_NOT_FOUND)); + + if (!invite.getUser().getId().equals(userId)) { + throw new CustomException(InviteErrorCode.INVITE_FORBIDDEN); + } + + inviteRepository.delete(invite); + } }