Skip to content

Commit 99816f2

Browse files
authored
Merge pull request #297 from GDGoCINHA/develop
Merge Dev
2 parents 3dab9f3 + 6ba0912 commit 99816f2

13 files changed

Lines changed: 172 additions & 5 deletions

File tree

database_schema.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,21 @@ CREATE TABLE IF NOT EXISTS guestbook_entry (
210210
won_at TIMESTAMP
211211
);
212212
CREATE INDEX IF NOT EXISTS idx_guestbook_created_at ON guestbook_entry(created_at);
213+
214+
-- 16. 신입생 지원 알림 신청 (recruit_member_memo)
215+
CREATE TABLE IF NOT EXISTS recruit_member_memo (
216+
id BIGSERIAL PRIMARY KEY,
217+
name VARCHAR(255) NOT NULL,
218+
phone_number VARCHAR(20) NOT NULL,
219+
email VARCHAR(255) NOT NULL,
220+
privacy_agreement BOOLEAN NOT NULL,
221+
freshman_memo_agreement BOOLEAN NOT NULL,
222+
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
223+
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
224+
);
225+
CREATE INDEX IF NOT EXISTS idx_recruit_member_memo_phone_number
226+
ON recruit_member_memo(phone_number);
227+
CREATE INDEX IF NOT EXISTS idx_recruit_member_memo_email_lower
228+
ON recruit_member_memo((lower(email)));
229+
CREATE INDEX IF NOT EXISTS idx_recruit_member_memo_created_at
230+
ON recruit_member_memo(created_at DESC);

src/main/java/inha/gdgoc/domain/recruit/member/controller/RecruitMemberController.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import static inha.gdgoc.domain.recruit.member.controller.message.RecruitMemberMessage.EMAIL_DUPLICATION_CHECK_SUCCESS;
44
import static inha.gdgoc.domain.recruit.member.controller.message.RecruitMemberMessage.MEMBER_LIST_RETRIEVED_SUCCESS;
5+
import static inha.gdgoc.domain.recruit.member.controller.message.RecruitMemberMessage.MEMBER_MEMO_SAVE_SUCCESS;
56
import static inha.gdgoc.domain.recruit.member.controller.message.RecruitMemberMessage.MEMBER_RETRIEVED_SUCCESS;
67
import static inha.gdgoc.domain.recruit.member.controller.message.RecruitMemberMessage.MEMBER_SAVE_SUCCESS;
78
import static inha.gdgoc.domain.recruit.member.controller.message.RecruitMemberMessage.PAYMENT_MARKED_COMPLETE_SUCCESS;
@@ -14,6 +15,7 @@
1415
import inha.gdgoc.domain.recruit.member.dto.request.CheckPhoneNumberRequest;
1516
import inha.gdgoc.domain.recruit.member.dto.request.CheckStudentIdRequest;
1617
import inha.gdgoc.domain.recruit.member.dto.request.PaymentUpdateRequest;
18+
import inha.gdgoc.domain.recruit.member.dto.request.RecruitMemberMemoRequest;
1719
import inha.gdgoc.domain.recruit.member.dto.response.CheckEmailResponse;
1820
import inha.gdgoc.domain.recruit.member.dto.response.CheckPhoneNumberResponse;
1921
import inha.gdgoc.domain.recruit.member.dto.response.CheckStudentIdResponse;
@@ -84,6 +86,14 @@ public ResponseEntity<ApiResponse<Void, Void>> recruitMemberAddMultipart(
8486
return ResponseEntity.ok(ApiResponse.ok(MEMBER_SAVE_SUCCESS));
8587
}
8688

89+
@PostMapping("/memo")
90+
public ResponseEntity<ApiResponse<Void, Void>> recruitMemberMemoAdd(
91+
@Valid @RequestBody RecruitMemberMemoRequest recruitMemberMemoRequest
92+
) {
93+
recruitMemberService.addRecruitMemberMemo(recruitMemberMemoRequest);
94+
return ResponseEntity.ok(ApiResponse.ok(MEMBER_MEMO_SAVE_SUCCESS));
95+
}
96+
8797
@PostMapping("/check/student-id")
8898
public ResponseEntity<ApiResponse<CheckStudentIdResponse, Void>> duplicatedStudentIdDetails(
8999
@Valid @RequestBody CheckStudentIdRequest request

src/main/java/inha/gdgoc/domain/recruit/member/controller/message/RecruitMemberMessage.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
public class RecruitMemberMessage {
44
public static final String MEMBER_SAVE_SUCCESS = "성공적으로 해당 학기 멤버 가입을 완료했습니다.";
5+
public static final String MEMBER_MEMO_SAVE_SUCCESS = "성공적으로 신입생 지원 알림 신청을 완료했습니다.";
56
public static final String STUDENT_ID_DUPLICATION_CHECK_SUCCESS = "성공적으로 학번 중복 조회를 완료했습니다.";
67
public static final String PHONE_NUMBER_DUPLICATION_CHECK_SUCCESS = "성공적으로 전화번호 중복 조회를 완료했습니다.";
78
public static final String EMAIL_DUPLICATION_CHECK_SUCCESS = "성공적으로 이메일 중복 조회를 완료했습니다.";
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package inha.gdgoc.domain.recruit.member.dto.request;
2+
3+
import inha.gdgoc.domain.recruit.member.entity.RecruitMemberMemo;
4+
import jakarta.validation.constraints.AssertTrue;
5+
import jakarta.validation.constraints.Email;
6+
import jakarta.validation.constraints.NotBlank;
7+
import jakarta.validation.constraints.Pattern;
8+
import java.util.Locale;
9+
import lombok.Getter;
10+
import lombok.Setter;
11+
12+
@Getter
13+
@Setter
14+
public class RecruitMemberMemoRequest {
15+
16+
@NotBlank(message = "이름은 필수 입력 값입니다.")
17+
private String name;
18+
19+
@NotBlank(message = "전화번호는 필수 입력 값입니다.")
20+
@Pattern(regexp = "^010-?\\d{4}-?\\d{4}$", message = "전화번호 형식은 010-XXXX-XXXX 또는 010XXXXXXXX 이어야 합니다.")
21+
private String phoneNumber;
22+
23+
@NotBlank(message = "이메일은 필수 입력 값입니다.")
24+
@Email(message = "유효하지 않은 이메일 형식입니다.")
25+
private String email;
26+
27+
@AssertTrue(message = "개인정보 처리방침 동의는 필수입니다.")
28+
private Boolean privacyAgreement;
29+
30+
@AssertTrue(message = "신입생 지원 알림 신청 동의는 필수입니다.")
31+
private Boolean freshmanMemoAgreement;
32+
33+
public RecruitMemberMemo toEntity() {
34+
String normalizedPhone = phoneNumber.replaceAll("[^0-9]", "");
35+
String normalizedEmail = email.trim().toLowerCase(Locale.ROOT);
36+
37+
return RecruitMemberMemo.builder()
38+
.name(name.trim())
39+
.phoneNumber(normalizedPhone)
40+
.email(normalizedEmail)
41+
.privacyAgreement(Boolean.TRUE.equals(privacyAgreement))
42+
.freshmanMemoAgreement(Boolean.TRUE.equals(freshmanMemoAgreement))
43+
.build();
44+
}
45+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package inha.gdgoc.domain.recruit.member.entity;
2+
3+
import inha.gdgoc.global.entity.BaseEntity;
4+
import jakarta.persistence.Column;
5+
import jakarta.persistence.Entity;
6+
import jakarta.persistence.GeneratedValue;
7+
import jakarta.persistence.GenerationType;
8+
import jakarta.persistence.Id;
9+
import jakarta.persistence.Table;
10+
import lombok.AccessLevel;
11+
import lombok.AllArgsConstructor;
12+
import lombok.Builder;
13+
import lombok.Getter;
14+
import lombok.NoArgsConstructor;
15+
16+
@Entity
17+
@Table(name = "recruit_member_memo")
18+
@Getter
19+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
20+
@AllArgsConstructor
21+
@Builder
22+
public class RecruitMemberMemo extends BaseEntity {
23+
24+
@Id
25+
@GeneratedValue(strategy = GenerationType.IDENTITY)
26+
@Column(name = "id", nullable = false)
27+
private Long id;
28+
29+
@Column(name = "name", nullable = false)
30+
private String name;
31+
32+
@Column(name = "phone_number", nullable = false)
33+
private String phoneNumber;
34+
35+
@Column(name = "email", nullable = false)
36+
private String email;
37+
38+
@Column(name = "privacy_agreement", nullable = false)
39+
private Boolean privacyAgreement;
40+
41+
@Column(name = "freshman_memo_agreement", nullable = false)
42+
private Boolean freshmanMemoAgreement;
43+
}

src/main/java/inha/gdgoc/domain/recruit/member/exception/RecruitMemberErrorCode.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
@RequiredArgsConstructor
88
public enum RecruitMemberErrorCode implements ErrorCode {
99

10+
// 409 CONFLICT
11+
RECRUIT_MEMBER_ALREADY_APPLIED(HttpStatus.CONFLICT, "이미 지원을 완료하였습니다."),
12+
1013
// 404 NOT FOUND
1114
RECRUIT_MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "해당 멤버를 찾을 수 없습니다.");
1215

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package inha.gdgoc.domain.recruit.member.repository;
2+
3+
import inha.gdgoc.domain.recruit.member.entity.RecruitMemberMemo;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
6+
public interface RecruitMemberMemoRepository extends JpaRepository<RecruitMemberMemo, Long> {
7+
boolean existsByPhoneNumber(String phoneNumber);
8+
}

src/main/java/inha/gdgoc/domain/recruit/member/service/RecruitMemberService.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
package inha.gdgoc.domain.recruit.member.service;
22

33
import static inha.gdgoc.domain.recruit.member.exception.RecruitMemberErrorCode.RECRUIT_MEMBER_NOT_FOUND;
4+
import static inha.gdgoc.domain.recruit.member.exception.RecruitMemberErrorCode.RECRUIT_MEMBER_ALREADY_APPLIED;
45

56
import com.fasterxml.jackson.databind.ObjectMapper;
67
import inha.gdgoc.domain.recruit.member.dto.request.ApplicationRequest;
8+
import inha.gdgoc.domain.recruit.member.dto.request.RecruitMemberMemoRequest;
79
import inha.gdgoc.domain.recruit.member.dto.response.CheckEmailResponse;
810
import inha.gdgoc.domain.recruit.member.dto.response.CheckPhoneNumberResponse;
911
import inha.gdgoc.domain.recruit.member.dto.response.CheckStudentIdResponse;
@@ -14,6 +16,7 @@
1416
import inha.gdgoc.domain.recruit.member.enums.SurveyType;
1517
import inha.gdgoc.domain.recruit.member.exception.RecruitMemberException;
1618
import inha.gdgoc.domain.recruit.member.repository.AnswerRepository;
19+
import inha.gdgoc.domain.recruit.member.repository.RecruitMemberMemoRepository;
1720
import inha.gdgoc.domain.recruit.member.repository.RecruitMemberRepository;
1821
import inha.gdgoc.global.util.SemesterCalculator;
1922
import java.util.List;
@@ -27,6 +30,7 @@
2730
@Service
2831
public class RecruitMemberService {
2932
private final RecruitMemberRepository recruitMemberRepository;
33+
private final RecruitMemberMemoRepository recruitMemberMemoRepository;
3034
private final AnswerRepository answerRepository;
3135
private final ObjectMapper objectMapper;
3236
private final SemesterCalculator semesterCalculator;
@@ -53,14 +57,27 @@ public void addRecruitMember(ApplicationRequest applicationRequest) {
5357
answerRepository.saveAll(answers);
5458
}
5559

60+
@Transactional
61+
public void addRecruitMemberMemo(RecruitMemberMemoRequest recruitMemberMemoRequest) {
62+
String cleanPhone = normalizePhoneNumber(recruitMemberMemoRequest.getPhoneNumber());
63+
boolean alreadyApplied = recruitMemberRepository.existsByPhoneNumber(cleanPhone);
64+
boolean alreadyMemoRequested = recruitMemberMemoRepository.existsByPhoneNumber(cleanPhone);
65+
66+
if (alreadyApplied || alreadyMemoRequested) {
67+
throw new RecruitMemberException(RECRUIT_MEMBER_ALREADY_APPLIED);
68+
}
69+
70+
recruitMemberMemoRepository.save(recruitMemberMemoRequest.toEntity());
71+
}
72+
5673
public CheckStudentIdResponse isRegisteredStudentId(String studentId) {
5774
boolean exists = recruitMemberRepository.existsByStudentId(studentId);
5875

5976
return new CheckStudentIdResponse(exists);
6077
}
6178

6279
public CheckPhoneNumberResponse isRegisteredPhoneNumber(String phoneNumber) {
63-
String cleanPhone = phoneNumber.replaceAll("[^0-9]", "");
80+
String cleanPhone = normalizePhoneNumber(phoneNumber);
6481
boolean exists = recruitMemberRepository.existsByPhoneNumber(cleanPhone);
6582

6683
return new CheckPhoneNumberResponse(exists);
@@ -102,4 +119,8 @@ public Page<RecruitMember> searchMembersByNamePage(String name, Pageable pageabl
102119
return recruitMemberRepository.findByNameContainingIgnoreCase(name, pageable);
103120
}
104121

122+
private String normalizePhoneNumber(String phoneNumber) {
123+
return phoneNumber.replaceAll("[^0-9]", "");
124+
}
125+
105126
}

src/main/java/inha/gdgoc/domain/user/service/UserService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,5 @@ private String maskEmail(String email) {
6868
+ localPart.substring(localPart.length() - endLen)
6969
+ domainPart;
7070
}
71-
}
71+
72+
}

src/main/java/inha/gdgoc/global/security/SecurityConfig.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
4949
"/api/v1/game/**",
5050
"/api/v1/recruit/member/apply/**",
5151
"/api/v1/recruit/member/check/**",
52+
"/api/v1/recruit/member/memo",
5253
"/api/v1/fileupload",
5354
"/api/v1/manito/verify")
5455
.permitAll()

0 commit comments

Comments
 (0)