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
@@ -1,6 +1,10 @@
package clap.server.adapter.inbound.web.dto.admin.response;

import io.swagger.v3.oas.annotations.media.Schema;

public record FindAllDepartmentsResponse(
Long departmentId,
String name) {
String name,
@Schema(description = "담당자 권한 여부")
Boolean isManager) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void getCommentErrorCode() {}
@GetMapping("/statistic")
@DevelopOnlyApi
@Operation(summary = "작업 통계 관련 에러 코드 나열")
@ApiErrorCodes(LabelErrorCode.class)
@ApiErrorCodes(StatisticsErrorCode.class)
public void getStatisticsErrorCode() {}

@GetMapping("/label")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,26 @@
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.entity.task.TaskEntity;
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
import clap.server.adapter.outbound.persistense.mapper.MemberPersistenceMapper;
import clap.server.adapter.outbound.persistense.mapper.TaskPersistenceMapper;
import clap.server.adapter.outbound.persistense.repository.member.MemberRepository;
import clap.server.adapter.outbound.persistense.repository.task.TaskRepository;
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.member.Member;
import clap.server.domain.model.task.Task;
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.Set;
import java.util.stream.Collectors;

@PersistenceAdapter
@RequiredArgsConstructor
public class MemberPersistenceAdapter implements LoadMemberPort, CommandMemberPort {
private final MemberRepository memberRepository;
private final MemberPersistenceMapper memberPersistenceMapper;
private final TaskRepository taskRepository;
private final TaskPersistenceMapper taskPersistenceMapper;

@Override
public Optional<Member> findById(final Long id) {
Expand Down Expand Up @@ -83,14 +77,6 @@ public List<Member> findActiveManagers() {
.collect(Collectors.toList());
}

@Override
public List<Task> findTasksByMemberIdAndStatus(final Long memberId, final List<TaskStatus> taskStatuses) {
List<TaskEntity> taskEntities = taskRepository.findByProcessor_MemberIdAndTaskStatusIn(memberId, taskStatuses);
return taskEntities.stream()
.map(taskPersistenceMapper::toDomain)
.collect(Collectors.toList());
}

@Override
public Page<Member> findAllMembers(final Pageable pageable) {
return memberRepository.findAllMembers(pageable).map(memberPersistenceMapper::toDomain);
Expand All @@ -117,4 +103,8 @@ public Optional<Member> findByEmail(final String email) {
.map(memberPersistenceMapper::toDomain);
}

@Override
public boolean existsByNicknamesOrEmails(Set<String> nicknames, Set<String> emails) {
return memberRepository.existsByNicknamesOrEmails(nicknames, emails);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.time.LocalTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
@PersistenceAdapter
Expand Down Expand Up @@ -116,5 +117,12 @@ public List<Task> findTeamStatus(final Long memberId, final FilterTeamStatusRequ
.map(taskPersistenceMapper::toDomain).toList();
}

@Override
public List<Task> findTasksByMemberIdAndStatus(final Long memberId, final List<TaskStatus> taskStatuses) {
List<TaskEntity> taskEntities = taskRepository.findByProcessor_MemberIdAndTaskStatusIn(memberId, taskStatuses);
return taskEntities.stream()
.map(taskPersistenceMapper::toDomain)
.collect(Collectors.toList());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,7 @@ public class DepartmentEntity extends BaseTimeEntity {
@Column(nullable = false)
@Enumerated(EnumType.STRING)
private DepartmentStatus status;

@Column(nullable = false)
private boolean isManager;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
public interface DepartmentPersistenceMapper extends PersistenceMapper<DepartmentEntity, Department> {

@Mapping(source = "admin.memberId", target = "adminId")
@Mapping(source = "manager", target = "isManager")
Department toDomain(DepartmentEntity entity);

@Mapping(source = "adminId", target = "admin.memberId")
@Mapping(source = "manager", target = "isManager")
DepartmentEntity toEntity(Department domain);
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,12 @@ protected Member toDomainAdmin(MemberEntity admin) {
if (admin == null) return null;
return Member.builder()
.memberId(admin.getMemberId())
.memberInfo(new MemberInfo(
admin.getName(),
admin.getEmail(),
admin.getNickname(),
admin.isReviewer(),
departmentPersistenceMapper.toDomain(admin.getDepartment()),
admin.getRole(),
admin.getDepartmentRole()
))
.memberInfo(MemberInfo.builder()
.name(admin.getName())
.email(admin.getEmail())
.nickname(admin.getNickname())
.isReviewer(admin.isReviewer())
.build())
.createdAt(admin.getCreatedAt())
.updatedAt(admin.getUpdatedAt())
.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
import clap.server.adapter.outbound.persistense.entity.member.constant.MemberRole;
import clap.server.adapter.outbound.persistense.entity.member.constant.MemberStatus;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;
import java.util.Set;

@Repository
public interface MemberRepository extends JpaRepository<MemberEntity, Long>, MemberCustomRepository {
Expand All @@ -28,5 +31,8 @@ public interface MemberRepository extends JpaRepository<MemberEntity, Long>, Me
Optional<MemberEntity> findByNameAndEmail(String name, String email);

Optional<MemberEntity> findByEmail(String email);

@Query("SELECT COUNT(m) > 0 FROM MemberEntity m WHERE m.nickname IN :nicknames OR m.email IN :emails")
boolean existsByNicknamesOrEmails(@Param("nicknames") Set<String> nicknames, @Param("emails") Set<String> emails);
}

Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ public class DepartmentResponseMapper {
private DepartmentResponseMapper() {
throw new IllegalStateException("Utility class");
}

public static FindAllDepartmentsResponse toFindAllDepartmentsResponse(Department department) {
return new FindAllDepartmentsResponse(department.getDepartmentId(), department.getName());
return new FindAllDepartmentsResponse(
department.getDepartmentId(),
department.getName(),
department.isManager());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ public Member findActiveMember(Long memberId) {
() -> new ApplicationException(MemberErrorCode.ACTIVE_MEMBER_NOT_FOUND));
}

public int getRemainingTasks(Long memberId) {
List<TaskStatus> targetStatuses = List.of(TaskStatus.IN_PROGRESS, TaskStatus.IN_REVIEWING);
return loadMemberPort.findTasksByMemberIdAndStatus(memberId, targetStatuses).size();
}

public List<Member> findActiveManagers() {
List<Member> activeManagers = loadMemberPort.findActiveManagers();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.springframework.data.domain.Pageable;

import java.util.Optional;
import java.util.Set;

public interface LoadMemberPort {
Optional<Member> findById(Long id);
Expand All @@ -19,8 +20,6 @@ public interface LoadMemberPort {

List<Member> findActiveManagers();

List<Task> findTasksByMemberIdAndStatus(Long memberId, List<TaskStatus> taskStatuses);

Optional<Member> findReviewerById(Long id);

Optional<Member> findByNickname(String nickname);
Expand All @@ -37,4 +36,5 @@ public interface LoadMemberPort {

Optional<Member> findByEmail(String email);

boolean existsByNicknamesOrEmails(Set<String> nicknames, Set<String> emails);
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,6 @@ public interface LoadTaskPort {
List<Task> findTaskBoardByFilter(Long processorId, List<TaskStatus> statuses, LocalDateTime untilDateTime, FilterTaskBoardRequest request);

List<Task> findTeamStatus(Long memberId, FilterTeamStatusRequest filter);

List<Task> findTasksByMemberIdAndStatus(Long memberId, List<TaskStatus> taskStatuses);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import clap.server.domain.model.member.Department;
import clap.server.domain.model.member.Member;
import clap.server.domain.model.member.MemberInfo;
import clap.server.domain.policy.member.ManagerDepartmentPolicy;
import clap.server.exception.ApplicationException;
import clap.server.exception.code.DepartmentErrorCode;
import clap.server.exception.code.MemberErrorCode;
Expand All @@ -28,6 +29,7 @@
public class CsvParseService {

private final LoadDepartmentPort loadDepartmentPort;
private final ManagerDepartmentPolicy managerDepartmentPolicy;

public List<Member> parseDataAndMapToMember(MultipartFile file) {
List<Member> members = new ArrayList<>();
Expand Down Expand Up @@ -59,6 +61,7 @@ private Member mapToMember(String[] fields, List<Department> departments) {
.findFirst()
.orElseThrow(() -> new ApplicationException(DepartmentErrorCode.DEPARTMENT_NOT_FOUND));

managerDepartmentPolicy.validateDepartment(department, MemberRole.valueOf(fields[3].trim()));
MemberInfo memberInfo = toMemberInfo(
fields[0].trim(), // name
fields[4].trim(), // email
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

@ApplicationService
@RequiredArgsConstructor
Expand All @@ -29,20 +31,29 @@ public int registerMembersFromCsv(Long adminId, MultipartFile file) {
List<Member> members = csvParser.parseDataAndMapToMember(file);
Member admin = memberService.findActiveMember(adminId);

members.forEach(member -> {
String nickname = member.getMemberInfo().getNickname();
String email = member.getMemberInfo().getEmail();
if (loadMemberPort.findByNickname(nickname).isPresent() ||
loadMemberPort.findByEmail(email).isPresent()) {
throw new ApplicationException(MemberErrorCode.DUPLICATE_NICKNAME_OR_EMAIL);
}
});
validateMembers(members);

List<Member> newMembers = members.stream()
.map(memberData -> Member.createMember(admin, memberData.getMemberInfo()))
.map(memberData ->
Member.createMember(admin, memberData.getMemberInfo()))
.toList();

commandMemberPort.saveAll(newMembers);
return members.size();
}

public void validateMembers(List<Member> members) {
Set<String> nicknames = new HashSet<>();
Set<String> emails = new HashSet<>();

for (Member member : members) {
nicknames.add(member.getMemberInfo().getNickname());
emails.add(member.getMemberInfo().getEmail());
}

if(loadMemberPort.existsByNicknamesOrEmails(nicknames, emails)) {
throw new ApplicationException(MemberErrorCode.DUPLICATE_NICKNAME_OR_EMAIL);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,14 @@
import clap.server.domain.model.member.Department;
import clap.server.domain.model.member.Member;
import clap.server.domain.model.member.MemberInfo;
import clap.server.domain.policy.member.ManagerDepartmentPolicy;
import clap.server.exception.ApplicationException;
import clap.server.exception.AuthException;
import clap.server.exception.code.AuthErrorCode;
import clap.server.exception.code.DepartmentErrorCode;
import clap.server.exception.code.MemberErrorCode;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.transaction.annotation.Transactional;


import java.util.List;
import java.util.Set;

@ApplicationService
@RequiredArgsConstructor
Expand All @@ -29,6 +26,7 @@ class RegisterMemberService implements RegisterMemberUsecase {
private final CommandMemberPort commandMemberPort;
private final LoadDepartmentPort loadDepartmentPort;
private final LoadMemberPort loadMemberPort;
private final ManagerDepartmentPolicy managerDepartmentPolicy;

@Override
@Transactional
Expand All @@ -37,13 +35,11 @@ public void registerMember(Long adminId, RegisterMemberRequest request) {
Department department = loadDepartmentPort.findById(request.departmentId())
.orElseThrow(() -> new ApplicationException(DepartmentErrorCode.DEPARTMENT_NOT_FOUND));

loadMemberPort.findByNickname(request.nickname()).ifPresent(
member -> {
throw new ApplicationException(MemberErrorCode.DUPLICATE_NICKNAME);
}
);
if (loadMemberPort.existsByNicknamesOrEmails(Set.of(request.nickname()), Set.of(request.email()))) {
throw new ApplicationException(MemberErrorCode.DUPLICATE_NICKNAME_OR_EMAIL);
}

// TODO: 인프라팀만 담당자가 될 수 있도록 수정해야함
managerDepartmentPolicy.validateDepartment(department, request.role());
MemberInfo memberInfo = MemberInfo.toMemberInfo(request.name(), request.email(), request.nickname(), request.isReviewer(),
department, request.role(), request.departmentRole());
Member member = Member.createMember(admin, memberInfo);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package clap.server.application.service.task;

import clap.server.adapter.inbound.web.dto.task.response.FindManagersResponse;
import clap.server.application.port.inbound.task.FindManagersUsecase;
import clap.server.adapter.outbound.persistense.entity.task.constant.TaskStatus;
import clap.server.application.port.inbound.domain.MemberService;
import clap.server.application.port.inbound.task.FindManagersUsecase;
import clap.server.application.port.outbound.task.LoadTaskPort;
import clap.server.common.annotation.architecture.ApplicationService;
import clap.server.domain.model.member.Member;
import jakarta.transaction.Transactional;
Expand All @@ -17,14 +19,16 @@
public class FindManagersService implements FindManagersUsecase {

private final MemberService memberService;
private final LoadTaskPort loadTaskPort;

@Transactional
@Override
public List<FindManagersResponse> findManagers() {
List<TaskStatus> targetStatuses = List.of(TaskStatus.IN_PROGRESS, TaskStatus.IN_REVIEWING);
List<Member> managers = memberService.findActiveManagers();
return managers.stream()
.map(manager -> {
int remainingTasks = memberService.getRemainingTasks(manager.getMemberId());
int remainingTasks = loadTaskPort.findTasksByMemberIdAndStatus(manager.getMemberId(), targetStatuses).size();
return toFindManagersResponse(manager, remainingTasks);
}).toList();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,7 @@ public void updateTaskOrderAndStatus(Long processorId, UpdateTaskOrderRequest re

TaskHistory taskHistory = TaskHistory.createTaskHistory(TaskHistoryType.STATUS_SWITCHED, updatedTask, targetStatus.getDescription(), null,null);
commandTaskHistoryPort.save(taskHistory);
//TODO: 최종 단계에서 주석 처리 해제
//publishNotification(targetTask, NotificationType.STATUS_SWITCHED, String.valueOf(updatedTask.getTaskStatus()));
publishNotification(targetTask, NotificationType.STATUS_SWITCHED, String.valueOf(updatedTask.getTaskStatus()));
}

/**
Expand All @@ -182,7 +181,7 @@ public void validateRequest(UpdateTaskOrderRequest request, TaskStatus targetSta
}
}

private void publishNotification(Task task, NotificationType notificationType, String message, String taskTitle) {
private void publishNotification(Task task, NotificationType notificationType, String message) {
List<Member> receivers = List.of(task.getRequester(), task.getProcessor());
receivers.forEach(receiver -> {
boolean isManager = receiver.getMemberInfo().getRole() == MemberRole.ROLE_MANAGER;
Expand Down
Loading