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

Dau 알림 추가 #186

Merged
merged 2 commits into from
Jan 3, 2024
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.moing.backend.domain.history.domain.service.AlarmHistoryDeleteService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
Expand All @@ -11,6 +12,7 @@

@Service
@RequiredArgsConstructor
@Profile("prod")
public class CleanupUseCase {

private final AlarmHistoryDeleteService alarmHistoryDeleteService;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package com.moing.backend.domain.member.domain.repository;

import com.moing.backend.domain.member.domain.entity.Member;
import com.moing.backend.domain.statistics.application.dto.DailyStats;

import java.util.Optional;

public interface MemberCustomRepository {
boolean checkNickname(String nickname);

boolean checkNickname(String nickname);
Optional<Member> findNotDeletedBySocialId(String socialId);

Optional<Member> findNotDeletedByEmail(String email);
Optional<Member> findNotDeletedByMemberId(Long id);
DailyStats getDailyStats();
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package com.moing.backend.domain.member.domain.repository;

import com.moing.backend.domain.member.domain.entity.Member;
import com.moing.backend.domain.mission.domain.entity.constant.MissionType;
import com.moing.backend.domain.statistics.application.dto.DailyStats;
import com.querydsl.jpa.impl.JPAQueryFactory;

import javax.persistence.EntityManager;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Optional;

import static com.moing.backend.domain.member.domain.entity.QMember.member;
import static com.moing.backend.domain.mission.domain.entity.QMission.mission;
import static com.moing.backend.domain.team.domain.entity.QTeam.team;

public class MemberCustomRepositoryImpl implements MemberCustomRepository {

Expand Down Expand Up @@ -52,4 +58,71 @@ public Optional<Member> findNotDeletedByMemberId(Long id) {
.where(member.isDeleted.eq(false))
.fetchOne());
}

@Override
public DailyStats getDailyStats() {
LocalDateTime now = LocalDateTime.now(ZoneId.of("Asia/Seoul"));
LocalDateTime startOfToday = now.toLocalDate().atStartOfDay();
LocalDateTime endOfToday = startOfToday.plusDays(1);
LocalDateTime startOfYesterday = startOfToday.minusDays(1);

long todayNewMembers = queryFactory
.selectFrom(member)
.where(member.createdDate.between(startOfToday, endOfToday))
.fetchCount();

long yesterdayNewMembers = queryFactory
.selectFrom(member)
.where(member.createdDate.between(startOfYesterday, startOfToday))
.fetchCount();

long todayNewTeams = queryFactory
.selectFrom(team)
.where(team.createdDate.between(startOfToday, endOfToday))
.fetchCount();

long yesterdayNewTeams = queryFactory
.selectFrom(team)
.where(team.createdDate.between(startOfYesterday, startOfToday))
.fetchCount();

long todayRepeatMissions = queryFactory
.select(mission)
.from(mission)
.where(mission.createdDate.between(startOfToday, endOfToday)
.and(mission.type.eq(MissionType.REPEAT)))
.fetchCount();

long yesterdayRepeatMissions = queryFactory
.select(mission)
.from(mission)
.where(mission.createdDate.between(startOfYesterday, startOfToday)
.and(mission.type.eq(MissionType.REPEAT)))
.fetchCount();

long todayOnceMissions = queryFactory
.select(mission)
.from(mission)
.where(mission.createdDate.between(startOfToday, endOfToday)
.and(mission.type.eq(MissionType.ONCE)))
.fetchCount();

long yesterdayOnceMissions = queryFactory
.select(mission)
.from(mission)
.where(mission.createdDate.between(startOfYesterday, startOfToday)
.and(mission.type.eq(MissionType.ONCE)))
.fetchCount();

return new DailyStats(
todayNewMembers,
yesterdayNewMembers,
todayNewTeams,
yesterdayNewTeams,
todayRepeatMissions,
yesterdayRepeatMissions,
todayOnceMissions,
yesterdayOnceMissions);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,5 @@

import com.moing.backend.domain.member.domain.entity.Member;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface MemberRepository extends JpaRepository<Member, Long>, MemberCustomRepository {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import com.moing.backend.domain.member.domain.entity.Member;
import com.moing.backend.domain.member.domain.repository.MemberRepository;
import com.moing.backend.domain.member.exception.NotFoundBySocialIdException;
import com.moing.backend.domain.statistics.application.dto.DailyStats;
import com.moing.backend.global.annotation.DomainService;
import lombok.RequiredArgsConstructor;

import javax.transaction.Transactional;
import java.math.BigInteger;

@DomainService
@Transactional
Expand All @@ -21,4 +23,8 @@ public Member getMemberBySocialId(String socialId){
public Member getMemberByMemberId(Long memberId) {
return memberRepository.findNotDeletedByMemberId(memberId).orElseThrow(()->new NotFoundBySocialIdException());
}

public DailyStats getDailyStats(){
return memberRepository.getDailyStats();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.moing.backend.domain.teamScore.application.service.TeamScoreLogicUseCase;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
Expand All @@ -26,6 +27,7 @@
@EnableAsync
@EnableScheduling // 스케줄링 활성화
@RequiredArgsConstructor
@Profile("prod")
public class MissionStateScheduleUseCase {

private final MissionStateUseCase missionStateUseCase;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.moing.backend.domain.statistics.application.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.SqlResultSetMapping;

@Getter
@AllArgsConstructor
@NoArgsConstructor
public class DailyStats {

private long todayNewMembers;
private long yesterdayNewMembers;
private long todayNewTeams;
private long yesterdayNewTeams;
private long todayRepeatMission;
private long yesterdayRepeatMission;
private long todayOnceMission;
private long yesterdayOnceMission;

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.moing.backend.domain.statistics.application.service;

import com.moing.backend.domain.member.domain.service.MemberGetService;
import com.moing.backend.domain.statistics.application.dto.DailyStats;
import com.moing.backend.global.config.slack.util.WebhookUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.LinkedHashMap;
import java.util.Map;

@Slf4j
@Service
@Transactional
@EnableAsync
@EnableScheduling
@RequiredArgsConstructor
@Profile("prod")
public class DAUScheduleUseCase {

private final WebhookUtil webhookUtil;
private final MemberGetService memberGetService;

public static final String DAILY_TEAM_CREATION_COUNT = "[DAU] 일일 모임 생성 수";
public static final String DAILY_NEW_MEMBER_COUNT = "[DAU] 일일 신규 가입자 수";
public static final String DAILY_REPEAT_MISSION_COUNT = "[DAU] 일일 반복 미션 생성 개수";
public static final String DAILY_ONCE_MISSION_COUNT = "[DAU] 일일 한번 미션 생성 개수";

/*
DAU 정보 : 일일 모임 생성 수, 일일 신규 가입자 수, 일일 반복 미션 생성 수, 일일 한번 미션 생성 수
*/
@Scheduled(cron = "0 59 23 * * *")
public void DailyTeamCreationInfoAlarm() {
Map<String, Long> todayStats = new LinkedHashMap<>();
Map<String, Long> yesterdayStats = new LinkedHashMap<>();

DailyStats dailyStats = memberGetService.getDailyStats();
todayStats.put(DAILY_TEAM_CREATION_COUNT, dailyStats.getTodayNewTeams());
yesterdayStats.put(DAILY_TEAM_CREATION_COUNT, dailyStats.getYesterdayNewTeams());

todayStats.put(DAILY_NEW_MEMBER_COUNT, dailyStats.getTodayNewMembers());
yesterdayStats.put(DAILY_NEW_MEMBER_COUNT, dailyStats.getYesterdayNewMembers());

todayStats.put(DAILY_REPEAT_MISSION_COUNT, dailyStats.getTodayRepeatMission());
yesterdayStats.put(DAILY_REPEAT_MISSION_COUNT, dailyStats.getYesterdayRepeatMission());

todayStats.put(DAILY_ONCE_MISSION_COUNT, dailyStats.getTodayOnceMission());
yesterdayStats.put(DAILY_ONCE_MISSION_COUNT, dailyStats.getYesterdayRepeatMission());
webhookUtil.sendDailyStatsMessage(todayStats, yesterdayStats);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class ExceptionEventHandler {

private final WebhookUtil webhookUtil;

@Async("asyncTaskExecutor")
@Async
@EventListener
public void onExceptionEvent(ExceptionEvent event) {
webhookUtil.sendSlackAlertErrorLog(event.getRequest(), event.getException());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,18 @@
import com.moing.backend.global.config.slack.team.dto.TeamCreateEvent;
import com.moing.backend.global.config.slack.util.WebhookUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

@RequiredArgsConstructor
@Component
public class TeamCreateHandler {

private final WebhookUtil webhookUtil;

@Async("asyncTaskExecutor")
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
@Async
@EventListener
public void onTeamCreateEvent(TeamCreateEvent event) {
webhookUtil.sendSlackTeamCreatedMessage(event.getTeamName(), event.getLeaderId());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.slack.api.webhook.WebhookPayloads.payload;

Expand All @@ -25,7 +27,7 @@ public class SlackAdapter implements WebhookUtil {
private String errorWebhookUrl;

@Value("${webhook.slack.team_alarm_url}")
private String teamAlarmWebhookUrl;
private String infoWebhookUrl;

private final Slack slackClient = Slack.getInstance();

Expand All @@ -51,7 +53,7 @@ public void sendSlackAlertErrorLog(HttpServletRequest request, Exception e) {
public void sendSlackTeamCreatedMessage(String teamName, Long leaderId) {
String message = String.format("[새로운 소모임 '%s'이(가) 생성되었습니다.]", teamName);
List<Attachment> attachments = List.of(generateSlackTeamAttachment(teamName, leaderId));
sendSlackMessage(teamAlarmWebhookUrl, message, attachments);
sendSlackMessage(infoWebhookUrl, message, attachments);
}

private Attachment generateSlackTeamAttachment(String teamName, Long leaderId) {
Expand All @@ -65,6 +67,27 @@ private Attachment generateSlackTeamAttachment(String teamName, Long leaderId) {
.build();
}

@Override
public void sendDailyStatsMessage(Map<String, Long> todayStats, Map<String, Long> yesterdayStats) {
String message = "[일일 통계 알림]";
List<Attachment> attachments = todayStats.keySet().stream()
.map(key -> generateDailyStatsAttachment(key, todayStats.get(key), yesterdayStats.getOrDefault(key, 0L)))
.collect(Collectors.toList());

sendSlackMessage(infoWebhookUrl, message, attachments);
}

private Attachment generateDailyStatsAttachment(String title, long todayCount, long yesterdayCount) {
return Attachment.builder()
.color("1A66CC") // 색상 설정
.title(title)
.fields(List.of(
generateSlackField("Today", String.valueOf(todayCount) + " 개"),
generateSlackField("Yesterday", String.valueOf(yesterdayCount) + " 개")
))
.build();
}

// attachment 생성 메서드
private Attachment generateSlackErrorAttachment(Exception e, HttpServletRequest request) {
String requestTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(LocalDateTime.now());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.moing.backend.global.config.slack.util;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

public interface WebhookUtil {

void sendSlackAlertErrorLog(HttpServletRequest request, Exception e);

void sendSlackTeamCreatedMessage(String teamName, Long leaderId);

void sendDailyStatsMessage(Map<String, Long> todayStats, Map<String, Long> yesterdayStats);
}
Loading