Skip to content

Commit

Permalink
Merge branch 'feature/#15'
Browse files Browse the repository at this point in the history
  • Loading branch information
Kim-Wongi-1 committed Dec 24, 2024
2 parents c73a591 + 63b174a commit 9d475b0
Show file tree
Hide file tree
Showing 23 changed files with 483 additions and 6 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,14 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

implementation 'org.springframework.boot:spring-boot-starter-mail'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'

implementation 'com.google.firebase:firebase-admin:9.4.1'

implementation 'io.jsonwebtoken:jjwt-api:0.12.3'
implementation 'io.jsonwebtoken:jjwt-impl:0.12.3'
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import com.galendar.domain.user.entity.UserEntity;
import com.galendar.domain.user.repository.UserRepository;
import com.galendar.global.common.dto.response.ResponseData;
import com.galendar.global.security.CustomUserDetails;
import com.galendar.global.firebase.service.FcmTokenService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
Expand All @@ -27,7 +27,7 @@
public class AuthController {

private final AuthService authService;

private final FcmTokenService fcmTokenService;
@Operation(summary = "회원 가입", description = "회원가입을 처리합니다.")
@PostMapping("/signup")
public ResponseEntity<ResponseData<String>> signup(@Validated @RequestBody SignupRequest request) {
Expand All @@ -39,6 +39,7 @@ public ResponseEntity<ResponseData<String>> signup(@Validated @RequestBody Signu
@PostMapping
public ResponseEntity<ResponseData<JsonWebTokenResponse>> auth(@Validated @RequestBody AuthenticationRequest request) {
JsonWebTokenResponse tokenResponse = authService.auth(request);
fcmTokenService.register(request.getEmail(), request.getFcmToken());
return ResponseEntity.ok(ResponseData.ok(tokenResponse, "로그인 성공"));
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.galendar.domain.auth.dto.request;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import lombok.Getter;
import lombok.Setter;
Expand All @@ -16,4 +15,6 @@ public class AuthenticationRequest {
@NotBlank(message = "비밀번호를 입력해주세요.")
private String password;

private String fcmToken;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.galendar.domain.contest.dto.response;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDate;

@AllArgsConstructor
@Getter
@Setter
public class ContestDeadlineResponse {
private Long id;
private String title;
private String email;
private LocalDate submitStartDate;
private LocalDate submitEndDate;
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ public class ContestEntity extends BaseTimeEntity {
@OneToMany(mappedBy = "contestEntity", orphanRemoval = true, cascade = CascadeType.ALL)
private List<ContestRegionEntity> contestRegions = new ArrayList<>();

@Column(nullable = false, unique = true)
private String strNo;

@NotNull
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
package com.galendar.domain.contest.repository.querydsl;

import com.galendar.domain.contest.dto.request.ContestRequest;
import com.galendar.domain.contest.dto.response.ContestDeadlineResponse;
import com.galendar.domain.contest.dto.response.ContestDetailResponse;
import com.galendar.domain.contest.dto.response.ContestResponse;

import java.util.List;
import java.util.Optional;

public interface ContestQueryRepository {
List<ContestResponse> findWithBookmark(ContestRequest request, Long userId);

List<ContestResponse> findWithBookmark(ContestRequest request, Long userId);
Optional<ContestDetailResponse> findByIdWithBookmark(Long id, Long userId);
List<ContestDeadlineResponse> findContestsBySubmitEndDates(List deadlineDates);

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.galendar.domain.contest.repository.querydsl;

import com.galendar.domain.contest.dto.request.ContestRequest;
import com.galendar.domain.contest.dto.response.ContestDeadlineResponse;
import com.galendar.domain.contest.dto.response.ContestDetailResponse;
import com.galendar.domain.contest.dto.response.ContestResponse;
import com.galendar.domain.region.dto.RegionDTO;
Expand All @@ -22,6 +23,7 @@
import static com.galendar.domain.contest.entity.QContestTargetEntity.contestTargetEntity;
import static com.galendar.domain.region.entity.QRegionEntity.regionEntity;
import static com.galendar.domain.target.entity.QTargetEntity.targetEntity;
import static com.galendar.domain.user.entity.QUserEntity.userEntity;
import static com.querydsl.core.group.GroupBy.groupBy;
import static com.querydsl.core.group.GroupBy.set;

Expand All @@ -32,7 +34,7 @@ public class ContestQueryRepositoryImpl implements ContestQueryRepository {
private final JPAQueryFactory queryFactory;

@Override
public List<ContestResponse> findWithBookmark(ContestRequest request, Long userId){
public List<ContestResponse> findWithBookmark(ContestRequest request, Long userId) {
return queryFactory
.select(contestProjection())
.from(contestEntity)
Expand All @@ -55,7 +57,6 @@ public List<ContestResponse> findWithBookmark(ContestRequest request, Long userI

@Override
public Optional<ContestDetailResponse> findByIdWithBookmark(Long id, Long userId) {

return queryFactory
.selectFrom(contestEntity)
.innerJoin(contestEntity.contestTargets, contestTargetEntity)
Expand Down Expand Up @@ -98,6 +99,22 @@ public Optional<ContestDetailResponse> findByIdWithBookmark(Long id, Long userId
).stream().findFirst();
}

public List<ContestDeadlineResponse> findContestsBySubmitEndDates(List deadlineDates) {
return queryFactory
.select(contestDeadlineProjection())
.from(contestEntity)
.innerJoin(bookmarkEntity).on(bookmarkEntity.contestEntity.eq(contestEntity))
.innerJoin(bookmarkEntity.userEntity, userEntity)
.where(
isSubmitEndDateIn(deadlineDates)
).fetch();
}

private BooleanExpression isSubmitEndDateIn(List deadlineDates) {
if (deadlineDates == null || deadlineDates.isEmpty()) return null;
return contestEntity.submitEndDate.in(deadlineDates);
}

private BooleanExpression eqId(Long id) {
if (id == null) return null;
return contestEntity.id.eq(id);
Expand Down Expand Up @@ -156,4 +173,15 @@ private ConstructorExpression<ContestResponse> contestProjection() {
);
}

private ConstructorExpression<ContestDeadlineResponse> contestDeadlineProjection() {
return Projections.constructor(
ContestDeadlineResponse.class,
contestEntity.id,
contestEntity.title,
userEntity.email,
contestEntity.submitStartDate,
contestEntity.submitEndDate
);
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.galendar.domain.contest.service.querydsl;

import com.galendar.domain.contest.dto.request.ContestRequest;
import com.galendar.domain.contest.dto.response.ContestDeadlineResponse;
import com.galendar.domain.contest.dto.response.ContestDetailResponse;
import com.galendar.domain.contest.dto.response.ContestResponse;

Expand All @@ -9,4 +10,5 @@
public interface ContestQueryService {
List<ContestResponse> list(ContestRequest request);
ContestDetailResponse get(Long id);
List<ContestDeadlineResponse> findContestsBySubmitEndDate(List dates);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.galendar.domain.contest.service.querydsl;

import com.galendar.domain.contest.dto.request.ContestRequest;
import com.galendar.domain.contest.dto.response.ContestDeadlineResponse;
import com.galendar.domain.contest.dto.response.ContestDetailResponse;
import com.galendar.domain.contest.dto.response.ContestResponse;
import com.galendar.domain.contest.exception.ContestNotFoundException;
Expand Down Expand Up @@ -30,4 +31,10 @@ public ContestDetailResponse get(Long id) {
return contestQueryRepository.findByIdWithBookmark(id, userSecurity.getUser().getId()).orElseThrow(() -> ContestNotFoundException.EXCEPTION);
}

@Override
public List<ContestDeadlineResponse> findContestsBySubmitEndDate(List dates) {
return contestQueryRepository.findContestsBySubmitEndDates(dates);
}


}
33 changes: 33 additions & 0 deletions src/main/java/com/galendar/global/config/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.galendar.global.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private int port;

@Bean
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(host, port);
}

@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
return redisTemplate;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.galendar.global.firebase.config;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
@ConfigurationProperties(prefix = "firebase")
public class FcmMessageProperties {
private Map<String, MessageTemplate> messages = new HashMap<>();

public Map<String, MessageTemplate> getMessages() {
return messages;
}

public void setMessages(Map<String, MessageTemplate> templates) {
this.messages = templates;
}

@Getter
@Setter
public static class MessageTemplate {
private String title;
private String body;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.galendar.global.firebase.config;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.messaging.FirebaseMessaging;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.io.InputStream;

@Configuration
public class FirebaseConfig {
@Value("${firebase.sdk.path}")
private String firebaseSdkPath;

private FirebaseApp firebaseApp;

@PostConstruct
public FirebaseApp initialize() throws IOException {
Resource resource = new ClassPathResource(firebaseSdkPath);
try (InputStream serviceAccount = resource.getInputStream()) {
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.build();
if (FirebaseApp.getApps().isEmpty()) {
firebaseApp = FirebaseApp.initializeApp(options);
}
}
return firebaseApp;
}

@Bean
public FirebaseMessaging firebaseMessaging() {
return FirebaseMessaging.getInstance(firebaseApp);
}

}
31 changes: 31 additions & 0 deletions src/main/java/com/galendar/global/firebase/dto/FcmMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.galendar.global.firebase.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Builder
@AllArgsConstructor
@Getter
public class FcmMessage {
private boolean validateOnly;
private Message message;

@Builder
@AllArgsConstructor
@Getter
public static class Message {
private Notification notification;
private String token;
}

@Builder
@AllArgsConstructor
@Getter
public static class Notification {
private String title;
private String body;
private String image;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.galendar.global.firebase.service;

import com.galendar.global.firebase.config.FcmMessageProperties;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Map;

@Service
@RequiredArgsConstructor
public class FcmMessageService {

private final FcmMessageProperties fcmMessageProperties;

public String getTitle(String type, Map<String, String> variables) {
String template = fcmMessageProperties.getMessages().get(type).getTitle();
return replaceVariables(template, variables);
}

public String getBody(String type, Map<String, String> variables) {
String template = fcmMessageProperties.getMessages().get(type).getBody();
return replaceVariables(template, variables);
}

private String replaceVariables(String template, Map<String, String> variables) {
for (Map.Entry<String, String> entry : variables.entrySet()) {
String placeholder = "${" + entry.getKey() + "}";
template = template.replace(placeholder, entry.getValue());
}
return template;
}

}
Loading

0 comments on commit 9d475b0

Please sign in to comment.