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
Expand Up @@ -18,11 +18,13 @@
import com.cokerthon.Team3_Backend.global.security.CurrentUser;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/api/community")
@RequiredArgsConstructor
@Tag(name = "Community API")
public class CommunityController {

private final CommunityService communityService;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package com.cokerthon.Team3_Backend.domain.emotion.controller;

import java.time.LocalDate;

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.EmotionDailyResDto;
import com.cokerthon.Team3_Backend.domain.emotion.dto.response.EmotionMonthlyResDto;
import com.cokerthon.Team3_Backend.domain.emotion.dto.response.EmotionResponseDto;
import com.cokerthon.Team3_Backend.domain.emotion.dto.response.EmotionWeeklyResDto;
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.emotion.service.query.EmotionQueryService;
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;
Expand All @@ -15,6 +21,8 @@
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

Expand All @@ -28,6 +36,7 @@
public class EmotionController {

private final EmotionCommandService emotionCommandService;
private final EmotionQueryService emotionQueryService;
private final UserRepository userRepository;

/**
Expand Down Expand Up @@ -176,4 +185,33 @@ public ResponseEntity<ApiResponse<Void>> deleteEmotion(
)
);
}

@GetMapping("/daily")
@Operation(summary = "감정 일별 조회", description = "특정 날짜의 감정 기록 리스트와 평균 점수를 조회합니다.")
public ApiResponse<EmotionDailyResDto> getDailyEmotions(
@CurrentUser User user,
@RequestParam(name = "date") @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date
) {
EmotionDailyResDto response = emotionQueryService.getDailyEmotions(user, date);
return ApiResponse.onSuccess(EmotionSuccessCode.EMOTION_FOUND, response);
}

@GetMapping("/weekly")
@Operation(summary = "이번 주 감정 조회", description = "이번 주 월~일요일의 요일별 평균 점수와 주간 전체 평균을 조회합니다.")
public ApiResponse<EmotionWeeklyResDto> getWeeklyEmotions(@CurrentUser User user) {
EmotionWeeklyResDto response = emotionQueryService.getWeeklyEmotions(user);
return ApiResponse.onSuccess(EmotionSuccessCode.EMOTION_FOUND, response);
}

@GetMapping("/monthly")
@Operation(summary = "감정 월별 조회", description = "특정 월의 일별 평균 점수 목록을 조회합니다.")
public ApiResponse<EmotionMonthlyResDto> getMonthlyEmotions(
@CurrentUser User user,
@RequestParam(name = "year") int year,
@RequestParam(name = "month") int month
) {
EmotionMonthlyResDto response = emotionQueryService.getMonthlyEmotions(user, year, month);
return ApiResponse.onSuccess(EmotionSuccessCode.EMOTION_FOUND, response);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ public static EmotionResponseDto.EmotionUpdateResponseDto toUpdateResponse(Emoti
);
}

public static EmotionResponseDto.EmotionDetailResponseDto toDetailResponse(EmotionRecord emotion) {
return new EmotionResponseDto.EmotionDetailResponseDto(
emotion.getId(),
emotion.getEmotionScore(),
emotion.getContent(),
emotion.getVisibility(),
emotion.getStatus(),
emotion.getCreatedAt(),
emotion.getUpdatedAt()
);
public static EmotionResponseDto.EmotionDetailResponseDto toDetailDto(EmotionRecord record) {
return EmotionResponseDto.EmotionDetailResponseDto.builder()
.emotionId(record.getId())
.emotionScore(record.getEmotionScore())
.content(record.getContent())
.visibility(record.getVisibility())
.status(record.getStatus())
.createdAt(record.getCreatedAt())
.updatedAt(record.getUpdatedAt())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.cokerthon.Team3_Backend.domain.emotion.dto.response;

import java.time.LocalDate;
import java.util.List;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

@Builder
@Schema(description = "감정 일별 조회 응답 DTO")
public record EmotionDailyResDto(
@Schema(description = "해당 날짜")
LocalDate date,

@Schema(description = "하루 평균 감정 점수")
double averageScore,

@Schema(description = "해당 일자 감정 일기 목록")
List<EmotionResponseDto.EmotionDetailResponseDto> emotions
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.cokerthon.Team3_Backend.domain.emotion.dto.response;

import java.time.LocalDate;
import java.util.List;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

@Builder
@Schema(description = "감정 월별 조회 응답 DTO")
public record EmotionMonthlyResDto(
@Schema(description = "조회한 연도 및 월 (YYYY-MM)")
String yearMonth,

@Schema(description = "일별 평균 점수 목록")
List<DayAverageDto> dayAverages
) {
public record DayAverageDto(
LocalDate date,
double dailyAverage // 데이터가 없으면 0.0
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

import java.time.LocalDateTime;

import lombok.Builder;

public class EmotionResponseDto {

public record EmotionCreateResponseDto(
Expand All @@ -24,6 +26,7 @@ public record EmotionUpdateResponseDto(
) {
}

@Builder
public record EmotionDetailResponseDto(
Long emotionId,
int emotionScore,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.cokerthon.Team3_Backend.domain.emotion.dto.response;

import java.time.LocalDate;
import java.util.List;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;

@Builder
@Schema(description = "이번 주 감정 조회 응답 DTO")
public record EmotionWeeklyResDto(
@Schema(description = "이번 주 전체 평균 점수 (예: 3.2도)")
double weeklyAverage,

@Schema(description = "요일별 요약 정보 목록")
List<DaySummaryDto> weeklyList
) {
public record DaySummaryDto(
String dayOfWeek,
LocalDate date,
double dailyAverage // 해당 날짜의 평균 점수
) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ public enum EmotionSuccessCode implements BaseSuccessCode {
EMOTION_UPDATED(HttpStatus.OK, "EMOTION_200_2", "감정 기록 수정 성공"),

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

EMOTION_FOUND(HttpStatus.OK, "EMOTION_200_4", "감정 기록 조회 성공"),
;

private final HttpStatus status;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.cokerthon.Team3_Backend.domain.emotion.repository;

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

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import com.cokerthon.Team3_Backend.domain.community.dto.response.CommunityResDto;
import com.cokerthon.Team3_Backend.domain.emotion.entity.EmotionRecord;
import com.cokerthon.Team3_Backend.domain.user.entity.User;

public interface EmotionRecordRepository extends JpaRepository<EmotionRecord, Long> {

Expand All @@ -19,4 +21,6 @@ public interface EmotionRecordRepository extends JpaRepository<EmotionRecord, Lo
"GROUP BY r.id, u.nickname, r.emotionScore, r.content, r.createdAt " +
"ORDER BY r.createdAt DESC")
List<CommunityResDto> findAllCommunityPosts();

List<EmotionRecord> findByUserAndCreatedAtBetween(User user, LocalDateTime startOfDay, LocalDateTime endOfDay);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,129 @@
package com.cokerthon.Team3_Backend.domain.emotion.service.query;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.YearMonth;
import java.util.ArrayList;
import java.util.List;

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

import com.cokerthon.Team3_Backend.domain.emotion.converter.EmotionConverter;
import com.cokerthon.Team3_Backend.domain.emotion.dto.response.EmotionDailyResDto;
import com.cokerthon.Team3_Backend.domain.emotion.dto.response.EmotionMonthlyResDto;
import com.cokerthon.Team3_Backend.domain.emotion.dto.response.EmotionResponseDto;
import com.cokerthon.Team3_Backend.domain.emotion.dto.response.EmotionWeeklyResDto;
import com.cokerthon.Team3_Backend.domain.emotion.entity.EmotionRecord;
import com.cokerthon.Team3_Backend.domain.emotion.repository.EmotionRecordRepository;
import com.cokerthon.Team3_Backend.domain.user.entity.User;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
@Transactional
public class EmotionQueryService {

private final EmotionRecordRepository emotionRecordRepository;

@Transactional(readOnly = true)
public EmotionDailyResDto getDailyEmotions(User user, LocalDate date) {
LocalDateTime startOfDay = date.atStartOfDay();
LocalDateTime endOfDay = date.atTime(LocalTime.MAX);

List<EmotionRecord> records = emotionRecordRepository.findByUserAndCreatedAtBetween(user, startOfDay, endOfDay);

double averageScore = records.stream()
.mapToInt(EmotionRecord::getEmotionScore)
.average()
.orElse(0.0);

averageScore = Math.round(averageScore * 10) / 10.0;

List<EmotionResponseDto.EmotionDetailResponseDto> emotionDtos = records.stream()
.map(EmotionConverter::toDetailDto)
.toList();

return EmotionDailyResDto.builder()
.date(date)
.averageScore(averageScore)
.emotions(emotionDtos)
.build();
}

@Transactional(readOnly = true)
public EmotionWeeklyResDto getWeeklyEmotions(User user) {
LocalDate today = LocalDate.now();
// 이번 주 월요일 찾기
LocalDate monday = today.with(java.time.temporal.TemporalAdjusters.previousOrSame(java.time.DayOfWeek.MONDAY));

List<EmotionWeeklyResDto.DaySummaryDto> weeklyList = new ArrayList<>();
double totalScoreSum = 0;
int daysWithData = 0;

// 월요일부터 일요일까지 7일간 반복
for (int i = 0; i < 7; i++) {
LocalDate targetDate = monday.plusDays(i);
LocalDateTime start = targetDate.atStartOfDay();
LocalDateTime end = targetDate.atTime(LocalTime.MAX);

List<EmotionRecord> dayRecords = emotionRecordRepository.findByUserAndCreatedAtBetween(user, start, end);

double dayAvg = dayRecords.stream()
.mapToInt(EmotionRecord::getEmotionScore)
.average().orElse(0.0);
dayAvg = Math.round(dayAvg * 10) / 10.0;

weeklyList.add(new EmotionWeeklyResDto.DaySummaryDto(
targetDate.getDayOfWeek().getDisplayName(java.time.format.TextStyle.SHORT, java.util.Locale.KOREAN),
targetDate,
dayAvg
));

if (dayAvg > 0) {
totalScoreSum += dayAvg;
daysWithData++;
}
}

// 주간 전체 평균 계산
double weeklyAvg = (daysWithData > 0) ? Math.round((totalScoreSum / daysWithData) * 10) / 10.0 : 0.0;

return EmotionWeeklyResDto.builder()
.weeklyAverage(weeklyAvg)
.weeklyList(weeklyList)
.build();
}

@Transactional(readOnly = true)
public EmotionMonthlyResDto getMonthlyEmotions(User user, int year, int month) {
YearMonth yearMonth = YearMonth.of(year, month);
LocalDate startOfMonth = yearMonth.atDay(1);
LocalDate endOfMonth = yearMonth.atEndOfMonth();

List<EmotionMonthlyResDto.DayAverageDto> dayAverages = new ArrayList<>();

// 1일부터 말일까지 반복
for (LocalDate date = startOfMonth; !date.isAfter(endOfMonth); date = date.plusDays(1)) {
LocalDateTime start = date.atStartOfDay();
LocalDateTime end = date.atTime(LocalTime.MAX);

List<EmotionRecord> dayRecords = emotionRecordRepository.findByUserAndCreatedAtBetween(user, start, end);

double avg = dayRecords.stream()
.mapToInt(EmotionRecord::getEmotionScore)
.average().orElse(0.0);

avg = Math.round(avg * 10) / 10.0;

dayAverages.add(new EmotionMonthlyResDto.DayAverageDto(date, avg));
}

return EmotionMonthlyResDto.builder()
.yearMonth(yearMonth.toString())
.dayAverages(dayAverages)
.build();
}
}