Skip to content

Commit

Permalink
Merge pull request #151 from GDG-on-Campus-KNU/dev
Browse files Browse the repository at this point in the history
* JPA N+1 수정, 페이징 조회 최적화 (#146)

* Feat: 더미데이터 삽입 편의 기능 - member

* Feat: 더미데이터 삽입 편의 기능 - application, team

* Feat: 더미데이터 삽입 편의 기능 - post

* Refac: jdbc insert 클래스 디렉토리 구분

* Feat: 더미데이터 nullable 필드 포함

* Feat: 팀 색성시 트랙별 멤버 조회 쿼리 - DB 필터링 이용

* Fix: 사용자 조회 N+1 수정 (fetch join)

* Test: 직렬, 권한별 사용자 조회 테스트 작성

* Feat: count 쿼리 필요 없는 페이징 수정 - Comment

* Feat: 페이징시 불필요한 count 쿼리 제거 - Post

* Feat: 지원서 상세 조회 답변 fetch join

* Feat: 게시글, 작성자 fetch join

* Feat: 중간 테이블 이용시 불필요한 join 제거 (native query 이용)

* Fix: Transactional 누락 추가

* Refac: team으로 member 정보를 찾는 동일한 쿼리 병합

* Docs: 주석 추가

* Test: 변경(통합)된 쿼리 테스트코드 수정

* 지원서 메모 동시성 제어 (#149)

* Feat: 낙관적 락을 위한 version 필드 추가

* Fix: version을 추가하여 api spec 수정 + 합격 활성 날짜 변경

* Feat: 버전체크 로직 작성

* Fix: ObjectOptimisticLockingFailureException 핸들링, 버전체크 도메인 로직으로 수정

* Test: 지원서 메모 동시성 테스트 작성

* Fix: 에러코드 수정(PN->AN)

* !HOTFIX: answer 없는 지원서도 조회되도록 쿼리 수정

* 기수 테이블 추가 / 지원서 기수별 구분 기능 추가 (#150)

* Fix: 다른 기수 중복 지원을 위해 unique 제약 조건 제거

* Feat: 기수 저장을 위한 엔티티 생성

* Feat: 기수와 지원서 간 연관관계 작성

* Feat: 기수 레포지토리 작성

* Feat: 기수 DTO 작성

* Feat: 기수 서비스 작성

* Feat: 기수 컨트롤러 작성

* Feat: 기수 ErrorCode 작성

* Fix: 중복 제출 오류 코드를 좀더 명확하게 수정

* Feat: 지원서 403 에러코드 추가

* Feat: ClassYear 업데이트 편의 메서드 추가

* Refac: 에러코드를 명확하게 변경

* Feat: 지원서 작성 시 기수 정보도 요청

* Fix: 누락된 갱신 로직 적용

* Feat: 기수 정보를 포함해 지원서를 조회

* Fix: 기수 정보 단건 조회 방식 수정

* Fix: 잘못된 응답코드 수정

* Feat: 기수 - 지원서 간 연관관계 설정

* Feat: 조건별 서류 조회(필터링)에 기수 id 추가

* Fix: LocalDateTime 통신 형태 지정

* Fix: 잘못된 초기 지원 중복 체크 로직 수정

* Fix: 지원서 auto auditing 제거

* Fix: 지원서 - 프레젠테이션 계층 간 의존성 제거

* Feat: 예외 상황 추가 및 에러메시지 자세하게 수정

* Feat: 기수 예외처리 추가

* Test: 지원서 테스트에 기수 관련 로직 추가 및 수정

* Docs: 조건별 지원 서류 조회 스웨거 설명 추가

* Rename: ApplicationRequestDTO -> ApplicationModel

* Fix: 고유 code로 수정

* Refac: 기수 관련 코드 분리

* Fix: 일관된 ErrorCode 사용

* Fix: 의미 없는 save 제거

* Fix: 객체 생성 방식 통일

* Fix: 테스트용 출력, 주석 삭제

* Docs: 분리한 기수 컨트롤러 스웨거 태그 추가

---------

Co-authored-by: kwonssshyeon <[email protected]>
Co-authored-by: kwonssshyeon <[email protected]>
  • Loading branch information
3 people authored Dec 29, 2024
2 parents 99ec019 + 832167d commit a989f94
Show file tree
Hide file tree
Showing 52 changed files with 1,191 additions and 184 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ out/

src/main/resources/static/index.html
src/main/resources/images/**
.env
.env
gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ public class ApplicationController {
최종제출 상태의 지원서는 조회가 불가능합니다.
""")
public ResponseEntity<ApplicationResponse> getApplication(@TokenMember JwtMemberDetail jwtMemberDetail, @RequestParam String name, @RequestParam String studentNumber) {
public ResponseEntity<ApplicationResponse> getApplication(@TokenMember JwtMemberDetail jwtMemberDetail,
@RequestParam String name,
@RequestParam String studentNumber,
@RequestParam Long classYearId) {
return ResponseEntity.ok()
.body(applicationService.getApplication(jwtMemberDetail.getEmail(), name, studentNumber));
.body(applicationService.getApplication(jwtMemberDetail.getEmail(), name, studentNumber, classYearId));
}

@PostMapping
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.gdsc_knu.official_homepage.controller.admin;

import com.gdsc_knu.official_homepage.dto.PagingResponse;
import com.gdsc_knu.official_homepage.dto.admin.application.AdminApplicationRequest;
import com.gdsc_knu.official_homepage.dto.admin.application.AdminApplicationResponse;
import com.gdsc_knu.official_homepage.entity.enumeration.ApplicationStatus;
import com.gdsc_knu.official_homepage.entity.enumeration.Track;
Expand All @@ -23,7 +24,7 @@
@RequiredArgsConstructor
public class AdminApplicationController {
private final AdminApplicationService applicationService;
private static final LocalDate ACTIVE_DATE = LocalDate.of(2024,9,14);
private static final LocalDate ACTIVE_DATE = LocalDate.of(2025,1,19);

@GetMapping("statistic")
@Operation(summary="지원서류 통계데이터 조회 API")
Expand All @@ -44,15 +45,16 @@ public ResponseEntity<Map<String, Integer>> getTrackStatistic() {
마크된것만 조회하려면 isMarked=true로 설정하세요.
isMarked를 비워두거나 false로 설정하면 전체가 조회됩니다.(track도 마찬가지로 비워두면 전체)""")
isMarked를 비워두거나 false로 설정하면 전체가 조회됩니다.(track, classYearId도 마찬가지로 비워두면 전체)""")
public ResponseEntity<PagingResponse<AdminApplicationResponse.Overview>> getApplicationListByOption(
@RequestParam(value = "track", required = false) Track track,
@RequestParam(value = "isMarked", required = false, defaultValue = "false") Boolean isMarked,
@RequestParam(value = "page", defaultValue = "0") int page,
@RequestParam(value = "size", defaultValue = "10") int size)
@RequestParam(value = "size", defaultValue = "10") int size,
@RequestParam(value = "classYearId", required = false) Long classYearId)

{
return ResponseEntity.ok().body(applicationService.getApplicationsByOption(page, size, track, isMarked));
return ResponseEntity.ok().body(applicationService.getApplicationsByOption(page, size, track, isMarked, classYearId));
}


Expand Down Expand Up @@ -107,11 +109,8 @@ public ResponseEntity<AdminApplicationResponse.Detail> getApplicationDetail(@Req
@PatchMapping("note")
@Operation(summary="지원서류 메모 API", description = "지원서류에 메모를 합니다.")
public ResponseEntity<Void> noteApplication(@RequestParam("id") Long id,
@RequestBody String note) {
applicationService.noteApplication(id, note);
@RequestBody AdminApplicationRequest.Append request) {
applicationService.noteApplication(id, request.getNote(), request.getVersion());
return new ResponseEntity<>(HttpStatus.OK);
}



}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.gdsc_knu.official_homepage.controller.admin;

import com.gdsc_knu.official_homepage.dto.admin.application.AdminApplicationRequest;
import com.gdsc_knu.official_homepage.dto.admin.application.AdminApplicationResponse;
import com.gdsc_knu.official_homepage.service.admin.AdminClassYearService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Tag(name = "Admin ClassYear", description = "기수 관리 관련 API")
@RestController
@RequiredArgsConstructor
@RequestMapping("api/admin/class-year")
public class AdminClassYearController {
private final AdminClassYearService classYearService;

@GetMapping()
@Operation(summary="기수 목록 조회 API", description = "기수 목록을 조회합니다.")
public ResponseEntity<List<AdminApplicationResponse.ClassYearResponse>> getClassYearList() {
return ResponseEntity.ok(classYearService.getClassYearList());
}

@GetMapping("/{id}")
@Operation(summary="기수 단건 조회 API", description = "기수를 단건 조회합니다.")
public ResponseEntity<AdminApplicationResponse.ClassYearResponse> getClassYear(@PathVariable("id") Long id) {
return ResponseEntity.ok(classYearService.getClassYear(id));
}

@PostMapping()
@Operation(summary="기수 추가 API", description = "기수를 추가합니다.")
public ResponseEntity<Void> addClassYear(@RequestBody AdminApplicationRequest.ClassYearRequest classYearRequest) {
classYearService.addClassYear(classYearRequest);
return new ResponseEntity<>(HttpStatus.CREATED);
}

@PutMapping()
@Operation(summary="기수 수정 API", description = "기수의 정보를 수정합니다.")
public ResponseEntity<Void> updateClassYear(@RequestParam("id") Long id,
@RequestBody AdminApplicationRequest.ClassYearRequest classYearRequest) {
classYearService.updateClassYear(id, classYearRequest);
return new ResponseEntity<>(HttpStatus.OK);
}

@DeleteMapping()
@Operation(summary="기수 삭제 API", description = "기수를 삭제 합니다.")
public ResponseEntity<Void> deleteClassYear(@RequestParam("id") Long id) {
classYearService.deleteClassYear(id);
return new ResponseEntity<>(HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
@AllArgsConstructor
public class PagingResponse<T> {
private final List<T> data;
private final boolean hasNext;
@JsonInclude(JsonInclude.Include.NON_NULL)
private final Integer page;
private final boolean hasNext;
@JsonInclude(JsonInclude.Include.NON_NULL)
private final Integer totalPage;

Expand All @@ -32,11 +32,11 @@ public static <U,T> PagingResponse<T> from(Page<U> data, Function<U,T> converter
.build();
}

public static <U,T> PagingResponse<T> withoutCountFrom(Page<U> data, int size, Function<U,T> converter) {
boolean hasNext = data.getNumberOfElements() >= size;
public static <U,T> PagingResponse<T> withoutCountFrom(List<U> data, int size, Function<U,T> converter) {
boolean hasNext = data.size() == size;

return PagingResponse.<T>builder()
.data(data.getContent().stream().map(converter).toList())
.data(data.stream().map(converter).toList())
.hasNext(hasNext)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.gdsc_knu.official_homepage.dto.admin.application;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.gdsc_knu.official_homepage.entity.ClassYear;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

public class AdminApplicationRequest {
@Getter
@AllArgsConstructor
@NoArgsConstructor
public static class Append {
private String note;
private Integer version;
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class ClassYearRequest {
private String name;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime applyStartDateTime;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime applyEndDateTime;

public ClassYear toEntity() {
return ClassYear.builder()
.name(name)
.applicationStartDateTime(applyStartDateTime)
.applicationEndDateTime(applyEndDateTime)
.build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.gdsc_knu.official_homepage.entity.ClassYear;
import com.gdsc_knu.official_homepage.entity.application.Application;
import com.gdsc_knu.official_homepage.entity.application.ApplicationAnswer;
import lombok.AllArgsConstructor;
Expand Down Expand Up @@ -60,7 +61,7 @@ public static Overview from(Application application){
return Overview.builder()
.id(application.getId())
.name(application.getName())
.submittedAt(application.getModifiedAt())
.submittedAt(application.getSubmittedAt())
.studentNumber(application.getStudentNumber())
.major(application.getMajor())
.track(application.getTrack().name())
Expand All @@ -77,6 +78,7 @@ public static Overview from(Application application){
@NoArgsConstructor
public static class Detail {
private Long id; // application id
private Integer version;
private String name;
private String studentNumber;
private String major;
Expand All @@ -96,13 +98,14 @@ public static class Detail {
public static Detail from(Application application){
return Detail.builder()
.id(application.getId())
.version(application.getVersion())
.name(application.getName())
.studentNumber(application.getStudentNumber())
.major(application.getMajor())
.phoneNumber(application.getPhoneNumber())
.email(application.getEmail())
.track(application.getTrack().name())
.submittedAt(application.getModifiedAt())
.submittedAt(application.getSubmittedAt())
.techStack(application.getTechStack())
.link(application.getLinks())
.isMarked(application.isMarked())
Expand Down Expand Up @@ -145,4 +148,30 @@ public static Result from(HttpStatus status, String message) {
.build();
}
}

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class ClassYearResponse {
private Long id;
private String name;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime applyStartDateTime;
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime applyEndDateTime;

public static AdminApplicationResponse.ClassYearResponse from(ClassYear classYear) {
return AdminApplicationResponse.ClassYearResponse.builder()
.id(classYear.getId())
.name(classYear.getName())
.applyStartDateTime(classYear.getApplicationStartDateTime())
.applyEndDateTime(classYear.getApplicationEndDateTime())
.build();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.gdsc_knu.official_homepage.dto.admin.team;

import com.gdsc_knu.official_homepage.dto.team.TeamResponse;
import com.gdsc_knu.official_homepage.entity.Member;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
Expand Down Expand Up @@ -31,5 +32,12 @@ public static class TeamMember {
private String name;
private String studentNumber;
private String profileUrl;

public static TeamMember from (Member member) {
return new TeamMember(member.getId(),
member.getName(),
member.getStudentNumber(),
member.getProfileUrl());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.gdsc_knu.official_homepage.dto.application;

import com.gdsc_knu.official_homepage.entity.enumeration.ApplicationStatus;
import com.gdsc_knu.official_homepage.entity.enumeration.Track;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.util.List;

@Getter
@AllArgsConstructor
@Builder
@NoArgsConstructor
public class ApplicationModel {
private String techStack;
private String links;
private ApplicationStatus applicationStatus;
private Track track;
private List<ApplicationAnswerDTO> answers;

public ApplicationModel(ApplicationRequest applicationRequest) {
this.techStack = applicationRequest.getTechStack();
this.links = applicationRequest.getLinks();
this.applicationStatus = applicationRequest.getApplicationStatus();
this.track = applicationRequest.getTrack();
this.answers = applicationRequest.getAnswers();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
@Builder
@Getter
public class ApplicationRequest {
@NotNull(message = "기수 정보는 필수입니다.")
private Long classYearId;

@NotNull(message = "잘못된 입력 형식입니다.")
private String techStack;

Expand Down
34 changes: 34 additions & 0 deletions src/main/java/com/gdsc_knu/official_homepage/entity/ClassYear.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.gdsc_knu.official_homepage.entity;

import com.gdsc_knu.official_homepage.entity.application.Application;
import jakarta.persistence.*;
import lombok.*;

import java.time.LocalDate;
import java.time.LocalDateTime;

@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ClassYear {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false, unique = true)
private String name;

@Column(nullable = false)
private LocalDateTime applicationStartDateTime;

@Column(nullable = false)
private LocalDateTime applicationEndDateTime;

public void update(String name, LocalDateTime applicationStartDateTime, LocalDateTime applicationEndDateTime) {
this.name = name;
this.applicationStartDateTime = applicationStartDateTime;
this.applicationEndDateTime = applicationEndDateTime;
}
}
Loading

0 comments on commit a989f94

Please sign in to comment.