Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RAC-277 refactor : 응답 내용 변경 및 슬랙 추가 #90

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ repositories {
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-amqp'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
Expand All @@ -37,6 +38,10 @@ dependencies {
testImplementation 'org.springframework.amqp:spring-rabbit-test'
testImplementation 'org.springframework.security:spring-security-test'

// https://mvnrepository.com/artifact/com.slack.api/slack-api-client
// 슬랙 api 추가
implementation group: 'com.slack.api', name: 'slack-api-client', version: '1.29.2'

implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
implementation 'io.jsonwebtoken:jjwt-impl:0.11.5'
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ public class MentoringApplyUseCase {
private final MentoringSaveService mentoringSaveService;
private final SeniorGetService seniorGetService;

public void applyMentoring(User user, MentoringApplyRequest request) {
public Long applyMentoring(User user, MentoringApplyRequest request) {
String[] dates = request.date().split(",");
if (dates.length != 3)
throw new MentoringDateException();
Senior senior = seniorGetService.bySeniorId(request.seniorId());
Mentoring mentoring = MentoringMapper.mapToMentoring(user, senior, request);
mentoringSaveService.save(mentoring);
Mentoring save = mentoringSaveService.save(mentoring);
return save.getMentoringId();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.postgraduate.domain.account.domain.service.AccountGetService;
import com.postgraduate.domain.mentoring.application.dto.req.MentoringDateRequest;
import com.postgraduate.domain.mentoring.domain.entity.Mentoring;
import com.postgraduate.domain.mentoring.domain.service.MentoringDeleteService;
import com.postgraduate.domain.mentoring.domain.service.MentoringGetService;
import com.postgraduate.domain.mentoring.domain.service.MentoringUpdateService;
import com.postgraduate.domain.mentoring.exception.MentoringNotExpectedException;
Expand Down Expand Up @@ -36,6 +37,7 @@ public class MentoringManageUseCase {
private final CheckIsMyMentoringUseCase checkIsMyMentoringUseCase;
private final MentoringUpdateService mentoringUpdateService;
private final MentoringGetService mentoringGetService;
private final MentoringDeleteService mentoringDeleteService;
private final RefuseSaveService refuseSaveService;
private final AccountGetService accountGetService;
private final SeniorGetService seniorGetService;
Expand Down Expand Up @@ -79,6 +81,11 @@ public Boolean updateExpected(User user, Long mentoringId, MentoringDateRequest
return account.isPresent();
}

public void delete(User user, Long mentoringId) {
Mentoring mentoring = checkIsMyMentoringUseCase.byUser(user, mentoringId);
mentoringDeleteService.deleteMentoring(mentoring);
}

@Scheduled(cron = "0 59 23 * * *", zone = "Asia/Seoul")
public void updateAutoCancel() {
LocalDateTime now = LocalDateTime.now()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.postgraduate.domain.mentoring.domain.service;

import com.postgraduate.domain.mentoring.domain.entity.Mentoring;
import com.postgraduate.domain.mentoring.domain.repository.MentoringRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class MentoringDeleteService {
private final MentoringRepository mentoringRepository;

public void deleteMentoring(Mentoring mentoring) {
mentoringRepository.deleteById(mentoring.getMentoringId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,20 @@ public ResponseDto<AppliedMentoringDetailResponse> getMentoringDetail(@Authentic
return ResponseDto.create(MENTORING_FIND.getCode(), GET_MENTORING_DETAIL_INFO.getMessage(), mentoringDetail);
}

@DeleteMapping("/me/{mentoringId}")
@Operation(summary = "[대학생] 결제 오류/취소시 멘토링 삭제", description = "대학생이 신청한 멘토링을 삭제합니다. (취소/거절 아닌 삭제)")
public ResponseDto<AppliedMentoringDetailResponse> deleteMentoring(@AuthenticationPrincipal User user,
@PathVariable Long mentoringId) {
manageUseCase.delete(user, mentoringId);
return ResponseDto.create(MENTORING_DELETE.getCode(), DELETE_MENTORING.getMessage());
}

@PostMapping("/applying")
@Operation(summary = "[대학생] 멘토링 신청", description = "대학생이 멘토링을 신청합니다.")
public ResponseDto applyMentoring(@AuthenticationPrincipal User user,
public ResponseDto<Long> applyMentoring(@AuthenticationPrincipal User user,
@RequestBody @Valid MentoringApplyRequest request) {
applyUseCase.applyMentoring(user, request);
return ResponseDto.create(MENTORING_CREATE.getCode(), CREATE_MENTORING.getMessage());
Long mentoringId = applyUseCase.applyMentoring(user, request);
return ResponseDto.create(MENTORING_CREATE.getCode(), CREATE_MENTORING.getMessage(), mentoringId);
}

@PatchMapping("/me/{mentoringId}/done")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ public enum MentoringResponseMessage {
GET_MENTORING_DETAIL_INFO("멘토링 상세 조회에 성공하였습니다."),
CREATE_MENTORING("멘토링 신청에 성공하였습니다."),
UPDATE_MENTORING("멘토링 상태 갱신에 성공하였습니다."),
DELETE_MENTORING("멘토링 삭제에 성공하였습니다."),

NOT_FOUND_MENTORING("멘토링이 존재하지 않습니다."),
NOT_FOUND_DETAIL("볼 수 없는 신청서 입니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
import com.postgraduate.domain.salary.util.SalaryUtil;
import com.postgraduate.domain.senior.domain.entity.Senior;
import com.postgraduate.domain.senior.domain.service.SeniorGetService;
import com.postgraduate.global.slack.SlackMessage;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.time.LocalDate;
import java.util.List;

Expand All @@ -21,9 +23,11 @@
public class SalaryManageUseCase {
private final SalarySaveService salarySaveService;
private final SeniorGetService seniorGetService;
private final SlackMessage slackMessage;

@Scheduled(cron = "0 0 0 10 * *", zone = "Asia/Seoul")
public void createSalary() {
public void createSalary() throws IOException {
slackMessage.sendSlackSalary();
List<Senior> seniors = seniorGetService.all();
LocalDate salaryDate = SalaryUtil.getSalaryDate();
seniors.forEach(senior -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.List;

@Service
@RequiredArgsConstructor
Expand All @@ -31,4 +32,8 @@ public Page<SeniorSalary> findDistinctSeniors(String search, Integer page) {
Pageable pageable = PageRequest.of(page - 1, ADMIN_PAGE_SIZE);
return salaryRepository.findDistinctBySearchSenior(search, pageable);
}

public List<Salary> findAll() {
return salaryRepository.findAll();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
Expand All @@ -39,6 +40,8 @@

import static com.postgraduate.global.config.security.jwt.constant.Type.ACCESS;
import static com.postgraduate.global.config.security.jwt.constant.Type.REFRESH;
import static org.springframework.http.HttpStatus.BAD_REQUEST;
import static org.springframework.http.HttpStatus.UNAUTHORIZED;

@Component
@RequiredArgsConstructor
Expand All @@ -57,7 +60,6 @@ public class JwtUtils {
private static final String ROLE = "role";
private static final String TYPE = "type";
private static final String AUTHORIZATION = "Authorization";
private static final int STATUS = 500;
private static final String CONTENT_TYPE = "application/json";
private static final String CHARACTER_ENCODING = "UTF-8";

Expand Down Expand Up @@ -111,7 +113,7 @@ private AuthDetails getDetails(HttpServletResponse response, Claims claims) {
AuthDetails authDetails = authDetailsService.loadUserByUsername(claims.getSubject());
return authDetails;
} catch (UserNotFoundException ex) {
jwtExceptionHandler(response, ex);
jwtExceptionHandler(BAD_REQUEST, response, ex);
throw ex;
}
}
Expand All @@ -123,10 +125,10 @@ public boolean validateToken(HttpServletResponse response, String token, Type ty
throw new InvalidTokenException();
return true;
} catch (ApplicationException | SignatureException | UnsupportedJwtException | IllegalArgumentException | MalformedJwtException e) {
jwtExceptionHandler(response, new InvalidTokenException());
jwtExceptionHandler(BAD_REQUEST, response, new InvalidTokenException());
return false;
} catch (ExpiredJwtException e) {
jwtExceptionHandler(response, new TokenExpiredException());
jwtExceptionHandler(UNAUTHORIZED, response, new TokenExpiredException());
return false;
}
}
Expand All @@ -136,8 +138,8 @@ private Claims parseClaims(String token) {
return parser.parseClaimsJws(token).getBody();
}

private void jwtExceptionHandler(HttpServletResponse response, ApplicationException ex) {
response.setStatus(STATUS);
private void jwtExceptionHandler(HttpStatus status, HttpServletResponse response, ApplicationException ex) {
response.setStatus(status.value());
response.setContentType(CONTENT_TYPE);
response.setCharacterEncoding(CHARACTER_ENCODING);
try {
Expand Down
87 changes: 87 additions & 0 deletions src/main/java/com/postgraduate/global/slack/SlackMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package com.postgraduate.global.slack;

import com.postgraduate.domain.salary.domain.entity.Salary;
import com.postgraduate.domain.salary.domain.service.SalaryGetService;
import com.slack.api.Slack;
import com.slack.api.model.Attachment;
import com.slack.api.model.Field;
import com.slack.api.webhook.Payload;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.time.LocalDate;
import java.util.List;

@Slf4j
@Component
@RequiredArgsConstructor
public class SlackMessage {
private final Slack slackClient = Slack.getInstance();
private final SalaryGetService salaryGetService;

@Value("${slack.url}")
private String webHookUrl;

public void sendSlackSalary() throws IOException{
List<Salary> salaries = salaryGetService.findAll();
List<Attachment> attachments = salaries.stream()
.filter(salary -> salary.getTotalAmount() > 0)
.map(salary -> generateSalarySlackAttachment(salary))
.toList();
slackClient.send(webHookUrl, Payload.builder()
.text("---" + LocalDate.now() + "에 정산할 목록 ---")
.attachments(attachments)
.build());

Attachment attachment = generateSalarySlackAttachment(salaries);
slackClient.send(webHookUrl, Payload.builder()
.attachments(List.of(attachment))
.build());
}

//attach 생성 -> Field를 리스트로 담자
private Attachment generateSalarySlackAttachment(Salary salary) {
return Attachment.builder()
.color("#36a64f")
.fields(generateSalaryFields(salary))
.build();
}

private Attachment generateSalarySlackAttachment(List<Salary> salaries) {
return Attachment.builder()
.color("#1900ff")
.fields(generateTotalField(salaries))
.build();
}

private List<Field> generateSalaryFields(Salary salary) {
return List.of(
generateSlackField(
"선배 닉네임 : " + salary.getSenior().getUser().getNickName(),
"금액 : " + salary.getTotalAmount()
));
}

private List<Field> generateTotalField(List<Salary> salaries) {
Integer totalAmount = salaries.stream()
.map(Salary::getTotalAmount)
.reduce(0, Integer::sum);

return List.of(
generateSlackField(
"총 정산 금액", String.valueOf(totalAmount) + "원"
));
}

// Field 생성 메서드
private Field generateSlackField(String title, String value) {
return Field.builder()
.title(title)
.value(value)
.valueShortEnough(false)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,9 @@ void getSeniorDone() {
Mentoring mentoring2 = new Mentoring(2L, user, senior, "A", "b", "a", 40, WAITING, LocalDateTime.now(), LocalDateTime.now());
Mentoring mentoring3 = new Mentoring(3L, user, senior, "A", "b", "a", 40, WAITING, LocalDateTime.now(), LocalDateTime.now());
List<Mentoring> mentorings = List.of(mentoring1, mentoring2, mentoring3);
Payment payment1 = new Payment(1l, mentoring1, salary, 10000, "1", "1", LocalDateTime.now(), LocalDateTime.now(), Status.DONE);
Payment payment2 = new Payment(2l, mentoring2, salary, 10000, "1", "1", LocalDateTime.now(), LocalDateTime.now(), Status.DONE);
Payment payment3 = new Payment(3l, mentoring3, salary, 10000, "1", "1", LocalDateTime.now(), LocalDateTime.now(), Status.DONE);
Payment payment1 = new Payment(1l, mentoring1, salary, 10000, "1", "1", "a", LocalDateTime.now(), LocalDateTime.now(), Status.DONE);
Payment payment2 = new Payment(2l, mentoring2, salary, 10000, "1", "1", "a", LocalDateTime.now(), LocalDateTime.now(), Status.DONE);
Payment payment3 = new Payment(3l, mentoring3, salary, 10000, "1", "1", "a", LocalDateTime.now(), LocalDateTime.now(), Status.DONE);
DoneSeniorMentoringInfo done1 = MentoringMapper.mapToSeniorDoneInfo(mentoring1, payment1);
DoneSeniorMentoringInfo done2 = MentoringMapper.mapToSeniorDoneInfo(mentoring2, payment2);
DoneSeniorMentoringInfo done3 = MentoringMapper.mapToSeniorDoneInfo(mentoring3, payment3);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ void getSalaryDetail() {
, 40, DONE
, LocalDateTime.now(), LocalDateTime.now());
Salary salary = mock(Salary.class);
Payment payment1 = new Payment(1L, mentoring1, salary, 1000, "a", "a", LocalDateTime.now(), LocalDateTime.now(), Status.DONE);
Payment payment2 = new Payment(2L, mentoring2, salary, 1000, "a", "a", LocalDateTime.now(), LocalDateTime.now(), Status.DONE);
Payment payment3 = new Payment(3L, mentoring3, salary, 1000, "a", "a", LocalDateTime.now(), LocalDateTime.now(), Status.DONE);
Payment payment1 = new Payment(1L, mentoring1, salary, 1000, "a", "a", "a", LocalDateTime.now(), LocalDateTime.now(), Status.DONE);
Payment payment2 = new Payment(2L, mentoring2, salary, 1000, "a", "a", "a", LocalDateTime.now(), LocalDateTime.now(), Status.DONE);
Payment payment3 = new Payment(3L, mentoring3, salary, 1000, "a", "a", "a", LocalDateTime.now(), LocalDateTime.now(), Status.DONE);
List<Payment> payments = List.of(payment1, payment2, payment3);

given(seniorGetService.byUser(user))
Expand Down
Loading