Skip to content

Commit 4e8458a

Browse files
authored
Merge pull request #304 from GDGoCINHA/develop
merge dev
2 parents 1dce22f + d47ec7b commit 4e8458a

40 files changed

Lines changed: 1384 additions & 82 deletions

File tree

.github/workflows/deploy-dev.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ jobs:
5454
AWS_TEST_RESOURCE_BUCKET=${{ secrets.AWS_TEST_RESOURCE_BUCKET }}
5555
GMAIL=${{ secrets.GMAIL }}
5656
GMAIL_PASSWORD=${{ secrets.GMAIL_PASSWORD }}
57+
ADMIN_LOGIN_ID=${{ secrets.ADMIN_LOGIN_ID }}
58+
ADMIN_LOGIN_PASSWORD=${{ secrets.ADMIN_LOGIN_PASSWORD }}
59+
APP_MAIL_RECRUIT_FROM=${{ secrets.APP_MAIL_RECRUIT_FROM }}
5760
DOZZLE_USERNAME=${{ secrets.DOZZLE_USERNAME }}
5861
DOZZLE_PASSWORD=${{ secrets.DOZZLE_PASSWORD }}
5962
EOF

.github/workflows/deploy-prod.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ jobs:
5757
AWS_TEST_RESOURCE_BUCKET=${{ secrets.AWS_TEST_RESOURCE_BUCKET }}
5858
GMAIL=${{ secrets.GMAIL }}
5959
GMAIL_PASSWORD=${{ secrets.GMAIL_PASSWORD }}
60+
ADMIN_LOGIN_ID=${{ secrets.ADMIN_LOGIN_ID }}
61+
ADMIN_LOGIN_PASSWORD=${{ secrets.ADMIN_LOGIN_PASSWORD }}
62+
APP_MAIL_RECRUIT_FROM=${{ secrets.APP_MAIL_RECRUIT_FROM }}
6063
DOZZLE_USERNAME=${{ secrets.DOZZLE_USERNAME }}
6164
DOZZLE_PASSWORD=${{ secrets.DOZZLE_PASSWORD }}
6265
EOF

src/main/java/inha/gdgoc/GdgocApplication.java

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

33
import org.springframework.boot.SpringApplication;
44
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.scheduling.annotation.EnableScheduling;
56

67
@SpringBootApplication
8+
@EnableScheduling
79
public class GdgocApplication {
810

911
public static void main(String[] args) {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package inha.gdgoc.domain.admin.recruit.member.controller;
2+
3+
import static inha.gdgoc.domain.admin.recruit.member.controller.message.RecruitMemberMemoAdminMessage.MEMBER_MEMO_NOTIFICATION_ENQUEUED;
4+
import static inha.gdgoc.domain.admin.recruit.member.controller.message.RecruitMemberMemoAdminMessage.MEMBER_MEMO_NOTIFICATION_FAILED_RETRIED;
5+
import static inha.gdgoc.domain.admin.recruit.member.controller.message.RecruitMemberMemoAdminMessage.MEMBER_MEMO_NOTIFICATION_TEMPLATE_RETRIEVED;
6+
7+
import inha.gdgoc.domain.admin.recruit.member.dto.request.RecruitMemberMemoOpeningNotificationRequest;
8+
import inha.gdgoc.domain.admin.recruit.member.dto.response.RecruitMemberMemoFailedRetryResponse;
9+
import inha.gdgoc.domain.admin.recruit.member.dto.response.RecruitMemberMemoOpeningNotificationEnqueueResponse;
10+
import inha.gdgoc.domain.admin.recruit.member.dto.response.RecruitMemberMemoNotificationTemplateResponse;
11+
import inha.gdgoc.domain.admin.recruit.member.service.RecruitMemberMemoAdminService;
12+
import inha.gdgoc.global.dto.response.ApiResponse;
13+
import io.swagger.v3.oas.annotations.Operation;
14+
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
15+
import jakarta.validation.Valid;
16+
import lombok.RequiredArgsConstructor;
17+
import org.springframework.http.ResponseEntity;
18+
import org.springframework.security.access.prepost.PreAuthorize;
19+
import org.springframework.web.bind.annotation.GetMapping;
20+
import org.springframework.web.bind.annotation.PostMapping;
21+
import org.springframework.web.bind.annotation.RequestBody;
22+
import org.springframework.web.bind.annotation.RequestMapping;
23+
import org.springframework.web.bind.annotation.RestController;
24+
25+
@RequiredArgsConstructor
26+
@RestController
27+
@RequestMapping("/api/v1/admin/recruit/member/memo/notifications")
28+
public class RecruitMemberMemoAdminController {
29+
30+
private static final String LEAD_OR_HR_RULE =
31+
"@accessGuard.check(authentication,"
32+
+ " T(inha.gdgoc.global.security.AccessGuard$AccessCondition).atLeast("
33+
+ "T(inha.gdgoc.domain.user.enums.UserRole).LEAD),"
34+
+ " T(inha.gdgoc.global.security.AccessGuard$AccessCondition).of("
35+
+ "T(inha.gdgoc.domain.user.enums.UserRole).CORE,"
36+
+ " T(inha.gdgoc.domain.user.enums.TeamType).HR))";
37+
38+
private final RecruitMemberMemoAdminService adminService;
39+
40+
@Operation(summary = "신입생 지원 오픈 알림 메일 기본 문구 조회", security = {@SecurityRequirement(name = "BearerAuth")})
41+
@PreAuthorize(LEAD_OR_HR_RULE)
42+
@GetMapping("/template")
43+
public ResponseEntity<ApiResponse<RecruitMemberMemoNotificationTemplateResponse, Void>> getTemplate() {
44+
RecruitMemberMemoNotificationTemplateResponse response = adminService.getTemplate();
45+
return ResponseEntity.ok(ApiResponse.ok(MEMBER_MEMO_NOTIFICATION_TEMPLATE_RETRIEVED, response));
46+
}
47+
48+
@Operation(summary = "신입생 지원 오픈 알림 메일 큐 적재", security = {@SecurityRequirement(name = "BearerAuth")})
49+
@PreAuthorize(LEAD_OR_HR_RULE)
50+
@PostMapping("/opening")
51+
public ResponseEntity<ApiResponse<RecruitMemberMemoOpeningNotificationEnqueueResponse, Void>> enqueueOpening(
52+
@Valid @RequestBody RecruitMemberMemoOpeningNotificationRequest request
53+
) {
54+
RecruitMemberMemoOpeningNotificationEnqueueResponse response = adminService.enqueueOpeningNotifications(request);
55+
return ResponseEntity.ok(ApiResponse.ok(MEMBER_MEMO_NOTIFICATION_ENQUEUED, response));
56+
}
57+
58+
@Operation(summary = "신입생 지원 오픈 알림 메일 실패 건 재시도", security = {@SecurityRequirement(name = "BearerAuth")})
59+
@PreAuthorize(LEAD_OR_HR_RULE)
60+
@PostMapping("/retry-failed")
61+
public ResponseEntity<ApiResponse<RecruitMemberMemoFailedRetryResponse, Void>> retryFailed() {
62+
RecruitMemberMemoFailedRetryResponse response = adminService.retryFailedNotifications();
63+
return ResponseEntity.ok(ApiResponse.ok(MEMBER_MEMO_NOTIFICATION_FAILED_RETRIED, response));
64+
}
65+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package inha.gdgoc.domain.admin.recruit.member.controller.message;
2+
3+
public final class RecruitMemberMemoAdminMessage {
4+
5+
public static final String MEMBER_MEMO_NOTIFICATION_ENQUEUED = "신입생 지원 알림 메일 발송 작업을 큐잉했습니다.";
6+
public static final String MEMBER_MEMO_NOTIFICATION_TEMPLATE_RETRIEVED = "신입생 지원 알림 기본 문구를 조회했습니다.";
7+
public static final String MEMBER_MEMO_NOTIFICATION_FAILED_RETRIED = "신입생 지원 알림 실패 건을 재시도 큐에 반영했습니다.";
8+
9+
private RecruitMemberMemoAdminMessage() {
10+
}
11+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package inha.gdgoc.domain.admin.recruit.member.dto.request;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
import jakarta.validation.constraints.Size;
5+
6+
public record RecruitMemberMemoOpeningNotificationRequest(
7+
@NotBlank(message = "메일 제목은 필수입니다.")
8+
@Size(max = 200, message = "메일 제목은 200자 이하여야 합니다.")
9+
String subject,
10+
11+
@NotBlank(message = "메일 본문은 필수입니다.")
12+
@Size(max = 5000, message = "메일 본문은 5000자 이하여야 합니다.")
13+
String body
14+
) {
15+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package inha.gdgoc.domain.admin.recruit.member.dto.response;
2+
3+
public record RecruitMemberMemoFailedRetryResponse(
4+
String semester,
5+
int retriedCount
6+
) {
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package inha.gdgoc.domain.admin.recruit.member.dto.response;
2+
3+
public record RecruitMemberMemoNotificationTemplateResponse(
4+
String semester,
5+
String defaultSubject,
6+
String defaultBody,
7+
String lastSubject,
8+
String lastBody
9+
) {
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package inha.gdgoc.domain.admin.recruit.member.dto.response;
2+
3+
import inha.gdgoc.domain.recruit.member.notification.service.RecruitMemberMemoNotificationEnqueueResult;
4+
5+
public record RecruitMemberMemoOpeningNotificationEnqueueResponse(
6+
String semester,
7+
int distinctTargetCount,
8+
int enqueuedCount,
9+
int alreadyProcessedCount
10+
) {
11+
public static RecruitMemberMemoOpeningNotificationEnqueueResponse from(
12+
RecruitMemberMemoNotificationEnqueueResult result
13+
) {
14+
return new RecruitMemberMemoOpeningNotificationEnqueueResponse(
15+
result.semester(),
16+
result.distinctTargetCount(),
17+
result.enqueuedCount(),
18+
result.alreadyProcessedCount()
19+
);
20+
}
21+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package inha.gdgoc.domain.admin.recruit.member.service;
2+
3+
import inha.gdgoc.domain.admin.recruit.member.dto.request.RecruitMemberMemoOpeningNotificationRequest;
4+
import inha.gdgoc.domain.admin.recruit.member.dto.response.RecruitMemberMemoFailedRetryResponse;
5+
import inha.gdgoc.domain.admin.recruit.member.dto.response.RecruitMemberMemoOpeningNotificationEnqueueResponse;
6+
import inha.gdgoc.domain.admin.recruit.member.dto.response.RecruitMemberMemoNotificationTemplateResponse;
7+
import inha.gdgoc.domain.recruit.member.notification.service.RecruitMemberMemoNotificationEnqueueResult;
8+
import inha.gdgoc.domain.recruit.member.notification.service.RecruitMemberMemoNotificationRetryResult;
9+
import inha.gdgoc.domain.recruit.member.notification.service.RecruitMemberMemoNotificationService;
10+
import inha.gdgoc.domain.recruit.member.notification.service.RecruitMemberMemoNotificationTemplateInfo;
11+
import lombok.RequiredArgsConstructor;
12+
import org.springframework.stereotype.Service;
13+
import org.springframework.transaction.annotation.Transactional;
14+
15+
@Service
16+
@RequiredArgsConstructor
17+
public class RecruitMemberMemoAdminService {
18+
19+
private final RecruitMemberMemoNotificationService notificationService;
20+
21+
@Transactional(readOnly = true)
22+
public RecruitMemberMemoNotificationTemplateResponse getTemplate() {
23+
RecruitMemberMemoNotificationTemplateInfo info = notificationService.getTemplateInfoForCurrentSemester();
24+
return new RecruitMemberMemoNotificationTemplateResponse(
25+
info.semester(),
26+
info.defaultSubject(),
27+
info.defaultBody(),
28+
info.lastSubject(),
29+
info.lastBody()
30+
);
31+
}
32+
33+
@Transactional
34+
public RecruitMemberMemoOpeningNotificationEnqueueResponse enqueueOpeningNotifications(
35+
RecruitMemberMemoOpeningNotificationRequest request
36+
) {
37+
RecruitMemberMemoNotificationEnqueueResult result =
38+
notificationService.enqueueOpeningNotificationsForCurrentSemester(
39+
request.subject(),
40+
request.body()
41+
);
42+
return RecruitMemberMemoOpeningNotificationEnqueueResponse.from(result);
43+
}
44+
45+
@Transactional
46+
public RecruitMemberMemoFailedRetryResponse retryFailedNotifications() {
47+
RecruitMemberMemoNotificationRetryResult result = notificationService.retryFailedForCurrentSemester();
48+
return new RecruitMemberMemoFailedRetryResponse(result.semester(), result.retriedCount());
49+
}
50+
}

0 commit comments

Comments
 (0)