接口規格
- 提供方:聚合數據
- 接口地址:
https://web.juhe.cn:8080/constellation/getAll
- 請求方式:GET/POST
- 參數:
key(AppKey)、consName(星座名)、type(today/tomorrow/week/month/year)
- 返回:JSON,含
error_code (0 成功)、result 對象
完整工具類(原生 HttpURLConnection,零依賴)
public class JuheConstellationClient {
private static final String API_URL = "https://web.juhe.cn:8080/constellation/getAll";
private static final String APP_KEY = "YOUR_APP_KEY_HERE"; // 替換為你的 Key
private static final int CONNECT_TIMEOUT = 5000;
private static final int READ_TIMEOUT = 10000;
private static final String USER_AGENT = "Mozilla/5.0 (Java HttpClient)";
public record ConstellationResult(
String name, String datetime, String overall, String love,
String career, String money, String health, String luckyColor,
String luckyNumber, String QFriend, String summary
) {}
public ConstellationResult getFortune(String consName, String type)
throws IOException, InterruptedException {
// 構建查詢參數
Map params = new LinkedHashMap<>();
params.put("key", APP_KEY);
params.put("consName", consName);
params.put("type", type);
String queryString = params.entrySet().stream()
.map(e -> e.getKey() + "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
String url = API_URL + "?" + queryString;
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(CONNECT_TIMEOUT);
conn.setReadTimeout(READ_TIMEOUT);
conn.setRequestProperty("User-Agent", USER_AGENT);
conn.setInstanceFollowRedirects(false);
int responseCode = conn.getResponseCode();
if (responseCode != 200) {
throw new IOException("HTTP " + responseCode + ": " + conn.getResponseMessage());
}
try (InputStream is = conn.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
String json = br.lines().collect(Collectors.joining());
return parseResponse(json);
}
} finally {
if (conn != null) conn.disconnect();
}
}
private ConstellationResult parseResponse(String json) {
// 簡易 JSON 解析,生產建議用 Jackson/Gson
// 假設結構:{"error_code":0,"result":{"name":"白羊座",...}}
int errIdx = json.indexOf("\"error_code\":");
if (errIdx >= 0) {
int errVal = Integer.parseInt(json.substring(errIdx + 13).split("[,} ]")[0]);
if (errVal != 0) {
int reasonIdx = json.indexOf("\"reason\":\"");
String reason = reasonIdx >= 0 ?
json.substring(reasonIdx + 10).split("\"")[0] : "Unknown error";
throw new IllegalStateException("API Error " + errVal + ": " + reason);
}
}
// 提取 result 物件字段(此處略,實際用 JSON 庫)
// return new ConstellationResult(...);
return null; // 佔位
}
}
現代化寫法:Java 11+ HttpClient(推薦)
public class ModernJuheClient {
private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(5))
.build();
private static final String API_URL = "https://web.juhe.cn:8080/constellation/getAll";
private static final String APP_KEY = "YOUR_APP_KEY";
public CompletableFuture getFortuneAsync(String consName, String type) {
String query = "key=" + APP_KEY + "&consName=" + URLEncoder.encode(consName, UTF_8)
+ "&type=" + type;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(API_URL + "?" + query))
.timeout(Duration.ofSeconds(10))
.header("User-Agent", "Java 11 HttpClient")
.GET()
.build();
return HTTP_CLIENT.sendAsync(request, BodyHandlers.ofString(StandardCharsets.UTF_8))
.thenApply(HttpResponse::body)
.thenApply(this::parseJson); // 用 Jackson ObjectMapper 解析
}
}
Spring Boot 整合:RestTemplate / WebClient
@Configuration
public class JuheConfig {
@Value("${juhe.app-key}")
private String appKey;
@Bean
public RestTemplate restTemplate() {
return new RestTemplateBuilder()
.setConnectTimeout(Duration.ofSeconds(5))
.setReadTimeout(Duration.ofSeconds(10))
.build();
}
@Bean
public WebClient webClient() {
return WebClient.builder()
.baseUrl("https://web.juhe.cn:8080")
.defaultHeader(HttpHeaders.USER_AGENT, "Spring Boot App")
.build();
}
}
@Service
public class FortuneService {
private final WebClient webClient;
private final String appKey;
public FortuneService(WebClient webClient, @Value("${juhe.app-key}") String appKey) {
this.webClient = webClient; this.appKey = appKey;
}
public Mono getFortune(String consName, String type) {
return webClient.get()
.uri(uriBuilder -> uriBuilder.path("/constellation/getAll")
.queryParam("key", appKey)
.queryParam("consName", consName)
.queryParam("type", type)
.build())
.retrieve()
.bodyToMono(new ParameterizedTypeReference>() {})
.map(ApiResponse::getResult);
}
}
錯誤處理與重試策略
- 超時設置:連接 3-5s,讀取 10-15s,防止卡死線程。
- 重試機制:
Resilience4j 或 Spring Retry 配置指數退避重試(針對 5xx、網絡異常)。
- 熔斷器:連續失敗達閾值快速失效,避免雪崩。
- 緩存:運勢數據日級緩存(Redis/Caffeine),減少外部調用。
- 密鑰管理:AppKey 放配置中心/環境變量,絕不硬編碼。
測試驗證
@Test
void testGetFortune() throws Exception {
JuheConstellationClient client = new JuheConstellationClient();
ConstellationResult result = client.getFortune("白羊座", "today");
assertNotNull(result);
assertEquals("白羊座", result.name());
System.out.println("今日綜合: " + result.overall());
}