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 @@ -85,6 +85,7 @@ private AbstractRequestMatcherRegistry<AuthorizeHttpRequestsConfigurer<HttpSecur
.requestMatchers(LOGIN_ENDPOINT).permitAll()
.requestMatchers(HEALTH_CHECK_ENDPOINT).permitAll()
.requestMatchers(REISSUANCE_ENDPOINT).permitAll()
.requestMatchers(PASSWORD_EMAIL_ENDPOINT).permitAll()
.requestMatchers(SWAGGER_ENDPOINTS).permitAll();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ private WebSecurityUrl() {
"/swagger-ui/**", "/swagger"
};
public static final String REISSUANCE_ENDPOINT = "/api/auths/reissuance";
public static final String PASSWORD_EMAIL_ENDPOINT = "/api/verifications/**";
public static final String TEMPORARY_TOKEN_ALLOWED_ENDPOINT = "/api/members/initial-password";
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import clap.server.adapter.outbound.jwt.JwtClaims;
import clap.server.adapter.outbound.jwt.access.AccessTokenClaimKeys;
import clap.server.application.port.outbound.auth.forbidden.ForbiddenTokenPort;
import clap.server.application.port.outbound.auth.JwtProvider;
import clap.server.application.port.outbound.auth.forbidden.ForbiddenTokenPort;
import clap.server.exception.JwtException;
import clap.server.exception.code.AuthErrorCode;
import io.jsonwebtoken.Claims;
Expand Down Expand Up @@ -49,7 +49,7 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
SWAGGER_ENDPOINTS
).flatMap(Arrays::stream).toArray(String[]::new);

public static final String[] ANONYMOUS_ENDPOINTS = {LOGIN_ENDPOINT, REISSUANCE_ENDPOINT};
public static final String[] ANONYMOUS_ENDPOINTS = {LOGIN_ENDPOINT, REISSUANCE_ENDPOINT, PASSWORD_EMAIL_ENDPOINT};

@Override
protected void doFilterInternal(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package clap.server.adapter.inbound.web.admin;

import clap.server.adapter.inbound.web.dto.admin.response.FindAllDepartmentsResponse;
import clap.server.application.mapper.DepartmentResponseMapper;
import clap.server.application.port.inbound.admin.FindAllDepartmentsUsecase;
import clap.server.common.annotation.architecture.WebAdapter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -23,10 +23,7 @@ public class FindDepartmentController {
@Operation(summary = "부서 조회 API")
@Secured("ROLE_ADMIN")
@GetMapping("/departments")
public List<FindAllDepartmentsResponse> findAllDepartments() {
return findAllDepartmentsUsecase.findAllDepartments()
.stream()
.map(DepartmentResponseMapper::toFindAllDepartmentsResponse)
.toList();
public ResponseEntity<List<FindAllDepartmentsResponse>> findAllDepartments() {
return ResponseEntity.ok(findAllDepartmentsUsecase.findAllDepartments());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import clap.server.exception.code.FileErrorcode;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
Expand All @@ -21,23 +22,19 @@

@Tag(name = "05. Admin [회원 관리]")
@WebAdapter
@RequiredArgsConstructor
@RequestMapping("/api/managements")
public class RegisterMemberCsvController {
private final RegisterMemberCSVUsecase registerMemberCSVUsecase;

public RegisterMemberCsvController(RegisterMemberCSVUsecase registerMemberCSVUsecase) {
this.registerMemberCSVUsecase = registerMemberCSVUsecase;
}

@Operation(summary = "CSV 파일로 회원 등록 API")
@PostMapping("/members/upload")
@Secured("ROLE_ADMIN")
public ResponseEntity<String> registerMembersFromCsv(
@AuthenticationPrincipal SecurityUserDetails userInfo,
@RequestParam("file") MultipartFile file) throws IOException {
if (!FileTypeValidator.validCSVFile(file.getInputStream())) {
throw new AdapterException(FileErrorcode.UNSUPPORTED_FILE_TYPE);
}
throw new AdapterException(FileErrorcode.UNSUPPORTED_FILE_TYPE);}
int addedCount = registerMemberCSVUsecase.registerMembersFromCsv(userInfo.getUserId(), file);
return ResponseEntity.ok(addedCount + "명의 회원이 등록되었습니다.");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ public record UpdateMemberRequest(
@NotBlank
@Pattern(regexp = "^[a-zA-Z0-9_+&*-]+(?:\\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,7}$",
message = "올바른 이메일 형식이 아닙니다.")
@Schema(description = "회원 이메일", example = "siena@gmail.com")
String email,
@NotNull @Schema(description = "승인 권한 여부")
Boolean isReviewer,
@NotNull @Schema(description = "부서 ID")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package clap.server.adapter.inbound.web.dto.member.response;

import jakarta.validation.constraints.NotBlank;

public record SendVerificationCodeRequest(
@NotBlank
String nickname,
@NotBlank
String email
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

package clap.server.adapter.inbound.web.dto.member.response;

import jakarta.validation.constraints.NotBlank;

public record VerifyCodeRequest(
@NotBlank
String email,
@NotBlank
String code
) {
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
package clap.server.adapter.inbound.web.member;

import clap.server.adapter.inbound.security.service.SecurityUserDetails;
import clap.server.adapter.inbound.web.dto.member.response.SendVerificationCodeRequest;
import clap.server.adapter.inbound.web.dto.member.response.VerifyCodeRequest;
import clap.server.application.port.inbound.member.SendVerificationEmailUsecase;
import clap.server.application.port.inbound.member.VerifyEmailCodeUsecase;
import clap.server.common.annotation.architecture.WebAdapter;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Tag(name = "00. Auth [인증번호]")
@WebAdapter
@RequiredArgsConstructor
@RequestMapping("/api/members")
@RequestMapping("/api")
public class EmailVerificationController {
private final SendVerificationEmailUsecase sendVerificationEmailUsecase;
private final VerifyEmailCodeUsecase verifyEmailCodeUsecase;

@Operation(summary = "인증번호 전송 API")
@PostMapping("/verification/email")
public void sendVerificationEmail(@AuthenticationPrincipal SecurityUserDetails userInfo){
sendVerificationEmailUsecase.sendVerificationCode(userInfo.getUserId());
@PostMapping("/verifications/email")
public void sendVerificationEmail(@RequestBody SendVerificationCodeRequest request) {
sendVerificationEmailUsecase.sendVerificationCode(request);
}

@Operation(summary = "인증번호 검증 API")
@PostMapping("/verification")
public void sendVerificationEmail(@AuthenticationPrincipal SecurityUserDetails userInfo,
@RequestParam String code){
verifyEmailCodeUsecase.verifyEmailCode(userInfo.getUserId(), code);
@PostMapping("/verifications")
public void sendVerificationEmail(@RequestBody VerifyCodeRequest request) {
verifyEmailCodeUsecase.verifyEmailCode(request);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public class MemberPersistenceAdapter implements LoadMemberPort, CommandMemberPo
private final MemberPersistenceMapper memberPersistenceMapper;
private final TaskRepository taskRepository;
private final TaskPersistenceMapper taskPersistenceMapper;
private final JPAQueryFactory jpaQueryFactory;

@Override
public Optional<Member> findById(final Long id) {
Expand Down Expand Up @@ -110,5 +109,10 @@ public Page<Member> findAllMembers(Pageable pageable) {
public Page<Member> findMembersWithFilter(Pageable pageable, FindMemberRequest filterRequest, String sortDirection) {
return memberRepository.findMembersWithFilter(pageable, filterRequest,sortDirection).map(memberPersistenceMapper::toDomain);
}

@Override
public Optional<Member> findByNicknameOrEmail(String nickname, String email) {
return memberRepository.findByNicknameAndEmail(nickname, email).map(memberPersistenceMapper::toDomain);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,8 @@

@Repository
public interface MemberRepository extends JpaRepository<MemberEntity, Long>, MemberCustomRepository {


List<MemberEntity> findByRoleAndStatus(MemberRole role, MemberStatus status);


Optional<MemberEntity> findByStatusAndMemberId(MemberStatus memberStatus, Long memberId);

Optional<MemberEntity> findByNickname(String nickname);
Expand All @@ -24,7 +21,8 @@ public interface MemberRepository extends JpaRepository<MemberEntity, Long>, Me

List<MemberEntity> findAll(); // 전체 회원 조회


Optional<MemberEntity> findByMemberIdAndIsReviewerTrue(Long memberId);

Optional<MemberEntity> findByNicknameAndEmail(String nickname, String email);
}

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package clap.server.application.port.inbound.admin;

import clap.server.domain.model.member.Department;
import clap.server.adapter.inbound.web.dto.admin.response.FindAllDepartmentsResponse;

import java.util.List;

public interface FindAllDepartmentsUsecase {
List<Department> findAllDepartments();
List<FindAllDepartmentsResponse> findAllDepartments();
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,19 +30,6 @@ public int getRemainingTasks(Long memberId) {
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<Member> findActiveManagers() {
List<Member> activeManagers = loadMemberPort.findActiveManagers();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package clap.server.application.port.inbound.member;

import clap.server.adapter.inbound.web.dto.member.response.SendVerificationCodeRequest;

public interface SendVerificationEmailUsecase {
void sendVerificationCode(Long memberId);
void sendVerificationCode(SendVerificationCodeRequest request);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package clap.server.application.port.inbound.member;

import clap.server.adapter.inbound.web.dto.member.response.VerifyCodeRequest;

public interface VerifyEmailCodeUsecase {
void verifyEmailCode(Long memberId, String code);
void verifyEmailCode(VerifyCodeRequest request);
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ public interface LoadMemberPort {

Page<Member> findMembersWithFilter(Pageable pageable, FindMemberRequest filterRequest, String sortDirection);


Optional<Member> findByNicknameOrEmail(String nickname, String email);
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package clap.server.application.service.admin;

import clap.server.adapter.inbound.web.dto.admin.response.FindAllDepartmentsResponse;
import clap.server.application.mapper.DepartmentResponseMapper;
import clap.server.application.port.inbound.admin.FindAllDepartmentsUsecase;
import clap.server.application.port.outbound.member.LoadDepartmentPort;
import clap.server.common.annotation.architecture.ApplicationService;
import clap.server.domain.model.member.Department;
import lombok.RequiredArgsConstructor;

import java.util.List;
Expand All @@ -14,8 +15,10 @@ public class FindAllDepartmentsService implements FindAllDepartmentsUsecase {
private final LoadDepartmentPort loadDepartmentPort;

@Override
public List<Department> findAllDepartments() {
return loadDepartmentPort.findActiveDepartments();
public List<FindAllDepartmentsResponse> findAllDepartments() {
return loadDepartmentPort.findActiveDepartments().stream()
.map(DepartmentResponseMapper::toFindAllDepartmentsResponse)
.toList();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public void updateMemberInfo(Long adminId, Long memberId, UpdateMemberRequest re

//TODO: 인프라팀만 담당자가 될 수 있도록 수정해야함
member.getMemberInfo().updateMemberInfoByAdmin(
request.name(), request.email(), request.isReviewer(),
request.name(), request.isReviewer(),
department, request.role(), request.departmentRole());
commandMemberPort.save(member);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
import clap.server.application.port.inbound.domain.MemberService;
import clap.server.application.port.outbound.member.CommandMemberPort;
import clap.server.application.port.outbound.member.LoadDepartmentPort;
import clap.server.application.port.outbound.member.LoadMemberPort;
import clap.server.common.annotation.architecture.ApplicationService;
import clap.server.domain.model.member.Department;
import clap.server.domain.model.member.Member;
import clap.server.domain.model.member.MemberInfo;
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;
Expand All @@ -24,8 +28,7 @@ class RegisterMemberService implements RegisterMemberUsecase {
private final MemberService memberService;
private final CommandMemberPort commandMemberPort;
private final LoadDepartmentPort loadDepartmentPort;
private final PasswordEncoder passwordEncoder;
private final CsvParseService csvParser;
private final LoadMemberPort loadMemberPort;

@Override
@Transactional
Expand All @@ -34,6 +37,12 @@ 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);
}
);

// TODO: 인프라팀만 담당자가 될 수 있도록 수정해야함
MemberInfo memberInfo = MemberInfo.toMemberInfo(request.name(), request.email(), request.nickname(), request.isReviewer(),
department, request.role(), request.departmentRole());
Expand Down
Loading