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 @@ -13,7 +13,7 @@ public class AvatarMasterResponse {

public static AvatarMasterResponse from(AvatarMaster master) {
return AvatarMasterResponse.builder()
.id(master.getId())
.id(master.getId() - 2)
.defaultImageUrl(master.getDefaultImageUrl())
.description(master.getDescription())
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,21 @@
package com.example.cp_main_be.domain.avatar.avatar.dto.response;

import com.example.cp_main_be.domain.avatar.avatar.domain.Avatar;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class AvatarSimpleResponse {
private Long id;
private String name;
private String imageUrl;

// Avatar 엔티티를 AvatarDto로 변환하는 생성자
public AvatarSimpleResponse(Avatar avatar) {
this.id = avatar.getId();
this.name = avatar.getNickname();
this.imageUrl = avatar.getAvatarMaster().getDefaultImageUrl();
public static AvatarSimpleResponse from(Avatar avatar) {
return AvatarSimpleResponse.builder()
.id(avatar.getId())
.name(avatar.getNickname())
.imageUrl(avatar.getImageUrl())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.example.cp_main_be.domain.garden.garden.dto.response;

import java.util.List;

public class GardenListResponse {
List<GardenResponse> responses;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.example.cp_main_be.domain.garden.garden.dto;
package com.example.cp_main_be.domain.garden.garden.dto.response;

import com.example.cp_main_be.domain.avatar.avatar.dto.response.AvatarSimpleResponse;
import com.example.cp_main_be.domain.garden.garden.domain.Garden;
import java.time.LocalDateTime;
import lombok.Builder;
Expand All @@ -13,6 +14,7 @@ public class GardenResponse {
private final Integer slotNumber;
private final Integer waterCount;
private final Integer sunlightCount;
private final AvatarSimpleResponse avatar;
private final LocalDateTime createdAt;
private final LocalDateTime updatedAt;

Expand All @@ -23,13 +25,15 @@ private GardenResponse(
Integer slotNumber,
Integer waterCount,
Integer sunlightCount,
AvatarSimpleResponse avatar,
LocalDateTime createdAt,
LocalDateTime updatedAt) {
this.id = id;
this.userId = userId;
this.slotNumber = slotNumber;
this.waterCount = waterCount;
this.sunlightCount = sunlightCount;
this.avatar = avatar;
this.createdAt = createdAt;
this.updatedAt = updatedAt;
}
Expand All @@ -41,6 +45,7 @@ public static GardenResponse from(Garden garden) {
.slotNumber(garden.getSlotNumber())
.waterCount(garden.getWaterCount())
.sunlightCount(garden.getSunlightCount())
.avatar(AvatarSimpleResponse.from(garden.getAvatar()))
.createdAt(garden.getCreatedAt())
.updatedAt(garden.getUpdatedAt())
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.example.cp_main_be.domain.garden.garden.presentation;

import com.example.cp_main_be.domain.garden.garden.dto.GardenResponse;
import com.example.cp_main_be.domain.garden.garden.dto.response.GardenBackgroundCandidateResponse;
import com.example.cp_main_be.domain.garden.garden.dto.response.GardenResponse;
import com.example.cp_main_be.domain.garden.garden.service.GardenService;
import com.example.cp_main_be.domain.member.user.domain.User;
import com.example.cp_main_be.domain.member.user.service.UserService;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import com.example.cp_main_be.domain.garden.garden.domain.GardenBackground;
import com.example.cp_main_be.domain.garden.garden.domain.repository.GardenBackgroundRepository;
import com.example.cp_main_be.domain.garden.garden.domain.repository.GardenRepository;
import com.example.cp_main_be.domain.garden.garden.dto.GardenResponse;
import com.example.cp_main_be.domain.garden.garden.dto.response.GardenBackgroundCandidateResponse;
import com.example.cp_main_be.domain.garden.garden.dto.response.GardenResponse;
import com.example.cp_main_be.domain.garden.wateringlog.domain.FriendWateringLog;
import com.example.cp_main_be.domain.garden.wateringlog.domain.repository.FriendWateringLogRepository;
import com.example.cp_main_be.domain.member.user.domain.User;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,51 +9,56 @@
public class HomeResponseDto {

private final UserInfo userInfo;
private final GardenInfo gardenInfo;
private final List<GardenSummaryInfo> gardenSummaries; // 본인의 모든 정원 목록
private final List<MissionInfo> todayMissions;
private final ActivityInfo activityInfo;

// private final ActivityInfo activityInfo; // 주간 미션 상태

// --- 내부 DTO 클래스들 ---

@Getter
@Builder
public static class UserInfo {
private String username; // [수정] nickname -> username
private Long id;
private String username;
private int level;
private long currentExp; // [수정] int -> long (User 엔티티의 experience 타입과 일치)
private long requiredExpForNextLevel; // [수정] int -> long
private int unreadNotificationCount;
private long currentExp;
private long requiredExpForNextLevel;
private int unreadNotificationCount; // 읽지 않은 알림 수 (새 메시지 1)
}

// 각 정원의 요약 정보 (홈 화면 슬라이드에 사용)
@Getter
@Builder
public static class GardenInfo {
private String plantName;
private String plantImageUrl;
private int waterCount;
private int maxWaterCount;
private int sunlightCount;
private int maxSunlightCount;
private String backgroundImageUrl;
private AvatarInfo avatar; // 아바타 정보를 정원 정보의 일부로 포함
public static class GardenSummaryInfo {
private Long gardenId; // 정원 ID 추가
private Integer gardenSlotNumber;
private AvatarInfo avatar; // 각 정원에 배치된 아바타 정보
private boolean isOwnerWateringAble; // 본인 정원에 물주기 가능한지 여부
private boolean isOwnerSunlightAble; // 본인 정원에 햇빛 주기 가능한지 여부
}

@Getter
@Builder
public static class AvatarInfo {
private String characterImageUrl;
private Long avatarId;
private String avatarName; // 아바타 마스터 이름 (식물 이름)
private String avatarImageUrl;
}

@Getter
@Builder
public static class MissionInfo {
private Long missionId; // 미션 ID 추가 (프론트엔드에서 미션 클릭 시 사용)
private String missionTitle;
private String missionType; // "QUIZ", "IMAGE_DIARY" 등
private String missionType;
private boolean isCompleted;
// private String description; // 필요시 미션 상세 설명 추가 가능
}

@Getter
@Builder
public static class ActivityInfo {
// 최근 7일간의 활동 기록 (true: 완료, false: 미완료)
private List<Boolean> weeklyMissionStatus;
}
// @Getter
// @Builder
// public static class ActivityInfo {
// private List<Boolean> weeklyMissionStatus; // 최근 7일간의 미션 완료 여부
// }
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
package com.example.cp_main_be.domain.home.service;

import com.example.cp_main_be.domain.avatar.avatar.domain.Avatar;
import com.example.cp_main_be.domain.garden.garden.domain.Garden;
import com.example.cp_main_be.domain.garden.garden.domain.repository.GardenRepository;
import com.example.cp_main_be.domain.home.HomeResponseDto;
import com.example.cp_main_be.domain.member.notification.domain.repository.NotificationRepository;
import com.example.cp_main_be.domain.member.user.domain.User;
import com.example.cp_main_be.domain.member.user.domain.repository.UserRepository;
import com.example.cp_main_be.domain.mission.daily_mission_master.domain.DailyMissionMaster;
import com.example.cp_main_be.domain.mission.user_daily_mission.domain.UserDailyMission;
import com.example.cp_main_be.domain.mission.user_daily_mission.domain.repository.UserDailyMissionRepository;
import com.example.cp_main_be.global.common.CustomApiException;
import com.example.cp_main_be.global.common.ErrorCode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
Expand All @@ -22,6 +20,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

// HomeService.java
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
Expand All @@ -32,105 +31,122 @@ public class HomeService {
private final UserDailyMissionRepository userDailyMissionRepository;
private final NotificationRepository notificationRepository;

// GardenService에서 가져오거나, 공통 유틸리티로 분리하면 더 좋습니다.
private LocalDateTime getStartOfCurrentWateringDay() {
LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Seoul"));
LocalDateTime todayNoon = now.toLocalDate().atTime(12, 0);
return now.isBefore(todayNoon) ? todayNoon.minusDays(1) : todayNoon;
}

private LocalDateTime getStartOfCurrentSunlightDay() {
LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Seoul"));
LocalDateTime todaySixAM = now.toLocalDate().atTime(6, 0);
return now.isBefore(todaySixAM) ? todaySixAM.minusDays(1) : todaySixAM;
}

public HomeResponseDto getHomeScreenData(Long userId) {
// 1. 핵심 엔티티 조회
User user =
userRepository
.findById(userId)
.orElseThrow(() -> new CustomApiException(ErrorCode.NOT_FOUND));

Garden garden =
gardenRepository
.findByUserWithDetails(user)
.orElseThrow(() -> new CustomApiException(ErrorCode.NOT_FOUND));

Avatar avatar = garden.getAvatar();

// 2. 부가 정보 조회
// 1. UserInfo 구성
int unreadNotificationCount = notificationRepository.countByReceiverAndIsReadFalse(user);

LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
LocalDateTime endOfDay = LocalDate.now().atTime(23, 59, 59);

List<UserDailyMission> todayMissions =
userDailyMissionRepository.findTodayMissionsWithMasterByUser(user, startOfDay, endOfDay);

// TODO: Repository에 주간 활동 기록 조회 로직 구현 필요
List<Boolean> weeklyStatus = createWeeklyMissionStatus(userId);

HomeResponseDto.ActivityInfo activityInfo =
HomeResponseDto.ActivityInfo.builder().weeklyMissionStatus(weeklyStatus).build();

// 3. DTO 조립
HomeResponseDto.UserInfo userInfo =
HomeResponseDto.UserInfo.builder()
.id(user.getId())
.username(user.getNickname())
.level(user.getLevel())
.currentExp(user.getExperience())
.requiredExpForNextLevel(calculateRequiredExpForLevel(user.getLevel() + 1))
.unreadNotificationCount(unreadNotificationCount)
.build();

HomeResponseDto.AvatarInfo avatarInfo =
HomeResponseDto.AvatarInfo.builder()
.characterImageUrl(avatar.getAvatarMaster().getDefaultImageUrl())
.build();

HomeResponseDto.GardenInfo gardenInfo =
HomeResponseDto.GardenInfo.builder()
.waterCount(garden.getWaterCount())
.maxWaterCount(100)
.sunlightCount(garden.getSunlightCount())
.maxSunlightCount(100)
.backgroundImageUrl(garden.getGardenBackground().getImageUrl())
.avatar(avatarInfo)
.build();

List<HomeResponseDto.MissionInfo> missionInfos =
todayMissions.stream()
// 2. GardenSummaries 구성 (모든 정원 순회)
List<HomeResponseDto.GardenSummaryInfo> gardenSummaries =
user.getGardens().stream()
.map(
mission -> {
DailyMissionMaster master = mission.getDailyMissionMaster();
return HomeResponseDto.MissionInfo.builder()
.missionTitle(master.getTitle())
.missionType(master.getMissionType().toString())
.isCompleted(mission.isCompleted())
garden -> {
// 각 정원의 물주기/햇빛주기 가능 여부 계산 (주인 본인이 주는 경우)
boolean isOwnerWateringAble =
garden.getLastWateredByOwnerAt() == null
|| garden
.getLastWateredByOwnerAt()
.plusHours(8)
.isBefore(LocalDateTime.now());

boolean isOwnerSunlightAble =
garden.getLastSunlightReceivedAt() == null
|| garden
.getLastSunlightReceivedAt()
.isBefore(getStartOfCurrentSunlightDay()); // 매일 06시 초기화

return HomeResponseDto.GardenSummaryInfo.builder()
.gardenId(garden.getId())
.gardenSlotNumber(garden.getSlotNumber())
.avatar(
HomeResponseDto.AvatarInfo.builder()
.avatarId(garden.getAvatar().getId())
.avatarName(garden.getAvatar().getNickname())
.avatarImageUrl(
garden.getAvatar().getAvatarMaster().getDefaultImageUrl())
.build())
.isOwnerWateringAble(isOwnerWateringAble)
.isOwnerSunlightAble(isOwnerSunlightAble)
.build();
})
.collect(Collectors.toList());

// 4. 최종 응답 반환
// 3. TodayMissions 구성
LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
LocalDateTime endOfDay = LocalDate.now().atTime(23, 59, 59);
List<HomeResponseDto.MissionInfo> todayMissions =
userDailyMissionRepository
.findTodayMissionsWithMasterByUser(user, startOfDay, endOfDay)
.stream()
.map(
userDailyMission ->
HomeResponseDto.MissionInfo.builder()
.missionId(userDailyMission.getDailyMissionMaster().getId()) // 마스터 미션 ID
.missionTitle(userDailyMission.getDailyMissionMaster().getTitle())
.missionType(
userDailyMission.getDailyMissionMaster().getMissionType().toString())
.isCompleted(userDailyMission.isCompleted())
.build())
.collect(Collectors.toList());

// // 4. ActivityInfo 구성
// List<Boolean> weeklyStatus = createWeeklyMissionStatus(userId);
// HomeResponseDto.ActivityInfo activityInfo =
// HomeResponseDto.ActivityInfo.builder().weeklyMissionStatus(weeklyStatus).build();

// 5. 최종 DTO 빌드
return HomeResponseDto.builder()
.userInfo(userInfo)
.gardenInfo(gardenInfo)
.todayMissions(missionInfos)
.activityInfo(activityInfo)
.gardenSummaries(gardenSummaries) // 변경된 부분
.todayMissions(todayMissions)
.build();
}

private long calculateRequiredExpForLevel(int level) {
return level * 100L;
return level * 100L; // 예시 로직
}

private List<Boolean> createWeeklyMissionStatus(Long userId) {
// 1. 기준 날짜 설정 (오늘 포함 7일 전)
// 기존 로직과 동일
LocalDateTime sevenDaysAgo = LocalDate.now().minusDays(6).atStartOfDay();

// 2. Repository를 통해 7일간의 모든 미션 기록을 한번에 조회
List<UserDailyMission> recentMissions =
userDailyMissionRepository.findByUserIdAndCreatedAtAfter(userId, sevenDaysAgo);

// 3. '완료된' 미션들의 날짜만 Set으로 추출 (중복 제거)
Set<LocalDate> completedDates =
recentMissions.stream()
.filter(UserDailyMission::isCompleted)
.map(mission -> mission.getCreatedAt().toLocalDate())
.collect(Collectors.toSet());

// 4. 최근 7일(오늘-6일전 ~ 오늘)을 순회하며 완료 여부 리스트 생성
return Stream.iterate(LocalDate.now().minusDays(6), date -> date.plusDays(1))
.limit(7)
.map(completedDates::contains) // 해당 날짜가 completedDates Set에 포함되어 있는지 확인
.map(completedDates::contains)
.collect(Collectors.toList());
}
}
Loading