From 530e43c4491776ffe1be83bfce18db088d4ee1aa Mon Sep 17 00:00:00 2001 From: yejin Date: Mon, 9 Feb 2026 14:43:04 +0900 Subject: [PATCH 1/3] =?UTF-8?q?Fix:=20=ED=8F=89=EC=9D=BC=EC=9D=84=20?= =?UTF-8?q?=EC=9A=94=EC=9D=BC(=EC=9B=94-=EA=B8=88)=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/{ => response}/HolidayResDto.java | 4 +- .../operatingtime/entity/DayOfWeek.java | 85 ++++++++++++++++++- .../domain/operatingtime/entity/Holiday.java | 2 +- .../scheduler/OperatingScheduler.java | 35 ++++++-- .../operatingtime/service/HolidayService.java | 2 +- .../service/OperatingService.java | 64 ++++++++++++-- .../teamcback/domain/place/entity/Place.java | 15 ++++ 7 files changed, 187 insertions(+), 20 deletions(-) rename src/main/java/devkor/com/teamcback/domain/operatingtime/dto/{ => response}/HolidayResDto.java (84%) diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/HolidayResDto.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/HolidayResDto.java similarity index 84% rename from src/main/java/devkor/com/teamcback/domain/operatingtime/dto/HolidayResDto.java rename to src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/HolidayResDto.java index f8a0251b..56dddf8e 100644 --- a/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/HolidayResDto.java +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/HolidayResDto.java @@ -1,4 +1,4 @@ -package devkor.com.teamcback.domain.operatingtime.dto; +package devkor.com.teamcback.domain.operatingtime.dto.response; import java.time.LocalDate; import java.time.format.DateTimeFormatter; @@ -16,7 +16,7 @@ public class HolidayResDto { public HolidayResDto(JSONObject itemJson) { this.name = itemJson.getString("dateName"); String holidayOrNot = itemJson.getString("isHoliday"); - this.isHoliday = holidayOrNot.equals("Y") ? true : false; + this.isHoliday = holidayOrNot.equals("Y"); String dateString = itemJson.getBigInteger("locdate").toString(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); this.date = LocalDate.parse(dateString, formatter); diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/DayOfWeek.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/DayOfWeek.java index 1c169df7..af5beed5 100644 --- a/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/DayOfWeek.java +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/DayOfWeek.java @@ -24,6 +24,89 @@ public String getOperatingTime(Place place) { return place.getWeekdayOperatingTime(); } }, + + + MONDAY { + @Override + public DayOfWeek findNext() { + return TUESDAY; + } + + @Override + public String getOperatingTime(Building building) { + return building.getWeekdayOperatingTime(); + } + + @Override + public String getOperatingTime(Place place) { + return place.getMondayOperatingTime() == null ? place.getWeekdayOperatingTime() : place.getMondayOperatingTime(); + } + }, + TUESDAY { + @Override + public DayOfWeek findNext() { + return WEDNESDAY; + } + + @Override + public String getOperatingTime(Building building) { + return building.getWeekdayOperatingTime(); + } + + @Override + public String getOperatingTime(Place place) { + return place.getTuesdayOperatingTime() == null ? place.getWeekdayOperatingTime() : place.getTuesdayOperatingTime(); + } + }, + WEDNESDAY { + @Override + public DayOfWeek findNext() { + return THURSDAY; + } + + @Override + public String getOperatingTime(Building building) { + return building.getWeekdayOperatingTime(); + } + + @Override + public String getOperatingTime(Place place) { + return place.getWednesdayOperatingTime() == null ? place.getWeekdayOperatingTime() : place.getWednesdayOperatingTime(); + } + }, + THURSDAY { + @Override + public DayOfWeek findNext() { + return FRIDAY; + } + + @Override + public String getOperatingTime(Building building) { + return building.getWeekdayOperatingTime(); + } + + @Override + public String getOperatingTime(Place place) { + return place.getThursdayOperatingTime() == null ? place.getWeekdayOperatingTime() : place.getThursdayOperatingTime(); + } + }, + FRIDAY { + @Override + public DayOfWeek findNext() { + return SATURDAY; + } + + @Override + public String getOperatingTime(Building building) { + return building.getWeekdayOperatingTime(); + } + + @Override + public String getOperatingTime(Place place) { + return place.getFridayOperatingTime() == null ? place.getWeekdayOperatingTime() : place.getFridayOperatingTime(); + } + }, + SATURDAY { @Override public DayOfWeek findNext() { @@ -43,7 +126,7 @@ public String getOperatingTime(Place place) { SUNDAY { @Override public DayOfWeek findNext() { - return WEEKDAY; + return MONDAY; } @Override diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/Holiday.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/Holiday.java index 916e7972..9e9339c9 100644 --- a/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/Holiday.java +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/Holiday.java @@ -1,6 +1,6 @@ package devkor.com.teamcback.domain.operatingtime.entity; -import devkor.com.teamcback.domain.operatingtime.dto.HolidayResDto; +import devkor.com.teamcback.domain.operatingtime.dto.response.HolidayResDto; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/OperatingScheduler.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/OperatingScheduler.java index a86bbc05..d11e2541 100644 --- a/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/OperatingScheduler.java +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/OperatingScheduler.java @@ -34,8 +34,9 @@ public class OperatingScheduler { private static Boolean isVacation = null; private static Boolean isEvenWeek = null; - @Scheduled(cron = "0 0 0 * * *") // 매일 자정마다 - @EventListener(ApplicationReadyEvent.class) + // 매일 자정마다 요일 등 조건에 맞는 운영 시간 지정해 저장 + @Scheduled(cron = "0 0 0 * * *") + //@EventListener(ApplicationReadyEvent.class) public void updateOperatingTime() { // 배포 서버에서만 실행 @@ -55,24 +56,27 @@ public void updateOperatingTime() { } - // @EventListener(ApplicationReadyEvent.class) // 테스트용 - @Scheduled(cron = "0 */10 9-18 * * *") // 10분마다 + // 10분마다 운영 여부 확인 + @EventListener(ApplicationReadyEvent.class) // 테스트용 + //@Scheduled(cron = "0 */10 9-18 * * *") public void updateOperatingDuringPeakHour() { // 배포 서버에서만 실행 - if(!env.equals("prod")) return; + //if(!env.equals("prod")) return; try{ log.info("운영 여부 업데이트"); - redisLockUtil.executeWithLock("lock", 1, 300, () -> { + setState(); + //redisLockUtil.executeWithLock("lock", 1, 300, () -> { operatingService.updateIsOperating(LocalTime.now(), dayOfWeek, isHoliday, isVacation, isEvenWeek); - return null; - }); + // return null; + //}); } catch (Exception e) { log.info("updateOperatingDuringPeakHour() 작업 실패: {}", e.getMessage(), e); } } + // 수업 시간 외에는 30분마다 운영 여부 확인 @Scheduled(cron = "0 0,30 0-8,19-23 * * *") // 30분마다 public void updateOperating() { @@ -126,6 +130,21 @@ private void setState() { private DayOfWeek findDayOfWeek(LocalDate date) { switch (date.getDayOfWeek()) { + case MONDAY -> { + return DayOfWeek.MONDAY; + } + case TUESDAY -> { + return DayOfWeek.TUESDAY; + } + case WEDNESDAY -> { + return DayOfWeek.WEDNESDAY; + } + case THURSDAY -> { + return DayOfWeek.THURSDAY; + } + case FRIDAY -> { + return DayOfWeek.FRIDAY; + } case SATURDAY -> { return DayOfWeek.SATURDAY; } diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/service/HolidayService.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/service/HolidayService.java index 70aae4d2..d7dac623 100644 --- a/src/main/java/devkor/com/teamcback/domain/operatingtime/service/HolidayService.java +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/service/HolidayService.java @@ -1,6 +1,6 @@ package devkor.com.teamcback.domain.operatingtime.service; -import devkor.com.teamcback.domain.operatingtime.dto.HolidayResDto; +import devkor.com.teamcback.domain.operatingtime.dto.response.HolidayResDto; import devkor.com.teamcback.domain.operatingtime.entity.Holiday; import devkor.com.teamcback.domain.operatingtime.repositoy.HolidayRepository; import java.net.URI; diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/service/OperatingService.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/service/OperatingService.java index 5dd84d0e..672f9e37 100644 --- a/src/main/java/devkor/com/teamcback/domain/operatingtime/service/OperatingService.java +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/service/OperatingService.java @@ -19,6 +19,7 @@ import java.time.format.DateTimeFormatter; import java.util.Comparator; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.regex.Pattern; import lombok.RequiredArgsConstructor; @@ -77,9 +78,12 @@ public void updateOperatingTime(DayOfWeek dayOfWeek, boolean isHoliday, boolean @Transactional public void updateIsOperating(LocalTime now, DayOfWeek dayOfWeek, boolean isHoliday, boolean isVacation, boolean isEvenWeek) { List buildings = buildingRepository.findAll(); - List placesWithCondition = operatingConditionRepository.findAll().stream().map(OperatingCondition::getPlace).distinct().toList(); + List placesWithCondition = findOperatingConditionList(dayOfWeek, isHoliday, isVacation, isEvenWeek).stream().map(OperatingCondition::getPlace).distinct().toList(); for(Building building : buildings) { + // 건물에 해당하는 장소 목록 + List places = placeRepository.findAllByBuilding(building); + // 상시 개방이 아닌 건물만 확인 if(!alwaysOpenBuildings.contains(building.getId())) { boolean isOperating = checkBuildingIsOperating(building, now, dayOfWeek); @@ -91,10 +95,32 @@ public void updateIsOperating(LocalTime now, DayOfWeek dayOfWeek, boolean isHoli changeNodeIsOperating(isOperating, building); // 건물 운영 여부 변경 building.setOperating(isOperating); - // 건물에 속하면서 장소만의 운영 시간이 없는 경우 운영 여부 동기화 - List places = placeRepository.findAllByBuilding(building); + // 건물 내 장소 운영 여부 변경 for(Place place : places) { - if(!placesWithCondition.contains(place)) place.setOperating(isOperating); + // 장소만의 시간이 없는 경우 건물과 동기화 + if(dayOfWeek.getOperatingTime(place) == null && !placesWithCondition.contains(place)) { + place.setOperating(isOperating); + } + } + } + } + + // 건물에 해당하는 장소에 운영 시간이 있는 경우 운영 여부 설정 + for(Place place : places) { + String todayOperatingTime = dayOfWeek.getOperatingTime(place); + + // 장소에 운영시간이 존재 + if(todayOperatingTime != null) { + // 설정된 오늘 시간과 일치하지 않으면(예: 휴무이면 다음 운영 시간으로 설정됨) 운영 x + if(!isTimeRangePattern(todayOperatingTime)) { + place.setOperating(true); + } + else if(!Objects.equals(place.getOperatingTime(), todayOperatingTime)) { + place.setOperating(false); + } + // 일치하면 운영 여부 확인 + else { + place.setOperating(isInOperatingTime(now, todayOperatingTime)); } } } @@ -144,15 +170,37 @@ private String findOtherBuildingOperatingTime(Building building, DayOfWeek dayOf private String findPlaceOperatingTime(Place place, DayOfWeek dayOfWeek) { String operatingTime = dayOfWeek.getOperatingTime(place); - // 장소의 운영 시간이 시간 형식이 아니면 건물의 운영 시간을 따라감 + // 장소의 운영 시간이 시간 형식이 아니면 if(!isTimeRangePattern(operatingTime)) { - if(place.getBuilding() == null) return DEFAULT_OPERATING_TIME; - operatingTime = place.getBuilding().getOperatingTime(); + // 장소의 다음 운영 시간 + operatingTime = findOtherPlaceOperatingTime(place, dayOfWeek); + if(operatingTime == null) { + // 건물이 있으면 건물 운영 시간으로 + if(place.getBuilding() != null) operatingTime = place.getBuilding().getOperatingTime(); + // 없으면 기본 시간 + else operatingTime = DEFAULT_OPERATING_TIME; + } } return operatingTime; } + /** + * 시간 형식인 다음 운영 시간을 찾음 + */ + private String findOtherPlaceOperatingTime(Place place, DayOfWeek dayOfWeek) { + DayOfWeek nextDay = dayOfWeek.findNext(); + String operatingTime = nextDay.getOperatingTime(place); + + while(dayOfWeek != nextDay) { + if(isTimeRangePattern(operatingTime)) return operatingTime; + nextDay = nextDay.findNext(); + operatingTime = nextDay.getOperatingTime(place); + } + + return null; + } + /** * 운영 조건에 해당하는 장소의 운영 시간 찾기 */ @@ -266,6 +314,8 @@ private OperatingCondition findOperatingConditionOfPlace(DayOfWeek dayOfWeek, bo * 오늘에 맞는 운영 조건 목록 찾기 */ private List findOperatingConditionList(DayOfWeek dayOfWeek, boolean isHoliday, boolean isVacation, boolean isEvenWeek) { + if(dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY) dayOfWeek = DayOfWeek.WEEKDAY; + List operatingConditionList = operatingConditionRepository.findByDayOfWeekAndIsHolidayAndIsVacationOrNot(dayOfWeek, isHoliday, isVacation); if(dayOfWeek == DayOfWeek.SATURDAY) { // 토요일인 경우 diff --git a/src/main/java/devkor/com/teamcback/domain/place/entity/Place.java b/src/main/java/devkor/com/teamcback/domain/place/entity/Place.java index f54656f1..f6c7f57a 100644 --- a/src/main/java/devkor/com/teamcback/domain/place/entity/Place.java +++ b/src/main/java/devkor/com/teamcback/domain/place/entity/Place.java @@ -52,6 +52,21 @@ public class Place extends BaseEntity { @Setter private String weekdayOperatingTime; + @Setter + private String mondayOperatingTime; + + @Setter + private String tuesdayOperatingTime; + + @Setter + private String wednesdayOperatingTime; + + @Setter + private String thursdayOperatingTime; + + @Setter + private String fridayOperatingTime; + @Setter private String saturdayOperatingTime; From 09281d9ed456730fcaa6b98decc9b533efe182f1 Mon Sep 17 00:00:00 2001 From: yejin Date: Mon, 9 Feb 2026 15:24:49 +0900 Subject: [PATCH 2/3] =?UTF-8?q?Feat:=20=EC=9E=A5=EC=86=8C=20=EC=9A=B4?= =?UTF-8?q?=EC=98=81=EC=8B=9C=EA=B0=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AdminOperatingTimeController.java | 49 +++++++++++++++++++ .../request/SavePlaceOperatingTimeReq.java | 27 ++++++++++ .../response/GetPlaceOperatingTimeRes.java | 34 +++++++++++++ .../response/SavePlaceOperatingTimeRes.java | 9 ++++ .../scheduler/OperatingScheduler.java | 15 +++--- .../service/AdminOperatingTimeService.java | 42 ++++++++++++++++ .../teamcback/domain/place/entity/Place.java | 12 +++++ 7 files changed, 180 insertions(+), 8 deletions(-) create mode 100644 src/main/java/devkor/com/teamcback/domain/operatingtime/controller/AdminOperatingTimeController.java create mode 100644 src/main/java/devkor/com/teamcback/domain/operatingtime/dto/request/SavePlaceOperatingTimeReq.java create mode 100644 src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/GetPlaceOperatingTimeRes.java create mode 100644 src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/SavePlaceOperatingTimeRes.java create mode 100644 src/main/java/devkor/com/teamcback/domain/operatingtime/service/AdminOperatingTimeService.java diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/controller/AdminOperatingTimeController.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/controller/AdminOperatingTimeController.java new file mode 100644 index 00000000..880e8b6b --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/controller/AdminOperatingTimeController.java @@ -0,0 +1,49 @@ +package devkor.com.teamcback.domain.operatingtime.controller; + +import devkor.com.teamcback.domain.operatingtime.dto.request.SavePlaceOperatingTimeReq; +import devkor.com.teamcback.domain.operatingtime.dto.response.GetPlaceOperatingTimeRes; +import devkor.com.teamcback.domain.operatingtime.dto.response.SavePlaceOperatingTimeRes; +import devkor.com.teamcback.domain.operatingtime.service.AdminOperatingTimeService; +import devkor.com.teamcback.global.response.CommonResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/admin/operating-time") +public class AdminOperatingTimeController { + private final AdminOperatingTimeService adminOperatingTimeService; + + @GetMapping("/places/{placeId}") + @Operation(summary = "장소의 운영시간 검색", + description = "장소 id로 운영시간 검색") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."), + @ApiResponse(responseCode = "404", description = "장소를 찾을 수 없습니다.", + content = @Content(schema = @Schema(implementation = CommonResponse.class))), + }) + public CommonResponse getPlaceOperatingTime( + @Parameter(name = "placeId", description = "장소 ID") @PathVariable Long placeId) { + return CommonResponse.success(adminOperatingTimeService.getPlaceOperatingTime(placeId)); + } + + @PutMapping("/places/{placeId}") + @Operation(summary = "장소의 운영시간 저장", + description = "장소가 각 요일별로 고정된 운영시간을 가지는 경우(00:00-00:00 형식 포함)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."), + @ApiResponse(responseCode = "404", description = "장소를 찾을 수 없습니다.", + content = @Content(schema = @Schema(implementation = CommonResponse.class))), + }) + public CommonResponse savePlaceOperatingTime( + @Parameter(name = "placeId", description = "장소 ID") @PathVariable Long placeId, + @Parameter(description = "요일별 운영 시간") @RequestBody SavePlaceOperatingTimeReq req) { + return CommonResponse.success(adminOperatingTimeService.savePlaceOperatingTime(placeId, req)); + } +} diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/request/SavePlaceOperatingTimeReq.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/request/SavePlaceOperatingTimeReq.java new file mode 100644 index 00000000..2da7ef19 --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/request/SavePlaceOperatingTimeReq.java @@ -0,0 +1,27 @@ +package devkor.com.teamcback.domain.operatingtime.dto.request; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; + +@Schema(description = "저장할 운영시간 정보") +@Getter +@Setter +public class SavePlaceOperatingTimeReq { + @Schema(description = "평일 운영시간", example = "09:00-21:00") + private String weekDayOperatingTime; + @Schema(description = "월요일 운영시간(평일 모두 같다면 없어도 됨)", example = "09:00-21:00") + private String mondayOperatingTime; + @Schema(description = "화요일 운영시간(평일 모두 같다면 없어도 됨)", example = "09:00-21:00") + private String tuesdayOperatingTime; + @Schema(description = "수요일 운영시간(평일 모두 같다면 없어도 됨)", example = "09:00-21:00") + private String wednesdayOperatingTime; + @Schema(description = "목요일 운영시간(평일 모두 같다면 없어도 됨)", example = "09:00-21:00") + private String thursdayOperatingTime; + @Schema(description = "금요일 운영시간(평일 모두 같다면 없어도 됨)", example = "09:00-21:00") + private String fridayOperatingTime; + @Schema(description = "토요일 운영시간", example = "09:00-20:00") + private String saturdayOperatingTime; + @Schema(description = "일요일 운영시간", example = "휴무") + private String sundayOperatingTime; +} diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/GetPlaceOperatingTimeRes.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/GetPlaceOperatingTimeRes.java new file mode 100644 index 00000000..c218ddb0 --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/GetPlaceOperatingTimeRes.java @@ -0,0 +1,34 @@ +package devkor.com.teamcback.domain.operatingtime.dto.response; + +import devkor.com.teamcback.domain.place.entity.Place; +import lombok.Getter; + +@Getter +public class GetPlaceOperatingTimeRes { + private Long placeId; + private String placeName; + private String todayOperatingTime; + private String weekDayOperatingTime; + private String mondayOperatingTime; + private String tuesdayOperatingTime; + private String wednesdayOperatingTime; + private String thursdayOperatingTime; + private String fridayOperatingTime; + private String saturdayOperatingTime; + private String sundayOperatingTime; + + + public GetPlaceOperatingTimeRes(Place place) { + this.placeId = place.getId(); + this.placeName = place.getName(); + this.todayOperatingTime = place.getOperatingTime(); + this.weekDayOperatingTime = place.getWeekdayOperatingTime(); + this.mondayOperatingTime = place.getMondayOperatingTime(); + this.tuesdayOperatingTime = place.getTuesdayOperatingTime(); + this.wednesdayOperatingTime = place.getWednesdayOperatingTime(); + this.thursdayOperatingTime = place.getThursdayOperatingTime(); + this.fridayOperatingTime = place.getFridayOperatingTime(); + this.saturdayOperatingTime = place.getSaturdayOperatingTime(); + this.sundayOperatingTime = place.getSundayOperatingTime(); + } +} diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/SavePlaceOperatingTimeRes.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/SavePlaceOperatingTimeRes.java new file mode 100644 index 00000000..2ffc8dd3 --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/SavePlaceOperatingTimeRes.java @@ -0,0 +1,9 @@ +package devkor.com.teamcback.domain.operatingtime.dto.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "장소 운영시간 저장 완료") +@JsonIgnoreProperties +public class SavePlaceOperatingTimeRes { +} diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/OperatingScheduler.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/OperatingScheduler.java index d11e2541..9b545e0f 100644 --- a/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/OperatingScheduler.java +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/scheduler/OperatingScheduler.java @@ -36,7 +36,7 @@ public class OperatingScheduler { // 매일 자정마다 요일 등 조건에 맞는 운영 시간 지정해 저장 @Scheduled(cron = "0 0 0 * * *") - //@EventListener(ApplicationReadyEvent.class) + @EventListener(ApplicationReadyEvent.class) public void updateOperatingTime() { // 배포 서버에서만 실행 @@ -57,20 +57,19 @@ public void updateOperatingTime() { // 10분마다 운영 여부 확인 - @EventListener(ApplicationReadyEvent.class) // 테스트용 - //@Scheduled(cron = "0 */10 9-18 * * *") + //@EventListener(ApplicationReadyEvent.class) // 테스트용 + @Scheduled(cron = "0 */10 9-18 * * *") public void updateOperatingDuringPeakHour() { // 배포 서버에서만 실행 - //if(!env.equals("prod")) return; + if(!env.equals("prod")) return; try{ log.info("운영 여부 업데이트"); - setState(); - //redisLockUtil.executeWithLock("lock", 1, 300, () -> { + redisLockUtil.executeWithLock("lock", 1, 300, () -> { operatingService.updateIsOperating(LocalTime.now(), dayOfWeek, isHoliday, isVacation, isEvenWeek); - // return null; - //}); + return null; + }); } catch (Exception e) { log.info("updateOperatingDuringPeakHour() 작업 실패: {}", e.getMessage(), e); } diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/service/AdminOperatingTimeService.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/service/AdminOperatingTimeService.java new file mode 100644 index 00000000..c468a135 --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/service/AdminOperatingTimeService.java @@ -0,0 +1,42 @@ +package devkor.com.teamcback.domain.operatingtime.service; + +import devkor.com.teamcback.domain.operatingtime.dto.request.SavePlaceOperatingTimeReq; +import devkor.com.teamcback.domain.operatingtime.dto.response.GetPlaceOperatingTimeRes; +import devkor.com.teamcback.domain.operatingtime.dto.response.SavePlaceOperatingTimeRes; +import devkor.com.teamcback.domain.operatingtime.repositoy.OperatingTimeRepository; +import devkor.com.teamcback.domain.place.entity.Place; +import devkor.com.teamcback.domain.place.repository.PlaceRepository; +import devkor.com.teamcback.global.exception.exception.GlobalException; +import devkor.com.teamcback.global.response.ResultCode; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class AdminOperatingTimeService { + private final OperatingTimeRepository operatingTimeRepository; + private final PlaceRepository placeRepository; + + @Transactional(readOnly = true) + public GetPlaceOperatingTimeRes getPlaceOperatingTime(Long placeId) { + Place place = findPlace(placeId); + + return new GetPlaceOperatingTimeRes(place); + } + + @Transactional + public SavePlaceOperatingTimeRes savePlaceOperatingTime(Long placeId, SavePlaceOperatingTimeReq req) { + Place place = findPlace(placeId); + + // 운영시간 수정 + place.updateOperatingTime(req); + + return new SavePlaceOperatingTimeRes(); + } + + private Place findPlace(Long placeId) { + return placeRepository.findById(placeId).orElseThrow(() -> new GlobalException(ResultCode.NOT_FOUND_PLACE)); + } + +} diff --git a/src/main/java/devkor/com/teamcback/domain/place/entity/Place.java b/src/main/java/devkor/com/teamcback/domain/place/entity/Place.java index f6c7f57a..8de937a6 100644 --- a/src/main/java/devkor/com/teamcback/domain/place/entity/Place.java +++ b/src/main/java/devkor/com/teamcback/domain/place/entity/Place.java @@ -1,5 +1,6 @@ package devkor.com.teamcback.domain.place.entity; +import devkor.com.teamcback.domain.operatingtime.dto.request.SavePlaceOperatingTimeReq; import devkor.com.teamcback.domain.place.dto.request.CreatePlaceReq; import devkor.com.teamcback.domain.place.dto.request.ModifyPlaceReq; import devkor.com.teamcback.domain.building.entity.Building; @@ -136,4 +137,15 @@ public void update(ModifyPlaceReq req, Building building, Node node) { this.node = node; this.description = req.getDescription(); } + + public void updateOperatingTime(SavePlaceOperatingTimeReq req) { + this.setWeekdayOperatingTime(req.getWeekDayOperatingTime()); + this.setMondayOperatingTime(req.getMondayOperatingTime()); + this.setTuesdayOperatingTime(req.getTuesdayOperatingTime()); + this.setWednesdayOperatingTime(req.getWednesdayOperatingTime()); + this.setThursdayOperatingTime(req.getThursdayOperatingTime()); + this.setFridayOperatingTime(req.getFridayOperatingTime()); + this.setSaturdayOperatingTime(req.getSaturdayOperatingTime()); + this.setSundayOperatingTime(req.getSundayOperatingTime()); + } } From 7a71c1a7014dee832b84b9e3e0a04a1103b7362a Mon Sep 17 00:00:00 2001 From: yejin Date: Mon, 9 Feb 2026 16:08:23 +0900 Subject: [PATCH 3/3] =?UTF-8?q?Feat:=20=EC=9E=A5=EC=86=8C=EC=9D=98=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=EB=B3=84=20=EC=9A=B4=EC=98=81=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../AdminOperatingTimeController.java | 17 ++++++ .../SavePlaceOperatingTimeConditionReq.java | 26 +++++++++ ...avePlaceOperatingTimeConditionTimeReq.java | 23 ++++++++ .../SavePlaceOperatingTimeConditionRes.java | 9 +++ .../entity/OperatingCondition.java | 8 +++ .../operatingtime/entity/OperatingTime.java | 7 +++ .../OperatingConditionRepository.java | 2 +- .../repositoy/OperatingTimeRepository.java | 2 + .../service/AdminOperatingTimeService.java | 56 +++++++++++++++++++ 9 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 src/main/java/devkor/com/teamcback/domain/operatingtime/dto/request/SavePlaceOperatingTimeConditionReq.java create mode 100644 src/main/java/devkor/com/teamcback/domain/operatingtime/dto/request/SavePlaceOperatingTimeConditionTimeReq.java create mode 100644 src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/SavePlaceOperatingTimeConditionRes.java diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/controller/AdminOperatingTimeController.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/controller/AdminOperatingTimeController.java index 880e8b6b..a227daee 100644 --- a/src/main/java/devkor/com/teamcback/domain/operatingtime/controller/AdminOperatingTimeController.java +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/controller/AdminOperatingTimeController.java @@ -1,7 +1,9 @@ package devkor.com.teamcback.domain.operatingtime.controller; +import devkor.com.teamcback.domain.operatingtime.dto.request.SavePlaceOperatingTimeConditionReq; import devkor.com.teamcback.domain.operatingtime.dto.request.SavePlaceOperatingTimeReq; import devkor.com.teamcback.domain.operatingtime.dto.response.GetPlaceOperatingTimeRes; +import devkor.com.teamcback.domain.operatingtime.dto.response.SavePlaceOperatingTimeConditionRes; import devkor.com.teamcback.domain.operatingtime.dto.response.SavePlaceOperatingTimeRes; import devkor.com.teamcback.domain.operatingtime.service.AdminOperatingTimeService; import devkor.com.teamcback.global.response.CommonResponse; @@ -11,6 +13,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -46,4 +49,18 @@ public CommonResponse savePlaceOperatingTime( @Parameter(description = "요일별 운영 시간") @RequestBody SavePlaceOperatingTimeReq req) { return CommonResponse.success(adminOperatingTimeService.savePlaceOperatingTime(placeId, req)); } + + @PostMapping("/places/{placeId}") + @Operation(summary = "장소의 조건 및 운영시간 저장(별도로 요일별 대표 운영 시간도 저장해야 함)", + description = "장소가 조건에 따라 유동적인 운영시간을 가지는 경우") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "정상 처리 되었습니다."), + @ApiResponse(responseCode = "404", description = "장소를 찾을 수 없습니다.", + content = @Content(schema = @Schema(implementation = CommonResponse.class))), + }) + public CommonResponse savePlaceOperatingTimeCondition( + @Parameter(name = "placeId", description = "장소 ID") @PathVariable Long placeId, + @Parameter(description = "조건 및 운영 시간") @RequestBody @Valid SavePlaceOperatingTimeConditionReq req) { + return CommonResponse.success(adminOperatingTimeService.savePlaceOperatingTimeCondition(placeId, req)); + } } diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/request/SavePlaceOperatingTimeConditionReq.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/request/SavePlaceOperatingTimeConditionReq.java new file mode 100644 index 00000000..122d8934 --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/request/SavePlaceOperatingTimeConditionReq.java @@ -0,0 +1,26 @@ +package devkor.com.teamcback.domain.operatingtime.dto.request; + +import devkor.com.teamcback.domain.operatingtime.entity.DayOfWeek; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Getter; +import lombok.Setter; + +import java.util.List; + +@Schema(description = "저장할 운영시간 정보(상관없는 조건은 null로 표시)") +@Getter +@Setter +public class SavePlaceOperatingTimeConditionReq { + @Schema(description = "요일", example = "WEEKDAY") + private DayOfWeek dayOfWeek; + @Schema(description = "짝수주 여부", example = "null") + private Boolean isEvenWeek; + @Schema(description = "공휴일 여부", example = "false") + private Boolean isHoliday; + @Schema(description = "방학 여부", example = "null") + private Boolean isVacation; + @Schema(description = "조건에 해당하는 운영시간 목록") + @NotEmpty(message = "최소 1개의 운영시간이 있어야 합니다.") + private List timeList; +} diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/request/SavePlaceOperatingTimeConditionTimeReq.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/request/SavePlaceOperatingTimeConditionTimeReq.java new file mode 100644 index 00000000..99b8450b --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/request/SavePlaceOperatingTimeConditionTimeReq.java @@ -0,0 +1,23 @@ +package devkor.com.teamcback.domain.operatingtime.dto.request; + +import jakarta.validation.constraints.Max; +import jakarta.validation.constraints.Min; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class SavePlaceOperatingTimeConditionTimeReq { + @Min(value = 0) + @Max(value = 23) + private int StartHour; + @Min(value = 0) + @Max(value = 59) + private int StartMinute; + @Min(value = 0) + @Max(value = 23) + private int endHour; + @Min(value = 0) + @Max(value = 59) + private int endMinute; +} diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/SavePlaceOperatingTimeConditionRes.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/SavePlaceOperatingTimeConditionRes.java new file mode 100644 index 00000000..6dd95d2d --- /dev/null +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/dto/response/SavePlaceOperatingTimeConditionRes.java @@ -0,0 +1,9 @@ +package devkor.com.teamcback.domain.operatingtime.dto.response; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema(description = "장소 운영시간 저장 완료") +@JsonIgnoreProperties +public class SavePlaceOperatingTimeConditionRes { +} diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/OperatingCondition.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/OperatingCondition.java index 57afb1e6..01705217 100644 --- a/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/OperatingCondition.java +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/OperatingCondition.java @@ -1,6 +1,7 @@ package devkor.com.teamcback.domain.operatingtime.entity; import devkor.com.teamcback.domain.common.entity.BaseEntity; +import devkor.com.teamcback.domain.operatingtime.dto.request.SavePlaceOperatingTimeConditionReq; import devkor.com.teamcback.domain.place.entity.Place; import jakarta.persistence.*; import lombok.Getter; @@ -31,4 +32,11 @@ public class OperatingCondition extends BaseEntity { @ManyToOne @JoinColumn(name = "place_id") private Place place; + + public OperatingCondition(SavePlaceOperatingTimeConditionReq req) { + this.dayOfWeek = req.getDayOfWeek(); + this.isEvenWeek = req.getIsEvenWeek(); + this.isHoliday = req.getIsHoliday(); + this.isVacation = req.getIsVacation(); + } } diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/OperatingTime.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/OperatingTime.java index 15bdc87a..cb3b01e7 100644 --- a/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/OperatingTime.java +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/entity/OperatingTime.java @@ -1,6 +1,7 @@ package devkor.com.teamcback.domain.operatingtime.entity; import devkor.com.teamcback.domain.building.entity.Building; +import devkor.com.teamcback.domain.operatingtime.dto.request.SavePlaceOperatingTimeConditionTimeReq; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -31,4 +32,10 @@ public class OperatingTime { @ManyToOne @JoinColumn(name = "operating_condition_id") private OperatingCondition operatingCondition; + + public OperatingTime(OperatingCondition operatingCondition, LocalTime startTime, LocalTime endTime) { + this.operatingCondition = operatingCondition; + this.startTime = startTime; + this.endTime = endTime; + } } diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/repositoy/OperatingConditionRepository.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/repositoy/OperatingConditionRepository.java index 591889c7..9d47d52c 100644 --- a/src/main/java/devkor/com/teamcback/domain/operatingtime/repositoy/OperatingConditionRepository.java +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/repositoy/OperatingConditionRepository.java @@ -15,5 +15,5 @@ public interface OperatingConditionRepository extends JpaRepository { List findAllByOperatingCondition(OperatingCondition operatingCondition); + + void deleteAllByOperatingCondition(OperatingCondition operatingCondition); } diff --git a/src/main/java/devkor/com/teamcback/domain/operatingtime/service/AdminOperatingTimeService.java b/src/main/java/devkor/com/teamcback/domain/operatingtime/service/AdminOperatingTimeService.java index c468a135..9f7ff439 100644 --- a/src/main/java/devkor/com/teamcback/domain/operatingtime/service/AdminOperatingTimeService.java +++ b/src/main/java/devkor/com/teamcback/domain/operatingtime/service/AdminOperatingTimeService.java @@ -1,8 +1,15 @@ package devkor.com.teamcback.domain.operatingtime.service; +import devkor.com.teamcback.domain.operatingtime.dto.request.SavePlaceOperatingTimeConditionReq; +import devkor.com.teamcback.domain.operatingtime.dto.request.SavePlaceOperatingTimeConditionTimeReq; import devkor.com.teamcback.domain.operatingtime.dto.request.SavePlaceOperatingTimeReq; import devkor.com.teamcback.domain.operatingtime.dto.response.GetPlaceOperatingTimeRes; +import devkor.com.teamcback.domain.operatingtime.dto.response.SavePlaceOperatingTimeConditionRes; import devkor.com.teamcback.domain.operatingtime.dto.response.SavePlaceOperatingTimeRes; +import devkor.com.teamcback.domain.operatingtime.entity.DayOfWeek; +import devkor.com.teamcback.domain.operatingtime.entity.OperatingCondition; +import devkor.com.teamcback.domain.operatingtime.entity.OperatingTime; +import devkor.com.teamcback.domain.operatingtime.repositoy.OperatingConditionRepository; import devkor.com.teamcback.domain.operatingtime.repositoy.OperatingTimeRepository; import devkor.com.teamcback.domain.place.entity.Place; import devkor.com.teamcback.domain.place.repository.PlaceRepository; @@ -12,9 +19,13 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalTime; +import java.util.List; + @Service @RequiredArgsConstructor public class AdminOperatingTimeService { + private final OperatingConditionRepository operatingConditionRepository; private final OperatingTimeRepository operatingTimeRepository; private final PlaceRepository placeRepository; @@ -35,8 +46,53 @@ public SavePlaceOperatingTimeRes savePlaceOperatingTime(Long placeId, SavePlaceO return new SavePlaceOperatingTimeRes(); } + @Transactional + public SavePlaceOperatingTimeConditionRes savePlaceOperatingTimeCondition(Long placeId, SavePlaceOperatingTimeConditionReq req) { + Place place = findPlace(placeId); + + // 운영조건 찾기 + OperatingCondition operatingCondition = findOperatingConditionOfPlace(req.getDayOfWeek(), req.getIsHoliday(), req.getIsVacation(), req.getIsEvenWeek(), place); + + // 기존 운영조건이 존재하면 해당하는 운영시간 모두 삭제 + if(operatingCondition != null) { + operatingTimeRepository.deleteAllByOperatingCondition(operatingCondition); + } + // 존재하지 않으면 새로 생성 + else { + operatingCondition = operatingConditionRepository.save(new OperatingCondition(req)); + } + + // 운영시간 저장 + for(SavePlaceOperatingTimeConditionTimeReq timeReq : req.getTimeList()) { + LocalTime startTime = LocalTime.of(timeReq.getStartHour(), timeReq.getStartMinute()); + LocalTime endTime = LocalTime.of(timeReq.getEndHour(), timeReq.getEndMinute()); + + operatingTimeRepository.save(new OperatingTime(operatingCondition, startTime, endTime)); + } + + return new SavePlaceOperatingTimeConditionRes(); + } + + /** + * 장소 id에 해당하는 장소 찾기 + */ private Place findPlace(Long placeId) { return placeRepository.findById(placeId).orElseThrow(() -> new GlobalException(ResultCode.NOT_FOUND_PLACE)); } + /** + * 해당 장소와 조건에 맞는 운영 조건 찾기 + */ + private OperatingCondition findOperatingConditionOfPlace(DayOfWeek dayOfWeek, Boolean isHoliday, Boolean isVacation, Boolean isEvenWeek, Place place) { + OperatingCondition operatingCondition = operatingConditionRepository.findByDayOfWeekAndIsHolidayAndIsVacationAndPlace(dayOfWeek, isHoliday, isVacation, place); + + if(operatingCondition == null) return null; + + if(dayOfWeek == DayOfWeek.SATURDAY) { // 토요일인 경우 + if(operatingCondition.getIsEvenWeek() == null || operatingCondition.getIsEvenWeek() == isEvenWeek) return operatingCondition; + else return null; + } + + return operatingCondition; + } }