From d754ae86d99fc6941c6aa3f6f15d50b43e1bb7aa Mon Sep 17 00:00:00 2001 From: cccyyy333 Date: Thu, 19 Feb 2026 11:05:21 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=90=9B=20Fix=20:=20block=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=8B=9C=20todo=EC=97=90=20=EC=8B=9C=EC=9E=91?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/umc/timeto/block/service/BlockServiceImpl.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java b/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java index 35d5bc1..57d7188 100644 --- a/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java +++ b/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java @@ -47,6 +47,10 @@ public BlockResponseDTO createBlock(Long todoId, BlockAddDTO req, Long memberId) .plusSeconds(duration.getSecond()); System.out.println("시작시간" +startAt + "끝나는시간" +endAt); + //생성 시 시작시간 추가되도록 + todo.updateStartAt(startAt); + + List overlaps = blockRepository .findByTodo_Folder_Goal_Member_MemberIdAndStartAtLessThanAndEndAtGreaterThan( From f9cc90251c7bb4f7381aacaafb674eaeceda7c91 Mon Sep 17 00:00:00 2001 From: cccyyy333 Date: Thu, 19 Feb 2026 17:52:45 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=F0=9F=90=9B=20Fix=20:=20=EB=82=B4=20?= =?UTF-8?q?=EB=AA=A9=ED=91=9C=EC=99=80=20=EB=8F=99=EA=B8=B0=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/repository/BlockRepository.java | 17 ++++++++++++++++ .../block/service/BlockServiceImpl.java | 20 +++++++++++++------ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/umc/timeto/block/repository/BlockRepository.java b/src/main/java/com/umc/timeto/block/repository/BlockRepository.java index b5e3fa9..89942db 100644 --- a/src/main/java/com/umc/timeto/block/repository/BlockRepository.java +++ b/src/main/java/com/umc/timeto/block/repository/BlockRepository.java @@ -2,6 +2,7 @@ import com.umc.timeto.block.entity.Block; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import java.time.LocalDateTime; import java.util.List; @@ -37,4 +38,20 @@ List findByTodo_Folder_Goal_Member_MemberIdAndBlockIdNotAndStartAtLessTha ); Optional findByBlockIdAndTodo_Folder_Goal_Member_MemberId(Long blockId, Long memberId); + + @Query(""" + select b + from Block b + join fetch b.todo t + join fetch t.folder f + join fetch f.goal g + where g.member.memberId = :memberId + and b.startAt between :start and :end +""") + List findBlocksWithTodo( + Long memberId, + LocalDateTime start, + LocalDateTime end + ); + } diff --git a/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java b/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java index 57d7188..906b1e3 100644 --- a/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java +++ b/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java @@ -61,6 +61,7 @@ public BlockResponseDTO createBlock(Long todoId, BlockAddDTO req, Long memberId) if (!overlaps.isEmpty()) { throw new GlobalException(ErrorCode.BLOCK_TIME_CONFLICT); + // 겹칠 시 가장 당일에 생성된 블록 중 가장 하단의 블록 밑에 자동 배치 } @@ -83,13 +84,20 @@ public List getBlockByDay(LocalDate date, Long memberId) LocalDateTime start = date.atStartOfDay(); LocalDateTime end = date.atTime(23, 59, 59); +// List blocks = +// blockRepository +// .findByTodo_Folder_Goal_Member_MemberIdAndStartAtBetween( +// memberId, +// start, +// end +// ); + List blocks = - blockRepository - .findByTodo_Folder_Goal_Member_MemberIdAndStartAtBetween( - memberId, - start, - end - ); + blockRepository.findBlocksWithTodo( + memberId, + start, + end + ); return blocks.stream() From f9eb7076f233b5d8218d1475b608c6396a4b136d Mon Sep 17 00:00:00 2001 From: cccyyy333 Date: Thu, 19 Feb 2026 18:32:42 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=F0=9F=90=9B=20Fix=20:=20=ED=95=98=EB=A3=A8?= =?UTF-8?q?=EB=A5=BC=205=EC=8B=9C=20=EA=B8=B0=EC=A4=80=EC=9C=BC=EB=A1=9C?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/service/BlockServiceImpl.java | 9 ++++-- .../block/service/BusinessDayPolicy.java | 28 +++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/umc/timeto/block/service/BusinessDayPolicy.java diff --git a/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java b/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java index 906b1e3..46fff94 100644 --- a/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java +++ b/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java @@ -29,6 +29,7 @@ public class BlockServiceImpl implements BlockService { private final TodoRepository todoRepository; private final BlockRepository blockRepository; + private final BusinessDayPolicy businessDayPolicy; @Override public BlockResponseDTO createBlock(Long todoId, BlockAddDTO req, Long memberId) { @@ -45,7 +46,7 @@ public BlockResponseDTO createBlock(Long todoId, BlockAddDTO req, Long memberId) .plusHours(duration.getHour()) .plusMinutes(duration.getMinute()) .plusSeconds(duration.getSecond()); - System.out.println("시작시간" +startAt + "끝나는시간" +endAt); + //생성 시 시작시간 추가되도록 todo.updateStartAt(startAt); @@ -81,8 +82,10 @@ public BlockResponseDTO createBlock(Long todoId, BlockAddDTO req, Long memberId) @Override public List getBlockByDay(LocalDate date, Long memberId) { - LocalDateTime start = date.atStartOfDay(); - LocalDateTime end = date.atTime(23, 59, 59); + LocalDateTime time_standard = date.atTime(5, 0); + + LocalDateTime start = businessDayPolicy.startOfBusinessDay(time_standard); + LocalDateTime end = businessDayPolicy.endOfBusinessDay(time_standard); // List blocks = // blockRepository diff --git a/src/main/java/com/umc/timeto/block/service/BusinessDayPolicy.java b/src/main/java/com/umc/timeto/block/service/BusinessDayPolicy.java new file mode 100644 index 0000000..386fa97 --- /dev/null +++ b/src/main/java/com/umc/timeto/block/service/BusinessDayPolicy.java @@ -0,0 +1,28 @@ +package com.umc.timeto.block.service; + +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; + +@Component +public class BusinessDayPolicy { + + private static final LocalTime DAY_START = LocalTime.of(5, 0); + + public LocalDateTime startOfBusinessDay(LocalDateTime base) { + LocalDate date = base.toLocalDate(); + + if (base.toLocalTime().isBefore(DAY_START)) { + date = date.minusDays(1); + } + + return date.atTime(DAY_START); + } + + public LocalDateTime endOfBusinessDay(LocalDateTime base) { + return startOfBusinessDay(base).plusDays(1); + } +} + From 1e3d181ae749c8f341d791ce0359f10200c3e405 Mon Sep 17 00:00:00 2001 From: cccyyy333 Date: Thu, 19 Feb 2026 20:08:12 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=90=9B=20Fix=20:=201.=20=EB=B8=94?= =?UTF-8?q?=EB=A1=9D=20=EC=86=8C=EC=9A=94=EC=8B=9C=EA=B0=84=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20api=20=EC=82=AD=EC=A0=9C=20->=20=ED=95=A0=EC=9D=BC?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=ED=9B=84=20=EB=B3=80=EA=B2=BD=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=202.=20block=20create=20=EB=B0=A9=EC=8B=9D?= =?UTF-8?q?=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../block/controller/BlockController.java | 38 ++++++------ .../block/controller/BlockControllerDocs.java | 58 ++++++++++--------- .../block/service/BlockServiceImpl.java | 58 +++++++++++-------- .../java/com/umc/timeto/todo/domain/Todo.java | 2 + 4 files changed, 85 insertions(+), 71 deletions(-) diff --git a/src/main/java/com/umc/timeto/block/controller/BlockController.java b/src/main/java/com/umc/timeto/block/controller/BlockController.java index b05a22b..6b6e555 100644 --- a/src/main/java/com/umc/timeto/block/controller/BlockController.java +++ b/src/main/java/com/umc/timeto/block/controller/BlockController.java @@ -15,7 +15,6 @@ import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.LocalTime; import java.time.YearMonth; import java.util.List; @@ -77,24 +76,25 @@ public ResponseEntity>> getBlockNumByMonth } - @PatchMapping("/{blockId}/duration") - @Override - public ResponseEntity> updateDuration( - @PathVariable Long blockId, - @RequestParam LocalTime duration, - Authentication authentication - ) { - - blockService.updateBlockDuration( - blockId, - getMemberId(authentication), - duration - ); - - return ResponseEntity.ok( - new ResponseDTO<>(ResponseCode.SUCCESS_UPDATE_BLOCK, null) - ); - } +// @PatchMapping("/{blockId}/duration") +// @Override +// public ResponseEntity> updateDuration( +// @PathVariable Long blockId, +// @DateTimeFormat(pattern = "HH:mm:ss") +// @RequestParam LocalTime duration, +// Authentication authentication +// ) { +// +// blockService.updateBlockDuration( +// blockId, +// getMemberId(authentication), +// duration +// ); +// +// return ResponseEntity.ok( +// new ResponseDTO<>(ResponseCode.SUCCESS_UPDATE_BLOCK, null) +// ); +// } diff --git a/src/main/java/com/umc/timeto/block/controller/BlockControllerDocs.java b/src/main/java/com/umc/timeto/block/controller/BlockControllerDocs.java index 892fcd4..66b04e0 100644 --- a/src/main/java/com/umc/timeto/block/controller/BlockControllerDocs.java +++ b/src/main/java/com/umc/timeto/block/controller/BlockControllerDocs.java @@ -17,13 +17,15 @@ import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.LocalTime; import java.time.YearMonth; import java.util.List; public interface BlockControllerDocs { @Operation(summary = "타임블럭 저장", description = "할 일 시작 시간을 받아서 블록을 저장합니다. 블록에는 일정 겹침 검사가 존재합니다." + - "(ex: 일정 1이 7:30~8:30 일떄, 일정2의 생성/이동은 8:30분 이상부터 가능)") + "(ex: 일정 1이 7:30~8:30 일떄, 일정2의 생성/이동은 8:30분 이상부터 가능)" + + "추가: startAt이 해당하는 날짜(당일 05시~다음날 05시 사이) 에 블록이 하나도 없을 경우에는 startAt의 값을 입력받아 생성됩니다." + + "하나라도 블록이 존재하면 가장 하단 블록의 endAt 시간대를 startAt으로 하여 생성됩니다. " + + "startAt의 날짜 부분에는 선택한 날짜를, 시간에는 현재 시간대나 default로 설정하고 싶은 시간을 입력해주세요. " ) @ApiResponses({ @ApiResponse( responseCode = "201", @@ -82,35 +84,35 @@ ResponseEntity>> getBlockNumByMonth( Authentication authentication ); - @Operation(summary = "블록 소요시간 변경", - description = "블록 소요시간 변경 시 사용합니다. 할 일 내부에서만 소요시간 변경이 가능하다면 사용하지 않아도 됩니다. ") - @ApiResponses({ - @ApiResponse( - responseCode = "200", - description = "블록을 성공적으로 업데이트했습니다" - ), - @ApiResponse( - responseCode = "404", - description = "해당 아이디를 가진 블록이 존재하지 않습니다.", - content = @Content(schema = @Schema(hidden = true)) - ), - @ApiResponse( - responseCode = "400", - description = "이미 해당 시간에 블록이 존재합니다." - ,content = @Content(schema = @Schema(hidden = true)) - ) - - }) - @PatchMapping("/{blockId}/duration") - ResponseEntity> updateDuration( - @PathVariable Long blockId, - @RequestParam LocalTime duration, - Authentication authentication - ); +// @Operation(summary = "블록 소요시간 변경", +// description = "블록 소요시간 변경 시 사용합니다. 할 일 내부에서만 소요시간 변경이 가능하다면 사용하지 않아도 됩니다. ") +// @ApiResponses({ +// @ApiResponse( +// responseCode = "200", +// description = "블록을 성공적으로 업데이트했습니다" +// ), +// @ApiResponse( +// responseCode = "404", +// description = "해당 아이디를 가진 블록이 존재하지 않습니다.", +// content = @Content(schema = @Schema(hidden = true)) +// ), +// @ApiResponse( +// responseCode = "400", +// description = "이미 해당 시간에 블록이 존재합니다." +// ,content = @Content(schema = @Schema(hidden = true)) +// ) +// +// }) +// @PatchMapping("/{blockId}/duration") +// ResponseEntity> updateDuration( +// @PathVariable Long blockId, +// @RequestParam LocalTime duration, +// Authentication authentication +// ); @Operation(summary = "블록 이동", description = "블록을 드래그&드롭으로 이동했을 때 정보를 갱신합니다. 변경된 시작 시간을 입력으로 받습니다. " + - "이동된 시간이 다른 일정과 겹칠 경우 갱신되지 않습니다. startAt format: yyyy-MM-dd'T'HH:mm") + "이동된 시간이 다른 일정과 겹칠 경우 갱신되지 않습니다. startAt format: yyyy-MM-dd'T'HH:mm(ex: 2026-02-14T:02:14) ") @ApiResponses({ @ApiResponse( responseCode = "200", diff --git a/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java b/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java index 46fff94..59afa09 100644 --- a/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java +++ b/src/main/java/com/umc/timeto/block/service/BlockServiceImpl.java @@ -18,6 +18,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.time.YearMonth; +import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; @@ -37,34 +38,51 @@ public BlockResponseDTO createBlock(Long todoId, BlockAddDTO req, Long memberId) Todo todo = todoRepository.findByTodoIdAndFolder_Goal_Member_MemberId(todoId,memberId) .orElseThrow(() -> new GlobalException(ErrorCode.TODO_NOT_FOUND)); - - // 블록 겹침 조회 LocalDateTime startAt = req.getStartAt(); - LocalTime duration = todo.getDuration(); - LocalDateTime endAt = startAt - .plusHours(duration.getHour()) - .plusMinutes(duration.getMinute()) - .plusSeconds(duration.getSecond()); + // Todo당 Block 1개 보장 + blockRepository.findByTodo_TodoId(todoId) + .ifPresent(b -> { + throw new GlobalException(ErrorCode.BAD_REQUEST); + }); - //생성 시 시작시간 추가되도록 - todo.updateStartAt(startAt); + // 05:00 기준 startAt에서 입력받은 날짜 범위 + LocalDateTime dayStart = businessDayPolicy.startOfBusinessDay(startAt); + LocalDateTime dayEnd = businessDayPolicy.endOfBusinessDay(startAt); - List overlaps = + + List todayBlocks = blockRepository - .findByTodo_Folder_Goal_Member_MemberIdAndStartAtLessThanAndEndAtGreaterThan( + .findByTodo_Folder_Goal_Member_MemberIdAndStartAtGreaterThanEqualAndStartAtLessThan( memberId, - endAt, - startAt + dayStart, + dayEnd ); - if (!overlaps.isEmpty()) { - throw new GlobalException(ErrorCode.BLOCK_TIME_CONFLICT); - // 겹칠 시 가장 당일에 생성된 블록 중 가장 하단의 블록 밑에 자동 배치 + + System.out.println("dayStart = " + dayStart); + System.out.println("dayEnd = " + dayEnd); + System.out.println("todayBlocks size = " + todayBlocks.size()); + + + + + if (todayBlocks.isEmpty()) { + // 오늘 블록 없으면 입력받은 시간에 생성 + startAt = req.getStartAt(); + } else { + // 가장 늦게 끝나는 블록 아래 배치 + Block lastBlock = todayBlocks.stream() + .max(Comparator.comparing(Block::getEndAt)) + .orElseThrow(); + + startAt = lastBlock.getEndAt(); } + //생성 시 시작시간 추가되도록 + todo.updateStartAt(startAt); //블록 저장 Block block = new Block(todo, startAt); @@ -87,14 +105,6 @@ public List getBlockByDay(LocalDate date, Long memberId) LocalDateTime start = businessDayPolicy.startOfBusinessDay(time_standard); LocalDateTime end = businessDayPolicy.endOfBusinessDay(time_standard); -// List blocks = -// blockRepository -// .findByTodo_Folder_Goal_Member_MemberIdAndStartAtBetween( -// memberId, -// start, -// end -// ); - List blocks = blockRepository.findBlocksWithTodo( memberId, diff --git a/src/main/java/com/umc/timeto/todo/domain/Todo.java b/src/main/java/com/umc/timeto/todo/domain/Todo.java index d7a138e..fb236b7 100644 --- a/src/main/java/com/umc/timeto/todo/domain/Todo.java +++ b/src/main/java/com/umc/timeto/todo/domain/Todo.java @@ -9,6 +9,7 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; +import org.hibernate.annotations.DynamicUpdate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -16,6 +17,7 @@ @Entity @Getter @NoArgsConstructor(access = AccessLevel.PROTECTED) +@DynamicUpdate @Table(name = "todo") public class Todo { @Id