Skip to content

Commit 6bc4251

Browse files
committed
Merge remote-tracking branch 'origin/feat/#38-1' into feat/#38-1
# Conflicts: # src/main/java/dmu/dasom/api/domain/applicant/repository/ApplicantRepository.java
2 parents 79d3fd5 + 8e3cfac commit 6bc4251

File tree

12 files changed

+138
-11
lines changed

12 files changed

+138
-11
lines changed

src/main/java/dmu/dasom/api/domain/applicant/dto/ApplicantCreateRequestDto.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package dmu.dasom.api.domain.applicant.dto;
22

3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.fasterxml.jackson.annotation.JsonSetter;
5+
import com.fasterxml.jackson.annotation.Nulls;
36
import dmu.dasom.api.domain.applicant.entity.Applicant;
47
import io.swagger.v3.oas.annotations.media.Schema;
58
import jakarta.validation.constraints.*;
@@ -9,6 +12,11 @@
912
@Schema(name = "ApplicantCreateRequestDto", description = "지원자 생성 요청 DTO")
1013
public class ApplicantCreateRequestDto {
1114

15+
@NotNull
16+
@Size(max = 16)
17+
@Schema(description = "이름", example = "홍길동")
18+
private String name;
19+
1220
@NotNull
1321
@Pattern(regexp = "^[0-9]{8}$")
1422
@Size(min = 8, max = 8)
@@ -39,15 +47,21 @@ public class ApplicantCreateRequestDto {
3947
private String reasonForApply;
4048

4149
@Size(max = 200)
42-
@Schema(description = "활동 희망사항", example = "동아리 활동 참여")
50+
@Schema(description = "활동 희망사항", example = "동아리 활동 참여", nullable = true)
4351
private String activityWish;
4452

4553
@NotNull
4654
@Schema(description = "개인정보 처리방침 동의 여부", example = "true")
4755
private Boolean isPrivacyPolicyAgreed;
4856

57+
@JsonProperty(defaultValue = "false")
58+
@JsonSetter(nulls = Nulls.SKIP)
59+
@Schema(description = "지원서 덮어쓰기 확인 여부", example = "false", defaultValue = "false", nullable = true)
60+
private Boolean isOverwriteConfirmed = false;
61+
4962
public Applicant toEntity() {
5063
return Applicant.builder()
64+
.name(this.name)
5165
.studentNo(this.studentNo)
5266
.contact(this.contact)
5367
.email(this.email)

src/main/java/dmu/dasom/api/domain/applicant/entity/Applicant.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package dmu.dasom.api.domain.applicant.entity;
22

3+
import dmu.dasom.api.domain.applicant.dto.ApplicantCreateRequestDto;
34
import dmu.dasom.api.domain.applicant.dto.ApplicantDetailsResponseDto;
45
import dmu.dasom.api.domain.applicant.dto.ApplicantResponseDto;
56
import dmu.dasom.api.domain.applicant.enums.ApplicantStatus;
@@ -9,6 +10,7 @@
910
import lombok.Builder;
1011
import lombok.Getter;
1112
import lombok.NoArgsConstructor;
13+
import org.hibernate.annotations.DynamicUpdate;
1214
import org.springframework.data.annotation.CreatedDate;
1315
import org.springframework.data.annotation.LastModifiedDate;
1416
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
@@ -17,6 +19,7 @@
1719

1820
@AllArgsConstructor
1921
@Builder
22+
@DynamicUpdate
2023
@Entity
2124
@EntityListeners(AuditingEntityListener.class)
2225
@Getter
@@ -32,7 +35,7 @@ public class Applicant {
3235
@Size(max = 16)
3336
private String name;
3437

35-
@Column(name = "student_no", nullable = false, length = 8)
38+
@Column(name = "student_no", nullable = false, unique = true, length = 8)
3639
@Pattern(regexp = "^[0-9]{8}$")
3740
@Size(min = 8, max = 8)
3841
private String studentNo;
@@ -105,4 +108,14 @@ public ApplicantDetailsResponseDto toApplicantDetailsResponse() {
105108
.build();
106109
}
107110

111+
public void overwrite(final ApplicantCreateRequestDto request) {
112+
this.name = request.getName();
113+
this.contact = request.getContact();
114+
this.email = request.getEmail();
115+
this.grade = request.getGrade();
116+
this.reasonForApply = request.getReasonForApply();
117+
this.activityWish = request.getActivityWish();
118+
this.isPrivacyPolicyAgreed = request.getIsPrivacyPolicyAgreed();
119+
}
120+
108121
}

src/main/java/dmu/dasom/api/domain/applicant/repository/ApplicantRepository.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
import java.util.List;
1111

12+
import java.util.Optional;
13+
1214
public interface ApplicantRepository extends JpaRepository<Applicant, Long> {
1315

1416
@Query("SELECT a FROM Applicant a ORDER BY a.id DESC")
@@ -17,4 +19,6 @@ public interface ApplicantRepository extends JpaRepository<Applicant, Long> {
1719
// 상태별 지원자 조회
1820
List<Applicant> findByStatus(ApplicantStatus status);
1921

22+
Optional<Applicant> findByStudentNo(final String studentNo);
23+
2024
}

src/main/java/dmu/dasom/api/domain/applicant/service/ApplicantServiceImpl.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@
1717
import org.springframework.data.domain.Page;
1818
import org.springframework.data.domain.PageRequest;
1919
import org.springframework.stereotype.Service;
20+
import org.springframework.transaction.annotation.Transactional;
21+
22+
import java.util.Optional;
2023

2124
import java.util.List;
2225

2326
@Slf4j
2427
@RequiredArgsConstructor
2528
@Service
29+
@Transactional
2630
public class ApplicantServiceImpl implements ApplicantService {
2731

2832
private final static int DEFAULT_PAGE_SIZE = 20;
@@ -33,6 +37,20 @@ public class ApplicantServiceImpl implements ApplicantService {
3337
// 지원자 저장
3438
@Override
3539
public void apply(final ApplicantCreateRequestDto request) {
40+
final Optional<Applicant> applicant = findByStudentNo(request.getStudentNo());
41+
42+
// 이미 지원한 학번이 존재할 경우
43+
if (applicant.isPresent()) {
44+
// 덮어쓰기 확인 여부가 false일 경우 예외 발생
45+
if (!request.getIsOverwriteConfirmed())
46+
throw new CustomException(ErrorCode.DUPLICATED_STUDENT_NO);
47+
48+
// 기존 지원자 정보 갱신 수행
49+
applicant.get().overwrite(request);
50+
return;
51+
}
52+
53+
// 새로운 지원자일 경우 저장
3654
applicantRepository.save(request.toEntity());
3755
}
3856

@@ -119,4 +137,9 @@ private Applicant findById(final Long id) {
119137
.orElseThrow(() -> new CustomException(ErrorCode.EMPTY_RESULT));
120138
}
121139

140+
// 학번으로 지원자 존재 여부 확인
141+
private Optional<Applicant> findByStudentNo(final String studentNo) {
142+
return applicantRepository.findByStudentNo(studentNo);
143+
}
144+
122145
}

src/main/java/dmu/dasom/api/domain/common/exception/ErrorCode.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ public enum ErrorCode {
1919
NOT_FOUND(404, "C010", "해당 리소스를 찾을 수 없습니다."),
2020
WRITE_FAIL(400, "C011", "데이터를 쓰는데 실패하였습니다."),
2121
EMPTY_RESULT(400, "C012", "조회 결과가 없습니다."),
22-
SEND_EMAIL_FAIL(400, "C014", "이메일 전송에 실패하였습니다."),
22+
DUPLICATED_STUDENT_NO(400, "C013", "이미 등록된 학번입니다."),
23+
SEND_EMAIL_FAIL(400, "C014", "이메일 전송에 실패하였습니다.")
2324
;
2425

2526
private final int status;

src/main/java/dmu/dasom/api/domain/member/controller/MemberController.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public ResponseEntity<Void> tokenRotation(@AuthenticationPrincipal final UserDet
6464
final HttpHeaders headers = new HttpHeaders();
6565
headers.add("Access-Token", tokenBox.getAccessToken());
6666
headers.add("Refresh-Token", tokenBox.getRefreshToken());
67+
headers.add("Authority", tokenBox.getAuthority());
6768

6869
return ResponseEntity.ok().headers(headers).build();
6970
}

src/main/java/dmu/dasom/api/domain/member/service/MemberServiceImpl.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@
99
import dmu.dasom.api.global.auth.jwt.JwtUtil;
1010
import dmu.dasom.api.global.auth.userdetails.UserDetailsImpl;
1111
import lombok.RequiredArgsConstructor;
12+
import org.springframework.security.core.GrantedAuthority;
1213
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
1314
import org.springframework.stereotype.Service;
1415
import org.springframework.transaction.annotation.Transactional;
1516

17+
import java.util.Collection;
18+
import java.util.Iterator;
19+
1620
@RequiredArgsConstructor
1721
@Service
1822
@Transactional(readOnly = true)
@@ -49,7 +53,13 @@ public void signUp(final SignupRequestDto request) {
4953
// 토큰 갱신
5054
@Override
5155
public TokenBox tokenRotation(final UserDetailsImpl userDetails) {
52-
return jwtUtil.tokenRotation(userDetails);
56+
final Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
57+
final Iterator<? extends GrantedAuthority> iterator = authorities.iterator();
58+
final GrantedAuthority auth = iterator.next();
59+
60+
final String authority = auth.getAuthority();
61+
62+
return jwtUtil.tokenRotation(userDetails.getUsername(), authority);
5363
}
5464

5565
}

src/main/java/dmu/dasom/api/domain/recruit/controller/RecruitController.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
import dmu.dasom.api.domain.applicant.dto.ApplicantCreateRequestDto;
44
import dmu.dasom.api.domain.applicant.service.ApplicantService;
5+
import dmu.dasom.api.domain.common.exception.ErrorResponse;
56
import io.swagger.v3.oas.annotations.Operation;
7+
import io.swagger.v3.oas.annotations.media.Content;
8+
import io.swagger.v3.oas.annotations.media.ExampleObject;
9+
import io.swagger.v3.oas.annotations.media.Schema;
610
import io.swagger.v3.oas.annotations.responses.ApiResponse;
711
import io.swagger.v3.oas.annotations.responses.ApiResponses;
812
import jakarta.validation.Valid;
@@ -23,8 +27,15 @@ public class RecruitController {
2327
// 지원하기
2428
@Operation(summary = "부원 지원하기")
2529
@ApiResponses(value = {
26-
@ApiResponse(responseCode = "200", description = "지원 성공")
27-
})
30+
@ApiResponse(responseCode = "200", description = "지원 성공"),
31+
@ApiResponse(responseCode = "400", description = "실패 케이스",
32+
content = @Content(
33+
mediaType = "application/json",
34+
schema = @Schema(implementation = ErrorResponse.class),
35+
examples = {
36+
@ExampleObject(
37+
name = "학번 중복",
38+
value = "{ \"code\": \"C013\", \"message\": \"이미 등록된 학번입니다.\" }")}))})
2839
@PostMapping("/apply")
2940
public ResponseEntity<Void> apply(@Valid @RequestBody final ApplicantCreateRequestDto request) {
3041
applicantService.apply(request);

src/main/java/dmu/dasom/api/global/auth/dto/TokenBox.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,8 @@ public class TokenBox {
1919
@NotNull
2020
private String refreshToken;
2121

22+
@Schema(description = "권한")
23+
@NotNull
24+
private String authority;
25+
2226
}

src/main/java/dmu/dasom/api/global/auth/filter/CustomAuthenticationFilter.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@
1717
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
1818
import org.springframework.security.core.Authentication;
1919
import org.springframework.security.core.AuthenticationException;
20+
import org.springframework.security.core.GrantedAuthority;
2021
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
2122

2223
import java.io.IOException;
24+
import java.util.Collection;
25+
import java.util.Iterator;
2326

2427
@RequiredArgsConstructor
2528
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
@@ -47,12 +50,19 @@ protected void successfulAuthentication(final HttpServletRequest request, final
4750
// 기존 토큰 만료 처리
4851
jwtUtil.blacklistTokens(authResult.getName());
4952

53+
final Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities();
54+
final Iterator<? extends GrantedAuthority> iterator = authorities.iterator();
55+
final GrantedAuthority auth = iterator.next();
56+
57+
final String authority = auth.getAuthority();
58+
5059
// 토큰 생성
51-
final TokenBox tokenBox = jwtUtil.generateTokenBox(authResult.getName());
60+
final TokenBox tokenBox = jwtUtil.generateTokenBox(authResult.getName(), authority);
5261

5362
response.setStatus(HttpStatus.OK.value());
5463
response.setHeader("Access-Token", tokenBox.getAccessToken());
5564
response.setHeader("Refresh-Token", tokenBox.getRefreshToken());
65+
response.setHeader("Authority", tokenBox.getAuthority());
5666
}
5767

5868
@Override

0 commit comments

Comments
 (0)