diff --git a/pom.xml b/pom.xml index 41a8dcf..0bd87c9 100644 --- a/pom.xml +++ b/pom.xml @@ -65,11 +65,7 @@ true - - javax.annotation - javax.annotation-api - 1.3.2 - + @@ -110,4 +106,33 @@ + + + coverage + + + + org.jacoco + jacoco-maven-plugin + 0.8.10 + + + + prepare-agent + + + + report + verify + + report + + + + + + + + + diff --git a/src/main/java/com/mosquito/project/config/CacheConfig.java b/src/main/java/com/mosquito/project/config/CacheConfig.java new file mode 100644 index 0000000..8017220 --- /dev/null +++ b/src/main/java/com/mosquito/project/config/CacheConfig.java @@ -0,0 +1,34 @@ +package com.mosquito.project.config; + +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.serializer.JdkSerializationRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +@Configuration +public class CacheConfig { + + @Bean + public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) { + RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofMinutes(5)) + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer( + new JdkSerializationRedisSerializer() + )); + + Map cacheConfigs = new HashMap<>(); + cacheConfigs.put("leaderboards", defaultConfig.entryTtl(Duration.ofMinutes(5))); + + return RedisCacheManager.builder(connectionFactory) + .cacheDefaults(defaultConfig) + .withInitialCacheConfigurations(cacheConfigs) + .build(); + } +} diff --git a/src/main/java/com/mosquito/project/service/ActivityService.java b/src/main/java/com/mosquito/project/service/ActivityService.java index 9a47286..b7a7539 100644 --- a/src/main/java/com/mosquito/project/service/ActivityService.java +++ b/src/main/java/com/mosquito/project/service/ActivityService.java @@ -42,6 +42,12 @@ public class ActivityService { private final Map apiKeys = new ConcurrentHashMap<>(); private final AtomicLong apiKeyIdCounter = new AtomicLong(); + private final DelayProvider delayProvider; + + public ActivityService(DelayProvider delayProvider) { + this.delayProvider = delayProvider; + } + public Activity createActivity(CreateActivityRequest request) { if (request.getEndTime().isBefore(request.getStartTime())) { throw new InvalidActivityDataException("活动结束时间不能早于开始时间。"); @@ -116,12 +122,15 @@ public class ActivityService { } private String hashApiKey(String apiKey, byte[] salt) { + // Strengthen hashing using PBKDF2WithHmacSHA256 try { - MessageDigest md = MessageDigest.getInstance("SHA-256"); - md.update(salt); - byte[] hashedApiKey = md.digest(apiKey.getBytes(StandardCharsets.UTF_8)); - return Base64.getEncoder().encodeToString(hashedApiKey); - } catch (NoSuchAlgorithmException e) { + javax.crypto.SecretKeyFactory skf = javax.crypto.SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256"); + javax.crypto.spec.PBEKeySpec spec = new javax.crypto.spec.PBEKeySpec( + apiKey.toCharArray(), salt, 185000, 256 + ); + byte[] derived = skf.generateSecret(spec).getEncoded(); + return Base64.getEncoder().encodeToString(derived); + } catch (Exception e) { throw new RuntimeException("无法创建API密钥哈希", e); } } @@ -211,8 +220,7 @@ public class ActivityService { // Simulate fetching and ranking data log.info("正在为活动ID {} 生成排行榜...", activityId); try { - // Simulate database query delay - Thread.sleep(2000); + delayProvider.delayMillis(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } diff --git a/src/main/java/com/mosquito/project/service/DelayProvider.java b/src/main/java/com/mosquito/project/service/DelayProvider.java new file mode 100644 index 0000000..07d3028 --- /dev/null +++ b/src/main/java/com/mosquito/project/service/DelayProvider.java @@ -0,0 +1,6 @@ +package com.mosquito.project.service; + +public interface DelayProvider { + void delayMillis(long millis) throws InterruptedException; +} + diff --git a/src/main/java/com/mosquito/project/service/NoOpDelayProvider.java b/src/main/java/com/mosquito/project/service/NoOpDelayProvider.java new file mode 100644 index 0000000..ad5f743 --- /dev/null +++ b/src/main/java/com/mosquito/project/service/NoOpDelayProvider.java @@ -0,0 +1,12 @@ +package com.mosquito.project.service; + +import org.springframework.stereotype.Component; + +@Component +public class NoOpDelayProvider implements DelayProvider { + @Override + public void delayMillis(long millis) { + // no-op by default to avoid blocking + } +} + diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml new file mode 100644 index 0000000..112b060 --- /dev/null +++ b/src/main/resources/application-dev.yml @@ -0,0 +1,10 @@ +spring: + profiles: + active: dev + redis: + host: localhost + port: ${spring.redis.port:6379} +logging: + level: + root: INFO + diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml new file mode 100644 index 0000000..7d868e7 --- /dev/null +++ b/src/main/resources/application-prod.yml @@ -0,0 +1,12 @@ +spring: + profiles: + active: prod + redis: + host: ${REDIS_HOST:localhost} + port: ${REDIS_PORT:6379} + flyway: + enabled: true +logging: + level: + root: INFO + diff --git a/src/main/resources/application-test.yml b/src/main/resources/application-test.yml new file mode 100644 index 0000000..2f91616 --- /dev/null +++ b/src/main/resources/application-test.yml @@ -0,0 +1,12 @@ +spring: + profiles: + active: test + redis: + host: localhost + port: ${spring.redis.port:6379} + flyway: + enabled: true +logging: + level: + root: WARN + diff --git a/src/test/java/com/mosquito/project/config/EmbeddedRedisConfiguration.java b/src/test/java/com/mosquito/project/config/EmbeddedRedisConfiguration.java index c2316f6..7e78455 100644 --- a/src/test/java/com/mosquito/project/config/EmbeddedRedisConfiguration.java +++ b/src/test/java/com/mosquito/project/config/EmbeddedRedisConfiguration.java @@ -3,8 +3,8 @@ package com.mosquito.project.config; import org.springframework.context.annotation.Configuration; import redis.embedded.RedisServer; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; import java.io.IOException; import java.net.ServerSocket;