Skip to content

Commit

Permalink
feat: 최근 접속 시간 조회 기능 구현 (#267) (#272)
Browse files Browse the repository at this point in the history
* feat: 최근 접속 시간 조회 기능 구현

* chore: GetMemberActivityResponse에서 변수명 수정

* chore: RedisConfig 통합 및 objectMapper 관련 변경

* chore: RecentActivityDto 생성 및 적용

* chore: dev, prod에 redis 컨테이너 추가

* chore: infra > development > redis.conf 추가

* fix: dev > docker-compose.yaml volume 정보

* fix: redis에도 network 정보 추가

* fix: dev > nginx.conf 수정

---------

Co-authored-by: mikekks <[email protected]>
  • Loading branch information
clean2001 and mikekks authored Dec 11, 2024
1 parent 53723f3 commit 082e600
Show file tree
Hide file tree
Showing 24 changed files with 396 additions and 19 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/aws-cicd-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
push:
branches:
- develop
- feat/LA-20_3
- feat/LA-27-2

env:
REGISTRY: "docker.io"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/aws-cicd-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on:
push:
branches:
- main
# - feat/LA-27-2

env:
REGISTRY: "docker.io"
Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,8 @@ credentials.json

layer-api/src/main/resources/tokens/StoredCredential
layer-batch/src/main/resources/application-secret.properties
layer-admin/src/main/resources/application-secret.properties
layer-admin/src/main/resources/application-secret.properties
layer-admin/src/main/resources/application.yml
layer-admin/src/main/resources/data.sql


5 changes: 5 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ project(":layer-api") {

implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'

//== jwt ==//
implementation 'io.jsonwebtoken:jjwt-api:0.12.5'
Expand Down Expand Up @@ -201,6 +202,7 @@ project(":layer-admin") {

dependencies {
implementation project(path: ':layer-domain')
implementation project(path: ':layer-common')

implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
Expand All @@ -211,6 +213,9 @@ project(":layer-admin") {

// Security
implementation 'org.springframework.boot:spring-boot-starter-security'

// redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.layer.common.exception;

public class AdminException extends BaseCustomException {
public AdminException(ExceptionType exceptionType) {
super(exceptionType);
}
}
68 changes: 68 additions & 0 deletions layer-admin/src/main/java/org/layer/config/RedisConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.layer.config;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
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.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.repository.configuration.EnableRedisRepositories;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;


@Configuration
@EnableRedisRepositories
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String host;

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

@Value("${spring.data.redis.password}")
private String password;

// prod에서 최근 서비스 이용 시점 기록 - 1번 데이터베이스
@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setDatabase(1); // 1번 데이터베이스

return new LettuceConnectionFactory(redisStandaloneConfiguration);

}

@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory());

PolymorphicTypeValidator typeValidator = BasicPolymorphicTypeValidator
.builder()
.allowIfSubType(Object.class)
.build();

ObjectMapper objectMapper = new ObjectMapper()
.findAndRegisterModules()
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
.activateDefaultTyping(typeValidator, ObjectMapper.DefaultTyping.NON_FINAL)
.registerModule(new JavaTimeModule());

template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer(objectMapper));

return template;
}

}
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package org.layer.member.controller.dto;

import java.time.LocalDateTime;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

import java.time.LocalDateTime;

@Schema(name = "GetMemberActivityResponse", description = "회원 활동 Dto")
public record GetMemberActivityResponse(
@NotNull
@Schema(description = "회원 이름", example = "홍길동")
String name,
@NotNull
@Schema(description = "최근 활동 날짜", example = "2024-11-30T16:21:47.031Z")
@Schema(description = "최근 활동 날짜, 최근 6개월 동안 접속 없을 시 null", example = "2024-11-30T16:21:47.031Z")
LocalDateTime recentActivityDate,
@NotNull
@Schema(description = "소속된 스페이스 수", example = "7")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package org.layer.member.service;

import java.util.List;
import lombok.RequiredArgsConstructor;

import org.layer.common.dto.RecentActivityDto;
import org.layer.domain.answer.repository.AdminAnswerRepository;
import org.layer.domain.member.entity.Member;
import org.layer.domain.member.repository.AdminMemberRepository;
Expand All @@ -11,10 +12,11 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;
import java.util.List;

@Service
@RequiredArgsConstructor
Expand All @@ -23,6 +25,7 @@ public class AdminMemberService {
private final AdminMemberRepository adminMemberRepository;
private final AdminMemberSpaceRelationRepository adminMemberSpaceRelationRepository;
private final AdminAnswerRepository adminAnswerRepository;
private final RedisTemplate<String, Object> redisTemplate;

@Value("${admin.password}")
private String password;
Expand All @@ -43,8 +46,12 @@ public GetMembersActivitiesResponse getMemberActivities(String password, int pag
Long spaceCount = adminMemberSpaceRelationRepository.countAllByMemberId(member.getId());
Long retrospectAnswerCount = adminAnswerRepository.countAllByMemberId(member.getId());

return new GetMemberActivityResponse(member.getName(), null, spaceCount, retrospectAnswerCount,
member.getCreatedAt(), member.getSocialType().name());
RecentActivityDto recentActivityDto = (RecentActivityDto)redisTemplate.opsForValue()
.get(Long.toString(member.getId()));

return new GetMemberActivityResponse(member.getName(),
recentActivityDto == null ? null : recentActivityDto.getRecentActivityDate(),
spaceCount, retrospectAnswerCount, member.getCreatedAt(), member.getSocialType().name());
}).toList();

return new GetMembersActivitiesResponse(responses);
Expand Down
5 changes: 5 additions & 0 deletions layer-admin/src/main/resources/application-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ spring:
show_sql: true
open-in-view: false
database: mysql
data:
redis:
host: ${DEV_REDIS_HOST}
port: ${DEV_REDIS_PORT}
password: ${DEV_REDIS_PASSWORD}

admin:
password: ${ADMIN_PASSWORD}
34 changes: 34 additions & 0 deletions layer-admin/src/main/resources/application-local.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
server:
port: 3000

spring:
config:
import: application-secret.properties
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:layer-local-db;DATABASE_TO_UPPER=FALSE;mode=mysql # H2 접속 정보 (전부 소문자로 지정)
username: sa
password:
h2:
console:
enabled: true
path: /h2-console
jpa:
database-platform: org.hibernate.dialect.H2Dialect
hibernate:
ddl-auto: create
properties:
hibernate:
format_sql: true
show_sql: true
open-in-view: false
defer-datasource-initialization: true

data:
redis:
host: localhost
port: 6379
password:

admin:
password: ${ADMIN_PASSWORD}
6 changes: 6 additions & 0 deletions layer-admin/src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,11 @@ spring:
open-in-view: false
database: mysql

data:
redis:
host: ${DEV_REDIS_HOST}
port: ${DEV_REDIS_PORT}
password: ${DEV_REDIS_PASSWORD}

admin:
password: ${ADMIN_PASSWORD}
8 changes: 8 additions & 0 deletions layer-api/infra/development/Dockerfile-redis
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Base image
FROM redis:latest

# Copy custom Redis configuration
COPY redis.conf /usr/local/etc/redis/redis.conf

# Command to run Redis with the custom configuration
CMD ["redis-server", "/usr/local/etc/redis/redis.conf"]
17 changes: 16 additions & 1 deletion layer-api/infra/development/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
services:
redis:
build:
context: .
dockerfile: Dockerfile-redis
ports:
- "6379:6379"
volumes:
- redis-data:/data # Persistent data storage
restart: always
networks:
- app-network

java-app:
image: docker.io/clean01/layer-server_layer-api:latest
container_name: layer-api
Expand Down Expand Up @@ -60,4 +72,7 @@ services:
- app-network

networks:
app-network:
app-network:

volumes:
redis-data:
2 changes: 1 addition & 1 deletion layer-api/infra/development/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ http {
# api.layerapp.io에 대한 서버 블록
server {
listen 80;
server_name api.layerapp.io;
server_name stgapi.layerapp.io;

location / {
proxy_pass http://layer-api;
Expand Down
20 changes: 20 additions & 0 deletions layer-api/infra/development/redis.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Save the DB snapshot every 180 seconds if at least 1 key changes
save 180 1

# Specify the filename for the RDB file
dbfilename dump.rdb

# Directory where the RDB snapshot will be saved
dir /data

# Enable RDB snapshot logging (optional for debugging)
loglevel notice

# Disable AOF (if you want only RDB persistence)
appendonly no

# Compression for RDB files (enabled by default)
rdbcompression yes

# Checksum verification for RDB files (enabled by default)
rdbchecksum yes
8 changes: 8 additions & 0 deletions layer-api/infra/production/Dockerfile-redis
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Base image
FROM redis:latest

# Copy custom Redis configuration
COPY redis.conf /usr/local/etc/redis/redis.conf

# Command to run Redis with the custom configuration
CMD ["redis-server", "/usr/local/etc/redis/redis.conf"]
17 changes: 16 additions & 1 deletion layer-api/infra/production/docker-compose-blue.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
services:
redis:
build:
context: .
dockerfile: Dockerfile-redis
ports:
- "6379:6379"
volumes:
- redis-data:/data # Persistent data storage
restart: always
networks:
- app-network

layer-api-blue:
image: docker.io/clean01/layer-server_layer-api:latest
container_name: layer-api-blue
Expand Down Expand Up @@ -48,4 +60,7 @@ services:
restart: always

networks:
app-network:
app-network:

volumes:
redis-data:
17 changes: 16 additions & 1 deletion layer-api/infra/production/docker-compose-green.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
services:
redis:
build:
context: .
dockerfile: Dockerfile-redis
ports:
- "6379:6379"
volumes:
- redis-data:/data # Persistent data storage
restart: always
networks:
- app-network

layer-api-green:
image: docker.io/clean01/layer-server_layer-api:latest
container_name: layer-api-green
Expand Down Expand Up @@ -48,4 +60,7 @@ services:
restart: always

networks:
app-network:
app-network:

volumes:
redis-data:
Loading

0 comments on commit 082e600

Please sign in to comment.