feat: add configurations and utility classes
This commit is contained in:
@@ -44,7 +44,6 @@ dependencies {
|
|||||||
implementation(libs.spring.boot.starter.validation)
|
implementation(libs.spring.boot.starter.validation)
|
||||||
implementation(libs.spring.boot.starter.redis)
|
implementation(libs.spring.boot.starter.redis)
|
||||||
implementation(libs.spring.boot.starter.cache)
|
implementation(libs.spring.boot.starter.cache)
|
||||||
implementation(libs.spring.boot.starter.security)
|
|
||||||
implementation(libs.spring.boot.starter.jpa)
|
implementation(libs.spring.boot.starter.jpa)
|
||||||
implementation(libs.mybatis.starter.core)
|
implementation(libs.mybatis.starter.core)
|
||||||
implementation(libs.flyway.core)
|
implementation(libs.flyway.core)
|
||||||
@@ -52,9 +51,7 @@ dependencies {
|
|||||||
implementation(libs.jackson.jsr310)
|
implementation(libs.jackson.jsr310)
|
||||||
testImplementation(libs.spring.boot.starter.test)
|
testImplementation(libs.spring.boot.starter.test)
|
||||||
testImplementation(libs.reactor.test)
|
testImplementation(libs.reactor.test)
|
||||||
testImplementation(libs.spring.security.test)
|
|
||||||
testImplementation(libs.mybatis.starter.test)
|
testImplementation(libs.mybatis.starter.test)
|
||||||
// runtimeOnly(libs.postgres.driver)
|
|
||||||
runtimeOnly(libs.mysql.driver)
|
runtimeOnly(libs.mysql.driver)
|
||||||
testRuntimeOnly(libs.h2.database)
|
testRuntimeOnly(libs.h2.database)
|
||||||
testRuntimeOnly(libs.junit.launcher)
|
testRuntimeOnly(libs.junit.launcher)
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.config;
|
||||||
|
|
||||||
|
import com.onixbyte.deltaforceguide.shared.JacksonRedisSerialiser;
|
||||||
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||||
|
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||||
|
import org.springframework.data.redis.serializer.RedisSerializationContext;
|
||||||
|
import org.springframework.data.redis.serializer.RedisSerializer;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration class for Redis-based caching components.
|
||||||
|
* <p>
|
||||||
|
* This configuration class provides beans for Redis cache management and template operations
|
||||||
|
* within the Helix application. It configures custom serialisation strategies using
|
||||||
|
* {@link GenericJackson2JsonRedisSerializer} for values and string serialisation for keys,
|
||||||
|
* ensuring optimal performance and compatibility with JSON-based data structures.
|
||||||
|
* <p>
|
||||||
|
* The configuration includes:
|
||||||
|
* <ul>
|
||||||
|
* <li>Custom {@link RedisCacheManager} with JSON serialisation support</li>
|
||||||
|
* <li>Configured {@link RedisTemplate} for direct Redis operations</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
* @see RedisCacheManager
|
||||||
|
* @see RedisTemplate
|
||||||
|
* @see GenericJackson2JsonRedisSerializer
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@EnableCaching
|
||||||
|
public class CacheConfig {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a custom Redis cache manager with JSON serialisation support.
|
||||||
|
* <p>
|
||||||
|
* This method configures a {@link RedisCacheManager} that uses string serialisation for cache
|
||||||
|
* keys and {@link GenericJackson2JsonRedisSerializer} for cache values. This setup ensures that
|
||||||
|
* complex objects can be stored and retrieved from Redis cache whilst maintaining readability
|
||||||
|
* and compatibility with JSON-based systems.
|
||||||
|
*
|
||||||
|
* @param connectionFactory the Redis connection factory used to establish connections
|
||||||
|
* @return a configured {@link RedisCacheManager} with custom serialisation settings
|
||||||
|
* @see RedisCacheManager
|
||||||
|
* @see GenericJackson2JsonRedisSerializer
|
||||||
|
* @see RedisSerializationContext
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public RedisCacheManager cacheManager(
|
||||||
|
RedisConnectionFactory connectionFactory
|
||||||
|
) {
|
||||||
|
var _keySerializer = RedisSerializer.string();
|
||||||
|
|
||||||
|
var cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
|
||||||
|
.serializeKeysWith(RedisSerializationContext.SerializationPair
|
||||||
|
.fromSerializer(_keySerializer))
|
||||||
|
.serializeValuesWith(RedisSerializationContext.SerializationPair
|
||||||
|
.fromSerializer(JacksonRedisSerialiser.INSTANCE))
|
||||||
|
.entryTtl(Duration.ofMinutes(90L));
|
||||||
|
|
||||||
|
return RedisCacheManager.RedisCacheManagerBuilder
|
||||||
|
.fromConnectionFactory(connectionFactory)
|
||||||
|
.cacheDefaults(cacheConfiguration)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Redis template for direct Redis operations with custom serialisation.
|
||||||
|
* <p>
|
||||||
|
* This method configures a {@link RedisTemplate} that uses string serialisation for keys
|
||||||
|
* and {@link GenericJackson2JsonRedisSerializer} for values. This template provides low-level
|
||||||
|
* access to Redis operations whilst ensuring consistent serialisation strategies across
|
||||||
|
* the application.
|
||||||
|
* <p>
|
||||||
|
* The template is fully configured and ready for use after bean creation.
|
||||||
|
*
|
||||||
|
* @param connectionFactory the Redis connection factory used to establish connections
|
||||||
|
* @return a fully configured {@link RedisTemplate} for Redis operations
|
||||||
|
* @see RedisTemplate
|
||||||
|
* @see GenericJackson2JsonRedisSerializer
|
||||||
|
* @see RedisSerializer
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public RedisTemplate<String, Object> redisTemplate(
|
||||||
|
RedisConnectionFactory connectionFactory
|
||||||
|
) {
|
||||||
|
var redisTemplate = new RedisTemplate<String, Object>();
|
||||||
|
redisTemplate.setConnectionFactory(connectionFactory);
|
||||||
|
redisTemplate.setKeySerializer(RedisSerializer.string());
|
||||||
|
redisTemplate.setValueSerializer(JacksonRedisSerialiser.INSTANCE);
|
||||||
|
redisTemplate.afterPropertiesSet();
|
||||||
|
return redisTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.config;
|
||||||
|
|
||||||
|
import com.onixbyte.deltaforceguide.properties.CorsProperties;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||||
|
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableConfigurationProperties({CorsProperties.class})
|
||||||
|
public class CorsConfig implements WebMvcConfigurer {
|
||||||
|
|
||||||
|
private final CorsProperties properties;
|
||||||
|
|
||||||
|
public CorsConfig(CorsProperties properties) {
|
||||||
|
this.properties = properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
|
registry.addMapping("/**")
|
||||||
|
.allowedOrigins(properties.allowedOrigins())
|
||||||
|
.allowedHeaders(properties.allowedHeaders())
|
||||||
|
.allowedMethods(toHttpMethodNames(properties.allowedMethods()))
|
||||||
|
.allowCredentials(properties.allowCredentials())
|
||||||
|
.maxAge(properties.maxAge().toSeconds())
|
||||||
|
.exposedHeaders(properties.exposedHeaders());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String[] toHttpMethodNames(HttpMethod[] methods) {
|
||||||
|
return Optional.ofNullable(methods)
|
||||||
|
.stream()
|
||||||
|
.flatMap(Stream::of)
|
||||||
|
.map(HttpMethod::name)
|
||||||
|
.toList()
|
||||||
|
.toArray(String[]::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.onixbyte.deltaforceguide.shared.JacksonModules;
|
||||||
|
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class JacksonConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public Jackson2ObjectMapperBuilderCustomizer jacksonCustomiser() {
|
||||||
|
return (builder) -> {
|
||||||
|
builder.modules(JacksonModules.DATE_TIME_MODULE);
|
||||||
|
builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.config;
|
||||||
|
|
||||||
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@MapperScan(basePackages = {"com.onixbyte.deltaforceguide.mapper"})
|
||||||
|
public class MyBatisConfig {
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.config;
|
||||||
|
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableJpaRepositories(basePackages = {"com.onixbyte.deltaforceguide.repository"})
|
||||||
|
public class SpringDataConfig {
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.properties;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.boot.context.properties.bind.DefaultValue;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
@ConfigurationProperties(prefix = "app.cors")
|
||||||
|
public record CorsProperties(
|
||||||
|
@DefaultValue({"Content-Type", "Authorization"})
|
||||||
|
String[] allowedHeaders,
|
||||||
|
@DefaultValue({"GET", "POST", "PUT", "PATCH", "DELETE"})
|
||||||
|
HttpMethod[] allowedMethods,
|
||||||
|
String[] allowedOrigins,
|
||||||
|
boolean allowCredentials,
|
||||||
|
boolean allowPrivateNetwork,
|
||||||
|
@DefaultValue("PT2H")
|
||||||
|
Duration maxAge,
|
||||||
|
@DefaultValue({"Content-Type", "Authorization"})
|
||||||
|
String[] exposedHeaders
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.shared;
|
||||||
|
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class providing predefined {@link DateTimeFormatter} instances for common date and
|
||||||
|
* time patterns. These formatters can be used to parse and format {@code java.time} objects
|
||||||
|
* consistently throughout an application.
|
||||||
|
*
|
||||||
|
* @author zihluwang
|
||||||
|
*/
|
||||||
|
public class DateTimeFormatters {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DateTimeFormatter} for formatting and parsing full date and time with seconds, using
|
||||||
|
* the pattern "yyyy-MM-dd HH:mm:ss".
|
||||||
|
* <p>
|
||||||
|
* Example: "{@code 2023-10-27 15:30:45}"
|
||||||
|
*/
|
||||||
|
public static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DateTimeFormatter} for formatting and parsing dates only, using the
|
||||||
|
* pattern "yyyy-MM-dd".
|
||||||
|
* <p>
|
||||||
|
* Example: "{@code 2023-10-27}"
|
||||||
|
*/
|
||||||
|
public static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DateTimeFormatter} for formatting and parsing times only, using the
|
||||||
|
* pattern "HH:mm:ss".
|
||||||
|
* <p>
|
||||||
|
* Example: "{@code 15:30:45}"
|
||||||
|
*/
|
||||||
|
public static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link DateTimeFormatter} for formatting and parsing year and month only, using the
|
||||||
|
* pattern "yyyy-MM".
|
||||||
|
* <p>
|
||||||
|
* Example: "{@code 2023-10}"
|
||||||
|
*/
|
||||||
|
public static final DateTimeFormatter YEAR_MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.shared;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
|
||||||
|
public class JacksonModules {
|
||||||
|
|
||||||
|
public static final SimpleModule DATE_TIME_MODULE = initialiseDateTimeModule();
|
||||||
|
|
||||||
|
private static SimpleModule initialiseDateTimeModule() {
|
||||||
|
var javaTimeModule = new JavaTimeModule();
|
||||||
|
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatters.DATE_TIME_FORMATTER));
|
||||||
|
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatters.DATE_TIME_FORMATTER));
|
||||||
|
|
||||||
|
javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatters.DATE_FORMATTER));
|
||||||
|
javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatters.DATE_FORMATTER));
|
||||||
|
|
||||||
|
javaTimeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatters.TIME_FORMATTER));
|
||||||
|
javaTimeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatters.TIME_FORMATTER));
|
||||||
|
|
||||||
|
return javaTimeModule;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package com.onixbyte.deltaforceguide.shared;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||||
|
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||||
|
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
|
||||||
|
|
||||||
|
public class JacksonRedisSerialiser {
|
||||||
|
|
||||||
|
public static final GenericJackson2JsonRedisSerializer INSTANCE = initialiseSerializer();
|
||||||
|
|
||||||
|
private static GenericJackson2JsonRedisSerializer initialiseSerializer() {
|
||||||
|
var serializer = new GenericJackson2JsonRedisSerializer();
|
||||||
|
|
||||||
|
serializer.configure((configurer) -> {
|
||||||
|
configurer.registerModule(new JavaTimeModule());
|
||||||
|
configurer.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||||
|
});
|
||||||
|
|
||||||
|
return serializer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user