Skip to content

Commit e668ccf

Browse files
authored
Merge branch 'dev' into hotfix/#101
2 parents eef8792 + 743a924 commit e668ccf

File tree

14 files changed

+526
-1
lines changed

14 files changed

+526
-1
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: "🍀다솜-이슈-템플릿"
2+
description: "해당 이슈 생성 템플릿으로 작성해주세요! (2025.08.08 update)"
3+
labels: [FEAT]
4+
title: "[feat] 개발 타이틀"
5+
body:
6+
- type: input
7+
id: parentKey
8+
attributes:
9+
label: "🎟️ 상위 작업 (Ticket Number)"
10+
description: "상위 작업의 Ticket Number를 기입해주세요"
11+
placeholder: "DASOMBE-00"
12+
validations:
13+
required: true
14+
15+
- type: dropdown
16+
id: projectKey
17+
attributes:
18+
label: "📦 프로젝트"
19+
description: "이슈를 생성할 Jira 프로젝트를 선택하세요"
20+
options:
21+
- DASOMFE
22+
- DASOMBE
23+
validations:
24+
required: true
25+
26+
- type: textarea
27+
id: purpose
28+
attributes:
29+
label: "🎯 목적(Purpose)"
30+
description: "해당 Issue가 발생한 원인과 배경을 설명한다."
31+
placeholder: "예: 회원 기능 구현을 위한 로그인 기능 추가 필요"
32+
validations:
33+
required: true
34+
35+
- type: textarea
36+
id: description
37+
attributes:
38+
label: "📝 상세 내용(Description)"
39+
description: "구현할 기능 또는 수정 사항에 대한 구체적인 설명"
40+
placeholder: "예: 이메일 및 비밀번호 입력값 검증 로직 추가, OAuth 연동 검토"
41+
validations:
42+
required: true

.github/pull_request_template.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# [태그] 제목
2+
3+
(예: [feat] 사용자 인증 기능 추가)
4+
5+
## Issue
6+
7+
- 예시: #111, #112
8+
(해당 PR과 관련된 이슈 번호를 명시하여 추적 용이성을 확보)
9+
10+
## 변경 내용
11+
12+
- 이번 PR에서 어떤 변경이 이루어졌는지 간략하게 기술합니다.
13+
(예: 기존 로그인 API에 JWT 기반 인증 로직 추가)
14+
15+
## 체크리스트
16+
17+
- PR을 제출하기 전에 다음 사항들을 확인해주세요:
18+
19+
- [ ] 커밋 메시지가 컨벤션을 따르는가?
20+
- [ ] 린트 검사를 통과했는가? (`npm run lint` or `npm run lint:fix`)
21+
- [ ] 로컬에서 빌드가 성공하는가? (`npm run build`)
22+
23+
## 테스트
24+
25+
- [ ] 로컬에서 테스트 완료
26+
- [ ] 기존 기능에 영향 없음 확인
27+
- [ ] 다양한 브라우저에서 테스트 (필요시)
28+
29+
## 리뷰 요구사항(필요시)
30+
31+
- 특별히 리뷰해주길 원하는 부분
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Close Jira issue
2+
on:
3+
issues:
4+
types:
5+
- closed
6+
7+
jobs:
8+
close-issue:
9+
name: Close Jira issue
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Login to Jira
14+
uses: atlassian/gajira-login@v3
15+
env:
16+
JIRA_BASE_URL: ${{ secrets.JIRA_URL }}
17+
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
18+
JIRA_USER_EMAIL: ${{ secrets.JIRA_EMAIL }}
19+
20+
- name: Extract Jira issue key from GitHub issue title
21+
id: extract-key
22+
run: |
23+
ISSUE_TITLE="${{ github.event.issue.title }}"
24+
JIRA_KEY=$(echo "$ISSUE_TITLE" | grep -oE '[A-Z]+-[0-9]+')
25+
echo "JIRA_KEY=$JIRA_KEY" >> $GITHUB_ENV
26+
27+
- name: Close Jira issue
28+
if: env.JIRA_KEY != ''
29+
uses: atlassian/gajira-transition@v3
30+
with:
31+
issue: ${{ env.JIRA_KEY }}
32+
transition: 완료
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
name: Create Jira issue
2+
on:
3+
issues:
4+
types:
5+
- opened
6+
jobs:
7+
create-issue:
8+
name: Create Jira issue
9+
runs-on: ubuntu-latest
10+
steps:
11+
- name: Login
12+
uses: atlassian/gajira-login@v3
13+
env:
14+
JIRA_BASE_URL: ${{ secrets.JIRA_URL }}
15+
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
16+
JIRA_USER_EMAIL: ${{ secrets.JIRA_EMAIL }}
17+
18+
- name: Checkout main code
19+
uses: actions/checkout@v4
20+
with:
21+
ref: main
22+
23+
- name: Issue Parser
24+
uses: stefanbuck/github-issue-parser@v3
25+
id: issue-parser
26+
with:
27+
template-path: .github/ISSUE_TEMPLATE/🍀다솜-이슈-템플릿.yml
28+
29+
- name: Log Issue Parser
30+
run: |
31+
echo '${{ steps.issue-parser.outputs.issueparser_parentKey }}'
32+
echo '${{ steps.issue-parser.outputs.__ticket_number }}'
33+
echo '${{ steps.issue-parser.outputs.jsonString }}'
34+
35+
- name: Convert markdown to Jira Syntax
36+
uses: peter-evans/jira2md@v1
37+
id: md2jira
38+
with:
39+
input-text: |
40+
### Github Issue Link
41+
- ${{ github.event.issue.html_url }}
42+
43+
${{ github.event.issue.body }}
44+
mode: md2jira
45+
46+
- name: Create Issue
47+
id: create
48+
uses: atlassian/gajira-create@v3
49+
with:
50+
project: DASOMBE
51+
issuetype: Task
52+
summary: "${{ github.event.issue.title }}"
53+
description: "${{ steps.md2jira.outputs.output-text }}"
54+
fields: |
55+
{
56+
"parent": {
57+
"key": "${{ steps.issue-parser.outputs.issueparser_parentKey}}"
58+
}
59+
}
60+
61+
- name: Log created issue
62+
run: echo "Jira Issue ${{ steps.issue-parser.outputs.parentKey }}/${{ steps.create.outputs.issue }} was created"
63+
64+
- name: Update issue title
65+
uses: actions-cool/issues-helper@v3
66+
with:
67+
actions: "update-issue"
68+
token: ${{ secrets.GITHUB_TOKEN }}
69+
title: "[${{ steps.create.outputs.issue }}] ${{ github.event.issue.title }}"
70+
71+
- name: Add comment with Jira issue link
72+
uses: actions-cool/issues-helper@v3
73+
with:
74+
actions: "create-comment"
75+
token: ${{ secrets.GITHUB_TOKEN }}
76+
issue-number: ${{ github.event.issue.number }}
77+
body: "Jira Issue Created: [${{ steps.create.outputs.issue }}](${{ secrets.JIRA_URL }}/browse/${{ steps.create.outputs.issue }})"

src/main/java/dmu/dasom/api/domain/executive/dto/ExecutiveRequestDto.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
@AllArgsConstructor
1515
@Schema(name = "ExecutiveRequestDto", description = "임원진 요청 DTO")
1616
public class ExecutiveRequestDto {
17-
17+
1818
@NotBlank(message = "임원진 이름은 필수 입력 사항입니다.")
1919
@Size(max = 50, message = "임원진 이름은 최대 50자입니다.")
2020
@Schema(description = "임원진 이름", example = "김다솜")

src/main/java/dmu/dasom/api/domain/executive/service/ExecutiveServiceImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public List<ExecutiveListResponseDto> getAllExecutives() {
4949
public ExecutiveCreationResponseDto createExecutive(ExecutiveRequestDto requestDto) {
5050
ExecutiveEntity saved = executiveRepository.save(requestDto.toEntity());
5151
return new ExecutiveCreationResponseDto(saved.getId());
52+
5253
}
5354

5455
// 임원진 멤버 삭제
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package dmu.dasom.api.domain.interview.controller;
2+
3+
import dmu.dasom.api.domain.applicant.entity.Applicant;
4+
import dmu.dasom.api.domain.applicant.repository.ApplicantRepository;
5+
import dmu.dasom.api.domain.common.exception.CustomException;
6+
import dmu.dasom.api.domain.common.exception.ErrorCode;
7+
import dmu.dasom.api.domain.google.service.EmailService;
8+
9+
import dmu.dasom.api.domain.interview.dto.InterviewReservationModifyRequestDto;
10+
import dmu.dasom.api.domain.interview.dto.VerificationCodeRequestDto;
11+
import dmu.dasom.api.domain.interview.service.InterviewService;
12+
import dmu.dasom.api.global.util.VerificationCodeManager;
13+
import io.swagger.v3.oas.annotations.Operation;
14+
import io.swagger.v3.oas.annotations.tags.Tag;
15+
import jakarta.mail.MessagingException;
16+
import jakarta.validation.Valid;
17+
import lombok.RequiredArgsConstructor;
18+
import org.springframework.http.ResponseEntity;
19+
import org.springframework.web.bind.annotation.*;
20+
21+
import java.util.List;
22+
import dmu.dasom.api.domain.interview.dto.InterviewReservationApplicantResponseDto;
23+
24+
@Tag(name = "Interview", description = "면접 관련 API")
25+
@RestController
26+
@RequiredArgsConstructor
27+
@RequestMapping("/api/interview")
28+
public class InterviewController {
29+
30+
private final InterviewService interviewService;
31+
private final ApplicantRepository applicantRepository;
32+
private final VerificationCodeManager verificationCodeManager;
33+
private final EmailService emailService; // 이메일 발송 서비스 (Google 기반)
34+
35+
/*
36+
* 면접 예약 수정을 위한 인증 코드 발송
37+
* - 지원자의 학번을 입력받아 해당 지원자를 조회
38+
* - VerificationCodeManager를 통해 인증 코드 생성 및 Redis 저장
39+
* - EmailService를 이용해 지원자 이메일로 인증 코드 발송
40+
*/
41+
@Operation(summary = "면접 예약 수정을 위한 인증 코드 발송", description = "지원자의 학번을 받아 이메일로 인증 코드를 발송합니다.")
42+
@PostMapping("/send-verification")
43+
public ResponseEntity<Void> sendVerificationCode(@Valid @RequestBody VerificationCodeRequestDto request) throws MessagingException {
44+
// 학번으로 지원자 조회 (없으면 예외 발생)
45+
Applicant applicant = applicantRepository.findByStudentNo(request.getStudentNo())
46+
.orElseThrow(() -> new CustomException(ErrorCode.APPLICANT_NOT_FOUND));
47+
48+
// 인증 코드 생성 후 Redis에 저장
49+
String code = verificationCodeManager.generateAndStoreCode(applicant.getStudentNo());
50+
51+
// 이메일 발송 (받는 사람 이메일, 이름, 코드 전달)
52+
emailService.sendVerificationEmail(applicant.getEmail(), applicant.getName(), code);
53+
54+
return ResponseEntity.ok().build();
55+
}
56+
57+
/*
58+
* 면접 예약 수정
59+
* - 사용자가 받은 인증 코드를 검증한 후
60+
* - InterviewService를 통해 예약 날짜/시간 수정 처리
61+
*/
62+
@Operation(summary = "면접 예약 수정", description = "이메일로 발송된 인증 코드를 통해 인증 후, 면접 날짜 및 시간을 수정합니다.")
63+
@PutMapping("/reservation/modify")
64+
public ResponseEntity<Void> modifyInterviewReservation(@Valid @RequestBody InterviewReservationModifyRequestDto request) {
65+
interviewService.modifyInterviewReservation(request);
66+
return ResponseEntity.ok().build();
67+
}
68+
69+
/*
70+
* 모든 면접 지원자 조회
71+
* - InterviewService를 통해 모든 지원자 + 예약 정보 반환
72+
*/
73+
@Operation(summary = "모든 면접 지원자 목록 조회", description = "모든 면접 지원자의 상세 정보와 예약 정보를 조회합니다.")
74+
@GetMapping("/applicants")
75+
public ResponseEntity<List<InterviewReservationApplicantResponseDto>> getAllInterviewApplicants() {
76+
List<InterviewReservationApplicantResponseDto> applicants = interviewService.getAllInterviewApplicants();
77+
return ResponseEntity.ok(applicants);
78+
}
79+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package dmu.dasom.api.domain.interview.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.Email;
5+
import jakarta.validation.constraints.NotNull;
6+
import jakarta.validation.constraints.Pattern;
7+
import jakarta.validation.constraints.Size;
8+
import lombok.*;
9+
10+
@Getter
11+
@Setter
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
@Builder
15+
@Schema(name = "InterviewReservationModifyRequestDto", description = "면접 예약 수정 요청 DTO")
16+
public class InterviewReservationModifyRequestDto {
17+
18+
@NotNull(message = "학번은 필수 값입니다.")
19+
@Pattern(regexp = "^[0-9]{8}$", message = "학번은 8자리 숫자로 구성되어야 합니다.")
20+
@Size(min = 8, max = 8)
21+
@Schema(description = "지원자 학번", example = "20250001")
22+
private String studentNo;
23+
24+
@NotNull(message = "이메일은 필수 값입니다.")
25+
@Email(message = "유효한 이메일 주소를 입력해주세요.")
26+
@Size(max = 64)
27+
@Schema(description = "지원자 이메일", example = "[email protected]")
28+
private String email;
29+
30+
@NotNull(message = "새로운 슬롯 ID는 필수 값입니다.")
31+
@Schema(description = "새롭게 예약할 면접 슬롯의 ID", example = "2")
32+
private Long newSlotId;
33+
34+
@NotNull(message = "인증 코드는 필수 값입니다.")
35+
@Schema(description = "이메일로 발송된 6자리 인증 코드", example = "123456")
36+
private String verificationCode;
37+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package dmu.dasom.api.domain.interview.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.NotNull;
5+
import jakarta.validation.constraints.Pattern;
6+
import jakarta.validation.constraints.Size;
7+
import lombok.Getter;
8+
9+
@Getter
10+
public class VerificationCodeRequestDto {
11+
12+
@NotNull(message = "학번은 필수 값입니다.")
13+
@Pattern(regexp = "^[0-9]{8}$", message = "학번은 8자리 숫자로 구성되어야 합니다.")
14+
@Size(min = 8, max = 8)
15+
@Schema(description = "지원자 학번", example = "20250001")
16+
private String studentNo;
17+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package dmu.dasom.api.domain.member.dto;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.Email;
5+
import jakarta.validation.constraints.NotNull;
6+
import jakarta.validation.constraints.Size;
7+
import lombok.Getter;
8+
9+
@Getter
10+
@Schema(name = "LoginRequestDto", description = "로그인 요청 DTO")
11+
public class LoginRequestDto {
12+
13+
@NotNull(message = "이메일은 필수 값입니다.")
14+
@Email(message = "유효한 이메일 주소를 입력해주세요.")
15+
@Size(max = 64)
16+
@Schema(description = "이메일 주소", example = "[email protected]")
17+
private String email;
18+
19+
@NotNull(message = "비밀번호는 필수 값입니다.")
20+
@Size(min = 8, max = 20, message = "비밀번호는 8자 이상 20자 이하로 입력해주세요.")
21+
@Schema(description = "비밀번호", example = "password123!")
22+
private String password;
23+
}

0 commit comments

Comments
 (0)