fix(server): handle daily password fetch errors

- Wrap the upstream call so connection and HTTP failures surface
  as 502 Bad Gateway instead of unhandled exceptions
- Return 502 Bad Gateway when the upstream payload is empty
- Expire the cached password at midnight rather than after 24 hours
This commit is contained in:
siujamo committed 2026-09-10 16:35:07 +08:00
1 parent d8b3ad2dee
commit ee7b96532d
1 file changed
+31 -12
@@ -8,12 +8,16 @@ 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.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
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.Duration;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Objects; import java.util.Objects;
/** /**
@@ -60,25 +64,40 @@ public class DailyPasswordManager {
public DailyPasswordResponse getDailyPassword() { public DailyPasswordResponse getDailyPassword() {
var key = CACHE_KEY_PREFIX + LocalDate.now(); var key = CACHE_KEY_PREFIX + LocalDate.now();
// Attempt to fetch the cached response
var cached = redisTemplate.opsForValue().get(key); var cached = redisTemplate.opsForValue().get(key);
if (cached != null) { if (cached != null) {
return (DailyPasswordResponse) cached; return (DailyPasswordResponse) cached;
} }
var response = restClient.get() DailyPasswordResponse response;
.uri((uriBuilder) -> uriBuilder try {
.path("/sjzmm") // Fetch from the upstream remote service
.queryParam("ckey", "") response = restClient.get()
.queryParam("type", "json") .uri((uriBuilder) -> uriBuilder
.build()) .path("/sjzmm")
.retrieve() .queryParam("ckey", "")
.body(DailyPasswordResponse.class); .queryParam("type", "json")
.build())
if (Objects.isNull(response)) { .retrieve()
throw new InternalServerErrorException("暂无每日密码数据。"); .body(DailyPasswordResponse.class);
} catch (Exception e) {
// Catch network errors, timeouts, or 4xx/5xx responses from the upstream service
throw new ResponseStatusException(HttpStatus.BAD_GATEWAY, "Failed to connect to the upstream service.", e);
} }
redisTemplate.opsForValue().set(key, response, Duration.ofDays(1L)); if (Objects.isNull(response)) {
throw new ResponseStatusException(HttpStatus.BAD_GATEWAY, "No daily password data available.");
}
// Calculate the precise duration until midnight to optimise Redis memory usage
var now = LocalDateTime.now();
var midnight = now.toLocalDate().atStartOfDay().plusDays(1);
var durationUntilMidnight = Duration.between(now, midnight);
// Save to Redis with the calculated TTL
redisTemplate.opsForValue().set(key, response, durationUntilMidnight);
return response; return response;
} }
} }