diff --git a/src/main/java/in/koreatech/koin/domain/user/controller/UserApi.java b/src/main/java/in/koreatech/koin/domain/user/controller/UserApi.java index d3b526da2d..336acee6a3 100644 --- a/src/main/java/in/koreatech/koin/domain/user/controller/UserApi.java +++ b/src/main/java/in/koreatech/koin/domain/user/controller/UserApi.java @@ -1,26 +1,28 @@ package in.koreatech.koin.domain.user.controller; +import static in.koreatech.koin.domain.user.model.UserType.OWNER; +import static in.koreatech.koin.domain.user.model.UserType.STUDENT; + import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import in.koreatech.koin.domain.user.dto.EmailCheckExistsRequest; import in.koreatech.koin.domain.user.dto.NicknameCheckExistsRequest; +import in.koreatech.koin.domain.user.dto.NotificationPermitRequest; +import in.koreatech.koin.domain.user.dto.NotificationStatusResponse; import in.koreatech.koin.domain.user.dto.StudentResponse; +import in.koreatech.koin.domain.user.dto.StudentUpdateRequest; +import in.koreatech.koin.domain.user.dto.StudentUpdateResponse; import in.koreatech.koin.domain.user.dto.UserLoginRequest; import in.koreatech.koin.domain.user.dto.UserLoginResponse; import in.koreatech.koin.domain.user.dto.UserTokenRefreshRequest; import in.koreatech.koin.domain.user.dto.UserTokenRefreshResponse; - -import static in.koreatech.koin.domain.user.model.UserType.OWNER; -import static in.koreatech.koin.domain.user.model.UserType.STUDENT; - import in.koreatech.koin.global.auth.Auth; -import in.koreatech.koin.domain.user.dto.NotificationPermitRequest; -import in.koreatech.koin.domain.user.dto.NotificationStatusResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -48,6 +50,23 @@ ResponseEntity getStudent( @Auth(permit = STUDENT) Long userId ); + @ApiResponses( + value = { + @ApiResponse(responseCode = "201"), + @ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "409", content = @Content(schema = @Schema(hidden = true))) + } + ) + @Operation(summary = "회원 정보 수정") + @SecurityRequirement(name = "Jwt Authentication") + @PutMapping("/user/student/me") + ResponseEntity updateStudent( + @Auth(permit = STUDENT) Long userId, + @Valid StudentUpdateRequest studentUpdateRequest + ); + @ApiResponses( value = { @ApiResponse(responseCode = "201"), @@ -123,20 +142,6 @@ ResponseEntity checkUserEmailExist( @Valid EmailCheckExistsRequest request ); - @ApiResponses( - value = { - @ApiResponse(responseCode = "200"), - @ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))), - @ApiResponse(responseCode = "409", content = @Content(schema = @Schema(hidden = true))), - } - ) - @Operation(summary = "닉네임 중복 체크") - @GetMapping("/user/check/nickname") - ResponseEntity checkDuplicationOfNickname( - @ModelAttribute("nickname") - @Valid NicknameCheckExistsRequest request - ); - @ApiResponses( value = { @ApiResponse(responseCode = "200"), @@ -182,4 +187,18 @@ ResponseEntity permitNotification( ResponseEntity rejectNotification( @Auth(permit = {STUDENT, OWNER}) Long memberId ); + + @ApiResponses( + value = { + @ApiResponse(responseCode = "200"), + @ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))), + @ApiResponse(responseCode = "409", content = @Content(schema = @Schema(hidden = true))), + } + ) + @Operation(summary = "닉네임 중복 체크") + @GetMapping("/user/check/nickname") + ResponseEntity checkDuplicationOfNickname( + @ModelAttribute("nickname") + @Valid NicknameCheckExistsRequest request + ); } diff --git a/src/main/java/in/koreatech/koin/domain/user/controller/UserController.java b/src/main/java/in/koreatech/koin/domain/user/controller/UserController.java index 6c748d6ad4..5ab795fd42 100644 --- a/src/main/java/in/koreatech/koin/domain/user/controller/UserController.java +++ b/src/main/java/in/koreatech/koin/domain/user/controller/UserController.java @@ -1,5 +1,7 @@ package in.koreatech.koin.domain.user.controller; +import static in.koreatech.koin.domain.user.model.UserType.STUDENT; + import java.net.URI; import org.springframework.http.ResponseEntity; @@ -7,12 +9,15 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; 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.RestController; import in.koreatech.koin.domain.user.dto.EmailCheckExistsRequest; import in.koreatech.koin.domain.user.dto.NicknameCheckExistsRequest; import in.koreatech.koin.domain.user.dto.StudentResponse; +import in.koreatech.koin.domain.user.dto.StudentUpdateRequest; +import in.koreatech.koin.domain.user.dto.StudentUpdateResponse; import in.koreatech.koin.domain.user.dto.UserLoginRequest; import in.koreatech.koin.domain.user.dto.UserLoginResponse; import in.koreatech.koin.domain.user.dto.UserTokenRefreshRequest; @@ -43,6 +48,15 @@ public ResponseEntity getStudent( return ResponseEntity.ok().body(studentResponse); } + @PutMapping("/user/student/me") + public ResponseEntity updateStudent( + @Auth(permit = STUDENT) Long userId, + @Valid @RequestBody StudentUpdateRequest request + ) { + StudentUpdateResponse studentUpdateResponse = studentService.updateStudent(userId, request); + return ResponseEntity.ok(studentUpdateResponse); + } + @PostMapping("/user/login") public ResponseEntity login( @RequestBody @Valid UserLoginRequest request diff --git a/src/main/java/in/koreatech/koin/domain/user/dto/StudentResponse.java b/src/main/java/in/koreatech/koin/domain/user/dto/StudentResponse.java index efc5fd3ce1..5482d3ba7d 100644 --- a/src/main/java/in/koreatech/koin/domain/user/dto/StudentResponse.java +++ b/src/main/java/in/koreatech/koin/domain/user/dto/StudentResponse.java @@ -12,16 +12,17 @@ public record StudentResponse( @Schema(description = "익명 닉네임", example = "익명_1676688416361") String anonymousNickname, - @Schema(description = "이메일 주소 \n", example = "koin123@koreatech.ac.kr") + @Schema(description = "이메일 주소", example = "koin123@koreatech.ac.kr") String email, @Schema(description = "성별(남:0, 여:1)", example = "1") String gender, - @Schema(description = "전공{기계공학부, 컴퓨터공학부, 메카트로닉스공학부, 전기전자통신공학부, 디자인건축공학부, 에너지신소재화학공학부, 산업경영학부}", example = "컴퓨터공학부") + @Schema(description = "전공{기계공학부, 컴퓨터공학부, 메카트로닉스공학부, 전기전자통신공학부, 디자인공학부, " + + "건축공학부, 화학생명공학부, 에너지신소재공학부, 산업경영학부, 고용서비스정책학과}", example = "컴퓨터공학부") String major, - @Schema(description = "이름 \n", example = "최준호") + @Schema(description = "이름", example = "최준호") String name, @Schema(description = "닉네임", example = "juno") diff --git a/src/main/java/in/koreatech/koin/domain/user/dto/StudentUpdateRequest.java b/src/main/java/in/koreatech/koin/domain/user/dto/StudentUpdateRequest.java new file mode 100644 index 0000000000..a5a782a0a5 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/user/dto/StudentUpdateRequest.java @@ -0,0 +1,40 @@ +package in.koreatech.koin.domain.user.dto; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Size; + +@JsonNaming(value = SnakeCaseStrategy.class) +public record StudentUpdateRequest + ( + @Schema(description = "성별(남:0, 여:1)", example = "1") + Integer gender, + + @Schema(description = "[NOT UPDATE]신원(학생, 사장님)", example = "학생") + Integer userIdentity, + + @Schema(description = "[NOT UPDATE]졸업 여부(true, false)", example = "false") + Boolean isGraduated, + + @Schema(description = "전공{기계공학부, 컴퓨터공학부, 메카트로닉스공학부, 전기전자통신공학부, 디자인공학부," + + "건축공학부, 화학생명공학부, 에너지신소재공학부, 산업경영학부, 고용서비스정책학과}", example = "컴퓨터공학부") + String major, + + @Size(max = 50, message = "이름의 길이는 최대 50자 입니다.") + @Schema(description = "이름", example = "최준호") + String name, + + @Size(max = 10, message = "닉네임은 10자 이내여야 합니다.") + @Schema(description = "닉네임", example = "juno") + String nickname, + + @Schema(description = "휴대폰 번호", example = "010-0000-0000") + String phoneNumber, + + @Size(min = 10, max = 10, message = "학번은 10자여야 합니다.") + @Schema(description = "학번", example = "2020136065") + String studentNumber + ) { +} diff --git a/src/main/java/in/koreatech/koin/domain/user/dto/StudentUpdateResponse.java b/src/main/java/in/koreatech/koin/domain/user/dto/StudentUpdateResponse.java new file mode 100644 index 0000000000..7cab3608e2 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/user/dto/StudentUpdateResponse.java @@ -0,0 +1,51 @@ +package in.koreatech.koin.domain.user.dto; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; + +import in.koreatech.koin.domain.user.model.Student; +import in.koreatech.koin.domain.user.model.User; +import io.swagger.v3.oas.annotations.media.Schema; + +@JsonNaming(value = SnakeCaseStrategy.class) +public record StudentUpdateResponse( + @Schema(description = "익명 닉네임", example = "익명_1676688416361") + String anonymousNickname, + + @Schema(description = "이메일 주소", example = "koin123@koreatech.ac.kr") + String email, + + @Schema(description = "성별(남:0, 여:1)", example = "1") + Integer gender, + + @Schema(description = "전공{기계공학부, 컴퓨터공학부, 메카트로닉스공학부, 전기전자통신공학부, 디자인공학부," + + "건축공학부, 화학생명공학부, 에너지신소재공학부, 산업경영학부, 고용서비스정책학과}", example = "컴퓨터공학부") + String major, + + @Schema(description = "이름", example = "최준호") + String name, + + @Schema(description = "닉네임", example = "juno") + String nickname, + + @Schema(description = "휴대폰 번호", example = "010-0000-0000") + String phoneNumber, + + @Schema(description = "학번", example = "2029136012") + String studentNumber +) { + + public static StudentUpdateResponse from(Student student) { + User user = student.getUser(); + return new StudentUpdateResponse( + student.getAnonymousNickname(), + user.getEmail(), + user.getGender().ordinal(), + student.getDepartment(), + user.getName(), + user.getNickname(), + user.getPhoneNumber(), + student.getStudentNumber() + ); + } +} diff --git a/src/main/java/in/koreatech/koin/domain/user/exception/StudentDepartmentNotValidException.java b/src/main/java/in/koreatech/koin/domain/user/exception/StudentDepartmentNotValidException.java new file mode 100644 index 0000000000..765b095853 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/user/exception/StudentDepartmentNotValidException.java @@ -0,0 +1,15 @@ +package in.koreatech.koin.domain.user.exception; + +public class StudentDepartmentNotValidException extends IllegalArgumentException { + + private static final String DEFAULT_MESSAGE = "학생의 전공 형식이 아닙니다."; + + public StudentDepartmentNotValidException(String message) { + super(message); + } + + public static StudentDepartmentNotValidException withDetail(String detail) { + String message = String.format("%s %s", DEFAULT_MESSAGE, detail); + return new StudentDepartmentNotValidException(message); + } +} diff --git a/src/main/java/in/koreatech/koin/domain/user/exception/UserGenderNotValidException.java b/src/main/java/in/koreatech/koin/domain/user/exception/UserGenderNotValidException.java new file mode 100644 index 0000000000..bec2bc76e9 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/user/exception/UserGenderNotValidException.java @@ -0,0 +1,15 @@ +package in.koreatech.koin.domain.user.exception; + +public class UserGenderNotValidException extends IllegalArgumentException { + + private static final String DEFAULT_MESSAGE = "잘못된 성별 인덱스입니다."; + + public UserGenderNotValidException(String message) { + super(message); + } + + public static UserGenderNotValidException withDetail(String detail) { + String message = String.format("%s %s", DEFAULT_MESSAGE, detail); + return new UserGenderNotValidException(message); + } +} diff --git a/src/main/java/in/koreatech/koin/domain/user/model/Student.java b/src/main/java/in/koreatech/koin/domain/user/model/Student.java index 84d0165292..0d003edd62 100644 --- a/src/main/java/in/koreatech/koin/domain/user/model/Student.java +++ b/src/main/java/in/koreatech/koin/domain/user/model/Student.java @@ -33,7 +33,6 @@ public class Student { @Column(name = "student_number", length = 20) private String studentNumber; - @Size(max = 50) @Column(name = "major", length = 50) private String department; @@ -49,8 +48,8 @@ public class Student { private User user; @Builder - private Student(String anonymousNickname, String studentNumber, String department, UserIdentity userIdentity, - Boolean isGraduated, User user) { + private Student(String anonymousNickname, String studentNumber, String department, + UserIdentity userIdentity, Boolean isGraduated, User user) { this.anonymousNickname = anonymousNickname; this.studentNumber = studentNumber; this.department = department; @@ -58,4 +57,9 @@ private Student(String anonymousNickname, String studentNumber, String departmen this.isGraduated = isGraduated; this.user = user; } + + public void update(String studentNumber, String department) { + this.studentNumber = studentNumber; + this.department = department; + } } diff --git a/src/main/java/in/koreatech/koin/domain/user/model/StudentDepartment.java b/src/main/java/in/koreatech/koin/domain/user/model/StudentDepartment.java new file mode 100644 index 0000000000..89fb734098 --- /dev/null +++ b/src/main/java/in/koreatech/koin/domain/user/model/StudentDepartment.java @@ -0,0 +1,33 @@ +package in.koreatech.koin.domain.user.model; + +import lombok.Getter; + +@Getter +public enum StudentDepartment { + COMPUTER("컴퓨터공학부"), + MECHANICAL("기계공학부"), + MECHATRONICS("메카트로닉스공학부"), + ELECTRONIC("전기전자통신공학부"), + DESIGN("디자인공학부"), + ARCHITECTURAL("건축공학부"), + CHEMICAL("화학생명공학부"), + ENERGY("에너지신소재공학부"), + INDUSTRIAL("산업경영학부"), + EMPLOYMENT("고용서비스정책학과"), + ; + + private final String value; + + StudentDepartment(String value) { + this.value = value; + } + + public static boolean isValid(String department) { + for (StudentDepartment value : StudentDepartment.values()) { + if (value.getValue().equals(department)) { + return true; + } + } + return false; + } +} diff --git a/src/main/java/in/koreatech/koin/domain/user/model/User.java b/src/main/java/in/koreatech/koin/domain/user/model/User.java index 1abdde3fe8..758b7bf361 100644 --- a/src/main/java/in/koreatech/koin/domain/user/model/User.java +++ b/src/main/java/in/koreatech/koin/domain/user/model/User.java @@ -143,4 +143,11 @@ public void updateLastLoggedTime(LocalDateTime lastLoggedTime) { public void updatePassword(PasswordEncoder passwordEncoder, String password) { this.password = passwordEncoder.encode(password); } + + public void update(String nickname, String name, String phoneNumber, UserGender gender) { + this.nickname = nickname; + this.name = name; + this.phoneNumber = phoneNumber; + this.gender = gender; + } } diff --git a/src/main/java/in/koreatech/koin/domain/user/model/UserGender.java b/src/main/java/in/koreatech/koin/domain/user/model/UserGender.java index d8dd6d82d4..65a4f8b919 100644 --- a/src/main/java/in/koreatech/koin/domain/user/model/UserGender.java +++ b/src/main/java/in/koreatech/koin/domain/user/model/UserGender.java @@ -1,7 +1,21 @@ package in.koreatech.koin.domain.user.model; +import java.util.Arrays; + +import in.koreatech.koin.domain.user.exception.UserGenderNotValidException; + public enum UserGender { MAN, WOMAN, ; + + public static UserGender from(Integer index) { + if (index == null) { + return null; + } + return Arrays.stream(values()) + .filter(it -> it.ordinal() == index) + .findAny() + .orElseThrow(() -> UserGenderNotValidException.withDetail("index : " + index)); + } } diff --git a/src/main/java/in/koreatech/koin/domain/user/repository/UserRepository.java b/src/main/java/in/koreatech/koin/domain/user/repository/UserRepository.java index 729de5ef22..b17508f4fb 100644 --- a/src/main/java/in/koreatech/koin/domain/user/repository/UserRepository.java +++ b/src/main/java/in/koreatech/koin/domain/user/repository/UserRepository.java @@ -32,5 +32,7 @@ default User getByNickname(String nickname) { .orElseThrow(() -> UserNotFoundException.withDetail("nickname: " + nickname)); } + boolean existsByNickname(String nickname); + void delete(User user); } diff --git a/src/main/java/in/koreatech/koin/domain/user/service/StudentService.java b/src/main/java/in/koreatech/koin/domain/user/service/StudentService.java index d1281b9195..7ede646e8c 100644 --- a/src/main/java/in/koreatech/koin/domain/user/service/StudentService.java +++ b/src/main/java/in/koreatech/koin/domain/user/service/StudentService.java @@ -4,8 +4,16 @@ import org.springframework.transaction.annotation.Transactional; import in.koreatech.koin.domain.user.dto.StudentResponse; +import in.koreatech.koin.domain.user.dto.StudentUpdateRequest; +import in.koreatech.koin.domain.user.dto.StudentUpdateResponse; +import in.koreatech.koin.domain.user.exception.DuplicationNicknameException; +import in.koreatech.koin.domain.user.exception.StudentDepartmentNotValidException; import in.koreatech.koin.domain.user.model.Student; +import in.koreatech.koin.domain.user.model.StudentDepartment; +import in.koreatech.koin.domain.user.model.User; +import in.koreatech.koin.domain.user.model.UserGender; import in.koreatech.koin.domain.user.repository.StudentRepository; +import in.koreatech.koin.domain.user.repository.UserRepository; import lombok.RequiredArgsConstructor; @Service @@ -14,9 +22,37 @@ public class StudentService { private final StudentRepository studentRepository; + private final UserRepository userRepository; public StudentResponse getStudent(Long userId) { Student student = studentRepository.getById(userId); return StudentResponse.from(student); } + + @Transactional + public StudentUpdateResponse updateStudent(Long userId, StudentUpdateRequest request) { + Student student = studentRepository.getById(userId); + User user = student.getUser(); + CheckNicknameDuplication(request.nickname()); + CheckDepartmentValid(request.major()); + user.update(request.nickname(), request.name(), + request.phoneNumber(), UserGender.from(request.gender())); + student.update(request.studentNumber(), request.major()); + studentRepository.save(student); + + return StudentUpdateResponse.from(student); + } + + public void CheckNicknameDuplication(String nickname) { + if (nickname != null && userRepository.existsByNickname(nickname)) { + throw DuplicationNicknameException.withDetail("nickname : " + nickname); + } + } + + public void CheckDepartmentValid(String department) { + if (department != null & !StudentDepartment.isValid(department)) { + throw StudentDepartmentNotValidException.withDetail("학부(학과) : " + department); + } + } } + diff --git a/src/main/java/in/koreatech/koin/global/exception/GlobalExceptionHandler.java b/src/main/java/in/koreatech/koin/global/exception/GlobalExceptionHandler.java index d9d39aa6c5..c826bcdfe8 100644 --- a/src/main/java/in/koreatech/koin/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/in/koreatech/koin/global/exception/GlobalExceptionHandler.java @@ -2,6 +2,8 @@ import java.time.format.DateTimeParseException; +import lombok.extern.slf4j.Slf4j; + import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; diff --git a/src/test/java/in/koreatech/koin/acceptance/CommunityApiTest.java b/src/test/java/in/koreatech/koin/acceptance/CommunityApiTest.java index aab9fc4768..24335156ad 100644 --- a/src/test/java/in/koreatech/koin/acceptance/CommunityApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/CommunityApiTest.java @@ -560,24 +560,36 @@ void getHotArticles() { softly -> { softly.assertThat(response.jsonPath().getList("").size()).isEqualTo(HOT_ARTICLE_LIMIT); - softly.assertThat(response.jsonPath().getLong("[0].id")).isEqualTo(articles.get(ARTICLE_COUNT - 1).getId()); + softly.assertThat(response.jsonPath().getLong("[0].id")) + .isEqualTo(articles.get(ARTICLE_COUNT - 1).getId()); softly.assertThat(response.jsonPath().getLong("[0].board_id")).isEqualTo(board.getId()); - softly.assertThat(response.jsonPath().getString("[0].title")).isEqualTo(articles.get(ARTICLE_COUNT - 1).getTitle()); + softly.assertThat(response.jsonPath().getString("[0].title")) + .isEqualTo(articles.get(ARTICLE_COUNT - 1).getTitle()); softly.assertThat(response.jsonPath().getString("[0].contentSummary")) .isEqualTo(articles.get(ARTICLE_COUNT - 1).getContentSummary()); softly.assertThat(response.jsonPath().getByte("[0].comment_count")) .isEqualTo((byte)articles.get(ARTICLE_COUNT - 1).getCommentCount()); - softly.assertThat(response.jsonPath().getLong("[0].hit")).isEqualTo(articles.get(ARTICLE_COUNT - 1).getHit()); - - softly.assertThat(response.jsonPath().getLong("[1].id")).isEqualTo(articles.get(ARTICLE_COUNT - 2).getId()); - softly.assertThat(response.jsonPath().getLong("[2].id")).isEqualTo(articles.get(ARTICLE_COUNT - 3).getId()); - softly.assertThat(response.jsonPath().getLong("[3].id")).isEqualTo(articles.get(ARTICLE_COUNT - 4).getId()); - softly.assertThat(response.jsonPath().getLong("[4].id")).isEqualTo(articles.get(ARTICLE_COUNT - 5).getId()); - softly.assertThat(response.jsonPath().getLong("[5].id")).isEqualTo(articles.get(ARTICLE_COUNT - 6).getId()); - softly.assertThat(response.jsonPath().getLong("[6].id")).isEqualTo(articles.get(ARTICLE_COUNT - 7).getId()); - softly.assertThat(response.jsonPath().getLong("[7].id")).isEqualTo(articles.get(ARTICLE_COUNT - 8).getId()); - softly.assertThat(response.jsonPath().getLong("[8].id")).isEqualTo(articles.get(ARTICLE_COUNT - 9).getId()); - softly.assertThat(response.jsonPath().getLong("[9].id")).isEqualTo(articles.get(ARTICLE_COUNT - 10).getId()); + softly.assertThat(response.jsonPath().getLong("[0].hit")) + .isEqualTo(articles.get(ARTICLE_COUNT - 1).getHit()); + + softly.assertThat(response.jsonPath().getLong("[1].id")) + .isEqualTo(articles.get(ARTICLE_COUNT - 2).getId()); + softly.assertThat(response.jsonPath().getLong("[2].id")) + .isEqualTo(articles.get(ARTICLE_COUNT - 3).getId()); + softly.assertThat(response.jsonPath().getLong("[3].id")) + .isEqualTo(articles.get(ARTICLE_COUNT - 4).getId()); + softly.assertThat(response.jsonPath().getLong("[4].id")) + .isEqualTo(articles.get(ARTICLE_COUNT - 5).getId()); + softly.assertThat(response.jsonPath().getLong("[5].id")) + .isEqualTo(articles.get(ARTICLE_COUNT - 6).getId()); + softly.assertThat(response.jsonPath().getLong("[6].id")) + .isEqualTo(articles.get(ARTICLE_COUNT - 7).getId()); + softly.assertThat(response.jsonPath().getLong("[7].id")) + .isEqualTo(articles.get(ARTICLE_COUNT - 8).getId()); + softly.assertThat(response.jsonPath().getLong("[8].id")) + .isEqualTo(articles.get(ARTICLE_COUNT - 9).getId()); + softly.assertThat(response.jsonPath().getLong("[9].id")) + .isEqualTo(articles.get(ARTICLE_COUNT - 10).getId()); } ); } diff --git a/src/test/java/in/koreatech/koin/acceptance/UserApiTest.java b/src/test/java/in/koreatech/koin/acceptance/UserApiTest.java index 3ec1e40be2..371efdfec7 100644 --- a/src/test/java/in/koreatech/koin/acceptance/UserApiTest.java +++ b/src/test/java/in/koreatech/koin/acceptance/UserApiTest.java @@ -1,5 +1,9 @@ package in.koreatech.koin.acceptance; +import static in.koreatech.koin.domain.user.model.UserType.STUDENT; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.SoftAssertions.assertSoftly; + import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -10,7 +14,6 @@ import in.koreatech.koin.domain.user.model.User; import in.koreatech.koin.domain.user.model.UserGender; import in.koreatech.koin.domain.user.model.UserIdentity; -import static in.koreatech.koin.domain.user.model.UserType.STUDENT; import in.koreatech.koin.domain.user.repository.StudentRepository; import in.koreatech.koin.domain.user.repository.UserRepository; import in.koreatech.koin.global.auth.JwtProvider; @@ -18,8 +21,6 @@ import io.restassured.http.ContentType; import io.restassured.response.ExtractableResponse; import io.restassured.response.Response; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.SoftAssertions.assertSoftly; class UserApiTest extends AcceptanceTest { @@ -157,6 +158,308 @@ void studentCheckMeNotFound() { .extract(); } + @Test + @DisplayName("학생이 정보를 수정한다") + void studentUpdateMe() { + Student student = Student.builder() + .studentNumber("2019136135") + .anonymousNickname("익명") + .department("컴퓨터공학부") + .userIdentity(UserIdentity.UNDERGRADUATE) + .isGraduated(false) + .user( + User.builder() + .password("1234") + .nickname("주노") + .name("최준호") + .phoneNumber("010-1234-5678") + .userType(STUDENT) + .gender(UserGender.MAN) + .email("test@koreatech.ac.kr") + .isAuthed(true) + .isDeleted(false) + .build() + ) + .build(); + + studentRepository.save(student); + String token = jwtProvider.createToken(student.getUser()); + + ExtractableResponse response = RestAssured + .given() + .header("Authorization", "Bearer " + token) + .contentType(ContentType.JSON) + .body(""" + { + "gender" : 1, + "major" : "기계공학부", + "name" : "서정빈", + "nickname" : "duehee", + "phone_number" : "010-2345-6789", + "student_number" : "2019136136" + } + """) + .when() + .put("/user/student/me") + .then() + .statusCode(HttpStatus.OK.value()) + .extract(); + } + + @Test + @DisplayName("학생이 정보를 수정한다 - 학번의 형식이 맞지 않으면 400") + void studentUpdateMeNotValidStudentNumber() { + Student student = Student.builder() + .studentNumber("2019136135") + .anonymousNickname("익명") + .department("컴퓨터공학부") + .userIdentity(UserIdentity.UNDERGRADUATE) + .isGraduated(false) + .user( + User.builder() + .password("1234") + .nickname("주노") + .name("최준호") + .phoneNumber("010-1234-5678") + .userType(STUDENT) + .gender(UserGender.MAN) + .email("test@koreatech.ac.kr") + .isAuthed(true) + .isDeleted(false) + .build() + ) + .build(); + + studentRepository.save(student); + String token = jwtProvider.createToken(student.getUser()); + + ExtractableResponse response = RestAssured + .given() + .header("Authorization", "Bearer " + token) + .contentType(ContentType.JSON) + .body(""" + { + "gender" : 0, + "major" : "메카트로닉스공학부", + "name" : "최주노", + "nickname" : "juno", + "phone_number" : "010-2345-6789", + "student_number" : "201913613" + } + """) + .when() + .put("/user/student/me") + .then() + .statusCode(HttpStatus.BAD_REQUEST.value()) + .extract(); + } + + @Test + @DisplayName("학생이 정보를 수정한다 - 학부의 형식이 맞지 않으면 400") + void studentUpdateMeNotValidDepartment() { + Student student = Student.builder() + .studentNumber("2019136135") + .anonymousNickname("익명") + .department("컴퓨터공학부") + .userIdentity(UserIdentity.UNDERGRADUATE) + .isGraduated(false) + .user( + User.builder() + .password("1234") + .nickname("주노") + .name("최준호") + .phoneNumber("010-1234-5678") + .userType(STUDENT) + .gender(UserGender.MAN) + .email("test@koreatech.ac.kr") + .isAuthed(true) + .isDeleted(false) + .build() + ) + .build(); + + studentRepository.save(student); + String token = jwtProvider.createToken(student.getUser()); + + ExtractableResponse response = RestAssured + .given() + .header("Authorization", "Bearer " + token) + .contentType(ContentType.JSON) + .body(""" + { + "gender" : 0, + "major" : "경영학과", + "name" : "최주노", + "nickname" : "juno", + "phone_number" : "010-2345-6789", + "student_number" : "2019136136" + } + """) + .when() + .put("/user/student/me") + .then() + .statusCode(HttpStatus.BAD_REQUEST.value()) + .extract(); + } + + @Test + @DisplayName("학생이 정보를 수정한다 - 토큰이 올바르지 않다면 401") + void studentUpdateMeUnAuthorized() { + Student student = Student.builder() + .studentNumber("2019136135") + .anonymousNickname("익명") + .department("컴퓨터공학부") + .userIdentity(UserIdentity.UNDERGRADUATE) + .isGraduated(false) + .user( + User.builder() + .password("1234") + .nickname("주노") + .name("최준호") + .phoneNumber("010-1234-5678") + .userType(STUDENT) + .gender(UserGender.MAN) + .email("test@koreatech.ac.kr") + .isAuthed(true) + .isDeleted(false) + .build() + ) + .build(); + + studentRepository.save(student); + String token = "invalidToken"; + + ExtractableResponse response = RestAssured + .given() + .header("Authorization", "Bearer " + token) + .contentType(ContentType.JSON) + .body(""" + { + "gender" : 0, + "major" : "메카트로닉스공학부", + "name" : "최주노", + "nickname" : "juno", + "phone_number" : "010-2345-6789", + "student_number" : "2019136136" + } + """) + .when() + .put("/user/student/me") + .then() + .statusCode(HttpStatus.UNAUTHORIZED.value()) + .extract(); + } + + @Test + @DisplayName("학생이 정보를 수정한다 - 회원을 찾을 수 없다면 404") + void studentUpdateMeNotFound() { + User user = + User.builder() + .password("1234") + .nickname("주노") + .name("최준호") + .phoneNumber("010-1234-5678") + .userType(STUDENT) + .gender(UserGender.MAN) + .email("test@koreatech.ac.kr") + .isAuthed(true) + .isDeleted(false) + .build(); + + userRepository.save(user); + String token = jwtProvider.createToken(user); + + ExtractableResponse response = RestAssured + .given() + .header("Authorization", "Bearer " + token) + .contentType(ContentType.JSON) + .body(""" + { + "gender" : 0, + "major" : "메카트로닉스공학부", + "name" : "최주노", + "nickname" : "juno", + "phone_number" : "010-2345-6789", + "student_number" : "2019136136" + } + """) + .when() + .put("/user/student/me") + .then() + .statusCode(HttpStatus.NOT_FOUND.value()) + .extract(); + } + + @Test + @DisplayName("학생이 정보를 수정한다 - 이미 있는 닉네임이라면 409") + void studentUpdateMeDuplicationNickname() { + Student student1 = Student.builder() + .studentNumber("2019136135") + .anonymousNickname("익명") + .department("컴퓨터공학부") + .userIdentity(UserIdentity.UNDERGRADUATE) + .isGraduated(false) + .user( + User.builder() + .password("1234") + .nickname("주노") + .name("최준호") + .phoneNumber("010-1234-5678") + .userType(STUDENT) + .gender(UserGender.MAN) + .email("test1@koreatech.ac.kr") + .isAuthed(true) + .isDeleted(false) + .build() + ) + .build(); + + Student student2 = Student.builder() + .studentNumber("2020136065") + .anonymousNickname("익명") + .department("컴퓨터공학부") + .userIdentity(UserIdentity.UNDERGRADUATE) + .isGraduated(false) + .user( + User.builder() + .password("1234") + .nickname("duehee") + .name("서정빈") + .phoneNumber("010-1234-5678") + .userType(STUDENT) + .gender(UserGender.MAN) + .email("test2@koreatech.ac.kr") + .isAuthed(true) + .isDeleted(false) + .build() + ) + .build(); + + studentRepository.save(student1); + studentRepository.save(student2); + String token = jwtProvider.createToken(student1.getUser()); + + ExtractableResponse response = RestAssured + .given() + .header("Authorization", "Bearer " + token) + .contentType(ContentType.JSON) + .body(""" + { + "gender" : 0, + "major" : "테스트학과", + "name" : "최주노", + "nickname" : "duehee", + "phone_number" : "010-2345-6789", + "student_number" : "2019136136" + } + """) + .when() + .put("/user/student/me") + .then() + .statusCode(HttpStatus.CONFLICT.value()) + .extract(); + } + @Test @DisplayName("회원이 탈퇴한다") void userWithdraw() {