Skip to content

Commit

Permalink
merge(develop) : 확정된 약속들 조회 API 구현
Browse files Browse the repository at this point in the history
[✨feat] 확정된 약속들 조회 API 구현
  • Loading branch information
jsoonworld authored Feb 27, 2025
2 parents aa50fca + c3cf5a1 commit 3ee91cc
Show file tree
Hide file tree
Showing 41 changed files with 1,212 additions and 857 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import org.noostak.appointment.dto.calendar.CalendarResponse;
import org.noostak.appointment.dto.calendar.MonthAppointment;
import org.noostak.appointment.dto.calendar.MonthAppointments;
import org.noostak.appointmentoption.common.exception.AppointmentOptionErrorCode;
import org.noostak.appointmentoption.common.exception.AppointmentOptionException;
import org.noostak.appointmentoption.domain.AppointmentOption;
import org.noostak.appointmentoption.domain.AppointmentOptionRepository;
import org.springframework.stereotype.Service;
Expand All @@ -30,7 +32,7 @@ public class CalendarServiceImpl implements CalendarService {
public CalendarResponse getCalendarViewByGroupId(Long groupId, int year, int month) {

List<Appointment> appointmentList =
appointmentRepository.getAllByGroupIdConfirmed(AppointmentStatus.CONFIRMED, groupId);
appointmentRepository.findAllByGroupIdConfirmed(AppointmentStatus.CONFIRMED, groupId);

// 이번 달의 캘린더 정보 목록 불러오기
ArrayList<MonthAppointments> currentMonthAppointments =
Expand Down Expand Up @@ -126,13 +128,14 @@ private ArrayList<MonthAppointments> getMonthAppointments(List<Appointment> appo
}

private AppointmentOption getAppointmentConfirmed(Appointment appointment, int year, int month) {
return appointmentOptionRepository.
getByAppointmentConfirmedYearAndMonth(
appointment, year, month);
return appointmentOptionRepository
.findByAppointmentConfirmedYearAndMonth(appointment.getId(), year, month)
.orElseThrow(() -> new AppointmentOptionException(AppointmentOptionErrorCode.APPOINTMENT_OPTION_NOT_FOUND));
}

private AppointmentOption getAppointmentConfirmed(LocalDate firstDate, LocalDate previousDate, Appointment appointment) {
return appointmentOptionRepository
.getAllByAppointmentConfirmedBetweenDate(appointment, previousDate, firstDate);
.findByAppointmentConfirmedBetweenDate(appointment.getId(), previousDate, firstDate)
.orElseThrow(() -> new AppointmentOptionException(AppointmentOptionErrorCode.APPOINTMENT_OPTION_NOT_FOUND));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum AppointmentErrorCode implements ErrorCode {

APPOINTMENT_CATEGORY_NOT_FOUND(HttpStatus.BAD_REQUEST, "유효하지 않은 카테고리입니다."),
APPOINTMENT_CATEGORY_NULL_OR_BLANK(HttpStatus.BAD_REQUEST, "약속 카테고리는 null이거나 공백일 수 없습니다."),
APPOINTMENT_CATEGORY_INVALID_FORMAT(HttpStatus.BAD_REQUEST, "약속 카테고리는 공백을 포함할 수 없습니다."),

APPOINTMENT_MEMBER_COUNT_NEGATIVE(HttpStatus.BAD_REQUEST, "약속 멤버 수는 음수가 될 수 없습니다."),
APPOINTMENT_MEMBER_COUNT_MAX(HttpStatus.BAD_REQUEST, "약속 멤버 수는 최대 50명을 초과할 수 없습니다."),
Expand All @@ -31,6 +32,9 @@ public enum AppointmentErrorCode implements ErrorCode {
HOST_SELECTION_TIME_NOT_FOUND(HttpStatus.BAD_REQUEST, "해당 약속의 호스트 선택 시간을 찾을 수 없습니다."),
MEMBER_AVAILABILITY_NOT_FOUND(HttpStatus.BAD_REQUEST, "해당 약속의 멤버 가용 시간을 찾을 수 없습니다."),
INVALID_DURATION(HttpStatus.BAD_REQUEST, "유효하지 않은 소요 시간입니다."),

APPOINTMENT_OPTION_NOT_FOUND(HttpStatus.BAD_REQUEST, "해당 약속의 옵션을 찾을 수 없습니다."),
CONFIRMED_OPTION_NOT_FOUND(HttpStatus.BAD_REQUEST, "확정된 약속 옵션을 찾을 수 없습니다."),
;

public static final String PREFIX = "[APPOINTMENT ERROR] ";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ public enum AppointmentSuccessCode implements SuccessCode {
APPOINTMENT_CREATED(HttpStatus.CREATED, "약속이 성공적으로 생성되었습니다."),

APPOINTMENT_CONFIRMED(HttpStatus.OK, "약속이 성공적으로 확정되었습니다."),

CONFIRMED_APPOINTMENT_RETRIEVED(HttpStatus.OK, "확정된 약속이 성공적으로 조회되었습니다."),
;

private final HttpStatus status;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,8 @@
package org.noostak.appointment.domain;

import org.noostak.appointment.common.exception.AppointmentErrorCode;
import org.noostak.appointment.common.exception.AppointmentException;
import org.noostak.appointment.domain.vo.AppointmentStatus;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface AppointmentRepository extends JpaRepository<Appointment, Long>, AppointmentRepositoryCustom {

@Query(nativeQuery = true, value =
"SELECT * " +
"FROM appointment " +
"WHERE appointment_status = :status "+
"AND groups_id = :groupId")
List<Appointment> findAllByGroupIdConfirmed(String status, Long groupId);


default List<Appointment> getAllByGroupIdConfirmed(AppointmentStatus status, Long groupId){
return findAllByGroupIdConfirmed(status.name(),groupId);
}

default Appointment getById(Long appointmentId){
return findById(appointmentId)
.orElseThrow(()-> new AppointmentException(AppointmentErrorCode.APPOINTMENT_NOT_FOUND));
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package org.noostak.appointment.domain;

import org.noostak.appointment.domain.vo.AppointmentStatus;

import java.util.List;

public interface AppointmentRepositoryCustom {
List<Appointment> findAllByGroupId(Long groupId);
List<Appointment> findAllByGroupIdConfirmed(AppointmentStatus status, Long groupId);
List<Appointment> findAllByGroupIdConfirmed(Long groupId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.noostak.appointment.domain.vo.AppointmentStatus;

import java.util.List;


import static org.noostak.appointment.domain.QAppointment.appointment;

@RequiredArgsConstructor
Expand All @@ -21,4 +21,29 @@ public List<Appointment> findAllByGroupId(Long groupId) {
.orderBy(appointment.createdAt.desc())
.fetch();
}

@Override
public List<Appointment> findAllByGroupIdConfirmed(AppointmentStatus status, Long groupId) {
return queryFactory
.selectFrom(appointment)
.where(
appointment.appointmentStatus.eq(status),
appointment.group.id.eq(groupId)
)
.orderBy(appointment.createdAt.desc())
.fetch();
}

@Override
public List<Appointment> findAllByGroupIdConfirmed(Long groupId) {
return queryFactory
.selectFrom(appointment)
.where(
appointment.appointmentStatus.eq(AppointmentStatus.CONFIRMED), // 확정된 약속만 조회
appointment.group.id.eq(groupId)
)
.orderBy(appointment.createdAt.desc())
.fetch();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.noostak.appointment.common.exception.AppointmentException;

import java.util.Arrays;
import java.util.Optional;

@Getter
@AllArgsConstructor
Expand All @@ -19,17 +20,22 @@ public enum AppointmentCategory {

public static AppointmentCategory from(String category) {
validateCategory(category);

String trimmedCategory = category.trim();
return Arrays.stream(values())
.filter(c -> c.getMessage().equals(trimmedCategory))
.findFirst()
return findCategory(category)
.orElseThrow(() -> new AppointmentException(AppointmentErrorCode.APPOINTMENT_CATEGORY_NOT_FOUND));
}

private static void validateCategory(String category) {
if (category == null || category.isBlank()) {
throw new AppointmentException(AppointmentErrorCode.APPOINTMENT_CATEGORY_NULL_OR_BLANK);
}
if (!category.trim().equals(category) || category.contains(" ")) {
throw new AppointmentException(AppointmentErrorCode.APPOINTMENT_CATEGORY_INVALID_FORMAT);
}
}

private static Optional<AppointmentCategory> findCategory(String category) {
return Arrays.stream(values())
.filter(c -> c.message.equals(category))
.findFirst();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,4 @@ public ResponseEntity<SuccessResponse> confirmAppointment(
appointmentService.confirmAppointment(appointmentOptionId);
return ResponseEntity.ok(SuccessResponse.of(APPOINTMENT_CONFIRMED));
}

@GetMapping("/{appointmentOptionId}/confirmed")
public ResponseEntity<SuccessResponse<AppointmentConfirmedOptionResponse>> getConfirmedAppointment(
// @AuthenticationPrincipal Long memberId,
@PathVariable Long appointmentOptionId
) {
Long memberId = 1L;
AppointmentConfirmedOptionResponse response = appointmentService.getConfirmedAppointmentOption(memberId, appointmentOptionId);
return ResponseEntity.ok(SuccessResponse.of(APPOINTMENT_CONFIRMED, response));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.noostak.appointmentoption.application;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.noostak.appointment.domain.Appointment;
import org.noostak.appointmentmember.domain.AppointmentMember;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,69 +1,8 @@
package org.noostak.appointmentoption.domain;

import org.noostak.appointment.domain.Appointment;
import org.noostak.appointmentoption.domain.vo.AppointmentOptionStatus;
import org.noostak.appointmentoption.common.exception.AppointmentOptionErrorCode;
import org.noostak.appointmentoption.common.exception.AppointmentOptionException;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.time.LocalDate;

@Repository
public interface AppointmentOptionRepository extends JpaRepository<AppointmentOption, Long> {

@Query(nativeQuery = true,
value = "SELECT * " +
"FROM appointment_option " +
"WHERE appointment_id = :appointmentId " +
"AND appointment_option_status = :status " +
"AND YEAR(appointment_option_date) = :year " +
"AND MONTH(appointment_option_date) = :month " +
"LIMIT 1")
AppointmentOption findByAppointmentConfirmedYearAndMonth(
Long appointmentId,
String status,
int year,
int month);

default AppointmentOption getByAppointmentConfirmedYearAndMonth(Appointment appointment, int year, int month){
return findByAppointmentConfirmedYearAndMonth(
appointment.getId(),
AppointmentOptionStatus.CONFIRMED.name(),
year,
month
);
}

@Query(nativeQuery = true,
value = "SELECT * " +
"FROM appointment_option " +
"WHERE appointment_id = :appointmentId " +
"AND appointment_option_status = :status " +
"AND appointment_option_date BETWEEN :startDate AND :endDate " +
"LIMIT 1")
AppointmentOption findAllByAppointmentConfirmedBetweenDate(Long appointmentId,
String status,
LocalDate startDate,
LocalDate endDate);


default AppointmentOption getAllByAppointmentConfirmedBetweenDate(
Appointment appointment,
LocalDate startDate,
LocalDate endDate
) {
return findAllByAppointmentConfirmedBetweenDate(
appointment.getId(),
AppointmentOptionStatus.CONFIRMED.name(),
startDate,
endDate
);
}

default AppointmentOption getById(Long optionId){
return findById(optionId)
.orElseThrow(()-> new AppointmentOptionException(AppointmentOptionErrorCode.APPOINTMENT_OPTION_NOT_FOUND));
}
}
public interface AppointmentOptionRepository extends JpaRepository<AppointmentOption, Long>, AppointmentOptionRepositoryCustom {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.noostak.appointmentoption.domain;

import java.time.LocalDate;
import java.util.Optional;

public interface AppointmentOptionRepositoryCustom {
Optional<AppointmentOption> findByAppointmentConfirmedYearAndMonth(Long appointmentId, int year, int month);
Optional<AppointmentOption> findByAppointmentConfirmedBetweenDate(Long appointmentId, LocalDate startDate, LocalDate endDate);
Optional<AppointmentOption> findConfirmedOptionByAppointmentId(Long appointmentId);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package org.noostak.appointmentoption.domain;

import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.noostak.appointmentoption.domain.vo.AppointmentOptionStatus;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Optional;

import static org.noostak.appointmentoption.domain.QAppointmentOption.appointmentOption;

@RequiredArgsConstructor
public class AppointmentOptionRepositoryImpl implements AppointmentOptionRepositoryCustom {

private final JPAQueryFactory queryFactory;

@Override
public Optional<AppointmentOption> findByAppointmentConfirmedYearAndMonth(Long appointmentId, int year, int month) {
return Optional.ofNullable(
queryFactory
.selectFrom(appointmentOption)
.where(
appointmentOption.appointment.id.eq(appointmentId),
appointmentOption.status.eq(AppointmentOptionStatus.CONFIRMED),
appointmentOption.date.year().eq(year),
appointmentOption.date.month().eq(month)
)
.limit(1)
.fetchOne()
);
}

@Override
public Optional<AppointmentOption> findByAppointmentConfirmedBetweenDate(Long appointmentId, LocalDate startDate, LocalDate endDate) {
return Optional.ofNullable(
queryFactory
.selectFrom(appointmentOption)
.where(
appointmentOption.appointment.id.eq(appointmentId),
appointmentOption.status.eq(AppointmentOptionStatus.CONFIRMED),
appointmentOption.date.between(
startDate.atStartOfDay(),
endDate.atTime(23, 59, 59)
)
)
.limit(1)
.fetchOne()
);
}

@Override
public Optional<AppointmentOption> findConfirmedOptionByAppointmentId(Long appointmentId) {
return Optional.ofNullable(
queryFactory
.selectFrom(appointmentOption)
.where(
appointmentOption.appointment.id.eq(appointmentId),
appointmentOption.status.eq(AppointmentOptionStatus.CONFIRMED) // 확정된 옵션만 조회
)
.fetchOne()
);
}
}
11 changes: 11 additions & 0 deletions src/main/java/org/noostak/group/api/GroupController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.noostak.global.success.SuccessResponse;
import org.noostak.group.application.GroupService;
import org.noostak.group.dto.request.GroupCreateRequest;
import org.noostak.group.dto.response.confirmed.GroupConfirmedAppointmentsResponse;
import org.noostak.group.dto.response.create.GroupCreateResponse;
import org.noostak.group.dto.response.info.GroupInfoResponse;
import org.noostak.group.dto.response.ongoing.GroupOngoingAppointmentsResponse;
Expand Down Expand Up @@ -62,4 +63,14 @@ public ResponseEntity<SuccessResponse<GroupOngoingAppointmentsResponse>> getGrou
GroupOngoingAppointmentsResponse groupOngoingAppointments = groupService.getGroupOngoingAppointments(memberId, groupId);
return ResponseEntity.ok(SuccessResponse.of(GROUP_ONGOING_APPOINTMENTS_LOADED, groupOngoingAppointments));
}

@GetMapping("/{groupId}/appointments/confirmed")
public ResponseEntity<SuccessResponse<GroupConfirmedAppointmentsResponse>> getGroupConfirmedAppointments(
// @AuthenticationPrincipal Long memberId,
@PathVariable Long groupId
) {
Long memberId = 1L;
GroupConfirmedAppointmentsResponse groupConfirmedAppointments = groupService.getGroupConfirmedAppointments(memberId, groupId);
return ResponseEntity.ok(SuccessResponse.of(GROUP_CONFIRMED_APPOINTMENTS_LOADED, groupConfirmedAppointments));
}
}
Loading

0 comments on commit 3ee91cc

Please sign in to comment.