Compare commits
5
Commits
a4218bea4c
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a17131b62e | ||
|
|
8700555ae6 | ||
|
|
ee7b96532d | ||
|
|
d8b3ad2dee | ||
|
|
a8aabaa5a1 |
No files matched your search
@@ -1,89 +0,0 @@
|
|||||||
name: Build and Deploy
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
env:
|
|
||||||
APP_NAME: ahsarah-guide-server
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build-and-release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: server
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Set up JDK 21 (Corretto)
|
|
||||||
uses: actions/setup-java@v4
|
|
||||||
with:
|
|
||||||
java-version: 21
|
|
||||||
distribution: corretto
|
|
||||||
cache: gradle
|
|
||||||
|
|
||||||
- name: Build with Gradle
|
|
||||||
run: >
|
|
||||||
./gradlew bootJar -x test
|
|
||||||
-PartefactVersion="${{ gitea.event.release.tag_name }}"
|
|
||||||
-PbuildChannel=stable
|
|
||||||
-Pvendor=${{ vars.VENDOR }}
|
|
||||||
|
|
||||||
- name: Resolve JAR file path
|
|
||||||
id: jar
|
|
||||||
run: |
|
|
||||||
JAR_PATH=$(find build/libs -name '*.jar' | head -1)
|
|
||||||
echo "file=$JAR_PATH" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Upload JAR to Gitea Release
|
|
||||||
run: |
|
|
||||||
TAG="${{ gitea.event.release.tag_name }}"
|
|
||||||
FILE="${{ steps.jar.outputs.file }}"
|
|
||||||
ASSET_NAME="${APP_NAME}-${TAG}.jar"
|
|
||||||
curl -X POST \
|
|
||||||
-H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \
|
|
||||||
-H "Content-Type: multipart/form-data" \
|
|
||||||
-F "attachment=@${FILE};filename=${ASSET_NAME}" \
|
|
||||||
"${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/releases/${{ gitea.event.release.id }}/assets?name=${ASSET_NAME}"
|
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Log in to Docker Hub
|
|
||||||
uses: docker/login-action@v3
|
|
||||||
with:
|
|
||||||
username: ${{ vars.DOCKER_HUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKER_HUB_TOKEN }}
|
|
||||||
|
|
||||||
- name: Generate image tags
|
|
||||||
id: meta
|
|
||||||
run: |
|
|
||||||
DOCKERHUB_USER="${{ vars.DOCKER_HUB_USERNAME }}"
|
|
||||||
echo "tag_version=${DOCKERHUB_USER}/${APP_NAME}:${{ gitea.event.release.tag_name }}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "tag_latest=${DOCKERHUB_USER}/${APP_NAME}:latest" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Build and push Docker image
|
|
||||||
uses: docker/build-push-action@v6
|
|
||||||
with:
|
|
||||||
context: server
|
|
||||||
file: server/Dockerfile.ci
|
|
||||||
build-args: |
|
|
||||||
JAR_FILE=${{ steps.jar.outputs.file }}
|
|
||||||
IMAGE_VERSION=${{ gitea.event.release.tag_name }}
|
|
||||||
IMAGE_VENDOR=${{ vars.VENDOR }}
|
|
||||||
BUILD_DATE=${{ gitea.event.release.published_at }}
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
${{ steps.meta.outputs.tag_version }}
|
|
||||||
${{ steps.meta.outputs.tag_latest }}
|
|
||||||
|
|
||||||
- name: Push README to Docker Hub
|
|
||||||
uses: christian-korneck/update-container-description-action@v1
|
|
||||||
env:
|
|
||||||
DOCKER_USER: ${{ vars.DOCKER_HUB_USERNAME }}
|
|
||||||
DOCKER_PASS: ${{ secrets.DOCKER_HUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
destination_container_repo: ${{ vars.DOCKER_HUB_USERNAME }}/${{ env.APP_NAME }}
|
|
||||||
provider: dockerhub
|
|
||||||
readme_file: server/README.md
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
name: Build and Deploy
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: Build release archive
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: web
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup Node
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 24
|
|
||||||
cache: pnpm
|
|
||||||
|
|
||||||
- name: Setup pnpm
|
|
||||||
uses: pnpm/action-setup@v4
|
|
||||||
with:
|
|
||||||
version: 11
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: pnpm install --frozen-lockfile
|
|
||||||
|
|
||||||
- name: Build release archive
|
|
||||||
run: pnpm build:tar
|
|
||||||
|
|
||||||
- name: Clean up previous build artifacts on server
|
|
||||||
uses: appleboy/ssh-action@v1.0.3
|
|
||||||
with:
|
|
||||||
host: ${{ vars.DEPLOY_HOST }}
|
|
||||||
username: ${{ vars.DEPLOY_USER }}
|
|
||||||
port: ${{ vars.DEPLOY_PORT }}
|
|
||||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
|
||||||
command: rm -rf /tmp/dist.tar.gz /tmp/dist.tar.gz/
|
|
||||||
|
|
||||||
- name: Upload artifact to server
|
|
||||||
uses: appleboy/scp-action@v0.1.7
|
|
||||||
with:
|
|
||||||
host: ${{ vars.DEPLOY_HOST }}
|
|
||||||
username: ${{ vars.DEPLOY_USER }}
|
|
||||||
port: ${{ vars.DEPLOY_PORT }}
|
|
||||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
|
||||||
source: "dist.tar.gz"
|
|
||||||
target: "/tmp/"
|
|
||||||
|
|
||||||
upload-release-asset:
|
|
||||||
name: Upload to Gitea Release
|
|
||||||
needs: build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Download artifact from server
|
|
||||||
env:
|
|
||||||
DEPLOY_HOST: ${{ vars.DEPLOY_HOST }}
|
|
||||||
DEPLOY_USER: ${{ vars.DEPLOY_USER }}
|
|
||||||
DEPLOY_PORT: ${{ vars.DEPLOY_PORT }}
|
|
||||||
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
SSH_DIR="${RUNNER_TEMP}/ssh"
|
|
||||||
mkdir -p "$SSH_DIR"
|
|
||||||
chmod 700 "$SSH_DIR"
|
|
||||||
printf '%s\n' "$DEPLOY_SSH_KEY" > "$SSH_DIR/key"
|
|
||||||
chmod 600 "$SSH_DIR/key"
|
|
||||||
scp -i "$SSH_DIR/key" \
|
|
||||||
-P "$DEPLOY_PORT" \
|
|
||||||
-o StrictHostKeyChecking=accept-new \
|
|
||||||
-o UserKnownHostsFile="$SSH_DIR/known_hosts" \
|
|
||||||
"${DEPLOY_USER}@${DEPLOY_HOST}:/tmp/dist.tar.gz" \
|
|
||||||
./dist.tar.gz
|
|
||||||
rm -rf "$SSH_DIR"
|
|
||||||
|
|
||||||
- name: Upload release asset via Gitea API
|
|
||||||
run: |
|
|
||||||
set -e
|
|
||||||
RELEASE_ID=$(jq -r '.release.id' "$GITEA_EVENT_PATH")
|
|
||||||
URL="${GITEA_SERVER_URL}/api/v1/repos/${GITEA_REPOSITORY}/releases/${RELEASE_ID}/assets?name=dist.tar.gz"
|
|
||||||
curl -fsSL \
|
|
||||||
-X POST \
|
|
||||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
|
||||||
-H "Content-Type: application/gzip" \
|
|
||||||
--data-binary "@dist.tar.gz" \
|
|
||||||
"${URL}"
|
|
||||||
|
|
||||||
deploy-to-server:
|
|
||||||
name: Deploy to onixbyte.cn
|
|
||||||
needs: [build, upload-release-asset]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Extract archive and deploy
|
|
||||||
uses: appleboy/ssh-action@v1.0.3
|
|
||||||
with:
|
|
||||||
host: ${{ vars.DEPLOY_HOST }}
|
|
||||||
username: ${{ vars.DEPLOY_USER }}
|
|
||||||
port: ${{ vars.DEPLOY_PORT }}
|
|
||||||
key: ${{ secrets.DEPLOY_SSH_KEY }}
|
|
||||||
script_stop: true
|
|
||||||
script: |
|
|
||||||
set -e
|
|
||||||
DEPLOY_PATH="${{ vars.DEPLOY_PATH }}"
|
|
||||||
mkdir -p "$DEPLOY_PATH"
|
|
||||||
rm -rf "$DEPLOY_PATH"/*
|
|
||||||
tar -xzf /tmp/dist.tar.gz -C "$DEPLOY_PATH" --strip-components=1
|
|
||||||
chown -R caddy:caddy "$DEPLOY_PATH"
|
|
||||||
rm -f /tmp/dist.tar.gz
|
|
||||||
@@ -29,6 +29,47 @@ spring:
|
|||||||
# Uncomment if the server requires a password.
|
# Uncomment if the server requires a password.
|
||||||
#
|
#
|
||||||
# password: qwerty
|
# password: qwerty
|
||||||
|
mail:
|
||||||
|
# SMTP endpoint.
|
||||||
|
host: smtp.example.com
|
||||||
|
|
||||||
|
# SMTP port.
|
||||||
|
#
|
||||||
|
# 465 uses implicit TLS (SMTPS). If outbound 465 is blocked on the network, switch to 587 and
|
||||||
|
# replace the `ssl` property below with `starttls.enable` instead.
|
||||||
|
port: 465
|
||||||
|
|
||||||
|
# Sending address, which is also used as the `From` address of every dispatched message.
|
||||||
|
#
|
||||||
|
# It MUST belong to a verified sending domain in the Tencent Cloud Email console.
|
||||||
|
username: app@example.com
|
||||||
|
|
||||||
|
# SMTP password of the sending address above, as configured in the Tencent Cloud Email console.
|
||||||
|
password: 123456
|
||||||
|
|
||||||
|
# Encoding applied to the message headers and body.
|
||||||
|
default-encoding: UTF-8
|
||||||
|
|
||||||
|
properties:
|
||||||
|
mail:
|
||||||
|
smtp:
|
||||||
|
# Whether the SMTP server requires authentication.
|
||||||
|
auth: true
|
||||||
|
|
||||||
|
# Implicit TLS, required by port 465.
|
||||||
|
#
|
||||||
|
# When using port 587 + STARTTLS instead, drop this property, then enable STARTTLS in
|
||||||
|
# its place:
|
||||||
|
#
|
||||||
|
# starttls:
|
||||||
|
# enable: true
|
||||||
|
ssl:
|
||||||
|
enable: true
|
||||||
|
|
||||||
|
# Fail fast instead of hanging when the SMTP server is unreachable.
|
||||||
|
connectiontimeout: 10000
|
||||||
|
timeout: 10000
|
||||||
|
writetimeout: 10000
|
||||||
|
|
||||||
logging:
|
logging:
|
||||||
pattern:
|
pattern:
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package com.onixbyte.ahsarahguide.config;
|
package com.onixbyte.ahsarahguide.config;
|
||||||
|
|
||||||
import com.onixbyte.ahsarahguide.shared.JacksonRedisSerialiser;
|
import com.onixbyte.ahsarahguide.shared.JacksonRedisSerialiser;
|
||||||
|
import org.springframework.cache.CacheManager;
|
||||||
import org.springframework.cache.annotation.EnableCaching;
|
import org.springframework.cache.annotation.EnableCaching;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||||
@@ -39,21 +41,24 @@ import java.time.Duration;
|
|||||||
public class CacheConfig {
|
public class CacheConfig {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a custom Redis cache manager with JSON serialisation support.
|
* Creates the application's primary cache manager, backed by Redis.
|
||||||
* <p>
|
* <p>
|
||||||
* This method configures a {@link RedisCacheManager} that uses string serialisation for cache
|
* This manager is marked {@link Primary}, so it is the one selected for any
|
||||||
* keys and {@link GenericJackson2JsonRedisSerializer} for cache values. This setup ensures that
|
* {@code @Cacheable} or {@code @CacheEvict} annotation that does not name a manager
|
||||||
* complex objects can be stored and retrieved from Redis cache whilst maintaining readability
|
* explicitly, as well as for any {@link CacheManager} that is injected by type.
|
||||||
* and compatibility with JSON-based systems.
|
* <p>
|
||||||
|
* Cache entries are keyed using string serialisation and values are stored as JSON via
|
||||||
|
* {@link JacksonRedisSerialiser}. Every entry expires after two hours, making this manager
|
||||||
|
* suitable for data that is expensive to compute but not required to survive for long.
|
||||||
*
|
*
|
||||||
* @param connectionFactory the Redis connection factory used to establish connections
|
* @param connectionFactory the Redis connection factory used to establish connections
|
||||||
* @return a configured {@link RedisCacheManager} with custom serialisation settings
|
* @return a fully configured {@link CacheManager} with a two-hour default entry TTL
|
||||||
|
* @see #longTermCacheManager(RedisConnectionFactory)
|
||||||
* @see RedisCacheManager
|
* @see RedisCacheManager
|
||||||
* @see GenericJackson2JsonRedisSerializer
|
|
||||||
* @see RedisSerializationContext
|
|
||||||
*/
|
*/
|
||||||
|
@Primary
|
||||||
@Bean
|
@Bean
|
||||||
public RedisCacheManager cacheManager(
|
public CacheManager defaultCacheManager(
|
||||||
RedisConnectionFactory connectionFactory
|
RedisConnectionFactory connectionFactory
|
||||||
) {
|
) {
|
||||||
var _keySerializer = RedisSerializer.string();
|
var _keySerializer = RedisSerializer.string();
|
||||||
@@ -63,7 +68,40 @@ public class CacheConfig {
|
|||||||
.fromSerializer(_keySerializer))
|
.fromSerializer(_keySerializer))
|
||||||
.serializeValuesWith(RedisSerializationContext.SerializationPair
|
.serializeValuesWith(RedisSerializationContext.SerializationPair
|
||||||
.fromSerializer(JacksonRedisSerialiser.INSTANCE))
|
.fromSerializer(JacksonRedisSerialiser.INSTANCE))
|
||||||
.entryTtl(Duration.ofMinutes(90L));
|
.entryTtl(Duration.ofHours(2L));
|
||||||
|
|
||||||
|
return RedisCacheManager.RedisCacheManagerBuilder
|
||||||
|
.fromConnectionFactory(connectionFactory)
|
||||||
|
.cacheDefaults(cacheConfiguration)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a secondary cache manager for data that must be retained for longer.
|
||||||
|
* <p>
|
||||||
|
* This manager is not {@link Primary}; it must be requested by name wherever it is needed,
|
||||||
|
* as in {@code @Cacheable(cacheManager = "longTermCacheManager")}.
|
||||||
|
* <p>
|
||||||
|
* Serialisation is configured identically to {@link #defaultCacheManager}, but entries
|
||||||
|
* expire after one day rather than two hours.
|
||||||
|
*
|
||||||
|
* @param connectionFactory the Redis connection factory used to establish connections
|
||||||
|
* @return a fully configured {@link CacheManager} with a one-day default entry TTL
|
||||||
|
* @see #defaultCacheManager(RedisConnectionFactory)
|
||||||
|
* @see RedisCacheManager
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public CacheManager longTermCacheManager(
|
||||||
|
RedisConnectionFactory connectionFactory
|
||||||
|
) {
|
||||||
|
var _keySerializer = RedisSerializer.string();
|
||||||
|
|
||||||
|
var cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
|
||||||
|
.serializeKeysWith(RedisSerializationContext.SerializationPair
|
||||||
|
.fromSerializer(_keySerializer))
|
||||||
|
.serializeValuesWith(RedisSerializationContext.SerializationPair
|
||||||
|
.fromSerializer(JacksonRedisSerialiser.INSTANCE))
|
||||||
|
.entryTtl(Duration.ofDays(1L));
|
||||||
|
|
||||||
return RedisCacheManager.RedisCacheManagerBuilder
|
return RedisCacheManager.RedisCacheManagerBuilder
|
||||||
.fromConnectionFactory(connectionFactory)
|
.fromConnectionFactory(connectionFactory)
|
||||||
|
|||||||
@@ -4,16 +4,15 @@ import com.fasterxml.jackson.databind.DeserializationFeature;
|
|||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
|
||||||
import com.onixbyte.ahsarahguide.domain.dto.DailyPasswordResponse;
|
import com.onixbyte.ahsarahguide.domain.dto.DailyPasswordResponse;
|
||||||
import com.onixbyte.ahsarahguide.exeption.InternalServerErrorException;
|
|
||||||
import com.onixbyte.ahsarahguide.shared.JacksonModules;
|
import com.onixbyte.ahsarahguide.shared.JacksonModules;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.cache.annotation.Cacheable;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.client.RestClient;
|
import org.springframework.web.client.RestClient;
|
||||||
|
import org.springframework.web.server.ResponseStatusException;
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
import java.time.LocalDate;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -24,15 +23,13 @@ import java.util.Objects;
|
|||||||
@Component
|
@Component
|
||||||
public class DailyPasswordManager {
|
public class DailyPasswordManager {
|
||||||
|
|
||||||
private static final String CACHE_KEY_PREFIX = "daily-password:";
|
private static final String CACHE_NAME = "daily-password";
|
||||||
|
|
||||||
private final RestClient restClient;
|
private final RestClient restClient;
|
||||||
private final RedisTemplate<String, Object> redisTemplate;
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public DailyPasswordManager(
|
public DailyPasswordManager(
|
||||||
RestClient.Builder restClientBuilder,
|
RestClient.Builder restClientBuilder
|
||||||
RedisTemplate<String, Object> redisTemplate
|
|
||||||
) {
|
) {
|
||||||
var snakeCaseMapper = new ObjectMapper();
|
var snakeCaseMapper = new ObjectMapper();
|
||||||
snakeCaseMapper.setPropertyNamingStrategy(
|
snakeCaseMapper.setPropertyNamingStrategy(
|
||||||
@@ -50,35 +47,39 @@ public class DailyPasswordManager {
|
|||||||
new MappingJackson2HttpMessageConverter(snakeCaseMapper));
|
new MappingJackson2HttpMessageConverter(snakeCaseMapper));
|
||||||
})
|
})
|
||||||
.build();
|
.build();
|
||||||
this.redisTemplate = redisTemplate;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the daily password from cache or generates a new one.
|
* Retrieves the daily password from cache or generates a new one.
|
||||||
* @return the daily password response
|
* @return the daily password response
|
||||||
*/
|
*/
|
||||||
|
@Cacheable(
|
||||||
|
cacheNames = CACHE_NAME,
|
||||||
|
key = "T(java.time.LocalDate).now().toString()",
|
||||||
|
sync = true,
|
||||||
|
unless = "#result == null",
|
||||||
|
cacheManager = "longTermCacheManager"
|
||||||
|
)
|
||||||
public DailyPasswordResponse getDailyPassword() {
|
public DailyPasswordResponse getDailyPassword() {
|
||||||
var key = CACHE_KEY_PREFIX + LocalDate.now();
|
DailyPasswordResponse response;
|
||||||
|
try {
|
||||||
var cached = redisTemplate.opsForValue().get(key);
|
response = restClient.get()
|
||||||
if (cached != null) {
|
.uri((uriBuilder) -> uriBuilder
|
||||||
return (DailyPasswordResponse) cached;
|
.path("/sjzmm")
|
||||||
|
.queryParam("ckey", "")
|
||||||
|
.queryParam("type", "json")
|
||||||
|
.build())
|
||||||
|
.retrieve()
|
||||||
|
.body(DailyPasswordResponse.class);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Catch network errors, timeouts, or 4xx/5xx responses from the upstream service
|
||||||
|
throw new ResponseStatusException(HttpStatus.BAD_GATEWAY, "无法获取每日密码数据。", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = restClient.get()
|
|
||||||
.uri((uriBuilder) -> uriBuilder
|
|
||||||
.path("/sjzmm")
|
|
||||||
.queryParam("ckey", "")
|
|
||||||
.queryParam("type", "json")
|
|
||||||
.build())
|
|
||||||
.retrieve()
|
|
||||||
.body(DailyPasswordResponse.class);
|
|
||||||
|
|
||||||
if (Objects.isNull(response)) {
|
if (Objects.isNull(response)) {
|
||||||
throw new InternalServerErrorException("暂无每日密码数据。");
|
throw new ResponseStatusException(HttpStatus.BAD_GATEWAY, "无法获取每日密码数据。");
|
||||||
}
|
}
|
||||||
|
|
||||||
redisTemplate.opsForValue().set(key, response, Duration.ofDays(1L));
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user