Skip to content

Commit

Permalink
[FEAT] Application 관련 API 개발 완료 (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
yesjuhee authored and yeobi01 committed Aug 21, 2024
1 parent 665ba9d commit d342ba2
Show file tree
Hide file tree
Showing 17 changed files with 546 additions and 7 deletions.
23 changes: 23 additions & 0 deletions src/docs/asciidoc/application.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
== 가입 신청 관리 API
:source-highlighter: highlightjs

---
=== 가입 신청 리스트 조회 (GET /applications)
====
operation::application-controller-test/get-applications[snippets="http-request,query-parameters,http-response,response-fields"]
====

=== 가입 신청자 상세 정보 조회 (GET /applications/{applicationId})
====
operation::application-controller-test/get-application[snippets="http-request,path-parameters,http-response,response-fields"]
====

=== 교수/기업 가입 허가 (PATCH /applications/{applicationId})
====
operation::application-controller-test/update-application[snippets="http-request,path-parameters,http-response,response-fields"]
====

=== 교수/기업 가입 거절 (DELETE /applications/{applicationId})
====
operation::application-controller-test/reject-application[snippets="http-request,path-parameters,http-response"]
====
13 changes: 12 additions & 1 deletion src/docs/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,15 @@
:toclevels: 3
:seclinks:

include::project.adoc[]
=== 커스텀 예외 코드
include::{snippets}/exception-code-controller-test/get-exception-codes/exception-response-fields.adoc[]

include::auth-controller-test.adoc[]
include::jobInterview.adoc[]
include::talk.adoc[]
include::notice.adoc[]
include::eventNotice.adoc[]
include::eventPeriod.adoc[]
include::gallery.adoc[]
include::project.adoc[]
include::application.adoc[]
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.Arrays;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand Down Expand Up @@ -40,6 +41,7 @@ public class RegisterRequest {

public RegisterRequest(String name, String phoneNumber, UserType userType, String email, String signUpSource,
StudentInfoDto studentInfo, String division, String position) {
validateUserType(userType);
validateStudentInfo(userType, studentInfo);
this.name = name;
this.phoneNumber = phoneNumber;
Expand All @@ -58,4 +60,10 @@ private void validateStudentInfo(UserType userType, StudentInfoDto studentInfo)
}
}
}

private void validateUserType(UserType userType) {
if(Arrays.asList(UserType.ADMIN, UserType.PROFESSOR, UserType.COMPANY).contains(userType)) {
throw new BadRequestException(ExceptionCode.INVALID_USERTYPE);
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/com/scg/stop/auth/service/AuthService.java
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public RegisterResponse finishRegister(User user, RegisterRequest registerReques
, department);
studentRepository.save(student);
}
else if (Arrays.asList(UserType.INACTIVE_PROFESSOR, UserType.COMPANY, UserType.INACTIVE_COMPANY, UserType.PROFESSOR)
else if (Arrays.asList(UserType.INACTIVE_PROFESSOR, UserType.INACTIVE_COMPANY)
.contains(registerRequest.getUserType())) {
Application application = new Application(registerRequest.getDivision(), registerRequest.getPosition(),
user);
Expand Down
29 changes: 25 additions & 4 deletions src/main/java/com/scg/stop/global/exception/ExceptionCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,30 @@ public enum ExceptionCode {

INVALID_REQUEST(1000, "요청 형식이 올바르지 않습니다."),

// event domain
DUPLICATED_YEAR(1001, "해당 연도의 행사 기간이 이미 존재합니다."),
NOT_FOUND_EVENT_PERIOD(1002, "올해의 이벤트 기간이 존재하지 않습니다."),
INVALID_EVENT_PERIOD(1003, "이벤트 시작 일시 혹은 종료 일시가 올해를 벗어났습니다."),

// auth domain
UNABLE_TO_GET_USER_INFO(2001, "소셜 로그인 공급자로부터 유저 정보를 받아올 수 없습니다."),
UNABLE_TO_GET_ACCESS_TOKEN(2002, "소셜 로그인 공급자로부터 인증 토큰을 받아올 수 없습니다."),

UNAUTHORIZED_ACCESS(3000, "접근할 수 없는 리소스입니다."),
INVALID_REFRESH_TOKEN(3001,"유효하지 않은 Refresh Token 입니다."),
FAILED_TO_VALIDATE_TOKEN(3002,"토큰 검증에 실패했습니다."),
INVALID_ACCESS_TOKEN(3003,"유효하지 않은 Access Token 입니다."),

// user domain
NOT_FOUND_USER_ID(4000, "유저 id 를 찾을 수 없습니다."),
REGISTER_NOT_FINISHED(4001, "회원가입이 필요합니다."),
NOT_AUTHORIZED(4002, "유저 권한이 존재하지 않습니다."),
NOT_FOUND_DEPARTMENT(4003, "학과가 존재하지 않습니다."),
INVALID_STUDENTINFO(4004, "학과/학번 정보가 존재하지 않습니다."),
INVALID_USERTYPE(4005, "회원 가입 이용이 불가능한 회원 유형입니다."),
NOT_FOUND_APPLICATION_ID(4010, "ID에 해당하는 인증 신청 정보가 존재하지 않습니다."),
ALREADY_VERIFIED_USER(4011, "이미 인증 된 회원입니다."),

// project domain
NOT_FOUND_PROJECT(77000, "프로젝트를 찾을 수 없습니다."),
NOT_FOUND_PROJECT_THUMBNAIL(77001, "프로젝트 썸네일을 찾을 수 없습니다"),
NOT_FOUND_PROJECT_POSTER(77002, "프로젝트 포스터를 찾을 수 없습니다"),
Expand All @@ -36,9 +44,22 @@ public enum ExceptionCode {
NOT_FOUND_LIKE_PROJECT(77009, "좋아요 표시한 프로젝트가 존재하지 않습니다"),
NOT_FOUND_COMMENT(77010, "댓글을 찾을 수 없습니다"),
NOT_MATCH_USER(77011, "유저 정보가 일치하지 않습니다"),
NOT_FOUND_EVENT_PERIOD(77012, "행사 기간을 찾을 수 없습니다."),
NOT_EVENT_PERIOD(77013, "행사 기간이 아닙니다");
NOT_EVENT_PERIOD(77012, "행사 기간이 아닙니다"),

// notice domain
NOTICE_NOT_FOUND(10000, "요청한 ID에 해당하는 공지사항이 존재하지 않습니다."),
EVENT_NOTICE_NOT_FOUND(11000, "요청한 ID에 해당하는 이벤트가 존재하지 않습니다."),
FILE_NOT_FOUND(12000, "요청한 ID에 해당하는 파일이 존재하지 않습니다."),

// video domain
ID_NOT_FOUND(8200,"해당 ID에 해당하는 잡페어 인터뷰가 없습니다."),
TALK_ID_NOT_FOUND(8400, "해당 ID에 해당하는 대담 영상이 없습니다."),
NO_QUIZ(8401, "퀴즈 데이터가 존재하지 않습니다."),

// file domain
NOT_FOUND_FILE_ID(5001, "요청한 ID에 해당하는 파일이 존재하지 않습니다."),
NOT_FOUND_GALLERY_ID(9001, "요청한 ID에 해당하는 갤러리가 존재하지 않습니다.");

private final int code;
private final String message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.scg.stop.user.controller;

import com.scg.stop.auth.annotation.AuthUser;
import com.scg.stop.user.domain.AccessType;
import com.scg.stop.user.domain.User;
import com.scg.stop.user.dto.response.ApplicationDetailResponse;
import com.scg.stop.user.dto.response.ApplicationListResponse;
import com.scg.stop.user.service.ApplicationService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
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.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/applications")
public class ApplicationController {

private final ApplicationService applicationService;

@GetMapping
public ResponseEntity<Page<ApplicationListResponse>> getApplications(
@PageableDefault(page = 0, size = 10) Pageable pageable,
@AuthUser(accessType = {AccessType.ADMIN}) User user
) {
Page<ApplicationListResponse> applications = applicationService.getApplications(pageable);
return ResponseEntity.ok().body(applications);
}

@GetMapping("/{applicationId}")
public ResponseEntity<ApplicationDetailResponse> getApplication(
@PathVariable("applicationId") Long applicationId,
@AuthUser(accessType = {AccessType.ADMIN}) User user
) {
ApplicationDetailResponse application = applicationService.getApplication(applicationId);
return ResponseEntity.ok().body(application);
}

@PatchMapping("/{applicationId}")
public ResponseEntity<ApplicationDetailResponse> approveApplication(
@PathVariable("applicationId") Long applicationId,
@AuthUser(accessType = {AccessType.ADMIN}) User user
) {
ApplicationDetailResponse application = applicationService.approveApplication(applicationId);
return ResponseEntity.ok().body(application);
}

@DeleteMapping("/{applicationId}")
public ResponseEntity<Void> rejectApplication(
@PathVariable("applicationId") Long applicationId,
@AuthUser(accessType = {AccessType.ADMIN}) User user
) {
applicationService.rejectApplication(applicationId);
return ResponseEntity.noContent().build();
}
}
15 changes: 15 additions & 0 deletions src/main/java/com/scg/stop/user/domain/Application.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package com.scg.stop.user.domain;

import static jakarta.persistence.EnumType.STRING;
import static jakarta.persistence.FetchType.LAZY;
import static jakarta.persistence.GenerationType.IDENTITY;
import static lombok.AccessLevel.PROTECTED;

import com.scg.stop.global.domain.BaseTimeEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
Expand All @@ -28,6 +31,10 @@ public class Application extends BaseTimeEntity {

private String position; // 직책

@Column(nullable = false)
@Enumerated(value = STRING)
ApplicationStatus status = ApplicationStatus.INACTIVE;

@OneToOne(fetch = LAZY)
@JoinColumn(name = "user_id")
private User user;
Expand All @@ -37,4 +44,12 @@ public Application(String division, String position, User user) {
this.position = position;
this.user = user;
}

public void activate() {
status = ApplicationStatus.ACTIVE;
}

public void reject() {
status = ApplicationStatus.REJECTED;
}
}
8 changes: 8 additions & 0 deletions src/main/java/com/scg/stop/user/domain/ApplicationStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.scg.stop.user.domain;

public enum ApplicationStatus {

INACTIVE, // 승인 전
ACTIVE, // 승인 됨
REJECTED // 승인 거절 됨
}
9 changes: 9 additions & 0 deletions src/main/java/com/scg/stop/user/domain/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,15 @@ public class User extends BaseTimeEntity {
@OneToMany(fetch = LAZY, mappedBy = "user")
private List<Inquiry> inquiries = new ArrayList<>();

public void activateUser() {
if (userType == UserType.INACTIVE_COMPANY) {
userType = UserType.COMPANY;
} else if (userType == UserType.INACTIVE_PROFESSOR) {
userType = UserType.PROFESSOR;
}
application.activate();
}

public User(String socialLoginId) {
this.userType = UserType.TEMP;
this.socialLoginId = socialLoginId;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.scg.stop.user.dto.response;

import static lombok.AccessLevel.PRIVATE;

import com.scg.stop.user.domain.Application;
import com.scg.stop.user.domain.UserType;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = PRIVATE)
@AllArgsConstructor
public class ApplicationDetailResponse {

private Long id;
private String name;
private String phone;
private String email;
private String division;
private String position;
private UserType userType;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;

public static ApplicationDetailResponse from(Application application) {
return new ApplicationDetailResponse(
application.getId(),
application.getUser().getName(),
application.getUser().getPhone(),
application.getUser().getEmail(),
application.getDivision(),
application.getPosition(),
application.getUser().getUserType(),
application.getCreatedAt(),
application.getUpdatedAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.scg.stop.user.dto.response;

import static lombok.AccessLevel.PRIVATE;

import com.scg.stop.user.domain.Application;
import com.scg.stop.user.domain.UserType;
import java.time.LocalDateTime;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor(access = PRIVATE)
@AllArgsConstructor
public class ApplicationListResponse {

private Long id;
private String name;
private String division;
private String position;
private UserType userType;
private LocalDateTime createdAt;
private LocalDateTime updatedAt;

public static ApplicationListResponse from(Application application) {
return new ApplicationListResponse(
application.getId(),
application.getUser().getName(),
application.getDivision(),
application.getPosition(),
application.getUser().getUserType(),
application.getCreatedAt(),
application.getUpdatedAt()
);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
package com.scg.stop.user.repository;

import com.scg.stop.user.domain.Application;
import com.scg.stop.user.domain.ApplicationStatus;
import com.scg.stop.user.domain.UserType;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface ApplicationRepository extends JpaRepository<Application, Long> {

Page<Application> findByStatus(ApplicationStatus status, Pageable pageable);
}
Loading

0 comments on commit d342ba2

Please sign in to comment.