Skip to content
Open
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 @@ -31,7 +31,7 @@ public class JoinController {
MediaType.MULTIPART_FORM_DATA_VALUE })
@Operation(summary = "회원가입", description = "새로운 사용자를 등록합니다. 인증 이미지는 선택적으로 업로드할 수 있습니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "회원가입 성공", content = @Content(schema = @Schema(implementation = ApiResponseMessage.class))),
@ApiResponse(responseCode = "200", description = "0: 성공적으로 처리되었습니다.", content = @Content(schema = @Schema(implementation = ApiResponseMessage.class))),
@ApiResponse(responseCode = "400", description = "잘못된 요청", content = @Content(schema = @Schema(implementation = ApiResponseMessage.class))),
})
public ResponseEntity<ApiResponseMessage> signup(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,6 @@ public interface CertificationJpaRepository extends JpaRepository<Certification,
boolean existsByEmail(String email);

Optional<Certification> findByUsername(String username);

Optional<Certification> findByUserId(Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,9 @@ public Certification findByUsername(String username) {
public Optional<Certification> findById(Long userId){
return certificationJpaRepository.findById(userId);
}

public Certification findByUserId(Long userId) {
return certificationJpaRepository.findByUserId(userId)
.orElseThrow(() -> new CanNotFindUserException());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package site.danjam.mate.user_service.domain.certification.service;

import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import site.danjam.mate.common.annotation.MethodDescription;
import site.danjam.mate.common.exception.global.InvalidInputException;
import site.danjam.mate.user_service.domain.certification.domain.Certification;
import site.danjam.mate.user_service.domain.certification.repository.CertificationRepository;
import site.danjam.mate.user_service.domain.user.dto.UpdateLoginDTO;

@Service
@RequiredArgsConstructor
public class DeleteUserService {

CertificationRepository certificationRepository;
BCryptPasswordEncoder bCryptPasswordEncoder;

@MethodDescription(description = "회원을 탈퇴합니다.")
public void deleteUser(Long userId, UpdateLoginDTO dto) {
Certification certification = certificationRepository.findByUserId(userId);
if (!bCryptPasswordEncoder.matches(dto.getPassword(), certification.getPassword())) {
throw new InvalidInputException("비밀번호가 일치하지 않습니다.");
}
certification.softDelete(userId.toString());
certificationRepository.save(certification);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package site.danjam.mate.user_service.domain.user.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
Expand All @@ -9,16 +15,18 @@
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import site.danjam.mate.common.response.ApiResponseData;
import site.danjam.mate.common.response.ApiResponseError;
import site.danjam.mate.common.response.ApiResponseMessage;
import site.danjam.mate.common.security.GlobalCustomUserDetails;
import site.danjam.mate.user_service.auth.dto.CustomUserDetails;
import site.danjam.mate.user_service.domain.certification.service.DeleteUserService;
import site.danjam.mate.user_service.domain.user.dto.GreetingDTO;
import site.danjam.mate.user_service.domain.user.dto.MyProfileDTO;
import site.danjam.mate.user_service.domain.user.dto.UpdateLoginDTO;
Expand All @@ -28,45 +36,83 @@
@RestController
@RequestMapping("user-service/api/my-profile")
@RequiredArgsConstructor
@Tag(name="마이프로필", description = "마이프로필 관련 API")
public class MyProfileController {

private final MyProfileInfoService myProfileInfoService;

private final DeleteUserService deleteUserService;
@Operation(summary = "마이프로필 조회", description = "마이프로필을 조회합니다.\n\n응답 코드 예시:\n- 0: 성공적으로 처리되었습니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "0: 마이프로필 조회 성공", content = @Content(schema = @Schema(implementation = ApiResponseData.class))),
@ApiResponse(responseCode = "400", description = "100: 잘못된 입력값이 존재합니다.", content = @Content(schema = @Schema(implementation = ApiResponseError.class))),
@ApiResponse(responseCode = "500", description = "1: 예기치 못한 서버 오류가 발생했습니다.", content = @Content(schema = @Schema(implementation = ApiResponseError.class)))
})
@GetMapping
@PreAuthorize("hasAnyRole('STRANGER','AUTH_USER')")
public ResponseEntity<ApiResponseData<MyProfileDTO>> readMyProfile(@AuthenticationPrincipal GlobalCustomUserDetails globalCustomUserDetails) {
return ResponseEntity.ok(ApiResponseData.of(myProfileInfoService.readMyProfileInfo(globalCustomUserDetails.getUserId()), "마이프로필 조회 성공"));
}

@PatchMapping(value = "/login", consumes = {MediaType.APPLICATION_JSON_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<ApiResponseMessage> updateLoginInfo(@RequestHeader("username") String username,
@RequestPart UpdateLoginDTO dto,
@RequestPart(required = false) MultipartFile file) {
myProfileInfoService.updateLoginInfo(username, dto, file);
@Operation(summary = "마이프로필 기본정보 입력(mbti, 인삿말)", description = "마이프로필 기본정보를 입력합니다.\n\n응답 코드 예시:\n- 0: 성공적으로 처리되었습니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "0: 회원정보가 정상적으로 업데이트 되었습니다.", content = @Content(schema = @Schema(implementation = ApiResponseMessage.class))),
@ApiResponse(responseCode = "400", description = "100: 잘못된 입력값이 존재합니다.", content = @Content(schema = @Schema(implementation = ApiResponseError.class))),
@ApiResponse(responseCode = "500", description = "1: 예기치 못한 서버 오류가 발생했습니다.", content = @Content(schema = @Schema(implementation = ApiResponseError.class)))
})
@PutMapping("/basic-info")
@PreAuthorize("hasAnyRole('STRANGER','AUTH_USER')")
public ResponseEntity<ApiResponseMessage> updateBasicInfo(@AuthenticationPrincipal GlobalCustomUserDetails globalCustomUserDetails,
@RequestBody @Valid GreetingDTO dto) {
myProfileInfoService.updateBasicInfo(globalCustomUserDetails.getUserId(), dto);
return ResponseEntity.ok(ApiResponseMessage.of("회원정보가 정상적으로 업데이트 되었습니다."));
}

@PatchMapping(value = "/school", consumes = {MediaType.APPLICATION_JSON_VALUE,
@Operation(summary = "학교 정보 수정(학교, 학과, 인증 이미지)", description = "학교 정보를 수정합니다. 유저의 학교, 학과, 인증 이미지 수정.\n\n응답 코드 예시:\n- 0: 성공적으로 처리되었습니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "0: 회원정보가 정상적으로 업데이트 되었습니다.", content = @Content(schema = @Schema(implementation = ApiResponseMessage.class))),
@ApiResponse(responseCode = "400", description = "100: 잘못된 입력값이 존재합니다.", content = @Content(schema = @Schema(implementation = ApiResponseError.class))),
@ApiResponse(responseCode = "500", description = "1: 예기치 못한 서버 오류가 발생했습니다.", content = @Content(schema = @Schema(implementation = ApiResponseError.class)))
})
@PutMapping(value = "/school", consumes = {MediaType.APPLICATION_JSON_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<ApiResponseMessage> updateSchoolInfo(@RequestHeader("username") String username,
@PreAuthorize("hasAnyRole('STRANGER','AUTH_USER')")
public ResponseEntity<ApiResponseMessage> updateSchoolInfo(@AuthenticationPrincipal GlobalCustomUserDetails globalCustomUserDetails,
@RequestPart @Valid UpdateSchoolDTO dto,
@RequestPart(required = false) MultipartFile file) {
myProfileInfoService.updateSchoolInfo(username, dto, file);
myProfileInfoService.updateSchoolInfo(globalCustomUserDetails.getUserId(), dto, file);
return ResponseEntity.ok(ApiResponseMessage.of("회원정보가 정상적으로 업데이트 되었습니다."));
}

@PatchMapping("/greeting")
public ResponseEntity<ApiResponseMessage> updateGreetingInfo(@RequestHeader("username") String username,
@RequestBody @Valid GreetingDTO dto) {
myProfileInfoService.updateGreeting(username, dto);

@Operation(summary = "로그인 정보 수정(id, pw, 프로필이미지)", description = "학교 정보를 수정합니다. 유저의 학교, 학과, 인증 이미지 수정.\n\n응답 코드 예시:\n- 0: 성공적으로 처리되었습니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "0: 회원정보가 정상적으로 업데이트 되었습니다.", content = @Content(schema = @Schema(implementation = ApiResponseMessage.class))),
@ApiResponse(responseCode = "400", description = "100: 잘못된 입력값이 존재합니다.", content = @Content(schema = @Schema(implementation = ApiResponseError.class))),
@ApiResponse(responseCode = "500", description = "1: 예기치 못한 서버 오류가 발생했습니다.", content = @Content(schema = @Schema(implementation = ApiResponseError.class)))
})
@PutMapping(value = "/login-info", consumes = {MediaType.APPLICATION_JSON_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE})
@PreAuthorize("hasAnyRole('STRANGER','AUTH_USER')")
public ResponseEntity<ApiResponseMessage> updateLoginInfo(@AuthenticationPrincipal GlobalCustomUserDetails globalCustomUserDetails,
@RequestPart UpdateLoginDTO dto,
@RequestPart(required = false) MultipartFile file) {
myProfileInfoService.updateLoginInfo(globalCustomUserDetails.getUserId(), dto, file);
return ResponseEntity.ok(ApiResponseMessage.of("회원정보가 정상적으로 업데이트 되었습니다."));
}

//todo - 관련된 모든 유저 정보 삭제하도록 수정
@Operation(summary = "유저 탈퇴", description = "유저가 탈퇴합니다.\n\n응답 코드 예시:\n- 0: 성공적으로 처리되었습니다.")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "0: 정상적으로 탈되 되었습니다..", content = @Content(schema = @Schema(implementation = ApiResponseMessage.class))),
@ApiResponse(responseCode = "400", description = "100: 잘못된 입력값이 존재합니다.", content = @Content(schema = @Schema(implementation = ApiResponseError.class))),
@ApiResponse(responseCode = "500", description = "1: 예기치 못한 서버 오류가 발생했습니다.", content = @Content(schema = @Schema(implementation = ApiResponseError.class)))
})
@PreAuthorize("hasAnyRole('STRANGER','AUTH_USER')")
@DeleteMapping
public ResponseEntity<ApiResponseMessage> deleteUser(@RequestHeader("username") String username,
public ResponseEntity<ApiResponseMessage> deleteUser(@AuthenticationPrincipal GlobalCustomUserDetails globalCustomUserDetails,
@RequestBody UpdateLoginDTO dto) {
myProfileInfoService.deleteUser(username, dto);
deleteUserService.deleteUser(globalCustomUserDetails.getUserId(), dto);
return ResponseEntity.ok(ApiResponseMessage.of("정상적으로 탈퇴 되었습니다."));
}
//todo - 프로필 이미지 수정 api 추가
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import site.danjam.mate.common.annotation.MethodDescription;
import site.danjam.mate.user_service.domain.mate.dto.MateProfileDTO;
import site.danjam.mate.user_service.domain.user.domain.User;
import site.danjam.mate.user_service.domain.user.dto.GreetingDTO;
import site.danjam.mate.user_service.domain.user.dto.MyProfileDTO;
Expand Down Expand Up @@ -48,41 +47,54 @@ public MyProfileDTO readMyProfileInfo(Long userId) {
return MyProfileDTO.from(user,major);
}

@MethodDescription(description = "마이프로필 로그인정보(username, password, profileImg) 를 수정합니다.")
public void updateLoginInfo(String username, UpdateLoginDTO dto, MultipartFile file) {
Certification certification = certificationRepository.findByUsername(username);
User user = userRepository.findByMyProfile(certification);
@MethodDescription(description = "사용자의 mbti와 소개글을 수정합니다.")
@Transactional
public void updateBasicInfo(Long userId, GreetingDTO dto) {

if (file != null && !file.isEmpty()) {
user.updateProfileImg(multipartUtil.determineFileName(file, username, SUFFIX, PROFILE_BUCKET_NAME));
userRepository.save(user);
}
User user = userRepository.findById(userId);
user.updateGreeting(dto.getGreeting());
user.updateMbti(dto.getMbti());

userRepository.save(user);
}

@Transactional
@MethodDescription(description = "마이프로필 로그인정보(username, password, profileImg) 를 수정합니다.")
public void updateLoginInfo(Long userId, UpdateLoginDTO dto, MultipartFile file) {
Certification certification = certificationRepository.findByUserId(userId);
User user = userRepository.findById(userId);

if (dto.getUsername() != null && !dto.getUsername().isBlank()) {
if (certificationRepository.existsByUsername(dto.getUsername())) {
throw new DuplicateUsernameException();
}
certification.updateUsername(username);
certification.updateUsername(dto.getUsername());
}

if (dto.getPassword() != null && !dto.getPassword().isBlank()) {
certification.updatePassword(bCryptPasswordEncoder.encode(dto.getPassword()));
}

certificationRepository.save(certification);

if (file != null && !file.isEmpty()) {
user.updateProfileImg(multipartUtil.determineFileName(file, certification.getUsername(), SUFFIX, PROFILE_BUCKET_NAME));
userRepository.save(user);
}

}

@MethodDescription(description = "사용자의 학교 정보를 수정합니다.")
public void updateSchoolInfo(String username, UpdateSchoolDTO dto, MultipartFile file) {
Certification certification = certificationRepository.findByUsername(username);
User user = userRepository.findByMyProfile(certification);
public void updateSchoolInfo(Long userId, UpdateSchoolDTO dto, MultipartFile file) {
Certification certification = certificationRepository.findByUserId(userId);
User user = userRepository.findById(userId);
School school = schoolRepository.findById(dto.getSchoolId());

if (file == null || file.isEmpty()) {
throw new InvalidInputException("학교 인증 사진이 존재하지 않습니다.");
}

certification.updateAuthImgUrl(multipartUtil.determineFileName(file, username, SUFFIX, AUTH_BUCKET_NAME));
certification.updateAuthImgUrl(multipartUtil.determineFileName(file, certification.getUsername(), SUFFIX, AUTH_BUCKET_NAME));
user.updateSchool(school);
Major major = majorRepository.findByMajorNameAndSchoolId(dto.getMajor(), school.getId());
user.updateSchoolInfo(dto.getEntryYear(), school.getId(), major.getId());
Expand All @@ -91,24 +103,5 @@ public void updateSchoolInfo(String username, UpdateSchoolDTO dto, MultipartFile
userRepository.save(user);
}

@MethodDescription(description = "사용자의 mbti와 소개글을 수정합니다.")
public void updateGreeting(String username, GreetingDTO dto) {
Certification certification = certificationRepository.findByUsername(username);
User user = userRepository.findByMyProfile(certification);

user.updateGreeting(dto.getGreeting());
user.updateMbti(dto.getMbti());

userRepository.save(user);
}

@MethodDescription(description = "회원을 탈퇴합니다.")
public void deleteUser(String username, UpdateLoginDTO dto) {
Certification certification = certificationRepository.findByUsername(username);
if (!bCryptPasswordEncoder.matches(dto.getPassword(), certification.getPassword())) {
throw new InvalidInputException("비밀번호가 일치하지 않습니다.");
}
certification.softDelete(username);
certificationRepository.save(certification);
}
}