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,14 +1,16 @@
package DGU_AI_LAB.admin_be.domain.users.controller;

import DGU_AI_LAB.admin_be.domain.users.dto.request.PasswordUpdateRequestDTO;
import DGU_AI_LAB.admin_be.domain.users.dto.request.PhoneUpdateRequestDTO;
import DGU_AI_LAB.admin_be.domain.users.dto.response.UserResponseDTO;
import DGU_AI_LAB.admin_be.domain.users.service.UserService;
import DGU_AI_LAB.admin_be.global.auth.CustomUserDetails;
import DGU_AI_LAB.admin_be.global.common.SuccessResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
Expand All @@ -17,11 +19,39 @@ public class UserController {

private final UserService userService;

/**
* 사용자 정보 확인 API
* @param principal
* @return
*/
@GetMapping("/me")
public ResponseEntity<SuccessResponse<?>> getMyInfo(
@AuthenticationPrincipal CustomUserDetails principal
) {
return SuccessResponse.ok(userService.getMyInfo(principal.getUserId()));
}
}

/**
* 사용자 비밀번호 변경 API
* PATCH /api/users/me/password
*/
@PatchMapping("/me/password")
public ResponseEntity<SuccessResponse<?>> updateUserPassword(@AuthenticationPrincipal CustomUserDetails principal,
@RequestBody @Valid PasswordUpdateRequestDTO request
) {
UserResponseDTO updatedUser = userService.updatePassword(principal.getUserId(), request);
return SuccessResponse.ok(updatedUser);
}

/**
* 사용자 연락처 변경 API
* PATCH /api/users/me/phone
*/
@PatchMapping("/me/phone")
public ResponseEntity<SuccessResponse<?>> updateUserPhone(@AuthenticationPrincipal CustomUserDetails principal,
@RequestBody @Valid PhoneUpdateRequestDTO request
) {
UserResponseDTO updatedUser = userService.updatePhone(principal.getUserId(), request);
return SuccessResponse.ok(updatedUser);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package DGU_AI_LAB.admin_be.domain.users.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public record PasswordUpdateRequestDTO(
@NotBlank(message = "현재 비밀번호는 필수로 입력해야 합니다.")
String currentPassword,
@NotBlank(message = "새 비밀번호는 필수로 입력해야 합니다.")
// @Size(min = 8, message = "새 비밀번호는 최소 8자 이상이어야 합니다.")
String newPassword
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package DGU_AI_LAB.admin_be.domain.users.dto.request;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Pattern;

public record PhoneUpdateRequestDTO(
@NotBlank(message = "연락처는 필수로 입력해야 합니다.")
@Pattern(regexp = "^\\d{2,3}-\\d{3,4}-\\d{4}$", message = "유효한 전화번호 형식이 아닙니다. (예: 010-1234-5678)")
String newPhone
) {}
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,12 @@ public void updateUserInfo(String encodedPassword, Boolean isActive) {
if (encodedPassword != null) this.password = encodedPassword;
if (isActive != null) this.isActive = isActive;
}

public void updatePassword(String newEncodedPassword) {
this.password = newEncodedPassword;
}

public void updatePhone(String newPhone) {
this.phone = newPhone;
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
package DGU_AI_LAB.admin_be.domain.users.service;

import DGU_AI_LAB.admin_be.domain.groups.repository.GroupRepository;
import DGU_AI_LAB.admin_be.domain.users.dto.request.PasswordUpdateRequestDTO;
import DGU_AI_LAB.admin_be.domain.users.dto.request.PhoneUpdateRequestDTO;
import DGU_AI_LAB.admin_be.domain.users.dto.request.UserCreateRequestDTO;
import DGU_AI_LAB.admin_be.domain.users.dto.request.UserUpdateRequestDTO;
import DGU_AI_LAB.admin_be.domain.users.dto.response.MyInfoResponseDTO;
import DGU_AI_LAB.admin_be.domain.users.dto.response.UserResponseDTO;
import DGU_AI_LAB.admin_be.domain.users.dto.response.UserSummaryDTO;
import DGU_AI_LAB.admin_be.domain.users.entity.User;
import DGU_AI_LAB.admin_be.domain.users.repository.UserRepository;
import DGU_AI_LAB.admin_be.error.ErrorCode;
import DGU_AI_LAB.admin_be.error.exception.BusinessException;
import DGU_AI_LAB.admin_be.error.exception.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -23,7 +28,7 @@
public class UserService {

private final UserRepository userRepository;
private final GroupRepository groupRepository;
private final PasswordEncoder passwordEncoder;

private static final long UID_BASE = 10000; // TODO: 이부분 시스템에 맞추어서 수정하기

Expand Down Expand Up @@ -84,4 +89,45 @@ public MyInfoResponseDTO getMyInfo(Long userId) {
.orElseThrow(() -> new EntityNotFoundException(ErrorCode.ENTITY_NOT_FOUND));
return MyInfoResponseDTO.fromEntity(user);
}
}

/**
* 사용자 비밀번호 변경
*/
public UserResponseDTO updatePassword(Long userId, PasswordUpdateRequestDTO request) {
log.info("[updatePassword] userId={} 비밀번호 변경 시도", userId);

User user = userRepository.findById(userId)
.orElseThrow(() -> new EntityNotFoundException(ErrorCode.USER_NOT_FOUND)); // ⭐ USER_NOT_FOUND 사용

// 현재 비밀번호 확인
if (!passwordEncoder.matches(request.currentPassword(), user.getPassword())) {
log.warn("[updatePassword] userId={} 현재 비밀번호 불일치", userId);
throw new BusinessException(ErrorCode.INVALID_PASSWORD);
}

// 새 비밀번호가 현재 비밀번호와 동일한지 확인
if (passwordEncoder.matches(request.newPassword(), user.getPassword())) {
log.warn("[updatePassword] userId={} 새 비밀번호가 현재 비밀번호와 동일", userId);
throw new BusinessException(ErrorCode.PASSWORD_CHANGE_SAME_AS_OLD);
}

// 새 비밀번호 암호화 및 업데이트
String encodedNewPassword = passwordEncoder.encode(request.newPassword());
user.updatePassword(encodedNewPassword);
log.info("[updatePassword] userId={} 비밀번호 변경 완료", userId);
return UserResponseDTO.fromEntity(user);
}

/**
* 사용자 연락처 변경
*/
public UserResponseDTO updatePhone(Long userId, PhoneUpdateRequestDTO request) {
log.info("[updatePhone] userId={} 연락처 변경 시도", userId);

User user = userRepository.findById(userId)
.orElseThrow(() -> new EntityNotFoundException(ErrorCode.USER_NOT_FOUND));
user.updatePhone(request.newPhone());
log.info("[updatePhone] userId={} 연락처 변경 완료", userId);
return UserResponseDTO.fromEntity(user);
}
}
4 changes: 4 additions & 0 deletions src/main/java/DGU_AI_LAB/admin_be/error/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ public enum ErrorCode {
INVALID_AUTH_CODE(HttpStatus.BAD_REQUEST, "올바르지 않은 인증 코드입니다."),
GROUP_NOT_FOUND(HttpStatus.NOT_FOUND, "지정된 그룹을 찾을 수 없습니다."),
UID_ALLOCATION_FAILED(HttpStatus.BAD_REQUEST, "UID를 할당하는 데 실패했습니다."), // 이 부분 고민
INVALID_PASSWORD(HttpStatus.UNAUTHORIZED, "현재 비밀번호가 일치하지 않습니다."),
PASSWORD_CHANGE_SAME_AS_OLD(HttpStatus.BAD_REQUEST, "새 비밀번호가 현재 비밀번호와 동일합니다."),



/**
* Approval Error
Expand Down