회원관리 관리 수정

This commit is contained in:
2025-12-10 17:30:54 +09:00
parent 51cffccf4c
commit bdb5ba7011
12 changed files with 470 additions and 502 deletions

View File

@@ -0,0 +1,31 @@
package com.kamco.cd.kamcoback.auth;
import com.kamco.cd.kamcoback.members.service.AuthService;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AbstractAuthenticationFailureEvent;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class AuthFailureEventListener implements ApplicationListener<AbstractAuthenticationFailureEvent> {
private final AuthService authService;
@Override
public void onApplicationEvent(AbstractAuthenticationFailureEvent event) {
// 로그인 시도에 사용된 (username)
Object principal = event.getAuthentication().getPrincipal();
if (principal instanceof String username) {
// 로그인 실패 카운트 증가 로직 호출
authService.loginFail(UUID.fromString(username));
}
}
@Override
public boolean supportsAsyncExecution() {
return ApplicationListener.super.supportsAsyncExecution();
}
}

View File

@@ -13,7 +13,6 @@ import jakarta.validation.Valid;
import java.util.UUID; import java.util.UUID;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.PutMapping;
@@ -84,17 +83,17 @@ public class AdminApiController {
schema = @Schema(implementation = MembersDto.UpdateReq.class))) schema = @Schema(implementation = MembersDto.UpdateReq.class)))
@PathVariable @PathVariable
UUID uuid, UUID uuid,
@RequestBody MembersDto.UpdateReq updateReq) { @RequestBody @Valid MembersDto.UpdateReq updateReq) {
adminService.updateMembers(uuid, updateReq); adminService.updateMembers(uuid, updateReq);
return ApiResponseDto.createOK(UUID.randomUUID()); return ApiResponseDto.createOK(UUID.randomUUID());
} }
@Operation(summary = "회원 탈퇴", description = "회원 탈퇴") @Operation(summary = "관리자 계정 미사용 처리", description = "관리자 계정 미사용 처리")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
responseCode = "201", responseCode = "201",
description = "회원 탈퇴", description = "관리자 계정 미사용 처리",
content = content =
@Content( @Content(
mediaType = "application/json", mediaType = "application/json",
@@ -108,24 +107,4 @@ public class AdminApiController {
adminService.deleteAccount(uuid); adminService.deleteAccount(uuid);
return ApiResponseDto.createOK(uuid); return ApiResponseDto.createOK(uuid);
} }
@Operation(summary = "비밀번호 초기화", description = "비밀번호 초기화")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "201",
description = "비밀번호 초기화",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = Long.class))),
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@PatchMapping("/{memberId}/password")
public ApiResponseDto<Long> resetPassword(@PathVariable Long memberId) {
adminService.resetPassword(memberId);
return ApiResponseDto.createOK(memberId);
}
} }

View File

@@ -4,6 +4,7 @@ import com.kamco.cd.kamcoback.auth.JwtTokenProvider;
import com.kamco.cd.kamcoback.auth.RefreshTokenService; import com.kamco.cd.kamcoback.auth.RefreshTokenService;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.members.dto.SignInRequest; import com.kamco.cd.kamcoback.members.dto.SignInRequest;
import com.kamco.cd.kamcoback.members.service.AuthService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
@@ -13,6 +14,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import java.nio.file.AccessDeniedException; import java.nio.file.AccessDeniedException;
import java.time.Duration; import java.time.Duration;
import java.util.UUID;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpHeaders;
@@ -36,6 +38,7 @@ public class AuthController {
private final AuthenticationManager authenticationManager; private final AuthenticationManager authenticationManager;
private final JwtTokenProvider jwtTokenProvider; private final JwtTokenProvider jwtTokenProvider;
private final RefreshTokenService refreshTokenService; private final RefreshTokenService refreshTokenService;
private final AuthService authService;
@Value("${token.refresh-cookie-name}") @Value("${token.refresh-cookie-name}")
private String refreshCookieName; private String refreshCookieName;
@@ -68,6 +71,9 @@ public class AuthController {
String username = authentication.getName(); // UserDetailsService 에서 사용한 username String username = authentication.getName(); // UserDetailsService 에서 사용한 username
// 로그인 시간 저장
authService.saveLogin(UUID.fromString(username));
String accessToken = jwtTokenProvider.createAccessToken(username); String accessToken = jwtTokenProvider.createAccessToken(username);
String refreshToken = jwtTokenProvider.createRefreshToken(username); String refreshToken = jwtTokenProvider.createRefreshToken(username);
@@ -86,6 +92,7 @@ public class AuthController {
.build(); .build();
response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString()); response.addHeader(HttpHeaders.SET_COOKIE, cookie.toString());
return ApiResponseDto.ok(new TokenResponse(accessToken, refreshToken)); return ApiResponseDto.ok(new TokenResponse(accessToken, refreshToken));
} }
@@ -166,5 +173,7 @@ public class AuthController {
return ApiResponseDto.createOK(ResponseEntity.noContent().build()); return ApiResponseDto.createOK(ResponseEntity.noContent().build());
} }
public record TokenResponse(String accessToken, String refreshToken) {} public record TokenResponse(String accessToken, String refreshToken) {
}
} }

View File

@@ -10,13 +10,13 @@ import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.UUID; import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springdoc.core.annotations.ParameterObject; import org.springdoc.core.annotations.ParameterObject;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
@@ -48,24 +48,24 @@ public class MembersApiController {
return ApiResponseDto.ok(membersService.findByMembers(searchReq)); return ApiResponseDto.ok(membersService.findByMembers(searchReq));
} }
@Operation(summary = "회원정보 수정", description = "회원정보 수정")
@Operation(summary = "비밀번호 초기화", description = "비밀번호 초기화")
@ApiResponses( @ApiResponses(
value = { value = {
@ApiResponse( @ApiResponse(
responseCode = "201", responseCode = "201",
description = "회원정보 수정", description = "비밀번호 초기화",
content = content =
@Content( @Content(
mediaType = "application/json", mediaType = "application/json",
schema = @Schema(implementation = UUID.class))), schema = @Schema(implementation = Long.class))),
@ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content), @ApiResponse(responseCode = "400", description = "잘못된 요청 데이터", content = @Content),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content), @ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
}) })
@PutMapping("/{uuid}") @PatchMapping("/{memberId}/password")
public ApiResponseDto<UUID> updateMember( public ApiResponseDto<Long> resetPassword(@PathVariable Long memberId, @RequestBody @Valid MembersDto.InitReq initReq) {
@PathVariable UUID uuid, @RequestBody MembersDto.UpdateReq updateReq) { membersService.resetPassword(memberId, initReq);
// membersService.updateMember(uuid, updateReq); return ApiResponseDto.createOK(memberId);
return ApiResponseDto.createOK(uuid);
} }
} }

View File

@@ -1,7 +1,5 @@
package com.kamco.cd.kamcoback.members.dto; package com.kamco.cd.kamcoback.members.dto;
import com.kamco.cd.kamcoback.common.enums.RoleType;
import com.kamco.cd.kamcoback.common.utils.interfaces.EnumValid;
import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm; import com.kamco.cd.kamcoback.common.utils.interfaces.JsonFormatDttm;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotBlank;
@@ -23,33 +21,47 @@ public class MembersDto {
private Long id; private Long id;
private UUID uuid; private UUID uuid;
private String employeeNo; private String userRole;
private String name; private String name;
private String email; private String userId;
private String employeeNo;
private String tempPassword;
private String status; private String status;
private String roleName; @JsonFormatDttm
@JsonFormatDttm private ZonedDateTime createdDttm; private ZonedDateTime createdDttm;
@JsonFormatDttm private ZonedDateTime updatedDttm; @JsonFormatDttm
private ZonedDateTime updatedDttm;
@JsonFormatDttm
private ZonedDateTime firstLoginDttm;
@JsonFormatDttm
private ZonedDateTime lastLoginDttm;
public Basic( public Basic(
Long id, Long id,
UUID uuid, UUID uuid,
String employeeNo, String userRole,
String name, String name,
String email, String userId,
String employeeNo,
String tempPassword,
String status, String status,
String roleName,
ZonedDateTime createdDttm, ZonedDateTime createdDttm,
ZonedDateTime updatedDttm) { ZonedDateTime updatedDttm,
ZonedDateTime firstLoginDttm,
ZonedDateTime lastLoginDttm
) {
this.id = id; this.id = id;
this.uuid = uuid; this.uuid = uuid;
this.employeeNo = employeeNo; this.userRole = userRole;
this.name = name; this.name = name;
this.email = email; this.userId = userId;
this.employeeNo = employeeNo;
this.tempPassword = tempPassword;
this.status = status; this.status = status;
this.roleName = roleName;
this.createdDttm = createdDttm; this.createdDttm = createdDttm;
this.updatedDttm = updatedDttm; this.updatedDttm = updatedDttm;
this.firstLoginDttm = firstLoginDttm;
this.lastLoginDttm = lastLoginDttm;
} }
} }
@@ -59,21 +71,12 @@ public class MembersDto {
@AllArgsConstructor @AllArgsConstructor
public static class SearchReq { public static class SearchReq {
@Schema(description = "이름(name), 이메일(email), 사번(employeeNo)", example = "name") @Schema(description = "전체, 관리자(ROLE_ADMIN), 라벨러(ROLE_LABELER), 검수자(ROLE_REVIEWER)", example = "")
private String field; private String userRole;
@Schema(description = "키워드", example = "홍길동") @Schema(description = "키워드", example = "홍길동")
private String keyword; private String keyword;
@Schema(description = "라벨러 포함 여부", example = "true")
private boolean labeler = true;
@Schema(description = "검수자 포함 여부", example = "true")
private boolean reviewer = true;
@Schema(description = "운영자 포함 여부", example = "true")
private boolean admin = true;
// 페이징 파라미터 // 페이징 파라미터
@Schema(description = "페이지 번호 (0부터 시작) ", example = "0") @Schema(description = "페이지 번호 (0부터 시작) ", example = "0")
private int page = 0; private int page = 0;
@@ -146,29 +149,10 @@ public class MembersDto {
@Getter @Getter
@Setter @Setter
public static class RolesDto { public static class InitReq {
@Schema(description = "UUID", example = "4e89e487-c828-4a34-a7fc-0d5b0e3b53b5") @Schema(description = "패스워드", example = "")
private UUID uuid; @Size(max = 255)
private String password;
@Schema(description = "역할 ROLE_ADMIN, ROLE_LABELER, ROLE_REVIEWER", example = "ROLE_ADMIN")
@EnumValid(enumClass = RoleType.class)
private String roleName;
public RolesDto(UUID uuid, String roleName) {
this.uuid = uuid;
this.roleName = roleName;
}
}
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class StatusDto {
@Schema(description = "변경할 상태값 ACTIVE, INACTIVE, ARCHIVED", example = "ACTIVE")
@NotBlank
private String status;
} }
} }

View File

@@ -1,11 +1,9 @@
package com.kamco.cd.kamcoback.members.service; package com.kamco.cd.kamcoback.members.service;
import com.kamco.cd.kamcoback.auth.BCryptSaltGenerator;
import com.kamco.cd.kamcoback.members.dto.MembersDto; import com.kamco.cd.kamcoback.members.dto.MembersDto;
import com.kamco.cd.kamcoback.postgres.core.MembersCoreService; import com.kamco.cd.kamcoback.postgres.core.MembersCoreService;
import java.util.UUID; import java.util.UUID;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -17,69 +15,35 @@ public class AdminService {
private final MembersCoreService membersCoreService; private final MembersCoreService membersCoreService;
/** /**
* 회원가입 * 관리자 계정 등록
* *
* @param addReq * @param addReq
* @return * @return
*/ */
@Transactional @Transactional
public Long saveMember(MembersDto.AddReq addReq) { public Long saveMember(MembersDto.AddReq addReq) {
// salt 생성, 사번이 salt
String salt = BCryptSaltGenerator.generateSaltWithEmployeeNo(addReq.getUserId().trim());
// 패스워드 암호화, 초기 패스워드 고정
String hashedPassword = BCrypt.hashpw(addReq.getTempPassword(), salt);
addReq.setTempPassword(hashedPassword);
return membersCoreService.saveMembers(addReq); return membersCoreService.saveMembers(addReq);
} }
/**
* 관리자 계정 수정
*
* @param uuid
* @param updateReq
*/
@Transactional
public void updateMembers(UUID uuid, MembersDto.UpdateReq updateReq) { public void updateMembers(UUID uuid, MembersDto.UpdateReq updateReq) {
membersCoreService.updateMembers(uuid, updateReq); membersCoreService.updateMembers(uuid, updateReq);
} }
/**
* 역할 추가
*
* @param rolesDto
*/
@Transactional
public void saveRoles(MembersDto.RolesDto rolesDto) {
// membersCoreService.saveRoles(rolesDto);
}
/** /**
* 역할 삭제 * 관리자 계정 미사용 처리
*
* @param rolesDto
*/
public void deleteRoles(MembersDto.RolesDto rolesDto) {
// membersCoreService.deleteRoles(rolesDto);
}
/**
* 역할 수정
*
* @param statusDto
*/
public void updateStatus(UUID uuid, MembersDto.StatusDto statusDto) {
// membersCoreService.updateStatus(uuid, statusDto);
}
/**
* 회원 탈퇴
* *
* @param uuid * @param uuid
*/ */
@Transactional
public void deleteAccount(UUID uuid) { public void deleteAccount(UUID uuid) {
// membersCoreService.deleteAccount(uuid); membersCoreService.deleteAccount(uuid);
}
/**
* 패스워드 초기화
*
* @param id
*/
public void resetPassword(Long id) {
// membersCoreService.resetPassword(id);
} }
} }

View File

@@ -0,0 +1,35 @@
package com.kamco.cd.kamcoback.members.service;
import com.kamco.cd.kamcoback.postgres.core.MembersCoreService;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class AuthService {
private final MembersCoreService membersCoreService;
/**
* 로그인 일시 저장
*
* @param uuid
*/
@Transactional
public void saveLogin(UUID uuid) {
membersCoreService.saveLogin(uuid);
}
/**
* 로그인 실패 저장
*
* @param uuid
*/
@Transactional
public void loginFail(UUID uuid) {
membersCoreService.loginFail(uuid);
}
}

View File

@@ -1,11 +1,13 @@
package com.kamco.cd.kamcoback.members.service; package com.kamco.cd.kamcoback.members.service;
import com.kamco.cd.kamcoback.common.exception.CustomApiException;
import com.kamco.cd.kamcoback.members.dto.MembersDto; import com.kamco.cd.kamcoback.members.dto.MembersDto;
import com.kamco.cd.kamcoback.members.dto.MembersDto.Basic; import com.kamco.cd.kamcoback.members.dto.MembersDto.Basic;
import com.kamco.cd.kamcoback.postgres.core.MembersCoreService; import com.kamco.cd.kamcoback.postgres.core.MembersCoreService;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -23,38 +25,22 @@ public class MembersService {
* @return * @return
*/ */
public Page<Basic> findByMembers(MembersDto.SearchReq searchReq) { public Page<Basic> findByMembers(MembersDto.SearchReq searchReq) {
return null; // membersCoreService.findByMembers(searchReq); return membersCoreService.findByMembers(searchReq);
} }
/** /**
* 회원정보 수정 * 패스워드 사용자 변경
* *
* @param uuid * @param id
* @param updateReq * @param initReq
*/ */
// public void updateMember(UUID uuid, MembersDto.UpdateReq updateReq) { @Transactional
// public void resetPassword(Long id, MembersDto.InitReq initReq) {
// if (StringUtils.isNotBlank(updateReq.getPassword())) { if (!isValidPassword(initReq.getPassword())) {
// throw new CustomApiException("WRONG_PASSWORD", HttpStatus.BAD_REQUEST);
// if (!this.isValidPassword(updateReq.getPassword())) { }
// throw new CustomApiException("WRONG_PASSWORD", HttpStatus.BAD_REQUEST); membersCoreService.resetPassword(id, initReq);
// } }
//
// if (StringUtils.isBlank(updateReq.getEmployeeNo())) {
// throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST);
// }
//
// // salt 생성, 사번이 salt
// String salt =
// BCryptSaltGenerator.generateSaltWithEmployeeNo(updateReq.getEmployeeNo().trim());
//
// // 패스워드 암호화, 초기 패스워드 고정
// String hashedPassword = BCrypt.hashpw(updateReq.getPassword(), salt);
// updateReq.setPassword(hashedPassword);
// }
//
// membersCoreService.updateMembers(uuid, updateReq);
// }
/** /**
* 대문자 1개 이상 소문자 1개 이상 숫자 1개 이상 특수문자(!@#$) 1개 이상 * 대문자 1개 이상 소문자 1개 이상 숫자 1개 이상 특수문자(!@#$) 1개 이상

View File

@@ -1,16 +1,20 @@
package com.kamco.cd.kamcoback.postgres.core; package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.auth.BCryptSaltGenerator;
import com.kamco.cd.kamcoback.members.dto.MembersDto; import com.kamco.cd.kamcoback.members.dto.MembersDto;
import com.kamco.cd.kamcoback.members.dto.MembersDto.AddReq; import com.kamco.cd.kamcoback.members.dto.MembersDto.AddReq;
import com.kamco.cd.kamcoback.members.dto.MembersDto.Basic;
import com.kamco.cd.kamcoback.members.exception.MemberException.DuplicateMemberException; import com.kamco.cd.kamcoback.members.exception.MemberException.DuplicateMemberException;
import com.kamco.cd.kamcoback.members.exception.MemberException.DuplicateMemberException.Field; import com.kamco.cd.kamcoback.members.exception.MemberException.DuplicateMemberException.Field;
import com.kamco.cd.kamcoback.members.exception.MemberException.MemberNotFoundException; import com.kamco.cd.kamcoback.members.exception.MemberException.MemberNotFoundException;
import com.kamco.cd.kamcoback.postgres.entity.MemberEntity; import com.kamco.cd.kamcoback.postgres.entity.MemberEntity;
import com.kamco.cd.kamcoback.postgres.repository.members.MembersArchivedRepository;
import com.kamco.cd.kamcoback.postgres.repository.members.MembersRepository; import com.kamco.cd.kamcoback.postgres.repository.members.MembersRepository;
import java.time.ZonedDateTime;
import java.util.UUID; import java.util.UUID;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.mindrot.jbcrypt.BCrypt;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@Service @Service
@@ -18,10 +22,9 @@ import org.springframework.stereotype.Service;
public class MembersCoreService { public class MembersCoreService {
private final MembersRepository membersRepository; private final MembersRepository membersRepository;
private final MembersArchivedRepository memberArchivedRepository;
/** /**
* 회원가입 * 관리자 계정 등록
* *
* @param addReq * @param addReq
* @return * @return
@@ -30,11 +33,17 @@ public class MembersCoreService {
if (membersRepository.existsByUserId(addReq.getUserId())) { if (membersRepository.existsByUserId(addReq.getUserId())) {
throw new DuplicateMemberException(Field.USER_ID, addReq.getUserId()); throw new DuplicateMemberException(Field.USER_ID, addReq.getUserId());
} }
// salt 생성, 사번이 salt
String salt = BCryptSaltGenerator.generateSaltWithEmployeeNo(addReq.getUserId().trim());
// 패스워드 암호화, 초기 패스워드 고정
String hashedPassword = BCrypt.hashpw(addReq.getTempPassword(), salt);
MemberEntity memberEntity = new MemberEntity(); MemberEntity memberEntity = new MemberEntity();
memberEntity.setUserId(addReq.getUserId()); memberEntity.setUserId(addReq.getUserId());
memberEntity.setUserRole(addReq.getUserRole()); memberEntity.setUserRole(addReq.getUserRole());
memberEntity.setTempPassword(addReq.getTempPassword()); memberEntity.setTempPassword(addReq.getTempPassword()); // 임시 패스워드는 암호화 하지 않음
memberEntity.setPassword(addReq.getTempPassword()); memberEntity.setPassword(hashedPassword);
memberEntity.setName(addReq.getName()); memberEntity.setName(addReq.getName());
memberEntity.setEmployeeNo(addReq.getEmployeeNo()); memberEntity.setEmployeeNo(addReq.getEmployeeNo());
@@ -42,7 +51,7 @@ public class MembersCoreService {
} }
/** /**
* 회원정보 수정 * 관리자 계정 수정
* *
* @param uuid * @param uuid
* @param updateReq * @param updateReq
@@ -55,9 +64,9 @@ public class MembersCoreService {
memberEntity.setName(updateReq.getName()); memberEntity.setName(updateReq.getName());
} }
// 임시 패스워드는 암호화 하지 않음
if (StringUtils.isNotBlank(updateReq.getTempPassword())) { if (StringUtils.isNotBlank(updateReq.getTempPassword())) {
memberEntity.setTempPassword(updateReq.getTempPassword()); memberEntity.setTempPassword(updateReq.getTempPassword());
memberEntity.setPassword(updateReq.getTempPassword());
} }
if (StringUtils.isNotBlank(memberEntity.getEmployeeNo())) { if (StringUtils.isNotBlank(memberEntity.getEmployeeNo())) {
@@ -66,123 +75,42 @@ public class MembersCoreService {
membersRepository.save(memberEntity); membersRepository.save(memberEntity);
} }
//
// /**
// * 역할 추가 /**
// * * 관리자 계정 미사용 처리
// * @param rolesDto *
// */ * @param uuid
// public void saveRoles(MembersDto.RolesDto rolesDto) { */
// public void deleteAccount(UUID uuid) {
// MemberEntity memberEntity = MemberEntity memberEntity =
// membersRepository membersRepository.findByUUID(uuid).orElseThrow(() -> new MemberNotFoundException());
// .findByUUID(rolesDto.getUuid())
// .orElseThrow(() -> new MemberNotFoundException()); memberEntity.setStatus("INACTIVE");
// memberEntity.setUpdatedDttm(ZonedDateTime.now());
// if (memberRoleRepository.findByUuidAndRoleName(rolesDto)) { membersRepository.save(memberEntity);
// throw new MemberException.DuplicateMemberException( }
// MemberException.DuplicateMemberException.Field.DEFAULT, "중복된 역할이 있습니다.");
// }
// /**
// MemberRoleEntityId memberRoleEntityId = new MemberRoleEntityId(); * 패스워드 변경
// memberRoleEntityId.setMemberUuid(rolesDto.getUuid()); *
// memberRoleEntityId.setRoleName(rolesDto.getRoleName()); * @param id
// */
// MemberRoleEntity memberRoleEntity = new MemberRoleEntity(); public void resetPassword(Long id, MembersDto.InitReq initReq) {
// memberRoleEntity.setId(memberRoleEntityId); MemberEntity memberEntity =
// memberRoleEntity.setMemberUuid(memberEntity); membersRepository.findById(id).orElseThrow(() -> new MemberNotFoundException());
// memberRoleEntity.setCreatedDttm(ZonedDateTime.now());
// memberRoleRepository.save(memberRoleEntity); String salt =
// } BCryptSaltGenerator.generateSaltWithEmployeeNo(memberEntity.getEmployeeNo().trim());
// // 패스워드 암호화
// /** String hashedPassword = BCrypt.hashpw(initReq.getPassword(), salt);
// * 역할 삭제
// * memberEntity.setPassword(hashedPassword);
// * @param rolesDto memberEntity.setStatus("ACTIVE");
// */ memberEntity.setUpdatedDttm(ZonedDateTime.now());
// public void deleteRoles(MembersDto.RolesDto rolesDto) { membersRepository.save(memberEntity);
// MemberEntity memberEntity = }
// membersRepository
// .findByUUID(rolesDto.getUuid())
// .orElseThrow(() -> new MemberNotFoundException());
//
// MemberRoleEntityId memberRoleEntityId = new MemberRoleEntityId();
// memberRoleEntityId.setMemberUuid(rolesDto.getUuid());
// memberRoleEntityId.setRoleName(rolesDto.getRoleName());
//
// MemberRoleEntity memberRoleEntity = new MemberRoleEntity();
// memberRoleEntity.setId(memberRoleEntityId);
// memberRoleEntity.setMemberUuid(memberEntity);
//
// memberRoleRepository.delete(memberRoleEntity);
// }
//
// /**
// * 상태 수정
// *
// * @param statusDto
// */
// public void updateStatus(UUID uuid, MembersDto.StatusDto statusDto) {
// MemberEntity memberEntity =
// membersRepository.findByUUID(uuid).orElseThrow(() -> new MemberNotFoundException());
//
// memberEntity.setStatus(statusDto.getStatus());
// memberEntity.setUpdatedDttm(ZonedDateTime.now());
// membersRepository.save(memberEntity);
// }
//
// /**
// * 회원 탈퇴
// *
// * @param uuid
// */
// public void deleteAccount(UUID uuid) {
// MemberEntity memberEntity =
// membersRepository.findByUUID(uuid).orElseThrow(() -> new MemberNotFoundException());
//
// MemberArchivedEntityId memberArchivedEntityId = new MemberArchivedEntityId();
// memberArchivedEntityId.setUserId(memberEntity.getId());
// memberArchivedEntityId.setUuid(memberEntity.getUuid());
//
// MemberArchivedEntity memberArchivedEntity = new MemberArchivedEntity();
// memberArchivedEntity.setId(memberArchivedEntityId);
// memberArchivedEntity.setEmployeeNo(memberEntity.getEmployeeNo());
// memberArchivedEntity.setName(memberEntity.getName());
// memberArchivedEntity.setPassword(memberEntity.getPassword());
// memberArchivedEntity.setEmail(memberEntity.getEmail());
// memberArchivedEntity.setStatus(memberEntity.getStatus());
// memberArchivedEntity.setCreatedDttm(memberEntity.getCreatedDttm());
// memberArchivedEntity.setArchivedDttm(ZonedDateTime.now());
// memberArchivedRepository.save(memberArchivedEntity);
//
// memberEntity.setStatus("ARCHIVED");
// memberEntity.setName("**********");
// memberEntity.setEmployeeNo("**********");
// memberEntity.setPassword("**********");
// memberEntity.setEmail("**********");
// memberEntity.setUpdatedDttm(ZonedDateTime.now());
// membersRepository.save(memberEntity);
// }
//
// /**
// * 패스워드 초기화
// *
// * @param id
// */
// public void resetPassword(Long id) {
// MemberEntity memberEntity =
// membersRepository.findById(id).orElseThrow(() -> new MemberNotFoundException());
//
// String salt =
// BCryptSaltGenerator.generateSaltWithEmployeeNo(memberEntity.getEmployeeNo().trim());
// // 패스워드 암호화, 초기 패스워드 고정
// String hashedPassword = BCrypt.hashpw(password, salt);
//
// memberEntity.setPassword(hashedPassword);
// memberEntity.setStatus("INACTIVE");
// memberEntity.setUpdatedDttm(ZonedDateTime.now());
// membersRepository.save(memberEntity);
// }
// //
/** /**
@@ -191,7 +119,42 @@ public class MembersCoreService {
* @param searchReq * @param searchReq
* @return * @return
*/ */
// public Page<Basic> findByMembers(MembersDto.SearchReq searchReq) { public Page<Basic> findByMembers(MembersDto.SearchReq searchReq) {
// return membersRepository.findByMembers(searchReq); return membersRepository.findByMembers(searchReq);
// } }
/**
* 최초 로그인 저장 마지막 로그인 저장
*
* @param uuid
*/
public void saveLogin(UUID uuid) {
MemberEntity memberEntity =
membersRepository.findByUUID(uuid).orElseThrow(() -> new MemberNotFoundException());
if (memberEntity.getFirstLoginDttm() == null) {
memberEntity.setFirstLoginDttm(ZonedDateTime.now());
}
memberEntity.setLastLoginDttm(ZonedDateTime.now());
memberEntity.setLoginFailCount(0);
membersRepository.save(memberEntity);
}
/**
* 로그인 실패시 상태 저장
*
* @param uuid
*/
public void loginFail(UUID uuid) {
MemberEntity memberEntity =
membersRepository.findByUUID(uuid).orElseThrow(() -> new MemberNotFoundException());
int failCnt = memberEntity.getLoginFailCount() + 1;
if (failCnt >= 5) {
memberEntity.setStatus("INACTIVE");
}
memberEntity.setLoginFailCount(failCnt);
membersRepository.save(memberEntity);
}
} }

View File

@@ -73,4 +73,14 @@ public class MemberEntity {
@ColumnDefault("now()") @ColumnDefault("now()")
@Column(name = "updated_dttm") @Column(name = "updated_dttm")
private ZonedDateTime updatedDttm = ZonedDateTime.now(); private ZonedDateTime updatedDttm = ZonedDateTime.now();
@Column(name = "first_login_dttm")
private ZonedDateTime firstLoginDttm;
@Column(name = "last_login_dttm")
private ZonedDateTime lastLoginDttm;
@Column(name = "login_fail_count")
@ColumnDefault("0")
private Integer loginFailCount = 0;
} }

View File

@@ -1,20 +1,19 @@
package com.kamco.cd.kamcoback.postgres.repository.members; package com.kamco.cd.kamcoback.postgres.repository.members;
import com.kamco.cd.kamcoback.members.dto.MembersDto;
import com.kamco.cd.kamcoback.members.dto.MembersDto.Basic;
import com.kamco.cd.kamcoback.postgres.entity.MemberEntity; import com.kamco.cd.kamcoback.postgres.entity.MemberEntity;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import org.springframework.data.domain.Page;
public interface MembersRepositoryCustom { public interface MembersRepositoryCustom {
boolean existsByUserId(String userId); boolean existsByUserId(String userId);
Optional<MemberEntity> findByUserId(String employeeNo); Optional<MemberEntity> findByUserId(String userId);
Optional<MemberEntity> findByUUID(UUID uuid); Optional<MemberEntity> findByUUID(UUID uuid);
//
// Page<Basic> findByMembers(MembersDto.SearchReq searchReq);
//
// Page<Basic> findByMembers(MembersDto.SearchReq searchReq);
// Optional<MemberEntity> findByEmployeeNo(String employeeNo);
} }

View File

@@ -1,11 +1,20 @@
package com.kamco.cd.kamcoback.postgres.repository.members; package com.kamco.cd.kamcoback.postgres.repository.members;
import com.kamco.cd.kamcoback.members.dto.MembersDto;
import com.kamco.cd.kamcoback.members.dto.MembersDto.Basic;
import com.kamco.cd.kamcoback.postgres.entity.MemberEntity; import com.kamco.cd.kamcoback.postgres.entity.MemberEntity;
import com.kamco.cd.kamcoback.postgres.entity.QMemberEntity; import com.kamco.cd.kamcoback.postgres.entity.QMemberEntity;
import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.types.Projections;
import com.querydsl.jpa.impl.JPAQueryFactory; import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository; import org.springframework.stereotype.Repository;
@Repository @Repository
@@ -31,86 +40,85 @@ public class MembersRepositoryImpl implements MembersRepositoryCustom {
!= null; != null;
} }
/**
* 사용자 조회 user id
*
* @param userId
* @return
*/
@Override @Override
public Optional<MemberEntity> findByUserId(String userId) { public Optional<MemberEntity> findByUserId(String userId) {
return Optional.ofNullable( return Optional.ofNullable(
queryFactory.selectFrom(memberEntity).where(memberEntity.userId.eq(userId)).fetchOne()); queryFactory.selectFrom(memberEntity).where(memberEntity.userId.eq(userId)).fetchOne());
} }
// /** /**
// * 회원정보 목록 조회 * 회원정보 목록 조회
// * *
// * @param searchReq * @param searchReq
// * @return * @return
// */ */
// @Override @Override
// public Page<Basic> findByMembers(MembersDto.SearchReq searchReq) { public Page<Basic> findByMembers(MembersDto.SearchReq searchReq) {
// Pageable pageable = searchReq.toPageable(); Pageable pageable = searchReq.toPageable();
// BooleanBuilder builder = new BooleanBuilder(); BooleanBuilder builder = new BooleanBuilder();
// BooleanBuilder leftBuilder = new BooleanBuilder();
// // 검색어
// if (StringUtils.isNotBlank(searchReq.getField())) { if (StringUtils.isNotBlank(searchReq.getKeyword())) {
// switch (searchReq.getField()) { String contains = "%" + searchReq.getKeyword() + "%";
// case "name" ->
// builder.and(memberEntity.name.containsIgnoreCase(searchReq.getKeyword().trim())); builder.and(
// } memberEntity.name.likeIgnoreCase(contains)
// } .or(memberEntity.userId.likeIgnoreCase(contains))
// .or(memberEntity.employeeNo.likeIgnoreCase(contains))
// List<String> roles = new ArrayList<>(); );
// // 라벨러 }
// if (searchReq.isLabeler()) {
// roles.add(RoleType.ROLE_LABELER.getId()); // 권한
// } if (StringUtils.isNotBlank(searchReq.getUserRole())) {
// builder.and(memberEntity.userRole.eq(searchReq.getUserRole()));
// // 시스템 전체 관리자 }
// if (searchReq.isAdmin()) {
// roles.add(RoleType.ROLE_ADMIN.getId()); List<MembersDto.Basic> content =
// } queryFactory
// .select(
// // 검수자 Projections.constructor(
// if (searchReq.isReviewer()) { MembersDto.Basic.class,
// roles.add(RoleType.ROLE_REVIEWER.getId()); memberEntity.id,
// } memberEntity.uuid,
// memberEntity.userRole,
// // 역할 in 조건 추가 memberEntity.name,
// if (!roles.isEmpty()) { memberEntity.userId,
// leftBuilder.and(memberRoleEntity.id.roleName.in(roles)); memberEntity.employeeNo,
// } memberEntity.tempPassword,
// memberEntity.status,
// List<MembersDto.Basic> content = memberEntity.createdDttm,
// queryFactory memberEntity.updatedDttm,
// .select( memberEntity.firstLoginDttm,
// Projections.constructor( memberEntity.lastLoginDttm
// MembersDto.Basic.class, ))
// memberEntity.id, .from(memberEntity)
// memberEntity.uuid, .where(builder)
// memberEntity.employeeNo, .offset(pageable.getOffset())
// memberEntity.name, .limit(pageable.getPageSize())
// null, .orderBy(memberEntity.createdDttm.desc())
// memberEntity.status, .fetch();
// memberRoleEntity.id.roleName,
// memberEntity.createdDttm, long total =
// memberEntity.updatedDttm)) queryFactory
// .from(memberEntity) .select(memberEntity)
// .leftJoin(memberRoleEntity) .from(memberEntity)
// .on(memberRoleEntity.memberUuid.uuid.eq(memberEntity.uuid).and(leftBuilder)) .fetchCount();
// .where(builder)
// .offset(pageable.getOffset()) return new PageImpl<>(content, pageable, total);
// .limit(pageable.getPageSize()) }
// .orderBy(memberEntity.createdDttm.desc())
// .fetch(); /**
// * 사용자 ID 조회 UUID
// long total = *
// queryFactory * @param uuid
// .select(memberEntity) * @return
// .from(memberEntity) */
// .leftJoin(memberRoleEntity)
// .on(memberRoleEntity.memberUuid.uuid.eq(memberEntity.uuid).and(leftBuilder))
// .fetchCount();
//
// return new PageImpl<>(content, pageable, total);
// }
//
@Override @Override
public Optional<MemberEntity> findByUUID(UUID uuid) { public Optional<MemberEntity> findByUUID(UUID uuid) {
return Optional.ofNullable( return Optional.ofNullable(