diff --git a/.gitignore b/.gitignore index c26cc44..b26b389 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,12 @@ +### mcp ### +.mcp.json + ### claude ### CLAUDE.md +.claude/settings.local.json +.claude/check-mcp-updates.sh +.claude/*.log +.claude/transcripts/ ### gradle ### HELP.md diff --git a/src/main/java/greenfirst/be/global/common/response/base/BaseResponseStatus.java b/src/main/java/greenfirst/be/global/common/response/base/BaseResponseStatus.java index a5a889a..3fcb6fe 100644 --- a/src/main/java/greenfirst/be/global/common/response/base/BaseResponseStatus.java +++ b/src/main/java/greenfirst/be/global/common/response/base/BaseResponseStatus.java @@ -70,7 +70,7 @@ public enum BaseResponseStatus implements BaseResponseStatusInterface { PASSWORD_NOT_MATCH(HttpStatus.BAD_REQUEST, false, 2025, "입력하신 비밀번호가 일치하지 않습니다. 다시 확인해주세요."), NO_EXIST_USER_STATISTICS(HttpStatus.NOT_FOUND, false, 2026, "존재하지 않는 유저 통계입니다."), INVALID_USER_TYPE(HttpStatus.BAD_REQUEST, false, 2027, "유효하지 않은 유저 타입입니다. 다시 확인해주세요."), - NO_AUTHORITY_TO_SEARCH_USER(HttpStatus.FORBIDDEN, false, 2028, "해당 타입의 유저를 조회할 권한이 없습니다."), + NO_AUTHORITY_TO_SEARCH_USER(HttpStatus.FORBIDDEN, false, 2028, "해당 유저를 조회할 권한이 없습니다."), /** * 3000: admin service error diff --git a/src/main/java/greenfirst/be/user/adapter/in/web/controller/GetUserDataController.java b/src/main/java/greenfirst/be/user/adapter/in/web/controller/GetUserDataController.java index a4e0069..e544ffa 100644 --- a/src/main/java/greenfirst/be/user/adapter/in/web/controller/GetUserDataController.java +++ b/src/main/java/greenfirst/be/user/adapter/in/web/controller/GetUserDataController.java @@ -4,10 +4,13 @@ import greenfirst.be.global.common.enums.common.UserType; import greenfirst.be.global.common.response.base.BaseResponse; import greenfirst.be.global.common.security.CustomUserDetails; +import greenfirst.be.user.adapter.in.web.response.UserProfileResponse; import greenfirst.be.user.adapter.in.web.response.UserSimpleDataListResponse; +import greenfirst.be.user.application.dto.in.GetUserByUuidInDto; import greenfirst.be.user.application.dto.in.GetUserSimpleDataListInDto; import greenfirst.be.user.application.dto.out.UserDataListOutDto; import greenfirst.be.user.application.service.GetUserDataService; +import greenfirst.be.user.domain.model.Users; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import lombok.RequiredArgsConstructor; @@ -18,12 +21,10 @@ import org.springframework.lang.Nullable; import org.springframework.security.access.prepost.PreAuthorize; 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.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import java.util.List; +import java.util.UUID; @Slf4j @@ -41,6 +42,8 @@ public class GetUserDataController { /** * 유저 데이터 조회 컨트롤러 * 1. (admin/agency) 전체 유저 목록 조회 + * 2. (common) 본인 프로필 조회 + * 3. (admin/agency) 다른 유저 프로필 조회 */ // 1. (admin/agency) 유저 목록 조회/검색 @@ -75,4 +78,49 @@ public BaseResponse getUserList( return new BaseResponse<>(response); } + + // 2. (common) 본인 프로필 조회 + @Operation(summary = "본인 프로필 조회", description = "로그인한 사용자 본인의 프로필 정보를 조회합니다", tags = { "User - Admin", "User - Agency", "User - Partner", "User - User" }) + @GetMapping("/profile/my") + @SecurityRequirement(name = "Bearer Auth") + public BaseResponse getMyProfile(@AuthenticationPrincipal CustomUserDetails authentication) { + + // 본인 프로필 조회 + Users user = getUserDataService.getByUserUuid(authentication.getUserUuid()); + + // mapping + UserProfileResponse response = modelMapper.map(user, UserProfileResponse.class); + + // result + return new BaseResponse<>(response); + } + + + // 3. (admin/agency) 다른 유저 프로필 조회 + @Operation(summary = "(관리자/대리점) 유저 프로필 조회", description = "UUID로 특정 사용자의 프로필 정보를 조회합니다 (권한 검증 포함)", tags = { "User - Admin", "User - Agency" }) + @GetMapping("/profile/{userUuid}") + @PreAuthorize("hasAnyAuthority('ADMIN', 'AGENCY')") + @SecurityRequirement(name = "Bearer Auth") + public BaseResponse getUserProfileByUuid( + @AuthenticationPrincipal CustomUserDetails authentication, + @PathVariable UUID userUuid + ) { + + // mapping + GetUserByUuidInDto inDto = GetUserByUuidInDto.builder() + .requestorType(authentication.getUserType()) + .requestorUuid(authentication.getUserUuid()) + .targetUserUuid(userUuid) + .build(); + + // 유저 프로필 조회 + Users user = getUserDataService.getUserByUuidWithAuthorityCheck(inDto); + + // mapping + UserProfileResponse response = modelMapper.map(user, UserProfileResponse.class); + + // result + return new BaseResponse<>(response); + } + } diff --git a/src/main/java/greenfirst/be/user/adapter/in/web/controller/PartnerAuthController.java b/src/main/java/greenfirst/be/user/adapter/in/web/controller/PartnerAuthController.java index bf1b66a..518ea81 100644 --- a/src/main/java/greenfirst/be/user/adapter/in/web/controller/PartnerAuthController.java +++ b/src/main/java/greenfirst/be/user/adapter/in/web/controller/PartnerAuthController.java @@ -16,9 +16,11 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.modelmapper.ModelMapper; +import org.springframework.web.bind.annotation.GetMapping; 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; import org.springframework.web.bind.annotation.RestController; @@ -37,13 +39,61 @@ public class PartnerAuthController { /** * 파트너 인증 컨트롤러 - * 1. (personal partner) 개인 파트너 회원가입 - * 2. (corp partner) 기업 파트너 회원가입 - * 3. (personal partner) 개인 파트너 로그인 - * 4. (corp partner) 기업 파트너 로그인 + * 1. 아이디 중복 확인 (기업 파트너 회원가입시 사용) + * 2. 휴대폰번호 중복 확인 + * 3. (personal partner) 개인 파트너 회원가입 + * 4. (corp partner) 기업 파트너 회원가입 + * 5. (personal partner) 개인 파트너 로그인 + * 6. (corp partner) 기업 파트너 로그인 */ - // 1. (personal partner) 개인 파트너 회원가입 + // 1. 아이디 중복 확인 (기업 파트너 회원가입시 사용) + /** + * API 설명: 기업 파트너 회원가입 시 로그인 아이디의 중복 여부를 확인합니다. + * + * 목표: + * - 입력받은 로그인 아이디가 이미 존재하는지 확인 + * - 중복 여부를 boolean으로 반환 + * + * 응답값: + * { + * "duplicateStatus": boolean // true: 중복됨, false: 사용 가능 + * } + */ + @Operation(summary = "아이디 중복 확인", description = "기업 파트너 회원가입 시 로그인 아이디 중복 여부 확인") + @GetMapping("/validation/login-id") + public BaseResponse checkLoginIdDuplicate( + @RequestParam String userLoginId + ) { + // TODO: 아이디 중복 확인 로직 구현 + return null; + } + + + // 2. 휴대폰번호 중복 확인 + /** + * API 설명: 회원가입 시 휴대폰번호의 중복 여부를 확인합니다. + * + * 목표: + * - 입력받은 휴대폰번호가 이미 등록되어 있는지 확인 + * - 중복 여부를 boolean으로 반환 + * + * 응답값: + * { + * "duplicateStatus": boolean // true: 중복됨, false: 사용 가능 + * } + */ + @Operation(summary = "휴대폰번호 중복 확인", description = "회원가입 시 휴대폰번호 중복 여부 확인") + @GetMapping("/validation/phone-number") + public BaseResponse checkPhoneNumberDuplicate( + @RequestParam String userPhoneNumber + ) { + // TODO: 휴대폰번호 중복 확인 로직 구현 + return null; + } + + + // 3. (personal partner) 개인 파트너 회원가입 @Operation(summary = "(개인 파트너) 개인 파트너 회원가입", description = "개인 파트너 회원가입") @PostMapping("/personal/signup") public BaseResponse signUpPersonalPartner(@RequestBody PersonalPartnerSignUpRequest request) { diff --git a/src/main/java/greenfirst/be/user/adapter/in/web/controller/UserManagementController.java b/src/main/java/greenfirst/be/user/adapter/in/web/controller/UserManagementController.java new file mode 100644 index 0000000..b7ef0a0 --- /dev/null +++ b/src/main/java/greenfirst/be/user/adapter/in/web/controller/UserManagementController.java @@ -0,0 +1,138 @@ +package greenfirst.be.user.adapter.in.web.controller; + + +import greenfirst.be.global.common.response.base.BaseResponse; +import greenfirst.be.global.common.security.CustomUserDetails; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +@Tag(name = "User - Management") +@Slf4j +@RestController +@RequestMapping("/api/v1/user") +@RequiredArgsConstructor +public class UserManagementController { + + // service + // private final UserManagementService userManagementService; + // util + // private final ModelMapper modelMapper; + + + /** + * 유저 관리 컨트롤러 + * 1. 프로필 수정 + * 2. 비밀번호 찾기 + * 3. 비밀번호 변경 + * 4. 회원 탈퇴 + */ + + // 1. 프로필 수정 + /** + * API 설명: 로그인한 사용자의 프로필 정보를 수정합니다. + * + * 목표: + * - 사용자 인증 정보(JWT)를 통해 본인 확인 + * - 사용자 이름, 주소, 사업자 정보 등 프로필 정보 수정 + * - 수정 완료 후 성공 응답 반환 + * + * 응답값: 없음 + */ + @Operation(summary = "프로필 수정", description = "로그인한 사용자의 프로필 정보를 수정합니다") + @PutMapping("/profile") + @SecurityRequirement(name = "Bearer Auth") + public BaseResponse updateProfile( + @RequestBody Object request, // TODO: UpdateProfileRequest 생성 필요 + @AuthenticationPrincipal CustomUserDetails authentication + ) { + // TODO: 프로필 수정 로직 구현 + return null; + } + + + // 2. 비밀번호 찾기 + /** + * API 설명: 사용자의 비밀번호를 찾기(재설정) 요청을 처리합니다. + * + * 목표: + * - 사용자 이름과 휴대폰 번호로 본인 확인 + * - 임시 비밀번호 생성 또는 비밀번호 재설정 링크 전송 + * - SMS/이메일을 통해 임시 비밀번호 또는 재설정 링크 전송 + * - 성공 응답 반환 + * + * 응답값: 없음 + * + * 참고: + * - 이 API는 인증이 필요하지 않음 (비로그인 상태에서 사용) + * - 보안을 위해 사용자 존재 여부를 직접적으로 노출하지 않음 + */ + @Operation(summary = "비밀번호 찾기", description = "사용자 정보로 비밀번호 재설정을 요청합니다") + @PostMapping("/password/reset") + public BaseResponse findPassword( + @RequestBody Object request // TODO: FindPasswordRequest 생성 필요 (userName, userPhoneNumber) + ) { + // TODO: 비밀번호 찾기 로직 구현 + return null; + } + + + // 3. 비밀번호 변경 + /** + * API 설명: 로그인한 사용자의 비밀번호를 변경합니다. + * + * 목표: + * - 사용자 인증 정보(JWT)를 통해 본인 확인 + * - 현재 비밀번호 검증 후 새로운 비밀번호로 변경 + * - 비밀번호 암호화 후 저장 + * - 변경 완료 후 성공 응답 반환 + * + * 응답값: 없음 + */ + @Operation(summary = "비밀번호 변경", description = "로그인한 사용자의 비밀번호를 변경합니다") + @PutMapping("/password") + @SecurityRequirement(name = "Bearer Auth") + public BaseResponse changePassword( + @RequestBody Object request, // TODO: ChangePasswordRequest 생성 필요 + @AuthenticationPrincipal CustomUserDetails authentication + ) { + // TODO: 비밀번호 변경 로직 구현 + return null; + } + + + // 4. 회원 탈퇴 + /** + * API 설명: 로그인한 사용자의 계정을 탈퇴(삭제) 처리합니다. + * + * 목표: + * - 사용자 인증 정보(JWT)를 통해 본인 확인 + * - 회원 탈퇴 전 비밀번호 재확인 (보안) + * - 계정 삭제 또는 비활성화 처리 + * - 관련 데이터 처리 (소프트 삭제 또는 익명화) + * - 탈퇴 완료 후 성공 응답 반환 + * + * 응답값: 없음 + */ + @Operation(summary = "회원 탈퇴", description = "로그인한 사용자의 계정을 탈퇴 처리합니다") + @DeleteMapping("/account") + @SecurityRequirement(name = "Bearer Auth") + public BaseResponse deleteAccount( + @RequestBody Object request, // TODO: DeleteAccountRequest 생성 필요 (비밀번호 확인용) + @AuthenticationPrincipal CustomUserDetails authentication + ) { + // TODO: 회원 탈퇴 로직 구현 + return null; + } + +} diff --git a/src/main/java/greenfirst/be/user/adapter/in/web/controller/UserTermsController.java b/src/main/java/greenfirst/be/user/adapter/in/web/controller/UserTermsController.java new file mode 100644 index 0000000..ac2aa16 --- /dev/null +++ b/src/main/java/greenfirst/be/user/adapter/in/web/controller/UserTermsController.java @@ -0,0 +1,162 @@ +package greenfirst.be.user.adapter.in.web.controller; + + +import greenfirst.be.global.common.response.base.BaseResponse; +import greenfirst.be.global.common.security.CustomUserDetails; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +@Tag(name = "User - Terms & Consent") +@Slf4j +@RestController +@RequestMapping("/api/v1/user") +@RequiredArgsConstructor +public class UserTermsController { + + // service + // private final UserTermsService userTermsService; + // util + // private final ModelMapper modelMapper; + + + /** + * 약관 및 수신동의 관리 컨트롤러 (추후 개발 예정) + * 1. 약관 목록 조회 + * 2. 수신동의 항목 조회 + * 3. 수신동의 상태 조회 + * 4. 수신동의 상태 수정 + */ + + // 1. 약관 목록 조회 + /** + * API 설명: 시스템에 등록된 약관 목록을 조회합니다. + * + * 목표: + * - 현재 유효한 약관 목록 조회 + * - 약관 ID, 제목, 내용, 필수 여부, 버전, 생성일 반환 + * - 회원가입 시 사용자에게 동의받을 약관 목록 제공 + * + * 응답값: + * [ + * { + * "termsId": "number", + * "termsTitle": "string", + * "termsContent": "string", + * "isRequired": "boolean", + * "termsVersion": "string", + * "createdAt": "string" + * } + * ] + * + * 참고: + * - 이 API는 인증이 필요하지 않음 (회원가입 전에 사용) + * - 추후 개발 예정 + */ + @Operation(summary = "약관 목록 조회", description = "시스템에 등록된 약관 목록을 조회합니다 (추후 개발 예정)") + @GetMapping("/terms") + public BaseResponse getTermsList() { + // TODO: 약관 목록 조회 로직 구현 (추후 개발) + return null; + } + + + // 2. 수신동의 항목 조회 + /** + * API 설명: 시스템에서 제공하는 수신동의 항목 목록을 조회합니다. + * + * 목표: + * - 수신동의 항목 목록 조회 (예: 마케팅 정보 수신, 이벤트 알림 등) + * - 각 항목의 ID, 제목, 설명, 필수 여부 반환 + * - 회원가입 또는 설정 페이지에서 사용 + * + * 응답값: + * [ + * { + * "receiveConsentId": "number", + * "consentTitle": "string", + * "consentDescription": "string", + * "isRequired": "boolean" + * } + * ] + * + * 참고: + * - 이 API는 인증이 필요하지 않음 (회원가입 전에 사용) + * - 추후 개발 예정 + */ + @Operation(summary = "수신동의 항목 조회", description = "시스템의 수신동의 항목 목록을 조회합니다 (추후 개발 예정)") + @GetMapping("/consent/items") + public BaseResponse getConsentItems() { + // TODO: 수신동의 항목 조회 로직 구현 (추후 개발) + return null; + } + + + // 3. 수신동의 상태 조회 + /** + * API 설명: 로그인한 사용자의 수신동의 상태를 조회합니다. + * + * 목표: + * - 사용자 인증 정보(JWT)를 통해 본인 확인 + * - 사용자의 각 수신동의 항목별 동의 여부 조회 + * - 항목 ID, 제목, 동의 여부 반환 + * + * 응답값: + * [ + * { + * "receiveConsentId": "number", + * "consentTitle": "string", + * "isAgreed": "boolean" + * } + * ] + * + * 참고: + * - 추후 개발 예정 + */ + @Operation(summary = "수신동의 상태 조회", description = "로그인한 사용자의 수신동의 상태를 조회합니다 (추후 개발 예정)") + @GetMapping("/consent/status") + @SecurityRequirement(name = "Bearer Auth") + public BaseResponse getConsentStatus( + @AuthenticationPrincipal CustomUserDetails authentication + ) { + // TODO: 수신동의 상태 조회 로직 구현 (추후 개발) + return null; + } + + + // 4. 수신동의 상태 수정 + /** + * API 설명: 로그인한 사용자의 수신동의 상태를 수정합니다. + * + * 목표: + * - 사용자 인증 정보(JWT)를 통해 본인 확인 + * - 사용자가 선택한 수신동의 항목들의 동의 여부 업데이트 + * - 마케팅 정보 수신, 이벤트 알림 등 항목별 동의/철회 처리 + * - 수정 완료 후 성공 응답 반환 + * + * 응답값: 없음 + * + * 참고: + * - 추후 개발 예정 + */ + @Operation(summary = "수신동의 상태 수정", description = "로그인한 사용자의 수신동의 상태를 수정합니다 (추후 개발 예정)") + @PutMapping("/consent/status") + @SecurityRequirement(name = "Bearer Auth") + public BaseResponse updateConsentStatus( + @RequestBody Object request, // TODO: UpdateConsentStatusRequest 생성 필요 + @AuthenticationPrincipal CustomUserDetails authentication + ) { + // TODO: 수신동의 상태 수정 로직 구현 (추후 개발) + return null; + } + +} diff --git a/src/main/java/greenfirst/be/user/adapter/in/web/response/UserProfileResponse.java b/src/main/java/greenfirst/be/user/adapter/in/web/response/UserProfileResponse.java new file mode 100644 index 0000000..d7248c3 --- /dev/null +++ b/src/main/java/greenfirst/be/user/adapter/in/web/response/UserProfileResponse.java @@ -0,0 +1,18 @@ +package greenfirst.be.user.adapter.in.web.response; + + +import greenfirst.be.user.domain.model.vo.PartnerOption; +import greenfirst.be.user.domain.model.vo.UserAddress; +import lombok.Getter; + + +@Getter +public class UserProfileResponse { + + private String userName; + private String userPhoneNumber; + private String userLoginId; + private UserAddress userAddress; + private PartnerOption partnerOption; + +} diff --git a/src/main/java/greenfirst/be/user/application/dto/in/GetUserByLoginIdInDto.java b/src/main/java/greenfirst/be/user/application/dto/in/GetUserByLoginIdInDto.java new file mode 100644 index 0000000..f2a1eb4 --- /dev/null +++ b/src/main/java/greenfirst/be/user/application/dto/in/GetUserByLoginIdInDto.java @@ -0,0 +1,26 @@ +package greenfirst.be.user.application.dto.in; + + +import greenfirst.be.global.common.enums.common.UserType; +import lombok.Builder; +import lombok.Getter; + +import java.util.UUID; + + +@Getter +public class GetUserByLoginIdInDto { + + private final UserType requestorType; + private final UUID requestorUuid; + private final String userLoginId; + + + @Builder + public GetUserByLoginIdInDto(UserType requestorType, UUID requestorUuid, String userLoginId) { + this.requestorType = requestorType; + this.requestorUuid = requestorUuid; + this.userLoginId = userLoginId; + } + +} diff --git a/src/main/java/greenfirst/be/user/application/dto/in/GetUserByUuidInDto.java b/src/main/java/greenfirst/be/user/application/dto/in/GetUserByUuidInDto.java new file mode 100644 index 0000000..cbbbde6 --- /dev/null +++ b/src/main/java/greenfirst/be/user/application/dto/in/GetUserByUuidInDto.java @@ -0,0 +1,26 @@ +package greenfirst.be.user.application.dto.in; + + +import greenfirst.be.global.common.enums.common.UserType; +import lombok.Builder; +import lombok.Getter; + +import java.util.UUID; + + +@Getter +public class GetUserByUuidInDto { + + private final UserType requestorType; + private final UUID requestorUuid; + private final UUID targetUserUuid; + + + @Builder + public GetUserByUuidInDto(UserType requestorType, UUID requestorUuid, UUID targetUserUuid) { + this.requestorType = requestorType; + this.requestorUuid = requestorUuid; + this.targetUserUuid = targetUserUuid; + } + +} diff --git a/src/main/java/greenfirst/be/user/application/service/GetUserDataService.java b/src/main/java/greenfirst/be/user/application/service/GetUserDataService.java index 7286e61..2297ab1 100644 --- a/src/main/java/greenfirst/be/user/application/service/GetUserDataService.java +++ b/src/main/java/greenfirst/be/user/application/service/GetUserDataService.java @@ -1,6 +1,8 @@ package greenfirst.be.user.application.service; +import greenfirst.be.user.application.dto.in.GetUserByLoginIdInDto; +import greenfirst.be.user.application.dto.in.GetUserByUuidInDto; import greenfirst.be.user.application.dto.in.GetUserSimpleDataListInDto; import greenfirst.be.user.application.dto.out.UserDataListOutDto; import greenfirst.be.user.application.port.out.UserQueryRepository; @@ -28,6 +30,8 @@ public class GetUserDataService { * 유저 데이터 조회 서비스 * 1. 유저 uuid로 유저 조회 * 2. (admin) 유저 목록 조회 + * 3. (admin/partner/user) 로그인id로 유저 조회 + * 4. (admin/agency) UUID로 유저 조회 (권한 검증 포함) */ // 1. 유저 uuid로 유저 조회 @@ -36,17 +40,45 @@ public Users getByUserUuid(UUID userUuid) { } - // 2. 유저 목록 조회 + // 2. (admin) 유저 목록 조회 public UserDataListOutDto getUserSimpleDataList(GetUserSimpleDataListInDto inDto) { // 요청자 조회 Users requestor = userQueryRepository.getByUserUuid(inDto.getRequestorUuid()); // 요청자 조회 권한 확인 - requestor.validateUserSearchAuthorization(inDto.getUserTypeList()); + requestor.validateUserSearchAuthorizationForTargetTypes(inDto.getUserTypeList()); // 조건에 맞는 유저 목록 조회 return userQueryRepository.getUserSimpleDataList(inDto); } + + // 3. (admin/partner/user) 로그인id로 유저 조회 + public Users getUserByLoginId(GetUserByLoginIdInDto inDto) { + + // 목표 대상 조회 + Users target = userQueryRepository.getByLoginId(inDto.getUserLoginId()); + + // 목표 대상 권한 조회 + target.validateUserSearchAuthorizationOfRequestor(inDto.getRequestorType(), inDto.getRequestorUuid()); + + // result + return target; + } + + + // 4. (admin/agency) UUID로 유저 조회 + public Users getUserByUuidWithAuthorityCheck(GetUserByUuidInDto inDto) { + + // 목표 대상 조회 + Users target = userQueryRepository.getByUserUuid(inDto.getTargetUserUuid()); + + // 목표 대상 권한 조회 + target.validateUserSearchAuthorizationOfRequestor(inDto.getRequestorType(), inDto.getRequestorUuid()); + + // result + return target; + } + } diff --git a/src/main/java/greenfirst/be/user/domain/model/Users.java b/src/main/java/greenfirst/be/user/domain/model/Users.java index ab53a44..5f463de 100644 --- a/src/main/java/greenfirst/be/user/domain/model/Users.java +++ b/src/main/java/greenfirst/be/user/domain/model/Users.java @@ -87,7 +87,8 @@ public static Users of(UserModelCreateCommand command) { * 도메인 로직 * 1. 관리자 지점 아이디 조회 * 2. 유저 종류 이름 조회 - * 3. 유저 검색 권한 확인 + * 3. 유저 검색 권한 확인 (Requestor가 주체, Target 타입들에 대해 본인이 조회 권한이 있는지 확인) + * 4. 유저 검색 권한 확인 (Target이 주체, Requestor가 조회 권한이 있는지 확인) */ // 1. 관리자 지점 아이디 조회 @@ -106,11 +107,11 @@ public String getUserTypeName() { } - // 3. 유저 검색 권한 확인 - public void validateUserSearchAuthorization(List userTypeList) { + // 3. 유저 검색 권한 확인 (Requestor가 주체, Target 타입들에 대해 본인이 조회 권한이 있는지 확인) + public void validateUserSearchAuthorizationForTargetTypes(List userTypeList) { // admin -> 모두 조회 가능 - if (this.userType == UserType.ADMIN) return; + if (this.userType == UserType.ADMIN) return; // 명시적 return // agency -> admin 조회 불가, agency는 본인만 조회 가능 if (this.userType == UserType.AGENCY && userTypeList.contains(UserType.ADMIN)) { @@ -125,4 +126,24 @@ public void validateUserSearchAuthorization(List userTypeList) { } } + + // 4. 유저 검색 권한 확인 (Target이 주체, Requestor가 조회 권한이 있는지 확인) + public void validateUserSearchAuthorizationOfRequestor(UserType requestorType, UUID requestorUuid) { + + // admin -> 모두 조회 가능 + if (requestorType == UserType.ADMIN) return; // 명시적 return + + // agency -> admin 조회 불가, agency는 본인 및 user, partner만 조회 가능 + if (requestorType == UserType.AGENCY && this.userType == UserType.ADMIN) { + throw new BaseException(BaseResponseStatus.NO_AUTHORITY_TO_SEARCH_USER); + } + + // user, partner -> 본인만 조회 가능 + if (requestorType == UserType.USER || requestorType == UserType.PERSONAL_PARTNER || requestorType == UserType.CORPORATE_PARTNER) { + if (this.userType != requestorType || !this.userUuid.equals(requestorUuid)) { + throw new BaseException(BaseResponseStatus.NO_AUTHORITY_TO_SEARCH_USER); + } + } + } + }