mirror of
https://github.com/cna-bootcamp/phonebill.git
synced 2026-06-13 12:09:10 +00:00
회선번호 처리 개선 및 다양한 API 기능 강화
- user-service: 회원등록 API를 upsert 방식으로 변경 (기존 사용자 업데이트 지원) - user-service: userName 필드 응답 누락 문제 해결 (DB 데이터 업데이트) - kos-mock: Mock 데이터 생성 기간을 3개월에서 6개월로 확장 - product-service: 회선번호 대시 처리 지원 (010-1234-5678, 01012345678 모두 허용) - bill-service: 회선번호 대시 선택적 처리 지원 (유연한 입력 형식) - api-gateway: CORS 중복 헤더 제거 필터 추가 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -29,7 +29,7 @@ import java.util.List;
|
||||
public class SecurityConfig {
|
||||
|
||||
private final JwtTokenProvider jwtTokenProvider;
|
||||
@Value("${cors.allowed-origins")
|
||||
@Value("${cors.allowed-origins}")
|
||||
private String allowedOrigins;
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -79,60 +79,6 @@ public class UserController {
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 고객 ID로 사용자 정보 조회
|
||||
* @param customerId 고객 ID
|
||||
* @return 사용자 정보
|
||||
*/
|
||||
@Operation(
|
||||
summary = "고객 ID로 사용자 정보 조회",
|
||||
description = "고객 ID로 해당 고객의 사용자 정보를 조회합니다."
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "조회 성공"),
|
||||
@ApiResponse(responseCode = "404", description = "사용자를 찾을 수 없음"),
|
||||
@ApiResponse(responseCode = "500", description = "서버 내부 오류")
|
||||
})
|
||||
@GetMapping("/by-customer/{customerId}")
|
||||
public ResponseEntity<UserInfoResponse> getUserInfoByCustomerId(
|
||||
@Parameter(description = "고객 ID", required = true)
|
||||
@PathVariable String customerId
|
||||
) {
|
||||
log.info("고객 ID로 사용자 정보 조회 요청: customerId={}", customerId);
|
||||
|
||||
UserInfoResponse response = userService.getUserInfoByCustomerId(customerId);
|
||||
|
||||
log.info("고객 ID로 사용자 정보 조회 성공: customerId={}", customerId);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 회선번호로 사용자 정보 조회
|
||||
* @param lineNumber 회선번호
|
||||
* @return 사용자 정보
|
||||
*/
|
||||
@Operation(
|
||||
summary = "회선번호로 사용자 정보 조회",
|
||||
description = "회선번호로 해당 회선의 사용자 정보를 조회합니다."
|
||||
)
|
||||
@ApiResponses(value = {
|
||||
@ApiResponse(responseCode = "200", description = "조회 성공"),
|
||||
@ApiResponse(responseCode = "404", description = "사용자를 찾을 수 없음"),
|
||||
@ApiResponse(responseCode = "500", description = "서버 내부 오류")
|
||||
})
|
||||
@GetMapping("/by-line/{lineNumber}")
|
||||
public ResponseEntity<UserInfoResponse> getUserInfoByLineNumber(
|
||||
@Parameter(description = "회선번호", required = true)
|
||||
@PathVariable String lineNumber
|
||||
) {
|
||||
log.info("회선번호로 사용자 정보 조회 요청: lineNumber={}", lineNumber);
|
||||
|
||||
UserInfoResponse response = userService.getUserInfoByLineNumber(lineNumber);
|
||||
|
||||
log.info("회선번호로 사용자 정보 조회 성공: lineNumber={}", lineNumber);
|
||||
return ResponseEntity.ok(response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 권한 부여
|
||||
* @param userId 사용자 ID
|
||||
|
||||
@@ -17,11 +17,4 @@ public class UserNotFoundException extends RuntimeException {
|
||||
return new UserNotFoundException("사용자를 찾을 수 없습니다. userId: " + userId);
|
||||
}
|
||||
|
||||
public static UserNotFoundException byCustomerId(String customerId) {
|
||||
return new UserNotFoundException("사용자를 찾을 수 없습니다. customerId: " + customerId);
|
||||
}
|
||||
|
||||
public static UserNotFoundException byLineNumber(String lineNumber) {
|
||||
return new UserNotFoundException("사용자를 찾을 수 없습니다. lineNumber: " + lineNumber);
|
||||
}
|
||||
}
|
||||
@@ -88,54 +88,6 @@ public class UserService {
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 고객 ID로 사용자 정보 조회
|
||||
* @param customerId 고객 ID
|
||||
* @return 사용자 정보
|
||||
*/
|
||||
public UserInfoResponse getUserInfoByCustomerId(String customerId) {
|
||||
AuthUserEntity user = authUserRepository.findByCustomerId(customerId)
|
||||
.orElseThrow(() -> UserNotFoundException.byCustomerId(customerId));
|
||||
|
||||
// 사용자 권한 목록 조회
|
||||
List<String> permissions = authUserPermissionRepository.findPermissionCodesByUserId(user.getUserId());
|
||||
|
||||
return UserInfoResponse.builder()
|
||||
.userId(user.getUserId())
|
||||
.customerId(user.getCustomerId())
|
||||
.lineNumber(user.getLineNumber())
|
||||
.userName(user.getUserName())
|
||||
.accountStatus(user.getAccountStatus().name())
|
||||
.lastLoginAt(user.getLastLoginAt())
|
||||
.lastPasswordChangedAt(user.getLastPasswordChangedAt())
|
||||
.permissions(permissions)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 회선번호로 사용자 정보 조회
|
||||
* @param lineNumber 회선번호
|
||||
* @return 사용자 정보
|
||||
*/
|
||||
public UserInfoResponse getUserInfoByLineNumber(String lineNumber) {
|
||||
AuthUserEntity user = authUserRepository.findByLineNumber(lineNumber)
|
||||
.orElseThrow(() -> UserNotFoundException.byLineNumber(lineNumber));
|
||||
|
||||
// 사용자 권한 목록 조회
|
||||
List<String> permissions = authUserPermissionRepository.findPermissionCodesByUserId(user.getUserId());
|
||||
|
||||
return UserInfoResponse.builder()
|
||||
.userId(user.getUserId())
|
||||
.customerId(user.getCustomerId())
|
||||
.lineNumber(user.getLineNumber())
|
||||
.userName(user.getUserName())
|
||||
.accountStatus(user.getAccountStatus().name())
|
||||
.lastLoginAt(user.getLastLoginAt())
|
||||
.lastPasswordChangedAt(user.getLastPasswordChangedAt())
|
||||
.permissions(permissions)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 권한 부여
|
||||
* @param userId 사용자 ID
|
||||
@@ -231,45 +183,53 @@ public class UserService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자 등록
|
||||
* 사용자 등록 또는 업데이트 (Upsert)
|
||||
* @param request 사용자 등록 요청
|
||||
* @return 등록된 사용자 정보
|
||||
* @return 등록/업데이트된 사용자 정보
|
||||
*/
|
||||
@Transactional
|
||||
public UserRegistrationResponse registerUser(UserRegistrationRequest request) {
|
||||
log.info("사용자 등록 요청: userId={}, customerId={}", request.getUserId(), request.getCustomerId());
|
||||
|
||||
// 중복 검사
|
||||
validateUserUniqueness(request);
|
||||
log.info("사용자 등록/업데이트 요청: userId={}, customerId={}", request.getUserId(), request.getCustomerId());
|
||||
|
||||
// 권한 코드 유효성 검증
|
||||
validatePermissionCodes(request.getPermissions());
|
||||
|
||||
// 사용자 엔티티 생성
|
||||
AuthUserEntity user = createUserEntity(request);
|
||||
// 기존 사용자 확인
|
||||
Optional<AuthUserEntity> existingUser = authUserRepository.findById(request.getUserId());
|
||||
|
||||
// 사용자 저장
|
||||
AuthUserEntity savedUser = authUserRepository.save(user);
|
||||
AuthUserEntity savedUser;
|
||||
boolean isUpdate = false;
|
||||
|
||||
// 권한 부여
|
||||
grantUserPermissions(savedUser.getUserId(), request.getPermissions());
|
||||
if (existingUser.isPresent()) {
|
||||
// 업데이트 로직
|
||||
savedUser = updateExistingUser(existingUser.get(), request);
|
||||
isUpdate = true;
|
||||
log.info("기존 사용자 업데이트: userId={}", request.getUserId());
|
||||
} else {
|
||||
// 새 사용자 등록 전 유니크 검사
|
||||
validateUserUniquenessForNewUser(request);
|
||||
|
||||
// 사용자 엔티티 생성
|
||||
AuthUserEntity user = createUserEntity(request);
|
||||
|
||||
// 사용자 저장
|
||||
savedUser = authUserRepository.save(user);
|
||||
log.info("신규 사용자 등록: userId={}", request.getUserId());
|
||||
}
|
||||
|
||||
// 권한 부여/업데이트
|
||||
updateUserPermissions(savedUser.getUserId(), request.getPermissions());
|
||||
|
||||
// 응답 생성
|
||||
UserRegistrationResponse response = buildRegistrationResponse(savedUser, request.getPermissions(), request.getUserName());
|
||||
UserRegistrationResponse response = buildRegistrationResponse(savedUser, request.getPermissions(), request.getUserName(), isUpdate);
|
||||
|
||||
log.info("사용자 등록 완료: userId={}", savedUser.getUserId());
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자 유니크 필드 중복 검사
|
||||
* 신규 사용자 등록 시 유니크 필드 중복 검사
|
||||
*/
|
||||
private void validateUserUniqueness(UserRegistrationRequest request) {
|
||||
// 사용자 ID 중복 확인
|
||||
if (authUserRepository.existsByUserId(request.getUserId())) {
|
||||
throw new RuntimeException("이미 존재하는 사용자 ID입니다: " + request.getUserId());
|
||||
}
|
||||
|
||||
private void validateUserUniquenessForNewUser(UserRegistrationRequest request) {
|
||||
// 고객 ID 중복 확인
|
||||
if (authUserRepository.existsByCustomerId(request.getCustomerId())) {
|
||||
throw new RuntimeException("이미 존재하는 고객 ID입니다: " + request.getCustomerId());
|
||||
@@ -281,6 +241,67 @@ public class UserService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 기존 사용자 정보 업데이트
|
||||
*/
|
||||
private AuthUserEntity updateExistingUser(AuthUserEntity existingUser, UserRegistrationRequest request) {
|
||||
// 다른 사용자가 같은 customerId나 lineNumber를 사용하는지 확인
|
||||
validateUniqueFieldsForUpdate(existingUser.getUserId(), request);
|
||||
|
||||
// Salt 생성 (UUID 기반)
|
||||
String salt = java.util.UUID.randomUUID().toString().replace("-", "").substring(0, 16);
|
||||
|
||||
// password + salt 결합 후 해시
|
||||
String saltedPassword = request.getPassword() + salt;
|
||||
String hashedPassword = passwordEncoder.encode(saltedPassword);
|
||||
|
||||
// 기존 엔티티 업데이트 (Builder 패턴 사용을 위해 새 엔티티 생성)
|
||||
AuthUserEntity updatedUser = AuthUserEntity.builder()
|
||||
.userId(existingUser.getUserId())
|
||||
.customerId(request.getCustomerId())
|
||||
.lineNumber(request.getLineNumber())
|
||||
.userName(request.getUserName())
|
||||
.passwordHash(hashedPassword)
|
||||
.passwordSalt(salt)
|
||||
.accountStatus(existingUser.getAccountStatus())
|
||||
.failedLoginCount(existingUser.getFailedLoginCount())
|
||||
.lastFailedLoginAt(existingUser.getLastFailedLoginAt())
|
||||
.accountLockedUntil(existingUser.getAccountLockedUntil())
|
||||
.lastLoginAt(existingUser.getLastLoginAt())
|
||||
.lastPasswordChangedAt(existingUser.getLastPasswordChangedAt())
|
||||
.build();
|
||||
|
||||
return authUserRepository.save(updatedUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 업데이트 시 다른 사용자와의 유니크 필드 중복 검사
|
||||
*/
|
||||
private void validateUniqueFieldsForUpdate(String userId, UserRegistrationRequest request) {
|
||||
// 현재 사용자가 아닌 다른 사용자가 같은 customerId를 사용하는지 확인
|
||||
Optional<AuthUserEntity> existingCustomer = authUserRepository.findByCustomerId(request.getCustomerId());
|
||||
if (existingCustomer.isPresent() && !existingCustomer.get().getUserId().equals(userId)) {
|
||||
throw new RuntimeException("이미 다른 사용자가 사용하는 고객 ID입니다: " + request.getCustomerId());
|
||||
}
|
||||
|
||||
// 현재 사용자가 아닌 다른 사용자가 같은 lineNumber를 사용하는지 확인
|
||||
Optional<AuthUserEntity> existingLine = authUserRepository.findByLineNumber(request.getLineNumber());
|
||||
if (existingLine.isPresent() && !existingLine.get().getUserId().equals(userId)) {
|
||||
throw new RuntimeException("이미 다른 사용자가 사용하는 회선번호입니다: " + request.getLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자 권한 업데이트 (기존 권한 모두 제거 후 새로 추가)
|
||||
*/
|
||||
private void updateUserPermissions(String userId, List<String> permissionCodes) {
|
||||
// 기존 권한 모두 철회
|
||||
authUserPermissionRepository.deleteAllByUserId(userId);
|
||||
|
||||
// 새 권한 부여
|
||||
grantUserPermissions(userId, permissionCodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* 권한 코드 유효성 검증
|
||||
*/
|
||||
@@ -332,9 +353,9 @@ public class UserService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자 등록 응답 생성
|
||||
* 사용자 등록/업데이트 응답 생성
|
||||
*/
|
||||
private UserRegistrationResponse buildRegistrationResponse(AuthUserEntity user, List<String> permissions, String userName) {
|
||||
private UserRegistrationResponse buildRegistrationResponse(AuthUserEntity user, List<String> permissions, String userName, boolean isUpdate) {
|
||||
UserRegistrationResponse.UserData userData = UserRegistrationResponse.UserData.builder()
|
||||
.userId(user.getUserId())
|
||||
.customerId(user.getCustomerId())
|
||||
@@ -345,9 +366,11 @@ public class UserService {
|
||||
.permissions(permissions)
|
||||
.build();
|
||||
|
||||
String message = isUpdate ? "사용자 정보가 성공적으로 업데이트되었습니다." : "사용자가 성공적으로 등록되었습니다.";
|
||||
|
||||
return UserRegistrationResponse.builder()
|
||||
.success(true)
|
||||
.message("사용자가 성공적으로 등록되었습니다.")
|
||||
.message(message)
|
||||
.data(userData)
|
||||
.build();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user