Skip to content

Commit

Permalink
Merge pull request #204 from TEAM-SAMSION/main
Browse files Browse the repository at this point in the history
release : 1.2.0 배포
  • Loading branch information
tlarbals824 authored Jan 28, 2024
2 parents a147ba8 + 78ec693 commit 94e395c
Show file tree
Hide file tree
Showing 27 changed files with 270 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
package com.pawith.alarmmodule.exception;

import com.pawith.commonmodule.exception.Error;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;

@AllArgsConstructor
@Getter
@RequiredArgsConstructor
public enum AlarmError implements Error {
DEVICE_TOKEN_NOT_FOUND("FCM 디바이스 토큰이 없습니다.", 5000),
FCM_SEND_ERROR("FCM 전송에 실패하였습니다.", 5001),;
DEVICE_TOKEN_NOT_FOUND("FCM 디바이스 토큰이 없습니다.", 5000, HttpStatus.NOT_FOUND),
FCM_SEND_ERROR("FCM 전송에 실패하였습니다.", 5001, HttpStatus.INTERNAL_SERVER_ERROR),
;

private String message;
private Integer errorCode;

@Override
public String getMessage() {
return message;
}

@Override
public int getErrorCode() {
return errorCode;
}
private final String message;
private final int errorCode;
private final HttpStatusCode httpStatusCode;
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public interface SetOperator<K> {

void addWithExpire(K k, long expire, TimeUnit timeUnit);

void addWithExpireAfterToday(K k);

void remove(K k);

boolean contains(K k);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,25 @@
import com.pawith.commonmodule.cache.operators.SetOperator;
import net.jodah.expiringmap.ExpirationPolicy;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

public class DefaultSetOperator<K> extends ExpiredStorage<K,K> implements SetOperator<K> {
public class DefaultSetOperator<K> extends ExpiredStorage<K, K> implements SetOperator<K> {
@Override
public void add(K k) {
storage.put(k,k);
storage.put(k, k);
}

@Override
public void addWithExpire(K k, long expire, TimeUnit timeUnit) {
storage.put(k,k,ExpirationPolicy.CREATED,expire, TimeUnit.MINUTES);
storage.put(k, k, ExpirationPolicy.CREATED, expire, TimeUnit.MINUTES);
}

@Override
public void addWithExpireAfterToday(K k) {
final long expiredDuration = Duration.between(LocalDateTime.now(), LocalDateTime.now().plusDays(1)).toMinutes();
storage.put(k, k, ExpirationPolicy.CREATED, expiredDuration, TimeUnit.MINUTES);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.pawith.commonmodule.exception;

import lombok.Getter;
import org.springframework.http.HttpStatusCode;

@Getter
public class BusinessException extends RuntimeException{
Expand All @@ -17,4 +18,8 @@ public String getMessage(){
public int getErrorCode(){
return error.getErrorCode();
}

public HttpStatusCode getHttpStatusCode(){
return error.getHttpStatusCode();
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
package com.pawith.commonmodule.exception;

import org.springframework.http.HttpStatusCode;


public interface Error {
String getMessage();
int getErrorCode();

HttpStatusCode getHttpStatusCode();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.pawith.commonmodule.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
Expand All @@ -11,6 +10,6 @@ public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public ResponseEntity<ErrorResponse> handleJwtException(BusinessException e) {
final ErrorResponse errorResponse = ErrorResponse.from(e);
return new ResponseEntity<>(errorResponse, HttpStatus.BAD_REQUEST);
return new ResponseEntity<>(errorResponse, e.getHttpStatusCode());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class ReissueUseCaseImpl implements ReissueUseCase {
@Override
public TokenReissueResponse reissue(String refreshTokenHeader) {
final String refreshToken = TokenExtractUtils.extractToken(refreshTokenHeader);
jwtProvider.validateToken(refreshToken, TokenType.REFRESH_TOKEN);
final String userEmail = jwtProvider.extractEmailFromToken(refreshToken, TokenType.REFRESH_TOKEN);
return reissueToken(refreshToken, userEmail);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@

import com.pawith.commonmodule.exception.Error;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;

@Getter
@RequiredArgsConstructor
public enum AuthError implements Error {

INVALID_TOKEN("유효하지 않은 토큰입니다.", 1000),
EXPIRED_TOKEN("만료된 토큰입니다.", 1001),
NOT_EXIST_TOKEN("토큰이 존재하지 않습니다.", 1002),
INVALID_AUTHORIZATION_TYPE("유효하지 않은 Authorization Type 입니다.", 1003),
EMPTY_AUTHORIZATION_HEADER("Authorization Header가 비어있습니다.", 1004),
OAUTH_FAIL("OAuth 인증에 실패하였습니다.", 1005);
INVALID_TOKEN("유효하지 않은 토큰입니다.", 1000, HttpStatus.BAD_REQUEST),
EXPIRED_TOKEN("만료된 토큰입니다.", 1001, HttpStatus.BAD_REQUEST),
NOT_EXIST_TOKEN("토큰이 존재하지 않습니다.", 1002, HttpStatus.BAD_REQUEST),
INVALID_AUTHORIZATION_TYPE("유효하지 않은 Authorization Type 입니다.", 1003, HttpStatus.BAD_REQUEST),
EMPTY_AUTHORIZATION_HEADER("Authorization Header가 비어있습니다.", 1004, HttpStatus.BAD_REQUEST),
OAUTH_FAIL("OAuth 인증에 실패하였습니다.", 1005, HttpStatus.BAD_REQUEST);

private final String message;
private final int errorCode;

AuthError(String message, int errorCode) {
this.message = message;
this.errorCode = errorCode;
}
private final HttpStatusCode httpStatusCode;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,16 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.TimeUnit;

@Slf4j
//@Component
@Component
public class IncompleteTodoCountNotificationHandler extends AbstractBatchSchedulingHandler<IncompleteTodoCountInfoDao> {
private static final Integer BATCH_SIZE = 100;
private static final String CRON_EXPRESSION = "0 0 0 0 0 0";
private static final String CRON_EXPRESSION = "0 0 20 * * *"; // 매일 20시에 실행
private static final String NOTIFICATION_MESSAGE = "[%s] 오늘이 지나기 전, %s님에게 남은 %d개의 todo를 완료해주세요!";

private final RegisterRepository registerRepository;
Expand All @@ -43,6 +44,7 @@ protected List<IncompleteTodoCountInfoDao> extractBatchData(Pageable pageable) {
protected void processBatch(List<IncompleteTodoCountInfoDao> executionResult) {
cachingUserInfo(executionResult);
final List<NotificationEvent> notificationEventList = executionResult.stream()
.filter(incompleteTodoCountInfoDao -> incompleteTodoCountInfoDao.getIncompleteTodoCount() > 0)
.map(incompleteTodoCountInfoDao -> {
final String userNickname = valueOperator.get(incompleteTodoCountInfoDao.getUserId());
final String message = String.format(NOTIFICATION_MESSAGE, incompleteTodoCountInfoDao.getTodoTeamName(), userNickname, incompleteTodoCountInfoDao.getIncompleteTodoCount());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.pawith.todoapplication.handler;

import com.pawith.todoapplication.handler.event.TodoCompletionCheckEvent;
import com.pawith.todoapplication.handler.event.TodoAssignStatusChangeEvent;
import com.pawith.tododomain.entity.Assign;
import com.pawith.tododomain.entity.Todo;
import com.pawith.tododomain.service.AssignQueryService;
Expand All @@ -23,11 +23,11 @@ public class TodoCompletionCheckOnTodoHandler {
private final TodoQueryService todoQueryService;

@EventListener
public void changeTodoStatus(TodoCompletionCheckEvent todoCompletionCheckEvent) throws InterruptedException {
public void changeTodoStatus(TodoAssignStatusChangeEvent todoAssignStatusChangeEvent) throws InterruptedException {
while(true) {
try {
final List<Assign> assigns = assignQueryService.findAllAssignByTodoId(todoCompletionCheckEvent.todoId());
final Todo todo = todoQueryService.findTodoByTodoId(todoCompletionCheckEvent.todoId());
final List<Assign> assigns = assignQueryService.findAllAssignByTodoId(todoAssignStatusChangeEvent.todoId());
final Todo todo = todoQueryService.findTodoByTodoId(todoAssignStatusChangeEvent.todoId());
final boolean isAllCompleteTodo = assigns.stream().allMatch(Assign::isCompleted);
todo.updateCompletionStatus(isAllCompleteTodo);
break;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.pawith.todoapplication.handler;

import com.pawith.commonmodule.cache.CacheTemplate;
import com.pawith.commonmodule.event.MultiNotificationEvent;
import com.pawith.commonmodule.event.NotificationEvent;
import com.pawith.todoapplication.handler.event.TodoAssignStatusChangeEvent;
import com.pawith.tododomain.entity.CompletionStatus;
import com.pawith.tododomain.repository.dao.IncompleteAssignInfoDao;
import com.pawith.tododomain.service.AssignQueryService;
import com.pawith.userdomain.entity.User;
import com.pawith.userdomain.service.UserQueryService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;

@Component
@RequiredArgsConstructor
@Transactional
public class TodoRemindHandler {
private static final String NOTIFICATION_MESSAGE = "'%s'을 %d명이 완료했어요! %s님도 얼른 완료해볼까요?";
private static final String REMIND_CACHE_KEY = "Remind:%s";

private final AssignQueryService assignQueryService;
private final UserQueryService userQueryService;
private final CacheTemplate<String, String> cacheTemplate;
private final ApplicationEventPublisher applicationEventPublisher;

@EventListener
public void remindTodo(final TodoAssignStatusChangeEvent todoAssignStatusChangeEvent){
final Long todoId = todoAssignStatusChangeEvent.todoId();
final String cacheKey = String.format(REMIND_CACHE_KEY, todoId);
final long completeAssignNumber = assignQueryService.countAssignByTodoIdAndCompleteStatus(todoId, CompletionStatus.COMPLETE);
if(isRemindable(todoId, completeAssignNumber)&& !cacheTemplate.opsForSet().contains(cacheKey)){
cacheTemplate.opsForSet().addWithExpireAfterToday(cacheKey);
final List<NotificationEvent> todoNotificationList = buildNotificationEvent(todoId, completeAssignNumber);
applicationEventPublisher.publishEvent(new MultiNotificationEvent(todoNotificationList));
}
}

private List<NotificationEvent> buildNotificationEvent(final Long todoId, final long completeAssignNumber) {
final List<IncompleteAssignInfoDao> incompleteAssignInfoDaoList = assignQueryService.findAllIncompleteAssignInfoByTodoId(todoId);
final List<Long> incompleteTodoUserIds = incompleteAssignInfoDaoList.stream()
.map(IncompleteAssignInfoDao::getUserId)
.toList();
final Map<Long, User> incompleteTodoUserMap = userQueryService.findMapWithUserIdKeyByIds(incompleteTodoUserIds);
return incompleteAssignInfoDaoList.stream()
.map(incompleteAssignInfo -> {
final User incompleteTodoUser = incompleteTodoUserMap.get(incompleteAssignInfo.getUserId());
final String notificationMessage = String.format(NOTIFICATION_MESSAGE, incompleteAssignInfo.getTodoDescription(), completeAssignNumber, incompleteTodoUser.getNickname());
return new NotificationEvent(incompleteTodoUser.getId(),incompleteAssignInfo.getTodoTeamName(), notificationMessage, todoId);
})
.toList();
}

private boolean isRemindable(final Long todoId, final long completeAssignNumber){
final long totalAssignNumber = assignQueryService.countAssignByTodoId(todoId);
return (float) completeAssignNumber >= (float) totalAssignNumber * 0.5;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.pawith.tododomain.service.AssignDeleteService;
import com.pawith.tododomain.service.RegisterDeleteService;
import com.pawith.tododomain.service.RegisterQueryService;
import com.pawith.tododomain.service.RegisterValidateService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
Expand All @@ -22,7 +21,6 @@
public class UserAccountDeleteOnTodoHandler {
private final RegisterQueryService registerQueryService;
private final RegisterDeleteService registerDeleteService;
private final RegisterValidateService registerValidateService;
private final AssignDeleteService assignDeleteService;

@EventListener
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.pawith.todoapplication.handler.event;

public record TodoAssignStatusChangeEvent(Long todoId) {
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import com.pawith.commonmodule.annotation.ApplicationService;
import com.pawith.todoapplication.dto.request.AssignChangeRequest;
import com.pawith.todoapplication.handler.event.TodoCompletionCheckEvent;
import com.pawith.todoapplication.handler.event.TodoAssignStatusChangeEvent;
import com.pawith.tododomain.entity.Assign;
import com.pawith.tododomain.entity.Register;
import com.pawith.tododomain.entity.Todo;
Expand Down Expand Up @@ -38,7 +38,7 @@ public void changeAssignStatus(Long todoId){
final User user = userUtils.getAccessUser();
final Assign assign = assignQueryService.findAssignByTodoIdAndUserId(todo.getId(), user.getId());
assign.updateCompletionStatus();
applicationEventPublisher.publishEvent(new TodoCompletionCheckEvent(todo.getId()));
applicationEventPublisher.publishEvent(new TodoAssignStatusChangeEvent(todo.getId()));
}

public void changeAssign(Long todoId, AssignChangeRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,10 @@ public class TodoGetUseCase {
private final AssignQueryService assignQueryService;
private final TodoNotificationQueryService todoNotificationQueryService;


public ListResponse<TodoInfoResponse> getTodoListByTodoTeamId(final Long todoTeamId) {
final User user = userUtils.getAccessUser();
final List<Assign> allAssigns = assignQueryService.findAllByUserIdAndTodoTeamIdAndScheduledDate(user.getId(), todoTeamId);
List<TodoInfoResponse> todoInfoResponses = allAssigns.stream()
final List<TodoInfoResponse> todoInfoResponses = allAssigns.stream()
.map(assign -> {
final Todo todo = assign.getTodo();
final Category category = todo.getCategory();
Expand All @@ -49,13 +48,13 @@ public ListResponse<TodoInfoResponse> getTodoListByTodoTeamId(final Long todoTea
return ListResponse.from(todoInfoResponses);
}

public ListResponse<CategorySubTodoResponse> getTodoListByCategoryId(Long categoryId, LocalDate moveDate) {
public ListResponse<CategorySubTodoResponse> getTodoListByCategoryId(final Long categoryId,final LocalDate moveDate) {
final User accessUser = userUtils.getAccessUser();

final List<Assign> assignList = assignQueryService.findAllAssignByCategoryIdAndScheduledDate(categoryId, moveDate);

final Map<Todo, List<Assign>> todoAssignMap = AssignUtils.convertToTodoAssignMap(assignList);
final List<Todo> todoList = List.copyOf(todoAssignMap.keySet());
final Collection<Todo> todoList = todoAssignMap.keySet();

final Map<Long, TodoNotification> todoNotificationMap =
todoNotificationQueryService.findMapTodoIdKeyAndTodoNotificationValueByTodoIdsAndUserId(todoList, accessUser.getId());
Expand All @@ -74,12 +73,12 @@ public ListResponse<CategorySubTodoResponse> getTodoListByCategoryId(Long catego
return ListResponse.from(subTodoResponseList);
}

private List<AssignUserInfoResponse> getAssignUserInfoResponses(List<Assign> assigns, Map<Long, User> userMap, Long accessUserId, AtomicReference<Boolean> isAssigned) {
private List<AssignUserInfoResponse> getAssignUserInfoResponses(final List<Assign> assigns, final Map<Long, User> userMap, final Long accessUserId, final AtomicReference<Boolean> isAssigned) {
return assigns.stream()
.map(assign -> {
final Register register = assign.getRegister();
final User findUser = userMap.get(register.getUserId());
if (Objects.equals(findUser.getId(), accessUserId)) {
if (findUser.isMatchingUser(accessUserId)) {
isAssigned.set(true);
}
return new AssignUserInfoResponse(findUser.getId(), findUser.getNickname(), assign.getCompletionStatus());
Expand Down
Loading

0 comments on commit 94e395c

Please sign in to comment.