diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/admin/FindManagersResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/admin/FindManagersResponse.java new file mode 100644 index 00000000..441d9909 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/admin/FindManagersResponse.java @@ -0,0 +1,15 @@ +package clap.server.adapter.inbound.web.dto.admin; + +import java.util.List; + +public record FindManagersResponse( + Long memberId, + String nickname, + String imageUrl, + int remainingTasks +) { + + public static List emptyListResponse() { + return List.of(); + } +} diff --git a/src/main/java/clap/server/adapter/inbound/web/member/ManagerController.java b/src/main/java/clap/server/adapter/inbound/web/member/ManagerController.java new file mode 100644 index 00000000..c7e4c966 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/member/ManagerController.java @@ -0,0 +1,32 @@ +package clap.server.adapter.inbound.web.member; + +import clap.server.application.port.inbound.domain.FindManagersUsecase; +import clap.server.adapter.inbound.web.dto.admin.FindManagersResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/manager") +@RequiredArgsConstructor +public class ManagerController { + + private final FindManagersUsecase findManagersUsecase; + + @GetMapping + public ResponseEntity> findManagers() { + + List managers = findManagersUsecase.execute(); + + if (managers.isEmpty()) { + return ResponseEntity.status(HttpStatus.NO_CONTENT).body(FindManagersResponse.emptyListResponse()); + } + + return ResponseEntity.ok(managers); + } +} diff --git a/src/main/java/clap/server/adapter/outbound/persistense/MemberPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/MemberPersistenceAdapter.java index 5b36e33a..7cac7770 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/MemberPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/MemberPersistenceAdapter.java @@ -2,26 +2,37 @@ import clap.server.adapter.inbound.web.dto.admin.FindMemberRequest; import clap.server.adapter.outbound.persistense.entity.member.MemberEntity; +import clap.server.adapter.outbound.persistense.entity.member.constant.MemberRole; import clap.server.adapter.outbound.persistense.entity.member.constant.MemberStatus; import clap.server.adapter.outbound.persistense.mapper.MemberPersistenceMapper; import clap.server.adapter.outbound.persistense.repository.member.MemberRepository; import clap.server.application.port.outbound.member.CommandMemberPort; import clap.server.application.port.outbound.member.LoadMemberPort; import clap.server.common.annotation.architecture.PersistenceAdapter; +import clap.server.domain.model.task.Task; +import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus ; +import clap.server.adapter.outbound.persistense.entity.task.TaskEntity; +import clap.server.adapter.outbound.persistense.repository.task.TaskRepository; +import clap.server.adapter.outbound.persistense.mapper.TaskPersistenceMapper; +import java.util.stream.Collectors; +import java.util.List; + import clap.server.domain.model.member.Member; +import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import java.util.List; import java.util.Optional; -import java.util.stream.Collectors; @PersistenceAdapter @RequiredArgsConstructor -public class MemberPersistenceAdapter implements LoadMemberPort, CommandMemberPort { +public class MemberPersistenceAdapter implements LoadMemberPort, CommandMemberPort { private final MemberRepository memberRepository; private final MemberPersistenceMapper memberPersistenceMapper; + private final TaskRepository taskRepository; + private final TaskPersistenceMapper taskPersistenceMapper; + private final JPAQueryFactory jpaQueryFactory; @Override public Optional findById(final Long id) { @@ -62,6 +73,28 @@ public void save(final Member member) { memberRepository.save(memberEntity); } + @Override + public List findActiveManagers() { + List memberEntities = memberRepository.findByRoleAndStatus(MemberRole.valueOf("ROLE_MANAGER"), MemberStatus.ACTIVE); + return memberEntities.stream() + .map(memberPersistenceMapper::toDomain) + .collect(Collectors.toList()); + } + + @Override + public int getRemainingTasks(Long memberId) { + List targetStatuses = List.of(TaskStatus.IN_PROGRESS, TaskStatus.PENDING_COMPLETED); + return findTasksByMemberIdAndStatus(memberId, targetStatuses).size(); + } + + @Override + public List findTasksByMemberIdAndStatus(Long memberId, List taskStatuses) { + List taskEntities = taskRepository.findByProcessor_MemberIdAndTaskStatusIn(memberId, taskStatuses); + return taskEntities.stream() + .map(taskPersistenceMapper::toDomain) + .collect(Collectors.toList()); + } + @Override public Page findAllMembers(Pageable pageable) { return memberRepository.findAllMembers(pageable).map(memberPersistenceMapper::toDomain); @@ -74,4 +107,3 @@ public Page findMembersWithFilter(Pageable pageable, FindMemberRequest f } } - diff --git a/src/main/java/clap/server/adapter/outbound/persistense/mapper/TaskHistoryPersistenceMapper.java b/src/main/java/clap/server/adapter/outbound/persistense/mapper/TaskHistoryPersistenceMapper.java index db46c6ef..0192851b 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/mapper/TaskHistoryPersistenceMapper.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/mapper/TaskHistoryPersistenceMapper.java @@ -1,12 +1,9 @@ package clap.server.adapter.outbound.persistense.mapper; import clap.server.adapter.outbound.persistense.entity.task.TaskHistoryEntity; -import clap.server.adapter.outbound.persistense.entity.task.TaskModificationInfo; import clap.server.adapter.outbound.persistense.mapper.common.PersistenceMapper; import clap.server.domain.model.task.TaskHistory; import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Mappings; @Mapper(componentModel = "spring", uses = {CommentPersistenceMapper.class, TaskPersistenceMapper.class, MemberPersistenceMapper.class}) public interface TaskHistoryPersistenceMapper extends PersistenceMapper { diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskRepository.java index 99099b28..006791ba 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/task/TaskRepository.java @@ -1,6 +1,5 @@ package clap.server.adapter.outbound.persistense.repository.task; - import clap.server.adapter.outbound.persistense.entity.task.TaskEntity; import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; import io.lettuce.core.dynamic.annotation.Param; @@ -8,9 +7,12 @@ import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; + import org.springframework.stereotype.Repository; import java.time.LocalDateTime; +import java.util.Collection; + import java.util.List; import java.util.Optional; @@ -26,6 +28,9 @@ List findYesterdayTaskByUpdatedAtIsBetween( ); + List findByProcessor_MemberIdAndTaskStatusIn(Long memberId, Collection taskStatuses); + + @Query("SELECT t FROM TaskEntity t " + "WHERE t.processor.memberId = :processorId " + "AND t.taskStatus IN :taskStatus " + @@ -45,12 +50,4 @@ Slice findTasksWithTaskStatusAndCompletedAt( Optional findTopByProcessor_MemberIdAndTaskStatusAndProcessorOrderAfterOrderByProcessorOrderDesc( Long processorId, TaskStatus taskStatus, Long processorOrder); -} - - - - - - - - +} \ No newline at end of file diff --git a/src/main/java/clap/server/application/mapper/ManagersMapper.java b/src/main/java/clap/server/application/mapper/ManagersMapper.java new file mode 100644 index 00000000..db7fb6d8 --- /dev/null +++ b/src/main/java/clap/server/application/mapper/ManagersMapper.java @@ -0,0 +1,27 @@ +package clap.server.application.mapper; + +import clap.server.adapter.inbound.web.dto.admin.FindManagersResponse; +import clap.server.domain.model.member.Member; +import clap.server.application.port.inbound.domain.MemberService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class ManagersMapper { + + private final MemberService memberService; + + public FindManagersResponse mapToFindManagersResponse(Member manager) { + int remainingTasks = memberService.getRemainingTasks(manager.getMemberId()); + String nickname = memberService.getMemberNickname(manager.getMemberId()); + String imageUrl = memberService.getMemberImageUrl(manager.getMemberId()); + + return new FindManagersResponse( + manager.getMemberId(), + nickname, + imageUrl, + remainingTasks + ); + } +} diff --git a/src/main/java/clap/server/application/port/inbound/domain/FindManagersUsecase.java b/src/main/java/clap/server/application/port/inbound/domain/FindManagersUsecase.java new file mode 100644 index 00000000..57327d16 --- /dev/null +++ b/src/main/java/clap/server/application/port/inbound/domain/FindManagersUsecase.java @@ -0,0 +1,8 @@ +package clap.server.application.port.inbound.domain; + +import clap.server.adapter.inbound.web.dto.admin.FindManagersResponse; +import java.util.List; + +public interface FindManagersUsecase { + List execute(); +} diff --git a/src/main/java/clap/server/application/port/inbound/domain/MemberService.java b/src/main/java/clap/server/application/port/inbound/domain/MemberService.java index b8396065..27f44000 100644 --- a/src/main/java/clap/server/application/port/inbound/domain/MemberService.java +++ b/src/main/java/clap/server/application/port/inbound/domain/MemberService.java @@ -25,6 +25,33 @@ public Member findActiveMember(Long memberId) { () -> new ApplicationException(MemberErrorCode.ACTIVE_MEMBER_NOT_FOUND)); } + public int getRemainingTasks(Long memberId) { + List targetStatuses = List.of(TaskStatus.IN_PROGRESS, TaskStatus.PENDING_COMPLETED); + return loadMemberPort.findTasksByMemberIdAndStatus(memberId, targetStatuses).size(); + } + + public String getMemberNickname(Long memberId) { + Member member = findById(memberId); + if (member.getMemberInfo() == null) { + throw new ApplicationException(MemberErrorCode.MEMBER_NOT_FOUND); + } + return member.getMemberInfo().getNickname(); + } + + public String getMemberImageUrl(Long memberId) { + Member member = findById(memberId); + return member.getImageUrl() != null ? member.getImageUrl() : "default-image-url"; + } + + public List findActiveManagers() { + List activeManagers = loadMemberPort.findActiveManagers(); + + if (activeManagers.isEmpty()) { + return List.of(); + } + return activeManagers; + } + public Member findReviewer(Long memberId) { return loadMemberPort.findReviewerById(memberId).orElseThrow( ()-> new ApplicationException(MemberErrorCode.NOT_A_REVIEWER) diff --git a/src/main/java/clap/server/application/port/outbound/member/LoadMemberPort.java b/src/main/java/clap/server/application/port/outbound/member/LoadMemberPort.java index 5b63970c..b5f1cfe9 100644 --- a/src/main/java/clap/server/application/port/outbound/member/LoadMemberPort.java +++ b/src/main/java/clap/server/application/port/outbound/member/LoadMemberPort.java @@ -4,10 +4,12 @@ import clap.server.domain.model.member.Member; import clap.server.domain.model.task.Task; // Task 클래스 임포트 확인 import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus; // TaskStatus 임포트 +import java.util.List; +// Task 클래스 임포트 확인 +// TaskStatus 임포트 import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; -import java.util.List; import java.util.Optional; public interface LoadMemberPort { @@ -15,6 +17,12 @@ public interface LoadMemberPort { Optional findActiveMemberById(Long id); + List findActiveManagers(); + + List findTasksByMemberIdAndStatus(Long memberId, List taskStatuses); + + int getRemainingTasks(Long memberId); + Optional findReviewerById(Long id); Optional findByNickname(String nickname); diff --git a/src/main/java/clap/server/application/service/FindActiveManagersService.java b/src/main/java/clap/server/application/service/FindActiveManagersService.java new file mode 100644 index 00000000..06ddf464 --- /dev/null +++ b/src/main/java/clap/server/application/service/FindActiveManagersService.java @@ -0,0 +1,33 @@ +package clap.server.application.service; + +import clap.server.adapter.inbound.web.dto.admin.FindManagersResponse; +import clap.server.domain.model.member.Member; +import clap.server.application.port.inbound.domain.MemberService; +import clap.server.application.mapper.ManagersMapper; +import clap.server.application.port.inbound.domain.FindManagersUsecase; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +@Service // 여기 추가 +@RequiredArgsConstructor +public class FindActiveManagersService implements FindManagersUsecase { + + private final MemberService memberService; + private final ManagersMapper findManagersResponseMapper; + + @Transactional + @Override + public List execute() { + + List managers = memberService.findActiveManagers(); + + // managers를 FindManagersResponse로 매핑 + return managers.stream() + .map(findManagersResponseMapper::mapToFindManagersResponse) + .collect(Collectors.toList()); + } +}