diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarCreateCommand.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarCreateCommand.java new file mode 100644 index 00000000..6b31c5ca --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarCreateCommand.java @@ -0,0 +1,6 @@ +package com.blackcompany.eeos.calendar.application.dto; + +import com.blackcompany.eeos.common.support.dto.AbstractRequestDto; + +public record CalendarCreateCommand(String title, String url, String type, Long startAt, Long endAt) + implements AbstractRequestDto {} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarQuery.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarQuery.java new file mode 100644 index 00000000..67a9a508 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarQuery.java @@ -0,0 +1,3 @@ +package com.blackcompany.eeos.calendar.application.dto; + +public record CalendarQuery(Integer year, Integer month, Integer date, Integer duration) {} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarResponse.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarResponse.java new file mode 100644 index 00000000..9a569179 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarResponse.java @@ -0,0 +1,19 @@ +package com.blackcompany.eeos.calendar.application.dto; + +import com.blackcompany.eeos.calendar.application.model.CalendarModel; +import com.blackcompany.eeos.common.support.dto.AbstractResponseDto; +import com.blackcompany.eeos.common.utils.DateConverter; + +public record CalendarResponse( + Long id, String title, String url, String type, Long startAt, Long endAt, String writer) + implements AbstractResponseDto { + + public static CalendarResponse toResponse(CalendarModel model, String writerName) { + Long startAt = DateConverter.toMillis(model.getStartAt()); + Long endAt = DateConverter.toMillis(model.getEndAt()); + String type = model.getType().name(); + + return new CalendarResponse( + model.getId(), model.getTitle(), model.getUrl(), type, startAt, endAt, writerName); + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarResponses.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarResponses.java new file mode 100644 index 00000000..39056348 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarResponses.java @@ -0,0 +1,6 @@ +package com.blackcompany.eeos.calendar.application.dto; + +import com.blackcompany.eeos.common.support.dto.AbstractResponseDto; +import java.util.List; + +public record CalendarResponses(List calendars) implements AbstractResponseDto {} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarUpdateCommand.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarUpdateCommand.java new file mode 100644 index 00000000..11c1565b --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/dto/CalendarUpdateCommand.java @@ -0,0 +1,6 @@ +package com.blackcompany.eeos.calendar.application.dto; + +import com.blackcompany.eeos.common.support.dto.AbstractRequestDto; + +public record CalendarUpdateCommand(String title, String url, String type, Long startAt, Long endAt) + implements AbstractRequestDto {} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/DeniedCalendarTypeException.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/DeniedCalendarTypeException.java new file mode 100644 index 00000000..ecc4851e --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/DeniedCalendarTypeException.java @@ -0,0 +1,21 @@ +package com.blackcompany.eeos.calendar.application.exception; + +import com.blackcompany.eeos.common.exception.BusinessException; +import com.blackcompany.eeos.member.application.model.Department; +import org.springframework.http.HttpStatus; + +public class DeniedCalendarTypeException extends BusinessException { + + private static final String FAIL_CODE = "10001"; + private final Department department; + + public DeniedCalendarTypeException(Department department) { + super(FAIL_CODE, HttpStatus.BAD_REQUEST); + this.department = department; + } + + @Override + public String getMessage() { + return String.format("%s 부서는 해당 타입의 칼렌더를 생성할 수 없습니다.", department.name()); + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/DeniedCalendarUpdateException.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/DeniedCalendarUpdateException.java new file mode 100644 index 00000000..50d11f06 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/DeniedCalendarUpdateException.java @@ -0,0 +1,18 @@ +package com.blackcompany.eeos.calendar.application.exception; + +import com.blackcompany.eeos.common.exception.BusinessException; +import org.springframework.http.HttpStatus; + +public class DeniedCalendarUpdateException extends BusinessException { + + private static final String FAIL_CODE = "10002"; + + public DeniedCalendarUpdateException() { + super(FAIL_CODE, HttpStatus.UNAUTHORIZED); + } + + @Override + public String getMessage() { + return "캘린더를 수정할 권한이 없습니다."; + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/InvalidDateException.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/InvalidDateException.java new file mode 100644 index 00000000..f95e2246 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/InvalidDateException.java @@ -0,0 +1,18 @@ +package com.blackcompany.eeos.calendar.application.exception; + +import com.blackcompany.eeos.common.exception.BusinessException; +import org.springframework.http.HttpStatus; + +public class InvalidDateException extends BusinessException { + + private static final String FAIL_CODE = "10003"; + + public InvalidDateException() { + super(FAIL_CODE, HttpStatus.BAD_REQUEST); + } + + @Override + public String getMessage() { + return "행사 시작 날짜는 행사 종료 날짜보다 이후일 수 없습니다."; + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/NotFoundCalendarTypeException.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/NotFoundCalendarTypeException.java new file mode 100644 index 00000000..f4760e78 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/exception/NotFoundCalendarTypeException.java @@ -0,0 +1,20 @@ +package com.blackcompany.eeos.calendar.application.exception; + +import com.blackcompany.eeos.common.exception.BusinessException; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class NotFoundCalendarTypeException extends BusinessException { + + private static final String FAIL_CODE = "10000"; + + public NotFoundCalendarTypeException() { + super(FAIL_CODE, HttpStatus.BAD_REQUEST); + } + + @Override + public String getMessage() { + return "캘린더 타입이 존재하지 않습니다."; + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/model/CalendarModel.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/model/CalendarModel.java new file mode 100644 index 00000000..1f7dfb0d --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/model/CalendarModel.java @@ -0,0 +1,88 @@ +package com.blackcompany.eeos.calendar.application.model; + +import java.time.LocalDateTime; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class CalendarModel { + + private Long id; + private String title; + private Long writer; + private LocalDateTime startAt; + private LocalDateTime endAt; + private CalendarType type; + private String url; + + public boolean isWriter(Long updater) { + return this.writer.equals(updater); + } + + public CalendarModel updateTitle(String title) { + this.title = title; + return this; + } + + public CalendarModel updateStartAt(LocalDateTime startAt) { + this.startAt = startAt; + return this; + } + + public CalendarModel updateEndAt(LocalDateTime endAt) { + this.endAt = endAt; + return this; + } + + public CalendarModel updateType(String type) { + this.type = CalendarType.findByName(type); + return this; + } + + public CalendarModel updateUrl(String url) { + this.url = url; + return this; + } + + public static CalendarModel create( + String title, + LocalDateTime startAt, + LocalDateTime endAt, + String type, + String url, + Long writer) { + return CalendarModel.builder() + .title(title) + .writer(writer) + .startAt(startAt) + .endAt(endAt) + .url(url) + .type(CalendarType.findByName(type)) + .build(); + } + + public static CalendarModel load( + Long id, + String title, + LocalDateTime startAt, + LocalDateTime endAt, + CalendarType type, + String url, + Long writer) { + return CalendarModel.builder() + .id(id) + .title(title) + .writer(writer) + .startAt(startAt) + .endAt(endAt) + .url(url) + .type(type) + .build(); + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/model/CalendarType.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/model/CalendarType.java new file mode 100644 index 00000000..c3e086d5 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/model/CalendarType.java @@ -0,0 +1,19 @@ +package com.blackcompany.eeos.calendar.application.model; + +import com.blackcompany.eeos.calendar.application.exception.NotFoundCalendarTypeException; +import java.util.Arrays; + +public enum CalendarType { + EVENT, + PRESENTATION, + ETC; + + CalendarType() {} + + public static CalendarType findByName(String name) { + return Arrays.stream(CalendarType.values()) + .filter(calendarType -> calendarType.name().equalsIgnoreCase(name)) + .findFirst() + .orElseThrow(NotFoundCalendarTypeException::new); + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/repository/CalendarRepository.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/repository/CalendarRepository.java new file mode 100644 index 00000000..fd3dc61f --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/repository/CalendarRepository.java @@ -0,0 +1,18 @@ +package com.blackcompany.eeos.calendar.application.repository; + +import com.blackcompany.eeos.calendar.application.model.CalendarModel; +import java.time.LocalDateTime; +import java.util.List; + +public interface CalendarRepository { + + Long save(CalendarModel calendar); + + CalendarModel findById(Long id); + + List findByBetweenDate(LocalDateTime startAt, LocalDateTime endAt); + + List findNotStarted(LocalDateTime from, LocalDateTime to); + + void delete(Long id); +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/service/CalendarCommandService.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/service/CalendarCommandService.java new file mode 100644 index 00000000..02b04d38 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/service/CalendarCommandService.java @@ -0,0 +1,94 @@ +package com.blackcompany.eeos.calendar.application.service; + +import com.blackcompany.eeos.calendar.application.dto.CalendarCreateCommand; +import com.blackcompany.eeos.calendar.application.dto.CalendarUpdateCommand; +import com.blackcompany.eeos.calendar.application.model.CalendarModel; +import com.blackcompany.eeos.calendar.application.repository.CalendarRepository; +import com.blackcompany.eeos.calendar.application.usecase.CreateCalendarUsecase; +import com.blackcompany.eeos.calendar.application.usecase.DeleteCalendarUsecase; +import com.blackcompany.eeos.calendar.application.usecase.UpdateCalendarUsecase; +import com.blackcompany.eeos.calendar.application.validator.CalendarValidator; +import com.blackcompany.eeos.common.utils.DateConverter; +import com.blackcompany.eeos.common.utils.RequestScope; +import com.blackcompany.eeos.member.application.model.MemberModel; +import com.blackcompany.eeos.member.application.repository.MemberRepository; +import java.time.LocalDateTime; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Slf4j +public class CalendarCommandService + implements CreateCalendarUsecase, UpdateCalendarUsecase, DeleteCalendarUsecase { + + private final MemberRepository memberRepository; + private final CalendarRepository repository; + private final CalendarValidator validator; + + @Override + @Transactional + public Long create(CalendarCreateCommand command) { + Long memberId = RequestScope.getMemberId(); + CalendarModel calendar = newCalendar(command, memberId); + MemberModel member = memberRepository.findById(memberId); + + validator.typeValidate(calendar, member.getDepartment()); + validator.durationValidate(calendar); + + return repository.save(calendar); + } + + @Override + @Transactional + public Long update(Long calendarId, CalendarUpdateCommand command) { + Long memberId = RequestScope.getMemberId(); + + CalendarModel model = repository.findById(calendarId); + + updateCalendar(memberId, command, model); + + repository.save(model); + + return model.getId(); + } + + @Override + @Transactional + public void delete(Long calendarId) { + Long memberId = RequestScope.getMemberId(); + + CalendarModel model = repository.findById(calendarId); + + validator.updateValidate(model, memberId); + + repository.delete(calendarId); + } + + private void updateCalendar(Long memberId, CalendarUpdateCommand command, CalendarModel model) { + MemberModel member = memberRepository.findById(memberId); + + validator.updateValidate(model, memberId); + validator.typeValidate(model, member.getDepartment()); + validator.durationValidate(model); + + LocalDateTime startAt = DateConverter.toLocalDateTime(command.startAt()); + LocalDateTime endAt = DateConverter.toLocalDateTime(command.endAt()); + + model + .updateEndAt(endAt) + .updateStartAt(startAt) + .updateTitle(command.title()) + .updateUrl(command.url()) + .updateType(command.type()); + } + + private CalendarModel newCalendar(CalendarCreateCommand command, Long memberId) { + LocalDateTime startAt = DateConverter.toLocalDateTime(command.startAt()); + LocalDateTime endAt = DateConverter.toLocalDateTime(command.endAt()); + return CalendarModel.create( + command.title(), startAt, endAt, command.type(), command.url(), memberId); + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/service/CalendarQueryService.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/service/CalendarQueryService.java new file mode 100644 index 00000000..b9bd1031 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/service/CalendarQueryService.java @@ -0,0 +1,71 @@ +package com.blackcompany.eeos.calendar.application.service; + +import com.blackcompany.eeos.calendar.application.dto.CalendarQuery; +import com.blackcompany.eeos.calendar.application.dto.CalendarResponse; +import com.blackcompany.eeos.calendar.application.model.CalendarModel; +import com.blackcompany.eeos.calendar.application.repository.CalendarRepository; +import com.blackcompany.eeos.calendar.application.usecase.GetCalendarUsecase; +import com.blackcompany.eeos.common.utils.DateUtil; +import com.blackcompany.eeos.member.application.exception.NotFoundMemberException; +import com.blackcompany.eeos.member.application.repository.MemberRepository; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CalendarQueryService implements GetCalendarUsecase { + + private final CalendarRepository repository; + private final MemberRepository memberRepository; + + @Override + public List getCalendar(CalendarQuery query) { + Integer year = query.year(); + Integer month = query.month(); + Integer date = query.date(); + Integer duration = query.duration(); + + boolean dateIsNull = Objects.isNull(date); + + LocalDate startDate = LocalDate.of(year, month, dateIsNull ? 1 : date); + + LocalDateTime start = LocalDateTime.of(startDate, LocalTime.MIDNIGHT); + LocalDateTime end = + dateIsNull + ? LocalDateTime.of( + LocalDate.of(year, month, DateUtil.getLastDay(year, month)), LocalTime.MIDNIGHT) + : startDate.plusDays(duration).atTime(LocalTime.MIDNIGHT); + + return getDefault(start, end); + } + + @Override + public List getCalendarForDDay(int DDay) { + + LocalDateTime now = LocalDate.now().atTime(LocalTime.MIDNIGHT); + LocalDateTime end = now.plusDays(DDay); + + return repository.findNotStarted(now, end).stream().map(this::createResponse).toList(); + } + + private List getDefault(LocalDateTime start, LocalDateTime end) { + return repository.findByBetweenDate(start, end).stream().map(this::createResponse).toList(); + } + + private CalendarResponse createResponse(CalendarModel model) { + return CalendarResponse.toResponse(model, getWriterName(model)); + } + + private String getWriterName(CalendarModel model) { + try { + return memberRepository.findNameById(model.getWriter()); + } catch (NotFoundMemberException e) { + throw new IllegalStateException("달력 작성자 이름 매핑 중 에러가 발생했습니다."); + } + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/CreateCalendarUsecase.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/CreateCalendarUsecase.java new file mode 100644 index 00000000..64329ddf --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/CreateCalendarUsecase.java @@ -0,0 +1,8 @@ +package com.blackcompany.eeos.calendar.application.usecase; + +import com.blackcompany.eeos.calendar.application.dto.CalendarCreateCommand; + +public interface CreateCalendarUsecase { + + Long create(CalendarCreateCommand command); +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/DeleteCalendarUsecase.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/DeleteCalendarUsecase.java new file mode 100644 index 00000000..89cafa63 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/DeleteCalendarUsecase.java @@ -0,0 +1,6 @@ +package com.blackcompany.eeos.calendar.application.usecase; + +public interface DeleteCalendarUsecase { + + void delete(Long calendarId); +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/GetCalendarUsecase.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/GetCalendarUsecase.java new file mode 100644 index 00000000..c6c104d4 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/GetCalendarUsecase.java @@ -0,0 +1,12 @@ +package com.blackcompany.eeos.calendar.application.usecase; + +import com.blackcompany.eeos.calendar.application.dto.CalendarQuery; +import com.blackcompany.eeos.calendar.application.dto.CalendarResponse; +import java.util.List; + +public interface GetCalendarUsecase { + + List getCalendar(CalendarQuery query); + + List getCalendarForDDay(int DDay); +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/UpdateCalendarUsecase.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/UpdateCalendarUsecase.java new file mode 100644 index 00000000..40d70dda --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/usecase/UpdateCalendarUsecase.java @@ -0,0 +1,8 @@ +package com.blackcompany.eeos.calendar.application.usecase; + +import com.blackcompany.eeos.calendar.application.dto.CalendarUpdateCommand; + +public interface UpdateCalendarUsecase { + + Long update(Long calendarId, CalendarUpdateCommand command); +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/application/validator/CalendarValidator.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/validator/CalendarValidator.java new file mode 100644 index 00000000..914df201 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/application/validator/CalendarValidator.java @@ -0,0 +1,61 @@ +package com.blackcompany.eeos.calendar.application.validator; + +import com.blackcompany.eeos.calendar.application.exception.DeniedCalendarTypeException; +import com.blackcompany.eeos.calendar.application.exception.DeniedCalendarUpdateException; +import com.blackcompany.eeos.calendar.application.exception.InvalidDateException; +import com.blackcompany.eeos.calendar.application.model.CalendarModel; +import com.blackcompany.eeos.calendar.application.model.CalendarType; +import com.blackcompany.eeos.member.application.model.Department; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import org.springframework.stereotype.Component; + +@Component +public class CalendarValidator { + + private final Map> AVAILABLE = new HashMap<>(); + + public CalendarValidator() { + final Set EVENT_AVAILABLE = Set.of(Department.EVENT, Department.PRESIDENT); + final Set PRESENTATION_AVAILABLE = Set.of(Department.PRESIDENT); + final Set ETC_AVAILABLE = + Set.of( + Department.PRESIDENT, + Department.EVENT, + Department.MANAGEMENT, + Department.MARKETING, + Department.NONE); + + AVAILABLE.put(CalendarType.ETC, ETC_AVAILABLE); + AVAILABLE.put(CalendarType.PRESENTATION, PRESENTATION_AVAILABLE); + AVAILABLE.put(CalendarType.EVENT, EVENT_AVAILABLE); + } + + public void updateValidate(CalendarModel calendar, Long memberId) { + if (!calendar.isWriter(memberId)) throw new DeniedCalendarUpdateException(); + } + + public void durationValidate(CalendarModel calendar) { + LocalDateTime startAt = calendar.getStartAt(); + LocalDateTime endAt = calendar.getEndAt(); + + if (startAt.isAfter(endAt)) throw new InvalidDateException(); + } + + public void typeValidate(CalendarModel calendar, Department department) { + CalendarType type = calendar.getType(); + Set departments = AVAILABLE.getOrDefault(type, Set.of()); + + if (!departments.contains(department)) { + throw new DeniedCalendarTypeException(department); + } + } + + public void urlValidator(CalendarModel calendar) { + String url = calendar.getUrl(); + + // TODO: 정규표현식으로 url 검증 + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarEntity.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarEntity.java new file mode 100644 index 00000000..71096bb4 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarEntity.java @@ -0,0 +1,78 @@ +package com.blackcompany.eeos.calendar.persistence; + +import com.blackcompany.eeos.calendar.application.model.CalendarModel; +import com.blackcompany.eeos.calendar.application.model.CalendarType; +import com.blackcompany.eeos.common.persistence.BaseEntity; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Index; +import jakarta.persistence.Table; +import java.time.LocalDateTime; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import lombok.experimental.SuperBuilder; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor(access = AccessLevel.PRIVATE) +@ToString +@SuperBuilder(toBuilder = true) +@Entity +@Table( + name = CalendarEntity.ENTITY_PREFIX, + indexes = { + @Index(name = "idx_calendar_start_at", columnList = "calendar_start_at"), + @Index(name = "idx_calendar_end_at", columnList = "calendar_end_at") + }) +public class CalendarEntity extends BaseEntity { + + public static final String ENTITY_PREFIX = "calendar"; + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = ENTITY_PREFIX + "_id", nullable = false) + private Long id; + + @Column(name = ENTITY_PREFIX + "_writer", nullable = false) + private Long writer; + + @Enumerated(EnumType.STRING) + @Column(name = ENTITY_PREFIX + "_type", nullable = false) + private CalendarType type; + + @Column(name = ENTITY_PREFIX + "_title", nullable = false) + private String title; + + @Column(name = ENTITY_PREFIX + "_url", nullable = false) + private String url; + + @Column(name = ENTITY_PREFIX + "_start_at", nullable = false) + private LocalDateTime startAt; + + @Column(name = ENTITY_PREFIX + "_end_at", nullable = false) + private LocalDateTime endAt; + + public CalendarModel toModel() { + return CalendarModel.load(id, title, startAt, endAt, type, url, writer); + } + + public static CalendarEntity toEntity(CalendarModel model) { + return CalendarEntity.builder() + .id(model.getId()) + .title(model.getTitle()) + .url(model.getUrl()) + .endAt(model.getEndAt()) + .startAt(model.getStartAt()) + .writer(model.getWriter()) + .type(model.getType()) + .build(); + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarJpaRepository.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarJpaRepository.java new file mode 100644 index 00000000..d13ddcc9 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarJpaRepository.java @@ -0,0 +1,34 @@ +package com.blackcompany.eeos.calendar.persistence; + +import java.time.LocalDateTime; +import java.util.List; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +public interface CalendarJpaRepository extends JpaRepository { + + // @Query("SELECT c FROM CalendarEntity c " + // + "WHERE (c.startAt <= :startAt and c.endAt <= :endAt)" // (행사 시작) ~ 조회 시작 날짜 ~ (행사 종료) ~ 조회 + // 마지막 날짜인 경우 + // + "OR (c.startAt >= :startAt and c.endAt <= :endAt)" // 조회 시작 날짜 ~ (행사 시작 ~ 행사 종료) ~ 조회 마지막 + // 날짜인 경우 + // + "OR (c.startAt >= :startAt and c.endAt >= :endAt)" // 조회 시작 날짜 ~ (행사 시작) ~ 조회 마지막 날짜 ~ (행사 + // 종료)인 경우 + // + "OR (c.startAt <= :startAt and c.endAt >= :endAt)" // (행사 시작) ~ 조회 시작 날짜 ~ 조회 마지막 날짜 ~ (행사 + // 종료)인 경우 + // + "ORDER BY c.createdDate ASC") + @Query( + "SELECT c FROM CalendarEntity c " + + "WHERE (c.startAt <= :endAt and c.endAt >= :startAt)" + + "ORDER BY c.startAt ASC") + List findBetween( + @Param("startAt") LocalDateTime startAt, @Param("endAt") LocalDateTime endAt); + + @Query( + "SELECT c FROM CalendarEntity c " + + "WHERE c.startAt BETWEEN :from AND :to " + + "ORDER BY c.startAt ASC") + List findNotStarted( + @Param("from") LocalDateTime from, @Param("to") LocalDateTime to); +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarRepositoryImpl.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarRepositoryImpl.java new file mode 100644 index 00000000..d2705083 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/persistence/CalendarRepositoryImpl.java @@ -0,0 +1,46 @@ +package com.blackcompany.eeos.calendar.persistence; + +import com.blackcompany.eeos.calendar.application.model.CalendarModel; +import com.blackcompany.eeos.calendar.application.repository.CalendarRepository; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class CalendarRepositoryImpl implements CalendarRepository { + + private final CalendarJpaRepository jpaRepository; + + @Override + public Long save(CalendarModel calendar) { + CalendarEntity entity = CalendarEntity.toEntity(calendar); + return jpaRepository.save(entity).getId(); + } + + @Override + public CalendarModel findById(Long id) { + Optional entity = jpaRepository.findById(id); + + return entity.map(CalendarEntity::toModel).orElseThrow(); + } + + @Override + public List findNotStarted(LocalDateTime from, LocalDateTime to) { + List entities = jpaRepository.findNotStarted(from, to); + return entities.stream().map(CalendarEntity::toModel).toList(); + } + + @Override + public List findByBetweenDate(LocalDateTime startAt, LocalDateTime endAt) { + List entities = jpaRepository.findBetween(startAt, endAt); + return entities.stream().map(CalendarEntity::toModel).toList(); + } + + @Override + public void delete(Long id) { + jpaRepository.deleteById(id); + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/calendar/presentation/controller/CalendarController.java b/eeos/src/main/java/com/blackcompany/eeos/calendar/presentation/controller/CalendarController.java new file mode 100644 index 00000000..f919ada5 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/calendar/presentation/controller/CalendarController.java @@ -0,0 +1,79 @@ +package com.blackcompany.eeos.calendar.presentation.controller; + +import com.blackcompany.eeos.calendar.application.dto.CalendarCreateCommand; +import com.blackcompany.eeos.calendar.application.dto.CalendarQuery; +import com.blackcompany.eeos.calendar.application.dto.CalendarResponse; +import com.blackcompany.eeos.calendar.application.dto.CalendarResponses; +import com.blackcompany.eeos.calendar.application.dto.CalendarUpdateCommand; +import com.blackcompany.eeos.calendar.application.usecase.CreateCalendarUsecase; +import com.blackcompany.eeos.calendar.application.usecase.DeleteCalendarUsecase; +import com.blackcompany.eeos.calendar.application.usecase.GetCalendarUsecase; +import com.blackcompany.eeos.calendar.application.usecase.UpdateCalendarUsecase; +import com.blackcompany.eeos.common.presentation.response.ApiResponse; +import com.blackcompany.eeos.common.presentation.response.ApiResponseBody.SuccessBody; +import com.blackcompany.eeos.common.presentation.response.ApiResponseGenerator; +import com.blackcompany.eeos.common.presentation.response.MessageCode; +import jakarta.validation.constraints.PositiveOrZero; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/api/calendars") +@RestController +@RequiredArgsConstructor +public class CalendarController { + + private final CreateCalendarUsecase createUsecase; + private final GetCalendarUsecase getUsecase; + private final UpdateCalendarUsecase updateUsecase; + private final DeleteCalendarUsecase deleteUsecase; + + @PostMapping + public ApiResponse> create(@RequestBody CalendarCreateCommand request) { + Long createdId = createUsecase.create(request); + return ApiResponseGenerator.success(createdId, HttpStatus.CREATED, MessageCode.CREATE); + } + + @GetMapping + public ApiResponse> getCalendar( + @RequestParam("year") Integer year, + @RequestParam("month") Integer month, + @RequestParam(value = "date", required = false) Integer date, + @RequestParam(value = "duration", required = false) Integer duration) { + List calendars = + getUsecase.getCalendar(new CalendarQuery(year, month, date, duration)); + CalendarResponses responses = new CalendarResponses(calendars); + return ApiResponseGenerator.success(responses, HttpStatus.OK, MessageCode.GET); + } + + @PutMapping("/{calendarId}") + public ApiResponse> updateCalendar( + @PathVariable("calendarId") Long calendarId, @RequestBody CalendarUpdateCommand command) { + Long id = updateUsecase.update(calendarId, command); + return ApiResponseGenerator.success(id, HttpStatus.OK, MessageCode.UPDATE); + } + + @DeleteMapping("/{calendarId}") + public ApiResponse> deleteCalendar( + @PathVariable("calendarId") Long calendarId) { + deleteUsecase.delete(calendarId); + return ApiResponseGenerator.success(HttpStatus.OK, MessageCode.DELETE); + } + + @GetMapping("/d-day") + public ApiResponse> getCalendarsForDDay( + @RequestParam("measure") @PositiveOrZero int measure) { + List calendars = getUsecase.getCalendarForDDay(measure); + CalendarResponses responses = new CalendarResponses(calendars); + return ApiResponseGenerator.success(responses, HttpStatus.OK, MessageCode.GET); + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/common/utils/DateConverter.java b/eeos/src/main/java/com/blackcompany/eeos/common/utils/DateConverter.java index c33586c9..370fb809 100644 --- a/eeos/src/main/java/com/blackcompany/eeos/common/utils/DateConverter.java +++ b/eeos/src/main/java/com/blackcompany/eeos/common/utils/DateConverter.java @@ -3,9 +3,11 @@ import java.sql.Timestamp; import java.time.Instant; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.ZoneId; import lombok.experimental.UtilityClass; +// DateConverter => 날짜 변환과 관련된 유틸 클래스 @UtilityClass public class DateConverter { private static final String KST = "Asia/Seoul"; @@ -42,4 +44,16 @@ public static LocalDate toLocalDate(Long epochMilli) { return Instant.ofEpochSecond(epochMilli / 1000).atZone(ZoneId.of(KST)).toLocalDate(); } + + public static LocalDateTime toLocalDateTime(Long epochMilli) { + if (epochMilli == null) { + return null; + } + + return Instant.ofEpochSecond(epochMilli / 1000).atZone(ZoneId.of(KST)).toLocalDateTime(); + } + + public static Long toMillis(LocalDateTime localDateTime) { + return localDateTime.atZone(ZoneId.of(KST)).toInstant().toEpochMilli(); + } } diff --git a/eeos/src/main/java/com/blackcompany/eeos/common/utils/DateUtil.java b/eeos/src/main/java/com/blackcompany/eeos/common/utils/DateUtil.java new file mode 100644 index 00000000..aaf2429e --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/common/utils/DateUtil.java @@ -0,0 +1,20 @@ +package com.blackcompany.eeos.common.utils; + +import java.time.YearMonth; +import lombok.experimental.UtilityClass; + +/** DateUtil => 날짜의 유틸 기능과 관련된 클래스 ex) 한 날짜의 마지막 날 구하기, 윤년인지 아닌지 판별하기 */ +@UtilityClass +public class DateUtil { + + /** + * 년, 월을 받아 해당 년도의 해당 월이 며칠까지 존재하는지 반환 + * + * @param year + * @param month + * @return + */ + public static int getLastDay(int year, int month) { + return YearMonth.of(year, month).lengthOfMonth(); + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/config/security/SecurityFilterChainConfig.java b/eeos/src/main/java/com/blackcompany/eeos/config/security/SecurityFilterChainConfig.java index 8f9110f1..9d292940 100644 --- a/eeos/src/main/java/com/blackcompany/eeos/config/security/SecurityFilterChainConfig.java +++ b/eeos/src/main/java/com/blackcompany/eeos/config/security/SecurityFilterChainConfig.java @@ -86,6 +86,7 @@ SecurityFilterChain authenticated(HttpSecurity httpSecurity) throws Exception { .requestMatchers("/api/teams/**") .requestMatchers("/api/admin/**") .requestMatchers("/api/team-building/**") + .requestMatchers("/api/semester-periods/**") .requestMatchers("/api/calendars/**"); }); diff --git a/eeos/src/main/java/com/blackcompany/eeos/member/application/repository/MemberRepository.java b/eeos/src/main/java/com/blackcompany/eeos/member/application/repository/MemberRepository.java index 1d2d911c..86f25edf 100644 --- a/eeos/src/main/java/com/blackcompany/eeos/member/application/repository/MemberRepository.java +++ b/eeos/src/main/java/com/blackcompany/eeos/member/application/repository/MemberRepository.java @@ -26,4 +26,6 @@ public interface MemberRepository { MemberModel save(MemberModel model); void deleteById(Long memberId); + + String findNameById(Long memberId); } diff --git a/eeos/src/main/java/com/blackcompany/eeos/member/persistence/JpaMemberRepository.java b/eeos/src/main/java/com/blackcompany/eeos/member/persistence/JpaMemberRepository.java index 43bb25c6..cc297c4e 100644 --- a/eeos/src/main/java/com/blackcompany/eeos/member/persistence/JpaMemberRepository.java +++ b/eeos/src/main/java/com/blackcompany/eeos/member/persistence/JpaMemberRepository.java @@ -2,6 +2,7 @@ import com.blackcompany.eeos.member.application.model.ActiveStatus; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -22,6 +23,9 @@ public interface JpaMemberRepository extends JpaRepository { @Query("SELECT m FROM MemberEntity m WHERE m.id IN :ids AND m.isDeleted=false ORDER BY m.name") List findMembersByIds(@Param("ids") List ids); + @Query("SELECT m.name FROM MemberEntity m WHERE m.id=:id AND m.isDeleted=false") + Optional findNameById(@Param("id") Long id); + // 동작하지 않는 쿼리 : SQL 의 FILED 함수 내에 List 이 들어갈 때, 단일 파라미터로 들어가기 때문에 CustomRepository 에서 동적으로 // 쿼리를 생성해서 처리 // @Query("SELECT m FROM MemberEntity m WHERE m.id IN :ids AND m.isDeleted=false ORDER BY diff --git a/eeos/src/main/java/com/blackcompany/eeos/member/persistence/MemberRepositoryImpl.java b/eeos/src/main/java/com/blackcompany/eeos/member/persistence/MemberRepositoryImpl.java index d1ddbc84..f77a4035 100644 --- a/eeos/src/main/java/com/blackcompany/eeos/member/persistence/MemberRepositoryImpl.java +++ b/eeos/src/main/java/com/blackcompany/eeos/member/persistence/MemberRepositoryImpl.java @@ -67,4 +67,9 @@ public MemberModel save(MemberModel model) { public void deleteById(Long memberId) { jpaRepository.deleteById(memberId); } + + @Override + public String findNameById(Long memberId) { + return jpaRepository.findNameById(memberId).orElseThrow(NotFoundMemberException::new); + } } diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/application/dto/request/CalendarApplicationCommand.java b/eeos/src/main/java/com/blackcompany/eeos/program/application/dto/request/SemesterPeriodApplicationCommand.java similarity index 89% rename from eeos/src/main/java/com/blackcompany/eeos/program/application/dto/request/CalendarApplicationCommand.java rename to eeos/src/main/java/com/blackcompany/eeos/program/application/dto/request/SemesterPeriodApplicationCommand.java index db2990b9..c6af146d 100644 --- a/eeos/src/main/java/com/blackcompany/eeos/program/application/dto/request/CalendarApplicationCommand.java +++ b/eeos/src/main/java/com/blackcompany/eeos/program/application/dto/request/SemesterPeriodApplicationCommand.java @@ -4,7 +4,7 @@ import jakarta.validation.constraints.NotNull; import java.sql.Timestamp; -public record CalendarApplicationCommand( +public record SemesterPeriodApplicationCommand( @NotNull(message = "시작 날짜는 필수입니다") Timestamp startDate, @NotNull(message = "종료 날짜는 필수입니다") Timestamp endDate) implements AbstractApplicationDto {} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/application/dto/response/CalendarPeriodApplicationQuery.java b/eeos/src/main/java/com/blackcompany/eeos/program/application/dto/response/SemesterPeriodApplicationQuery.java similarity index 85% rename from eeos/src/main/java/com/blackcompany/eeos/program/application/dto/response/CalendarPeriodApplicationQuery.java rename to eeos/src/main/java/com/blackcompany/eeos/program/application/dto/response/SemesterPeriodApplicationQuery.java index da41209f..0fec5db7 100644 --- a/eeos/src/main/java/com/blackcompany/eeos/program/application/dto/response/CalendarPeriodApplicationQuery.java +++ b/eeos/src/main/java/com/blackcompany/eeos/program/application/dto/response/SemesterPeriodApplicationQuery.java @@ -9,7 +9,7 @@ @Getter @AllArgsConstructor @NoArgsConstructor -public class CalendarPeriodApplicationQuery implements AbstractApplicationDto { +public class SemesterPeriodApplicationQuery implements AbstractApplicationDto { private Timestamp startDate; private Timestamp endDate; } diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/application/model/CalendarModel.java b/eeos/src/main/java/com/blackcompany/eeos/program/application/model/SemesterPeriodModel.java similarity index 94% rename from eeos/src/main/java/com/blackcompany/eeos/program/application/model/CalendarModel.java rename to eeos/src/main/java/com/blackcompany/eeos/program/application/model/SemesterPeriodModel.java index 2ce9fdd0..4a34797e 100644 --- a/eeos/src/main/java/com/blackcompany/eeos/program/application/model/CalendarModel.java +++ b/eeos/src/main/java/com/blackcompany/eeos/program/application/model/SemesterPeriodModel.java @@ -12,11 +12,11 @@ @AllArgsConstructor @Builder(toBuilder = true) @Slf4j -public class CalendarModel { +public class SemesterPeriodModel { private Timestamp startDate; private Timestamp endDate; - public CalendarModel() { + public SemesterPeriodModel() { LocalDate now = LocalDate.now(); // 9월 이후이면 9월 1일부터 다음 해 2월 말까지 diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/application/repository/CalendarRepository.java b/eeos/src/main/java/com/blackcompany/eeos/program/application/repository/CalendarRepository.java deleted file mode 100644 index d98c5654..00000000 --- a/eeos/src/main/java/com/blackcompany/eeos/program/application/repository/CalendarRepository.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.blackcompany.eeos.program.application.repository; - -import com.blackcompany.eeos.program.application.model.CalendarModel; -import java.util.Optional; - -public interface CalendarRepository { - Optional getCalendar(); - - CalendarModel updateCalendar(CalendarModel model); -} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/application/repository/SemesterPeriodRepository.java b/eeos/src/main/java/com/blackcompany/eeos/program/application/repository/SemesterPeriodRepository.java new file mode 100644 index 00000000..ea01f158 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/program/application/repository/SemesterPeriodRepository.java @@ -0,0 +1,10 @@ +package com.blackcompany.eeos.program.application.repository; + +import com.blackcompany.eeos.program.application.model.SemesterPeriodModel; +import java.util.Optional; + +public interface SemesterPeriodRepository { + Optional getSemesterPeriod(); + + SemesterPeriodModel updateSemesterPeriod(SemesterPeriodModel model); +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/application/service/CalendarService.java b/eeos/src/main/java/com/blackcompany/eeos/program/application/service/CalendarService.java deleted file mode 100644 index 968b6375..00000000 --- a/eeos/src/main/java/com/blackcompany/eeos/program/application/service/CalendarService.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.blackcompany.eeos.program.application.service; - -import com.blackcompany.eeos.program.application.dto.request.CalendarApplicationCommand; -import com.blackcompany.eeos.program.application.dto.response.CalendarPeriodApplicationQuery; -import com.blackcompany.eeos.program.application.model.CalendarModel; -import com.blackcompany.eeos.program.application.repository.CalendarRepository; -import com.blackcompany.eeos.program.application.support.CalendarProvider; -import com.blackcompany.eeos.program.application.usecase.GetCalendarUsecase; -import com.blackcompany.eeos.program.application.usecase.UpdateCalendarUsecase; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@RequiredArgsConstructor -@Transactional(readOnly = true) -public class CalendarService implements GetCalendarUsecase, UpdateCalendarUsecase { - private final CalendarRepository calendarRepository; - private final CalendarProvider calendarProvider; - - @Override - public CalendarPeriodApplicationQuery getCalendar() { - CalendarModel model = calendarProvider.getCalendar(); - return new CalendarPeriodApplicationQuery(model.getStartDate(), model.getEndDate()); - } - - @Override - @Transactional - public CalendarPeriodApplicationQuery updateCalendar(CalendarApplicationCommand command) { - CalendarModel model = - calendarRepository.updateCalendar( - new CalendarModel(command.startDate(), command.endDate())); - return new CalendarPeriodApplicationQuery(model.getStartDate(), model.getEndDate()); - } -} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/application/service/SemesterPeriodService.java b/eeos/src/main/java/com/blackcompany/eeos/program/application/service/SemesterPeriodService.java new file mode 100644 index 00000000..ddee1e93 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/program/application/service/SemesterPeriodService.java @@ -0,0 +1,37 @@ +package com.blackcompany.eeos.program.application.service; + +import com.blackcompany.eeos.program.application.dto.request.SemesterPeriodApplicationCommand; +import com.blackcompany.eeos.program.application.dto.response.SemesterPeriodApplicationQuery; +import com.blackcompany.eeos.program.application.model.SemesterPeriodModel; +import com.blackcompany.eeos.program.application.repository.SemesterPeriodRepository; +import com.blackcompany.eeos.program.application.support.SemesterPeriodProvider; +import com.blackcompany.eeos.program.application.usecase.GetSemesterPeriodUsecase; +import com.blackcompany.eeos.program.application.usecase.UpdateSemesterPeriodUsecase; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class SemesterPeriodService + implements GetSemesterPeriodUsecase, UpdateSemesterPeriodUsecase { + private final SemesterPeriodRepository semesterPeriodRepository; + private final SemesterPeriodProvider semesterPeriodProvider; + + @Override + public SemesterPeriodApplicationQuery getSemesterPeriod() { + SemesterPeriodModel model = semesterPeriodProvider.getSemesterPeriod(); + return new SemesterPeriodApplicationQuery(model.getStartDate(), model.getEndDate()); + } + + @Override + @Transactional + public SemesterPeriodApplicationQuery updateSemesterPeriod( + SemesterPeriodApplicationCommand command) { + SemesterPeriodModel model = + semesterPeriodRepository.updateSemesterPeriod( + new SemesterPeriodModel(command.startDate(), command.endDate())); + return new SemesterPeriodApplicationQuery(model.getStartDate(), model.getEndDate()); + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/application/support/CalendarProvider.java b/eeos/src/main/java/com/blackcompany/eeos/program/application/support/CalendarProvider.java deleted file mode 100644 index cd7a0bb1..00000000 --- a/eeos/src/main/java/com/blackcompany/eeos/program/application/support/CalendarProvider.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.blackcompany.eeos.program.application.support; - -import com.blackcompany.eeos.program.application.model.CalendarModel; -import com.blackcompany.eeos.program.application.repository.CalendarRepository; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class CalendarProvider { - private final CalendarRepository calendarRepository; - - public CalendarModel getCalendar() { - return calendarRepository.getCalendar().orElse(new CalendarModel()); - } -} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/application/support/SemesterPeriodProvider.java b/eeos/src/main/java/com/blackcompany/eeos/program/application/support/SemesterPeriodProvider.java new file mode 100644 index 00000000..9a3a59b4 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/program/application/support/SemesterPeriodProvider.java @@ -0,0 +1,16 @@ +package com.blackcompany.eeos.program.application.support; + +import com.blackcompany.eeos.program.application.model.SemesterPeriodModel; +import com.blackcompany.eeos.program.application.repository.SemesterPeriodRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class SemesterPeriodProvider { + private final SemesterPeriodRepository semesterPeriodRepository; + + public SemesterPeriodModel getSemesterPeriod() { + return semesterPeriodRepository.getSemesterPeriod().orElse(new SemesterPeriodModel()); + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/GetCalendarUsecase.java b/eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/GetCalendarUsecase.java deleted file mode 100644 index 75802d7a..00000000 --- a/eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/GetCalendarUsecase.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.blackcompany.eeos.program.application.usecase; - -import com.blackcompany.eeos.program.application.dto.response.CalendarPeriodApplicationQuery; - -public interface GetCalendarUsecase { - CalendarPeriodApplicationQuery getCalendar(); -} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/GetSemesterPeriodUsecase.java b/eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/GetSemesterPeriodUsecase.java new file mode 100644 index 00000000..f46d7b36 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/GetSemesterPeriodUsecase.java @@ -0,0 +1,7 @@ +package com.blackcompany.eeos.program.application.usecase; + +import com.blackcompany.eeos.program.application.dto.response.SemesterPeriodApplicationQuery; + +public interface GetSemesterPeriodUsecase { + SemesterPeriodApplicationQuery getSemesterPeriod(); +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/UpdateCalendarUsecase.java b/eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/UpdateCalendarUsecase.java deleted file mode 100644 index fcedfcb0..00000000 --- a/eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/UpdateCalendarUsecase.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.blackcompany.eeos.program.application.usecase; - -import com.blackcompany.eeos.program.application.dto.request.CalendarApplicationCommand; -import com.blackcompany.eeos.program.application.dto.response.CalendarPeriodApplicationQuery; - -public interface UpdateCalendarUsecase { - CalendarPeriodApplicationQuery updateCalendar(CalendarApplicationCommand command); -} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/UpdateSemesterPeriodUsecase.java b/eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/UpdateSemesterPeriodUsecase.java new file mode 100644 index 00000000..91bc7f6c --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/program/application/usecase/UpdateSemesterPeriodUsecase.java @@ -0,0 +1,8 @@ +package com.blackcompany.eeos.program.application.usecase; + +import com.blackcompany.eeos.program.application.dto.request.SemesterPeriodApplicationCommand; +import com.blackcompany.eeos.program.application.dto.response.SemesterPeriodApplicationQuery; + +public interface UpdateSemesterPeriodUsecase { + SemesterPeriodApplicationQuery updateSemesterPeriod(SemesterPeriodApplicationCommand command); +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/persistence/CalendarRepositoryImpl.java b/eeos/src/main/java/com/blackcompany/eeos/program/persistence/CalendarRepositoryImpl.java deleted file mode 100644 index 84a27f95..00000000 --- a/eeos/src/main/java/com/blackcompany/eeos/program/persistence/CalendarRepositoryImpl.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.blackcompany.eeos.program.persistence; - -import com.blackcompany.eeos.program.application.model.CalendarModel; -import com.blackcompany.eeos.program.application.repository.CalendarRepository; -import java.util.Optional; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; - -@Repository -@RequiredArgsConstructor -public class CalendarRepositoryImpl implements CalendarRepository { - private final JpaCalendarRepository jpaCalendarRepository; - - @Override - public Optional getCalendar() { - return jpaCalendarRepository.findTopByOrderByCreatedDateDesc().map(this::toModel); - } - - @Override - public CalendarModel updateCalendar(CalendarModel model) { - CalendarEntity entity = toEntity(model); - CalendarEntity savedEntity = jpaCalendarRepository.save(entity); - return toModel(savedEntity); - } - - private CalendarModel toModel(CalendarEntity entity) { - return CalendarModel.builder() - .startDate(entity.getStartDate()) - .endDate(entity.getEndDate()) - .build(); - } - - private CalendarEntity toEntity(CalendarModel model) { - return CalendarEntity.builder() - .startDate(model.getStartDate()) - .endDate(model.getEndDate()) - .build(); - } -} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/persistence/JpaCalendarRepository.java b/eeos/src/main/java/com/blackcompany/eeos/program/persistence/JpaCalendarRepository.java deleted file mode 100644 index ffb27d18..00000000 --- a/eeos/src/main/java/com/blackcompany/eeos/program/persistence/JpaCalendarRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.blackcompany.eeos.program.persistence; - -import java.util.Optional; -import org.springframework.data.jpa.repository.JpaRepository; - -public interface JpaCalendarRepository extends JpaRepository { - Optional findTopByOrderByCreatedDateDesc(); -} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/persistence/JpaSemesterPeriodRepository.java b/eeos/src/main/java/com/blackcompany/eeos/program/persistence/JpaSemesterPeriodRepository.java new file mode 100644 index 00000000..630a4ac0 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/program/persistence/JpaSemesterPeriodRepository.java @@ -0,0 +1,8 @@ +package com.blackcompany.eeos.program.persistence; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface JpaSemesterPeriodRepository extends JpaRepository { + Optional findTopByOrderByCreatedDateDesc(); +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/persistence/CalendarEntity.java b/eeos/src/main/java/com/blackcompany/eeos/program/persistence/SemesterPeriodEntity.java similarity index 70% rename from eeos/src/main/java/com/blackcompany/eeos/program/persistence/CalendarEntity.java rename to eeos/src/main/java/com/blackcompany/eeos/program/persistence/SemesterPeriodEntity.java index 81d45d66..d70546b9 100644 --- a/eeos/src/main/java/com/blackcompany/eeos/program/persistence/CalendarEntity.java +++ b/eeos/src/main/java/com/blackcompany/eeos/program/persistence/SemesterPeriodEntity.java @@ -15,16 +15,16 @@ @SuperBuilder(toBuilder = true) @Entity @Table( - name = CalendarEntity.ENTITY_PREFIX, + name = SemesterPeriodEntity.ENTITY_PREFIX, indexes = { @Index( - name = "idx_calendar_created_date_id", - columnList = "createdDate DESC, calendar_id DESC") + name = "idx_semester_period_created_date_id", + columnList = "createdDate DESC, semester_period_id DESC") }) -@SQLDelete(sql = "UPDATE calendar SET is_deleted=true where calendar_id=?") +@SQLDelete(sql = "UPDATE semester_period SET is_deleted=true where semester_period_id=?") @Where(clause = "is_deleted=false") -public class CalendarEntity extends BaseEntity { - public static final String ENTITY_PREFIX = "calendar"; +public class SemesterPeriodEntity extends BaseEntity { + public static final String ENTITY_PREFIX = "semester_period"; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/persistence/SemesterPeriodRepositoryImpl.java b/eeos/src/main/java/com/blackcompany/eeos/program/persistence/SemesterPeriodRepositoryImpl.java new file mode 100644 index 00000000..c271f71f --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/program/persistence/SemesterPeriodRepositoryImpl.java @@ -0,0 +1,39 @@ +package com.blackcompany.eeos.program.persistence; + +import com.blackcompany.eeos.program.application.model.SemesterPeriodModel; +import com.blackcompany.eeos.program.application.repository.SemesterPeriodRepository; +import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class SemesterPeriodRepositoryImpl implements SemesterPeriodRepository { + private final JpaSemesterPeriodRepository jpaSemesterPeriodRepository; + + @Override + public Optional getSemesterPeriod() { + return jpaSemesterPeriodRepository.findTopByOrderByCreatedDateDesc().map(this::toModel); + } + + @Override + public SemesterPeriodModel updateSemesterPeriod(SemesterPeriodModel model) { + SemesterPeriodEntity entity = toEntity(model); + SemesterPeriodEntity savedEntity = jpaSemesterPeriodRepository.save(entity); + return toModel(savedEntity); + } + + private SemesterPeriodModel toModel(SemesterPeriodEntity entity) { + return SemesterPeriodModel.builder() + .startDate(entity.getStartDate()) + .endDate(entity.getEndDate()) + .build(); + } + + private SemesterPeriodEntity toEntity(SemesterPeriodModel model) { + return SemesterPeriodEntity.builder() + .startDate(model.getStartDate()) + .endDate(model.getEndDate()) + .build(); + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/presentation/controller/CalendarController.java b/eeos/src/main/java/com/blackcompany/eeos/program/presentation/controller/CalendarController.java deleted file mode 100644 index 67f08664..00000000 --- a/eeos/src/main/java/com/blackcompany/eeos/program/presentation/controller/CalendarController.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.blackcompany.eeos.program.presentation.controller; - -import com.blackcompany.eeos.common.presentation.response.ApiResponse; -import com.blackcompany.eeos.common.presentation.response.ApiResponseBody; -import com.blackcompany.eeos.common.presentation.response.ApiResponseGenerator; -import com.blackcompany.eeos.common.presentation.response.MessageCode; -import com.blackcompany.eeos.program.application.dto.request.CalendarApplicationCommand; -import com.blackcompany.eeos.program.application.dto.response.CalendarPeriodApplicationQuery; -import com.blackcompany.eeos.program.application.usecase.GetCalendarUsecase; -import com.blackcompany.eeos.program.application.usecase.UpdateCalendarUsecase; -import com.blackcompany.eeos.program.presentation.dto.UpdateCalendarRequest; -import jakarta.validation.Valid; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/api") -public class CalendarController { - private final GetCalendarUsecase getCalendarUsecase; - private final UpdateCalendarUsecase updateCalendarUsecase; - - @PutMapping("/admin/calendars") // TODO : 인터셉터 단에서 어드민 검증 - public ApiResponse> updateCalendar( - @RequestBody @Valid UpdateCalendarRequest request) { - CalendarPeriodApplicationQuery response = - updateCalendarUsecase.updateCalendar( - new CalendarApplicationCommand(request.startDate(), request.endDate())); - return ApiResponseGenerator.success(response, HttpStatus.CREATED, MessageCode.CREATE); - } - - @GetMapping("/calendars") - public ApiResponse> getCalendar() { - CalendarPeriodApplicationQuery response = getCalendarUsecase.getCalendar(); - return ApiResponseGenerator.success(response, HttpStatus.OK, MessageCode.GET); - } -} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/presentation/controller/SemesterPeriodController.java b/eeos/src/main/java/com/blackcompany/eeos/program/presentation/controller/SemesterPeriodController.java new file mode 100644 index 00000000..de689903 --- /dev/null +++ b/eeos/src/main/java/com/blackcompany/eeos/program/presentation/controller/SemesterPeriodController.java @@ -0,0 +1,39 @@ +package com.blackcompany.eeos.program.presentation.controller; + +import com.blackcompany.eeos.common.presentation.response.ApiResponse; +import com.blackcompany.eeos.common.presentation.response.ApiResponseBody; +import com.blackcompany.eeos.common.presentation.response.ApiResponseGenerator; +import com.blackcompany.eeos.common.presentation.response.MessageCode; +import com.blackcompany.eeos.program.application.dto.request.SemesterPeriodApplicationCommand; +import com.blackcompany.eeos.program.application.dto.response.SemesterPeriodApplicationQuery; +import com.blackcompany.eeos.program.application.usecase.GetSemesterPeriodUsecase; +import com.blackcompany.eeos.program.application.usecase.UpdateSemesterPeriodUsecase; +import com.blackcompany.eeos.program.presentation.dto.UpdateSemesterPeriodRequest; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api") +public class SemesterPeriodController { + private final GetSemesterPeriodUsecase getSemesterPeriodUsecase; + private final UpdateSemesterPeriodUsecase updateSemesterPeriodUsecase; + + @PutMapping("/admin/semester-periods") // TODO : 인터셉터 단에서 어드민 검증 + public ApiResponse> + updateSemesterPeriod(@RequestBody @Valid UpdateSemesterPeriodRequest request) { + SemesterPeriodApplicationQuery response = + updateSemesterPeriodUsecase.updateSemesterPeriod( + new SemesterPeriodApplicationCommand(request.startDate(), request.endDate())); + return ApiResponseGenerator.success(response, HttpStatus.CREATED, MessageCode.CREATE); + } + + @GetMapping("/semester-periods") + public ApiResponse> + getSemesterPeriod() { + SemesterPeriodApplicationQuery response = getSemesterPeriodUsecase.getSemesterPeriod(); + return ApiResponseGenerator.success(response, HttpStatus.OK, MessageCode.GET); + } +} diff --git a/eeos/src/main/java/com/blackcompany/eeos/program/presentation/dto/UpdateCalendarRequest.java b/eeos/src/main/java/com/blackcompany/eeos/program/presentation/dto/UpdateSemesterPeriodRequest.java similarity index 92% rename from eeos/src/main/java/com/blackcompany/eeos/program/presentation/dto/UpdateCalendarRequest.java rename to eeos/src/main/java/com/blackcompany/eeos/program/presentation/dto/UpdateSemesterPeriodRequest.java index b8793649..32baff19 100644 --- a/eeos/src/main/java/com/blackcompany/eeos/program/presentation/dto/UpdateCalendarRequest.java +++ b/eeos/src/main/java/com/blackcompany/eeos/program/presentation/dto/UpdateSemesterPeriodRequest.java @@ -4,7 +4,7 @@ import jakarta.validation.constraints.NotNull; import java.sql.Timestamp; -public record UpdateCalendarRequest( +public record UpdateSemesterPeriodRequest( @NotNull(message = "시작일은 필수값입니다.") Timestamp startDate, @NotNull(message = "종료일은 필수값입니다.") Timestamp endDate) { diff --git a/eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java b/eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java index 7f18b1f8..a261e12a 100644 --- a/eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java +++ b/eeos/src/main/java/com/blackcompany/eeos/target/application/service/AttendService.java @@ -12,7 +12,7 @@ import com.blackcompany.eeos.program.application.model.ProgramModel; import com.blackcompany.eeos.program.application.model.converter.ProgramEntityConverter; import com.blackcompany.eeos.program.application.service.ProgramDateRangeService; -import com.blackcompany.eeos.program.application.support.CalendarProvider; +import com.blackcompany.eeos.program.application.support.SemesterPeriodProvider; import com.blackcompany.eeos.program.persistence.ProgramRepository; import com.blackcompany.eeos.target.application.dto.AttendInfoActiveStatusResponse; import com.blackcompany.eeos.target.application.dto.AttendInfoResponse; @@ -89,7 +89,7 @@ public class AttendService private final AttendCountCalculate attendCountCalculate; private final AttendPenaltyResponseConverter attendPenaltyResponseConverter; private final ProgramRankCounterRepository programRankCounterRepository; - private final CalendarProvider calendarProvider; + private final SemesterPeriodProvider semesterPeriodProvider; private final PenaltyPointRepository penaltyPointRepository; @Override @@ -253,8 +253,10 @@ public PageResponse findMyAttendInfo( public AttendSummaryInfoResponse getMyAttendSummary(Long startDate, Long endDate) { Long memberId = RequestScope.getMemberId(); - if (startDate == null) startDate = calendarProvider.getCalendar().getStartDate().getTime(); - if (endDate == null) endDate = calendarProvider.getCalendar().getEndDate().getTime(); + if (startDate == null) + startDate = semesterPeriodProvider.getSemesterPeriod().getStartDate().getTime(); + if (endDate == null) + endDate = semesterPeriodProvider.getSemesterPeriod().getEndDate().getTime(); List programs = programDateRangeService.getPrograms(startDate, endDate); @@ -285,10 +287,12 @@ public PageResponse getPenaltyInfos( Timestamp startTimestamp = startDate == null - ? calendarProvider.getCalendar().getStartDate() + ? semesterPeriodProvider.getSemesterPeriod().getStartDate() : new Timestamp(startDate); Timestamp endTimestamp = - endDate == null ? calendarProvider.getCalendar().getEndDate() : new Timestamp(endDate); + endDate == null + ? semesterPeriodProvider.getSemesterPeriod().getEndDate() + : new Timestamp(endDate); Page pages = penaltyPointRepository.findByPenaltyPointSum(startTimestamp, endTimestamp, pageable); @@ -331,8 +335,8 @@ public AttendPenaltyRankingResponse getMyPenaltyRanking(int rankOffset) { Long memberId = RequestScope.getMemberId(); - Timestamp startDate = calendarProvider.getCalendar().getStartDate(); - Timestamp endDate = calendarProvider.getCalendar().getEndDate(); + Timestamp startDate = semesterPeriodProvider.getSemesterPeriod().getStartDate(); + Timestamp endDate = semesterPeriodProvider.getSemesterPeriod().getEndDate(); Long myPenaltyPoint = penaltyPointRepository.findTotalPenaltyScoreByMemberId(startDate, endDate, memberId);