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
4 changes: 2 additions & 2 deletions .env
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
SPRING_PROFILES_ACTIVE=local
# prod에서 local로 변경
LOCAL_DB_URL=jdbc:mysql://localhost:3306/cokerthon-local?serverTimezone=Asia/Seoul&characterEncoding=UTF-8&createDatabaseIfNotExist=true
LOCAL_DB_URL=jdbc:mysql://localhost:3306/cokerthon
LOCAL_DB_USERNAME=root
LOCAL_DB_PASSWORD=dlgodnjs!
LOCAL_DB_PASSWORD=1234
PROD_DB_URL=jdbc:mysql://cokerthon-db.clgeccwgurgu.ap-northeast-2.rds.amazonaws.com:3306/cokathon?serverTimezone=Asia/Seoul&characterEncoding=UTF-8
PROD_DB_USERNAME=admin
PROD_DB_PASSWORD=cokerthonpassword
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
package com.cokerthon.Team3_Backend.domain.emotion.controller;

import com.cokerthon.Team3_Backend.domain.emotion.converter.EmotionConverter;
import com.cokerthon.Team3_Backend.domain.emotion.dto.request.EmotionRequestDto;
import com.cokerthon.Team3_Backend.domain.emotion.dto.response.EmotionResponseDto;
import com.cokerthon.Team3_Backend.domain.emotion.entity.EmotionRecord;
import com.cokerthon.Team3_Backend.domain.emotion.exception.code.EmotionSuccessCode;
import com.cokerthon.Team3_Backend.domain.emotion.service.command.EmotionCommandService;
import com.cokerthon.Team3_Backend.domain.user.entity.User;
import com.cokerthon.Team3_Backend.domain.user.repository.UserRepository;
import com.cokerthon.Team3_Backend.global.apiPayload.ApiResponse;
import com.cokerthon.Team3_Backend.global.apiPayload.code.GeneralErrorCode;
import com.cokerthon.Team3_Backend.global.exception.GeneralException;
import com.cokerthon.Team3_Backend.global.security.CurrentUser;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpSession;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Tag(
name = "Emotion",
description = "감정 기록 생성·수정·삭제 API"
)
@RestController
@RequestMapping("/api/emotions")
@RequiredArgsConstructor
public class EmotionController {

private final EmotionCommandService emotionCommandService;
private final UserRepository userRepository;

/**
* 감정 생성 API
* @param requestDto EmotionCreateRequestDto
* @param user 로그인 된 사용자 엔티티
* @return 감정 생성 완료 응답
*/
@Operation(
summary = "감정 기록 생성 by 임준서(개발 완료)",
description = """
현재 로그인한 사용자가 감정 기록을 생성합니다.

- 감정 점수는 1~5 사이 값만 허용됩니다.
- visibility = PUBLIC 인 경우 다른 사용자에게 공개됩니다.
"""
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "200",
description = "감정 기록 생성 성공"
),
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "400",
description = "잘못된 요청 (감정 점수 범위 오류 등)",
content = @Content
),
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "401",
description = "로그인되지 않은 사용자",
content = @Content
)
})
@PostMapping
public ResponseEntity<ApiResponse<EmotionResponseDto.EmotionCreateResponseDto>> createEmotion(
@RequestBody EmotionRequestDto.EmotionCreateRequestDto requestDto,
@CurrentUser User user
) {
EmotionRecord emotion =
emotionCommandService.createEmotion(user, requestDto);

return ResponseEntity.ok(
ApiResponse.onSuccess(
EmotionSuccessCode.EMOTION_CREATED,
EmotionConverter.toCreateResponse(emotion)
)
);
}

/**
* 감정 수정 API
* @param emotionId 감정 기록 ID
* @param requestDto EmotionUpdateRequestDto
* @param user 로그인 된 사용자 엔티티
* @return 감정 수정 완료 응답
*/
@Operation(
summary = "감정 기록 수정 by 임준서(개발 완료)",
description = """
기존에 작성한 감정 기록을 수정합니다.

- 작성자 본인만 수정할 수 있습니다.
- 감정 점수는 1~5 사이 값만 허용됩니다.
"""
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "200",
description = "감정 기록 수정 성공"
),
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "403",
description = "작성자가 아닌 경우",
content = @Content
),
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "404",
description = "감정 기록을 찾을 수 없음",
content = @Content
)
})
@PutMapping("/{emotionId}")
public ResponseEntity<ApiResponse<EmotionResponseDto.EmotionUpdateResponseDto>> updateEmotion(
@PathVariable Long emotionId,
@RequestBody EmotionRequestDto.EmotionUpdateRequestDto requestDto,
@CurrentUser User user
) {
emotionCommandService.updateEmotion(
emotionId,
user,
requestDto
);

return ResponseEntity.ok(
ApiResponse.onSuccess(
EmotionSuccessCode.EMOTION_UPDATED,
null
)
);
}

/**
* 감정 삭제 API
* @param emotionId 감정 기록 ID
* @param user 로그인 된 사용자 엔티티
* @return 감정 삭제 완료 응답
*/
@Operation(
summary = "감정 기록 삭제 by 임준서(개발 완료)",
description = """
감정 기록을 삭제합니다.

- 작성자 본인만 삭제할 수 있습니다.
- 삭제된 감정 기록은 복구할 수 없습니다.
"""
)
@ApiResponses({
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "200",
description = "감정 기록 삭제 성공"
),
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "403",
description = "작성자가 아닌 경우",
content = @Content
),
@io.swagger.v3.oas.annotations.responses.ApiResponse(
responseCode = "404",
description = "감정 기록을 찾을 수 없음",
content = @Content
)
})
@DeleteMapping("/{emotionId}")
public ResponseEntity<ApiResponse<Void>> deleteEmotion(
@PathVariable Long emotionId,
@CurrentUser User user
) {

emotionCommandService.deleteEmotion(emotionId, user);

return ResponseEntity.ok(
ApiResponse.onSuccess(
EmotionSuccessCode.EMOTION_DELETED,
null
)
);
}

/* ===== private ===== */

private User getLoginUser(Long userId) {
if (userId == null) {
throw new GeneralException(GeneralErrorCode.UNAUTHORIZED);
}

return userRepository.findById(userId)
.orElseThrow(() ->
new GeneralException(GeneralErrorCode.UNAUTHORIZED)
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.cokerthon.Team3_Backend.domain.emotion.converter;

import com.cokerthon.Team3_Backend.domain.emotion.dto.request.EmotionRequestDto;
import com.cokerthon.Team3_Backend.domain.emotion.dto.response.EmotionResponseDto;
import com.cokerthon.Team3_Backend.domain.emotion.entity.EmotionRecord;
import com.cokerthon.Team3_Backend.domain.user.entity.User;

public class EmotionConverter {

private EmotionConverter() {
}

/** 생성 요청 DTO → Entity */
public static EmotionRecord toEntity(
User user,
EmotionRequestDto.EmotionCreateRequestDto dto
) {
return EmotionRecord.builder()
.user(user)
.emotionScore(dto.emotionScore())
.content(dto.content())
.visibility(dto.visibility()) // 생성 시에만 설정
.build();
}

/** 수정 요청 DTO → Entity 반영 */
public static void updateEntity(
EmotionRecord emotion,
EmotionRequestDto.EmotionUpdateRequestDto dto
) {
emotion.update(
dto.emotionScore(),
dto.content()
);
}

/** Entity → 생성 응답 DTO */
public static EmotionResponseDto.EmotionCreateResponseDto toCreateResponse(
EmotionRecord emotion
) {
return new EmotionResponseDto.EmotionCreateResponseDto(emotion.getId());
}

/** Entity → 수정 응답 DTO */
public static EmotionResponseDto.EmotionUpdateResponseDto toUpdateResponse(
EmotionRecord emotion
) {
return new EmotionResponseDto.EmotionUpdateResponseDto(emotion.getId());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.cokerthon.Team3_Backend.domain.emotion.dto.request;

import com.cokerthon.Team3_Backend.domain.emotion.enums.Visibility;
import io.swagger.v3.oas.annotations.media.Schema;

public class EmotionRequestDto {

public record EmotionCreateRequestDto(
@Schema(description = "감정 점수 (1~5)", example = "4")
int emotionScore,

@Schema(description = "감정 내용", example = "오늘은 기분이 좋다.")
String content,

@Schema(description = "공개 여부", example = "PUBLIC")
Visibility visibility
) {
}

public record EmotionUpdateRequestDto(
@Schema(description = "감정 점수 (1~5)", example = "4")
int emotionScore,

@Schema(description = "감정 내용", example = "오늘은 기분이 좋다.")
String content
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.cokerthon.Team3_Backend.domain.emotion.dto.response;

public class EmotionResponseDto {

public record EmotionCreateResponseDto(
Long emotionId
) {
}

public record EmotionUpdateResponseDto(
Long emotionId
) {
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.cokerthon.Team3_Backend.domain.emotion.entity;

import com.cokerthon.Team3_Backend.domain.emotion.enums.Visiblity;
import com.cokerthon.Team3_Backend.domain.emotion.enums.Visibility;
import com.cokerthon.Team3_Backend.domain.user.entity.User;
import com.cokerthon.Team3_Backend.global.entity.BaseEntity;
import jakarta.persistence.*;
Expand Down Expand Up @@ -44,5 +44,21 @@ public class EmotionRecord extends BaseEntity {
/** 공개 여부 */
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private Visiblity visibility;
private Visibility visibility;


// == 비즈니스 로직 == //

/** 감정 수정 */
public void update(int emotionScore, String content) {
this.emotionScore = emotionScore;
this.content = content;
}

/** 작성자 검증 */
public void validateOwner(User user) {
if (!this.user.getId().equals(user.getId())) {
throw new IllegalStateException("작성자만 수정/삭제할 수 있습니다.");
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.cokerthon.Team3_Backend.domain.emotion.enums;

public enum Visiblity {
public enum Visibility {
PUBLIC,
PRIVATE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.cokerthon.Team3_Backend.domain.emotion.exception;

import com.cokerthon.Team3_Backend.global.apiPayload.code.BaseErrorCode;
import com.cokerthon.Team3_Backend.global.exception.GeneralException;

public class EmotionException extends GeneralException {
public EmotionException(BaseErrorCode code) {
super(code);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.cokerthon.Team3_Backend.domain.emotion.exception.code;

import com.cokerthon.Team3_Backend.global.apiPayload.code.BaseErrorCode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum EmotionErrorCode implements BaseErrorCode {

INVALID_EMOTION_SCORE(HttpStatus.BAD_REQUEST, "EMOTION_400_1", "감정 점수는 1~5 사이여야 합니다."),

EMOTION_FORBIDDEN(HttpStatus.FORBIDDEN, "EMOTION_403_1", "작성자만 수정/삭제할 수 있습니다."),

EMOTION_NOT_FOUND(HttpStatus.NOT_FOUND, "EMOTION_404_1", "감정 기록이 존재하지 않습니다."),

;

private final HttpStatus status;
private final String code;
private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.cokerthon.Team3_Backend.domain.emotion.exception.code;

import com.cokerthon.Team3_Backend.global.apiPayload.code.BaseSuccessCode;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.http.HttpStatus;

@Getter
@AllArgsConstructor
public enum EmotionSuccessCode implements BaseSuccessCode {

EMOTION_CREATED(HttpStatus.OK, "EMOTION_200_1", "감정 기록 생성 성공"),

EMOTION_UPDATED(HttpStatus.OK, "EMOTION_200_2", "감정 기록 수정 성공"),

EMOTION_DELETED(HttpStatus.OK, "EMOTION_200_3", "감정 기록 삭제 성공"),
;

private final HttpStatus status;
private final String code;
private final String message;
}
Loading