Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.CreateWorkScheduleRequestDto;
import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.ManagerWorkScheduleInquiryRequestDto;
import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.ManagerScheduleResponseDto;
import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.ManagerTodayScheduleResponseDto;
import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.UpdateWorkScheduleRequestDto;
import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.UpdateWorkerRequestDto;
import com.dreamteam.alter.application.aop.ManagerActionContext;
Expand Down Expand Up @@ -48,6 +49,9 @@ public class ManagerScheduleController implements ManagerScheduleControllerSpec
@Resource(name = "managerRemoveWorkerFromSchedule")
private final ManagerRemoveWorkerUseCase managerRemoveWorker;

@Resource(name = "managerGetTodayWorkScheduleList")
private final ManagerGetDailyScheduleListUseCase managerGetTodayScheduleList;

@Override
@PostMapping
public ResponseEntity<CommonApiResponse<Void>> createSchedule(
Expand All @@ -68,6 +72,15 @@ public ResponseEntity<CommonApiResponse<List<ManagerScheduleResponseDto>>> getSc
return ResponseEntity.ok(CommonApiResponse.of(managerGetScheduleList.execute(actor, request.getWorkspaceId(), request.getYear(), request.getMonth())));
}

@Override
@GetMapping("/today")
public ResponseEntity<CommonApiResponse<List<ManagerTodayScheduleResponseDto>>> getTodayScheduleList(
@RequestParam Long workspaceId
) {
ManagerActor actor = ManagerActionContext.getInstance().getActor();
return ResponseEntity.ok(CommonApiResponse.of(managerGetTodayScheduleList.execute(actor, workspaceId)));
}

@Override
@PutMapping("/{workShiftId}")
public ResponseEntity<CommonApiResponse<Void>> updateSchedule(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.dreamteam.alter.adapter.inbound.common.dto.ErrorResponse;
import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.AssignWorkerRequestDto;
import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.CreateWorkScheduleRequestDto;
import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.ManagerTodayScheduleResponseDto;
import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.ManagerWorkScheduleInquiryRequestDto;
import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.ManagerScheduleResponseDto;
import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.UpdateWorkScheduleRequestDto;
Expand Down Expand Up @@ -51,6 +52,16 @@ ResponseEntity<CommonApiResponse<List<ManagerScheduleResponseDto>>> getScheduleL
ManagerWorkScheduleInquiryRequestDto request
);

@Operation(summary = "금일 스케줄 목록 조회", description = "특정 워크스페이스의 오늘 날짜 스케줄 목록을 조회합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "금일 스케줄 목록 조회 성공"),
@ApiResponse(responseCode = "404", description = "해당 워크스페이스 찾을 수 없음")
})
ResponseEntity<CommonApiResponse<List<ManagerTodayScheduleResponseDto>>> getTodayScheduleList(
@Parameter(description = "워크스페이스 ID", example = "1", required = true)
@RequestParam Long workspaceId
);
Comment thread
juny0955 marked this conversation as resolved.

@Operation(summary = "스케줄 수정", description = "기존 스케줄의 정보를 수정합니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "스케줄 수정 성공"),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.dreamteam.alter.adapter.inbound.manager.schedule.dto;

import java.util.List;
import java.util.stream.Collectors;

import com.dreamteam.alter.domain.workspace.model.WorkspaceShiftTodayResponse;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder(access = AccessLevel.PRIVATE)
@Schema(description = "매니저 금일 근무자 응답")
public class ManagerTodayScheduleResponseDto {

@Schema(description = "근무자 ID", example = "1")
private Long workerId;

@Schema(description = "근무자 이름", example = "홍길동")
private String workerName;

@Schema(description = "근무자 프로필 이미지 S3 URL")
private String profileImageUrl;

@Schema(description = "금일 근무 목록")
private List<ManagerTodayScheduleShiftItem> shifts;

public static List<ManagerTodayScheduleResponseDto> from(List<WorkspaceShiftTodayResponse> responses) {
return responses.stream()
.collect(Collectors.groupingBy(WorkspaceShiftTodayResponse::workerId))
.values()
.stream()
.map(group -> {
WorkspaceShiftTodayResponse first = group.getFirst();
return ManagerTodayScheduleResponseDto.builder()
.workerId(first.workerId())
.workerName(first.workerName())
.profileImageUrl(first.profileImageUrl())
.shifts(
group.stream()
.map(r -> ManagerTodayScheduleShiftItem.of(r.shiftId(), r.startDateTime(), r.endDateTime()))
.toList()
)
.build();
})
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.dreamteam.alter.adapter.inbound.manager.schedule.dto;

import java.time.LocalDateTime;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder(access = AccessLevel.PRIVATE)
@Schema(description = "근무 항목")
public class ManagerTodayScheduleShiftItem {

@Schema(description = "스케줄 ID", example = "1")
private Long shiftId;

@Schema(description = "근무 시작 시간", example = "2024-01-15T09:00:00")
private LocalDateTime startDateTime;

@Schema(description = "근무 종료 시간", example = "2024-01-15T18:00:00")
private LocalDateTime endDateTime;

public static ManagerTodayScheduleShiftItem of(Long shiftId, LocalDateTime startDateTime, LocalDateTime endDateTime) {
return ManagerTodayScheduleShiftItem.builder()
.shiftId(shiftId)
.startDateTime(startDateTime)
.endDateTime(endDateTime)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package com.dreamteam.alter.adapter.outbound.workspace.persistence;

import com.dreamteam.alter.domain.file.type.FileStatus;
import com.dreamteam.alter.domain.file.type.FileTargetType;
import com.dreamteam.alter.domain.workspace.entity.WorkspaceWorker;
import com.dreamteam.alter.domain.user.entity.ManagerUser;
import com.dreamteam.alter.domain.user.entity.User;
import com.dreamteam.alter.domain.workspace.entity.Workspace;
import com.dreamteam.alter.domain.workspace.entity.WorkspaceShift;
import com.dreamteam.alter.domain.workspace.model.WorkspaceShiftTodayResponse;
import com.dreamteam.alter.domain.workspace.type.WorkspaceShiftStatus;
import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceShiftQueryRepository;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
Expand All @@ -16,14 +20,48 @@
import java.util.List;
import java.util.Optional;

import static com.dreamteam.alter.domain.file.entity.QFile.file;
import static com.dreamteam.alter.domain.user.entity.QUser.user;
import static com.dreamteam.alter.domain.workspace.entity.QWorkspaceShift.workspaceShift;
import static com.dreamteam.alter.domain.workspace.entity.QWorkspaceWorker.workspaceWorker;

@Repository
@RequiredArgsConstructor
public class WorkspaceShiftQueryRepositoryImpl implements WorkspaceShiftQueryRepository {

private final JPAQueryFactory queryFactory;

@Override
public List<WorkspaceShiftTodayResponse> getTodayShiftList(
Long workspaceId
) {
LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
LocalDateTime endOfDay = startOfDay.plusDays(1);
Comment thread
juny0955 marked this conversation as resolved.
return queryFactory
.select(Projections.constructor(WorkspaceShiftTodayResponse.class,
workspaceShift.id,
workspaceWorker.id,
user.name,
file.fileUrl,
workspaceShift.startDateTime,
workspaceShift.endDateTime
))
.from(workspaceShift)
.leftJoin(workspaceShift.assignedWorkspaceWorker, workspaceWorker)
.leftJoin(workspaceWorker.user, user)
.leftJoin(file).on(
file.targetType.eq(FileTargetType.USER_PROFILE)
.and(file.targetId.eq(user.id.stringValue()))
.and(file.status.eq(FileStatus.ATTACHED))
)
.where(workspaceShift.workspace.id.eq(workspaceId)
.and(workspaceShift.startDateTime.goe(startOfDay))
.and(workspaceShift.startDateTime.lt(endOfDay))
.and(workspaceShift.status.eq(WorkspaceShiftStatus.CONFIRMED)))
.orderBy(workspaceShift.startDateTime.asc())
.fetch();
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

@Override
public List<WorkspaceShift> findByUserAndDateRange(User user, int year, int month) {
return queryFactory
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.dreamteam.alter.application.workspace.usecase;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.ManagerTodayScheduleResponseDto;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

어댑터 DTO 매핑을 컨트롤러 경계로 이동하세요.

Application use case가 adapter.inbound DTO를 직접 반환하면 Hexagonal Architecture의 의존 방향이 깨집니다. executeWorkspaceShiftTodayResponse 같은 application/domain 응답 모델을 반환하고, ManagerTodayScheduleResponseDto::of 매핑은 controller에서 수행하는 구조가 더 적절합니다.

♻️ 구조 변경 방향
-import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.ManagerTodayScheduleResponseDto;
+import com.dreamteam.alter.domain.workspace.model.WorkspaceShiftTodayResponse;
-    public List<ManagerTodayScheduleResponseDto> execute(ManagerActor actor, Long workspaceId) {
+    public List<WorkspaceShiftTodayResponse> execute(ManagerActor actor, Long workspaceId) {
         if (!workspaceQueryRepository.existsByIdAndManagerUser(workspaceId, actor.getManagerUser())) {
             throw new CustomException(ErrorCode.WORKSPACE_NOT_FOUND);
         }
 
-        return workspaceShiftQueryRepository.getTodayShiftList(workspaceId).stream()
-            .map(ManagerTodayScheduleResponseDto::of)
-            .toList();
+        return workspaceShiftQueryRepository.getTodayShiftList(workspaceId);
     }

As per coding guidelines, src/main/java/com/dreamteam/alter/application/**: “This is the application layer containing use cases” and “No direct infrastructure dependencies”.

Also applies to: 32-34

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/main/java/com/dreamteam/alter/application/workspace/usecase/ManagerGetDailyWorkScheduleList.java`
at line 8, The use case currently returns an adapter DTO
(ManagerTodayScheduleResponseDto) which breaks hexagonal dependency rules;
change ManagerGetDailyWorkScheduleList.execute to return the application/domain
response model (e.g., WorkspaceShiftTodayResponse or similar domain DTO defined
in the application layer) instead of ManagerTodayScheduleResponseDto, remove the
import of
com.dreamteam.alter.adapter.inbound.manager.schedule.dto.ManagerTodayScheduleResponseDto
from the use case, and move the mapping call ManagerTodayScheduleResponseDto::of
into the controller layer so the controller invokes useCase.execute(...), maps
the returned WorkspaceShiftTodayResponse to ManagerTodayScheduleResponseDto via
ManagerTodayScheduleResponseDto.of, and update any callers/signatures
accordingly.

import com.dreamteam.alter.common.exception.CustomException;
import com.dreamteam.alter.common.exception.ErrorCode;
import com.dreamteam.alter.domain.user.context.ManagerActor;
import com.dreamteam.alter.domain.workspace.port.inbound.ManagerGetDailyScheduleListUseCase;
import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceQueryRepository;
import com.dreamteam.alter.domain.workspace.port.outbound.WorkspaceShiftQueryRepository;

import lombok.RequiredArgsConstructor;

@Service("managerGetTodayWorkScheduleList")
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ManagerGetDailyWorkScheduleList implements ManagerGetDailyScheduleListUseCase {

private final WorkspaceShiftQueryRepository workspaceShiftQueryRepository;
private final WorkspaceQueryRepository workspaceQueryRepository;

@Override
public List<ManagerTodayScheduleResponseDto> execute(ManagerActor actor, Long workspaceId) {
if (!workspaceQueryRepository.existsByIdAndManagerUser(workspaceId, actor.getManagerUser())) {
throw new CustomException(ErrorCode.WORKSPACE_NOT_FOUND);
}

return ManagerTodayScheduleResponseDto.from(workspaceShiftQueryRepository.getTodayShiftList(workspaceId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.dreamteam.alter.domain.workspace.model;

import java.time.LocalDateTime;

public record WorkspaceShiftTodayResponse(
Long shiftId,
Long workerId,
String workerName,
String profileImageUrl,
LocalDateTime startDateTime,
LocalDateTime endDateTime
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.dreamteam.alter.domain.workspace.port.inbound;

import com.dreamteam.alter.adapter.inbound.manager.schedule.dto.ManagerTodayScheduleResponseDto;
import com.dreamteam.alter.domain.user.context.ManagerActor;

import java.util.List;

public interface ManagerGetDailyScheduleListUseCase {
List<ManagerTodayScheduleResponseDto> execute(ManagerActor actor, Long workspaceId);
Comment thread
juny0955 marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
import com.dreamteam.alter.domain.user.entity.User;
import com.dreamteam.alter.domain.workspace.entity.Workspace;
import com.dreamteam.alter.domain.workspace.entity.WorkspaceShift;
import com.dreamteam.alter.domain.workspace.model.WorkspaceShiftTodayResponse;

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

public interface WorkspaceShiftQueryRepository {
List<WorkspaceShiftTodayResponse> getTodayShiftList(Long workspaceId);

List<WorkspaceShift> findByUserAndDateRange(User user, int year, int month);
List<WorkspaceShift> findByUserAndWeeklyRange(User user, LocalDate startDate, LocalDate endDate);
List<WorkspaceShift> findByUserAndDate(User user, int year, int month, int day);
Expand Down
Loading