Skip to content

Commit

Permalink
v0.0.6
Browse files Browse the repository at this point in the history
v0.0.6
  • Loading branch information
char-yb authored Aug 23, 2024
2 parents c37ddc8 + 01ede4b commit 5721690
Show file tree
Hide file tree
Showing 17 changed files with 229 additions and 14 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ out/
.vscode/

### Custom ###
logs
*.env
*.DS_Store
/src/main/generated
Expand Down
7 changes: 7 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ dependencies {
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

// logging
// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-logging
implementation 'org.springframework.boot:spring-boot-starter-logging:3.3.2'
implementation 'com.github.loki4j:loki-logback-appender:1.5.1'
implementation 'net.logstash.logback:logstash-logback-encoder:8.0'
implementation 'ch.qos.logback:logback-core:1.5.7'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import com.depromeet.stonebed.domain.fcm.dao.FcmRepository;
import com.depromeet.stonebed.domain.fcm.domain.FcmToken;
import com.depromeet.stonebed.domain.member.domain.MemberStatus;
import com.depromeet.stonebed.domain.missionRecord.dao.MissionRecordRepository;
import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecordStatus;
import com.depromeet.stonebed.global.common.constants.FcmNotificationConstants;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
Expand Down Expand Up @@ -58,12 +60,19 @@ public void sendReminderToIncompleteMissions() {
}

private List<String> getIncompleteMissionTokens() {
return missionRecordRepository.findAllByStatus(MissionRecordStatus.NOT_COMPLETED).stream()
LocalDateTime startOfDay = LocalDate.now().atStartOfDay();
LocalDateTime endOfDay = startOfDay.plusDays(1);

return missionRecordRepository
.findAllByCreatedAtBetweenAndStatusNot(
startOfDay, endOfDay, MissionRecordStatus.COMPLETED)
.stream()
.map(
missionRecord -> {
FcmToken fcmToken =
fcmRepository
.findByMember(missionRecord.getMember())
.findByMemberAndMemberStatus(
missionRecord.getMember(), MemberStatus.NORMAL)
.orElse(null);
return fcmToken != null ? fcmToken.getToken() : null;
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@ public class FcmTokenService {

@Transactional(readOnly = true)
public List<String> getAllTokens() {
return fcmRepository.findAll().stream()
.map(FcmToken::getToken)
.filter(token -> !token.isEmpty())
.toList();
return fcmRepository.findAllValidTokens();
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,20 @@

import com.depromeet.stonebed.domain.fcm.domain.FcmToken;
import com.depromeet.stonebed.domain.member.domain.Member;
import com.depromeet.stonebed.domain.member.domain.MemberStatus;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;

public interface FcmRepository extends JpaRepository<FcmToken, Long> {
public interface FcmRepository extends JpaRepository<FcmToken, Long>, FcmRepositoryCustom {
Optional<FcmToken> findByMember(Member member);

Optional<FcmToken> findByToken(String token);

List<FcmToken> findAll();

List<FcmToken> findAllByUpdatedAtBefore(LocalDateTime cutoffDate);

Optional<FcmToken> findByMemberAndMemberStatus(Member member, MemberStatus status);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.depromeet.stonebed.domain.fcm.dao;

import java.util.List;

public interface FcmRepositoryCustom {
List<String> findAllValidTokens();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.depromeet.stonebed.domain.fcm.dao;

import static com.depromeet.stonebed.domain.fcm.domain.QFcmToken.*;
import static com.depromeet.stonebed.domain.member.domain.QMember.*;

import com.depromeet.stonebed.domain.member.domain.MemberStatus;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class FcmRepositoryImpl implements FcmRepositoryCustom {

private final JPAQueryFactory jpaQueryFactory;

@Override
public List<String> findAllValidTokens() {
return jpaQueryFactory
.select(fcmToken.token)
.from(fcmToken)
.join(fcmToken.member, member)
.where(isMemberStatusNormal(), isTokenNotNull())
.fetch();
}

private BooleanExpression isMemberStatusNormal() {
return member.status.eq(MemberStatus.NORMAL);
}

private BooleanExpression isTokenNotNull() {
return fcmToken.token.isNotNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
Expand Down Expand Up @@ -123,6 +124,7 @@ public PresignedUrlResponse createMissionRecordPresignedUrl(
return PresignedUrlResponse.from(presignedUrl.toString());
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public ImageUrlResponse uploadCompleteMissionRecord(MissionRecordImageUploadRequest request) {
validateImageFileExtension(request.imageFileExtension());

Expand Down Expand Up @@ -162,6 +164,7 @@ public PresignedUrlResponse createMissionPresignedUrl(MissionImageCreateRequest
return PresignedUrlResponse.from(presignedUrl.toString());
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public ImageUrlResponse uploadCompleteMission(MissionImageUploadRequest request) {
validateImageFileExtension(request.imageFileExtension());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
Expand Down Expand Up @@ -186,7 +187,7 @@ public MissionTabResponse getMissionTabStatus(Long missionId) {
return new MissionTabResponse(missionRecord.getId(), imageUrl, missionRecordStatus);
}

@Transactional
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateMissionRecordWithImage(Long recordId, String imageUrl) {
MissionRecord missionRecord =
missionRecordRepository
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.depromeet.stonebed.domain.mission.domain.MissionHistory;
import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecord;
import com.depromeet.stonebed.domain.missionRecord.domain.MissionRecordStatus;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
Expand All @@ -18,7 +19,8 @@ Optional<MissionRecord> findByMemberAndMissionHistory(

Long countByMemberIdAndStatus(Long memberId, MissionRecordStatus status);

List<MissionRecord> findAllByStatus(MissionRecordStatus status);
List<MissionRecord> findAllByCreatedAtBetweenAndStatusNot(
LocalDateTime startTime, LocalDateTime endTime, MissionRecordStatus status);

List<MissionRecord> findByIdIn(List<Long> ids);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import java.time.format.DateTimeFormatter;

public record MissionRecordCalendarDto(
@Schema(description = "이미지 ID") Long imageId,
@Schema(description = "미션 기록 ID") Long recordId,
@Schema(description = "이미지 URL") String imageUrl,
@Schema(description = "미션 수행 일자") String missionDate) {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public record MissionRecordCalendarResponse(
@Schema(
description = "미션 기록 데이터 리스트",
example =
"[{\"imageId\": 1, \"imageUrl\": \"http://example.com/image1.jpg\", \"missionDate\": \"2024-01-01\"}]")
"[{\"recordId\": 1, \"imageUrl\": \"http://example.com/image1.jpg\", \"missionDate\": \"2024-01-01\"}]")
List<MissionRecordCalendarDto> list,
@Schema(description = "커서 위치", example = "2024-01-03") String nextCursor) {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.depromeet.stonebed.global.config.web;

import com.depromeet.stonebed.global.interceptor.MdcLoggingInterceptor;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@RequiredArgsConstructor
public class WebConfig implements WebMvcConfigurer {

private final MdcLoggingInterceptor mdcLoggingInterceptor;

@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(mdcLoggingInterceptor);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.depromeet.stonebed.global.interceptor;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.util.UUID;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

@Component
public class MdcLoggingInterceptor implements HandlerInterceptor {

@Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// handler가 HandlerMethod인지 확인
if (handler instanceof HandlerMethod handlerMethod) {
String handlerName = handlerMethod.getMethod().getName();
String methodName = handlerMethod.getBeanType().getSimpleName();
String controllerInfo = methodName + "." + handlerName;
String traceId = UUID.randomUUID().toString();
MDC.put("traceId", traceId);
MDC.put("serviceName", controllerInfo);
}
return true;
}

@Override
public void afterCompletion(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
MDC.clear();
}
}
5 changes: 4 additions & 1 deletion src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ spring:

logging:
level:
com.depromeet.stonebed.*.api.*: debug
com.depromeet.stonebed.*.*.api.*: debug
com.depromeet.stonebed.*.*.application.*: debug
org.hibernate.SQL: debug
org.hibernate.type: trace

fcm:
credential: ${FCM_CREDENTIAL}
85 changes: 85 additions & 0 deletions src/main/resources/logback-spring.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<configuration scan="true" scanPeriod="60 seconds">
<springProfile name="local">
<!-- 개발 환경 로그 설정 -->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>
<springProfile name="dev">
<!-- 개발 환경 로그 설정 -->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>

<springProfile name="prod">
<!-- 프로덕션 환경 로그 설정 -->
<root level="INFO">
<appender-ref ref="STDOUT"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="WARN_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>
</springProfile>

<!-- Appender 설정 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>{"traceId":"%X{traceId}","time":"%d{yyyy-MM-dd HH:mm:ss.SSS}", "service.name":"%X{serviceName}", "level":"%level", "message":"%msg"}%n</pattern>
</layout>
</encoder>
</appender>

<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>./logs/debug.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>./logs/debug.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>

<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>./logs/info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>./logs/info.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>

<appender name="WARN_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>./logs/warn.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>./logs/warn.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>

<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>./logs/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>./logs/error.%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>10MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
</appender>
</configuration>
Loading

0 comments on commit 5721690

Please sign in to comment.