Skip to content

Commit

Permalink
Merge pull request #41 from Seasoning-Today/feat/#36-implement-find-s…
Browse files Browse the repository at this point in the history
…olar-term-info-api

절기 조회 API 구현
  • Loading branch information
csct3434 authored Jan 30, 2024
2 parents 8cac559 + 26a7462 commit 3285eda
Show file tree
Hide file tree
Showing 8 changed files with 139 additions and 74 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package today.seasoning.seasoning.article.service;

import com.github.f4b6a3.tsid.TsidCreator;
import java.time.LocalDate;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
Expand All @@ -18,6 +17,7 @@
import today.seasoning.seasoning.common.aws.UploadFileInfo;
import today.seasoning.seasoning.common.exception.CustomException;
import today.seasoning.seasoning.common.util.TsidUtil;
import today.seasoning.seasoning.solarterm.domain.SolarTerm;
import today.seasoning.seasoning.solarterm.service.SolarTermService;
import today.seasoning.seasoning.user.domain.User;
import today.seasoning.seasoning.user.domain.UserRepository;
Expand All @@ -37,8 +37,6 @@ public class RegisterArticleService {
private int ARTICLE_IMAGES_LIMIT;

public Long doRegister(RegisterArticleCommand command) {
validateRequest(command);

Article article = createArticle(command);
articleRepository.save(article);

Expand All @@ -47,24 +45,21 @@ public Long doRegister(RegisterArticleCommand command) {
return article.getId();
}

private void validateRequest(RegisterArticleCommand command) {
if (!solarTermService.checkRecordOpen()) {
throw new CustomException(HttpStatus.FORBIDDEN, "등록 기간이 아닙니다.");
}

if (command.getImages().size() > ARTICLE_IMAGES_LIMIT) {
throw new CustomException(HttpStatus.BAD_REQUEST, "이미지 개수 초과");
}
}

private Article createArticle(RegisterArticleCommand command) {
User user = userRepository.findById(command.getUserId()).get();

return new Article(user, command.isPublished(), LocalDate.now().getYear(),
solarTermService.findCurrentTerm(), command.getContents());
SolarTerm solarTerm = solarTermService.findRecordSolarTerm()
.orElseThrow(() -> new CustomException(HttpStatus.FORBIDDEN, "등록 기간이 아닙니다."));

return new Article(user, command.isPublished(), solarTerm.getDate().getYear(),
solarTerm.getSequence(), command.getContents());
}

private void uploadAndRegisterArticleImages(Article article, List<MultipartFile> images) {
if (images.size() > ARTICLE_IMAGES_LIMIT) {
throw new CustomException(HttpStatus.BAD_REQUEST, "이미지 개수 초과");
}

for (int sequence = 0; sequence < images.size(); sequence++) {
MultipartFile image = images.get(sequence);
UploadFileInfo fileInfo = uploadImage(image);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,16 @@ public class UpdateArticleService {
private int ARTICLE_IMAGES_LIMIT;

public void doUpdate(UpdateArticleCommand command) {
Article article = findArticle(command.getArticleId());
Article article = articleRepository.findById(command.getArticleId())
.orElseThrow(() -> new CustomException(HttpStatus.NOT_FOUND, "기록장 조회 실패"));

validateRequest(article, command);
solarTermService.findRecordSolarTerm()
.orElseThrow(() -> new CustomException(HttpStatus.FORBIDDEN, "등록 기간이 아닙니다."));

Long ownerId = article.getUser().getId();
if (!ownerId.equals(command.getUserId())) {
throw new CustomException(HttpStatus.FORBIDDEN, "권한 없음");
}

deleteOldImages(article.getArticleImages());

Expand All @@ -44,26 +51,6 @@ public void doUpdate(UpdateArticleCommand command) {
updateArticle(article, command);
}

private void validateRequest(Article article, UpdateArticleCommand command) {
if (!solarTermService.checkRecordOpen()) {
throw new CustomException(HttpStatus.FORBIDDEN, "등록 기간이 아닙니다.");
}

if (command.getImages().size() > ARTICLE_IMAGES_LIMIT) {
throw new CustomException(HttpStatus.BAD_REQUEST, "이미지 개수 초과");
}

Long authorId = article.getUser().getId();
if (!authorId.equals(command.getUserId())) {
throw new CustomException(HttpStatus.FORBIDDEN, "권한 없음");
}
}

private Article findArticle(Long articleId) {
return articleRepository.findById(articleId)
.orElseThrow(() -> new CustomException(HttpStatus.NOT_FOUND, "기록장 조회 실패"));
}

private void deleteOldImages(List<ArticleImage> articleImages) {
articleImages.stream()
.map(ArticleImage::getFilename)
Expand All @@ -72,6 +59,10 @@ private void deleteOldImages(List<ArticleImage> articleImages) {
}

private void uploadAndRegisterArticleImages(Article article, List<MultipartFile> images) {
if (images.size() > ARTICLE_IMAGES_LIMIT) {
throw new CustomException(HttpStatus.BAD_REQUEST, "이미지 개수 초과");
}

for (int sequence = 0; sequence < images.size(); sequence++) {
MultipartFile image = images.get(sequence);
UploadFileInfo uploadFileInfo = uploadImage(image);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@
import javax.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import today.seasoning.seasoning.solarterm.dto.FindSolarTermInfoResponse;
import today.seasoning.seasoning.solarterm.service.FindAndRegisterSolarTermsService;
import today.seasoning.seasoning.solarterm.service.SolarTermService;

@RestController
@RequiredArgsConstructor
public class SolarTermController {

private final SolarTermService solarTermService;
private final FindAndRegisterSolarTermsService findAndRegisterSolarTermsService;

@PostMapping("/admin/solar-term")
Expand All @@ -20,4 +24,10 @@ public ResponseEntity<Void> registerSolarTermsOfYear(@Valid @RequestBody YearDto
return ResponseEntity.ok().build();
}

@GetMapping("/solarTerm")
public ResponseEntity<FindSolarTermInfoResponse> findSolarTermInfo() {
FindSolarTermInfoResponse solarTermInfoResponse = solarTermService.findSolarTermInfo();
return ResponseEntity.ok(solarTermInfoResponse);
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package today.seasoning.seasoning.solarterm.domain;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import javax.persistence.Entity;
import javax.persistence.Id;
import lombok.Getter;
Expand All @@ -18,25 +19,15 @@ public class SolarTerm extends BaseTimeEntity {

private int sequence;

private int year;
private LocalDate date;

private int month;

private int day;

public SolarTerm(int sequence, int year, int month, int day) {
public SolarTerm(int sequence, LocalDate date) {
this.id = TsidUtil.createLong();
this.sequence = sequence;
this.year = year;
this.month = month;
this.day = day;
this.date = date;
}

public SolarTerm(int sequence, LocalDate date) {
this.id = TsidUtil.createLong();
this.sequence = sequence;
this.year = date.getYear();
this.month = date.getMonthValue();
this.day = date.getDayOfMonth();
public int getDaysDiff(LocalDate date) {
return (int) Math.abs(ChronoUnit.DAYS.between(this.date, date));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

@Repository
public interface SolarTermRepository extends JpaRepository<SolarTerm, Long> {

@Query
List<SolarTerm> findByYearAndMonth(int year, int month);
List<SolarTerm> findAllByOrderByDateAsc();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package today.seasoning.seasoning.solarterm.dto;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import lombok.AllArgsConstructor;
import lombok.Getter;
import today.seasoning.seasoning.solarterm.domain.SolarTerm;

@Getter
@AllArgsConstructor
@JsonInclude(Include.NON_NULL)
public class FindSolarTermInfoResponse {

private final boolean recordable;
private final SolarTermDto currentTerm;
private final SolarTermDto nextTerm;
private final SolarTermDto recordTerm;

public static FindSolarTermInfoResponse build(SolarTerm currentSolarTerm, SolarTerm nextSolarTerm, SolarTerm recordSolarTerm, int recordPeriod) {
boolean recordable = recordSolarTerm != null;
SolarTermDto currentTerm = SolarTermDto.build(currentSolarTerm);
SolarTermDto nextTerm = SolarTermDto.build(nextSolarTerm);

SolarTermDto recordTerm = null;
if(recordable) {
recordTerm = new SolarTermDto(
recordSolarTerm.getSequence(),
recordSolarTerm.getDate().plusDays(recordPeriod));
}

return new FindSolarTermInfoResponse(recordable, currentTerm, nextTerm, recordTerm);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package today.seasoning.seasoning.solarterm.dto;

import java.time.LocalDate;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import today.seasoning.seasoning.solarterm.domain.SolarTerm;

@Getter
@RequiredArgsConstructor
public class SolarTermDto {

private final int sequence;
private final LocalDate date;

public static SolarTermDto build(SolarTerm solarTerm) {
return new SolarTermDto(solarTerm.getSequence(), solarTerm.getDate());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,30 @@

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import javax.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import today.seasoning.seasoning.notification.service.NotificationService;
import today.seasoning.seasoning.solarterm.domain.SolarTerm;
import today.seasoning.seasoning.solarterm.domain.SolarTermRepository;
import today.seasoning.seasoning.solarterm.dto.FindSolarTermInfoResponse;

@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class SolarTermService {

// 현재 절기 순번 : 1 ~ 24 (-1 : 절기가 아님)
private static int currentOpenTerm;
// 날짜상 현재 절기와 다음 절기
private SolarTerm currentSolarTerm;
private SolarTerm nextSolarTerm;
// 현재 기록장이 열린 절기
private Optional<SolarTerm> recordSolarTerm;

@Value("${ARTICLE-REGISTRATION-PERIOD}")
private int ARTICLE_REGISTRATION_PERIOD;
Expand All @@ -27,34 +34,56 @@ public class SolarTermService {
private final NotificationService notificationService;

@PostConstruct
protected void init() {
updateSolarTerms();
}

@Scheduled(cron = "5 0 0 * * *")
public void updateTerm() {
int openTerm = calculateOpenTerm(LocalDate.now());
protected void dailyUpdate() {
Optional<SolarTerm> pastRecordSolarTerm = recordSolarTerm;

if (currentOpenTerm < openTerm) {
notificationService.registerArticleOpenNotification(openTerm);
updateSolarTerms();

if (pastRecordSolarTerm.isEmpty() && recordSolarTerm.isPresent()) {
notificationService.registerArticleOpenNotification(recordSolarTerm.get().getSequence());
}
currentOpenTerm = openTerm;
}

private int calculateOpenTerm(LocalDate date) {
List<SolarTerm> solarTerms = solarTermRepository.findByYearAndMonth(date.getYear(), date.getMonthValue());
protected void updateSolarTerms() {
List<SolarTerm> solarTerms = solarTermRepository.findAllByOrderByDateAsc();

LocalDate today = LocalDate.now();

for (SolarTerm solarTerm : solarTerms) {
int daysDifferenceFromTerm = Math.abs(solarTerm.getDay() - date.getDayOfMonth());
if (daysDifferenceFromTerm < ARTICLE_REGISTRATION_PERIOD) {
return solarTerm.getSequence();
for (int i = 0; i + 1 < solarTerms.size(); i++) {
if (!solarTerms.get(i).getDate().isBefore(today)) {
currentSolarTerm = solarTerms.get(i);
nextSolarTerm = solarTerms.get(i + 1);

// 현재 기록장이 열린 절기를 계산
if (currentSolarTerm.getDaysDiff(today) <= ARTICLE_REGISTRATION_PERIOD) {
recordSolarTerm = Optional.of(currentSolarTerm);
} else if (nextSolarTerm.getDaysDiff(today) <= ARTICLE_REGISTRATION_PERIOD) {
recordSolarTerm = Optional.of(nextSolarTerm);
} else {
recordSolarTerm = Optional.empty();
}

break;
}
}
return -1;
}

public int findCurrentTerm() {
return currentOpenTerm;
log.info("현재 절기 : {} / 다음 절기 : {} / 열린 절기 : {}",
currentSolarTerm.getSequence(),
nextSolarTerm.getSequence(),
recordSolarTerm.map(SolarTerm::getSequence).orElse(-1));
}

public boolean checkRecordOpen() {
return currentOpenTerm > 0;
public Optional<SolarTerm> findRecordSolarTerm() {
return recordSolarTerm;
}

public FindSolarTermInfoResponse findSolarTermInfo() {
return FindSolarTermInfoResponse.build(currentSolarTerm, nextSolarTerm, recordSolarTerm.orElse(null),
ARTICLE_REGISTRATION_PERIOD);
}
}

0 comments on commit 3285eda

Please sign in to comment.