uri() {
+ return uriBuilder -> uriBuilder.queryParam("key", properties.getSoup().getKey()).build();
+ }
+}
diff --git a/src/main/java/com/cyf/devToolBox/juhe/soup/model/JuheSoupResponse.java b/src/main/java/com/cyf/devToolBox/juhe/soup/model/JuheSoupResponse.java
new file mode 100644
index 0000000..56fc0c7
--- /dev/null
+++ b/src/main/java/com/cyf/devToolBox/juhe/soup/model/JuheSoupResponse.java
@@ -0,0 +1,45 @@
+package com.cyf.devToolBox.juhe.soup.model;
+
+import com.cyf.devToolBox.juhe.JuheResponse;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+/**
+ * 聚合数据「每日一句」接口响应模型。
+ *
+ *
+ * {
+ * "reason": "success",
+ * "result": { "text": "无论有多困难,都坚强地抬头挺胸..." },
+ * "error_code": 0
+ * }
+ *
+ *
+ * @author cyf
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class JuheSoupResponse implements JuheResponse {
+
+ /** 返回说明,如:success */
+ private String reason;
+
+ /** 返回结果集 */
+ private Result result;
+
+ /** 返回码,0 表示成功 */
+ @JsonProperty("error_code")
+ private Integer errorCode;
+
+ /**
+ * 每日一句数据主体。
+ */
+ @Data
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Result {
+
+ /** 每日一句内容 */
+ private String text;
+ }
+}
diff --git a/src/main/java/com/cyf/devToolBox/juhe/weather/JuheWeatherClient.java b/src/main/java/com/cyf/devToolBox/juhe/weather/JuheWeatherClient.java
new file mode 100644
index 0000000..1ce796d
--- /dev/null
+++ b/src/main/java/com/cyf/devToolBox/juhe/weather/JuheWeatherClient.java
@@ -0,0 +1,69 @@
+package com.cyf.devToolBox.juhe.weather;
+
+import com.cyf.devToolBox.juhe.AbstractJuheClient;
+import com.cyf.devToolBox.juhe.JuheProperties;
+import com.cyf.devToolBox.juhe.weather.model.JuheWeatherResponse;
+import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+import org.springframework.web.util.UriBuilder;
+
+import java.net.URI;
+import java.util.function.Function;
+
+/**
+ * 聚合数据「天气查询」客户端。
+ *
+ * @author cyf
+ */
+@Component
+public class JuheWeatherClient extends AbstractJuheClient {
+
+ public JuheWeatherClient(JuheProperties properties, ClientHttpRequestFactory juheRequestFactory) {
+ super(properties, juheRequestFactory, properties.getWeather().getBaseUrl());
+ }
+
+ /**
+ * 使用配置中的默认 {@code future-num} 查询指定城市的天气。
+ */
+ public JuheWeatherResponse.Result query(String city) {
+ return query(city, properties.getWeather().getFutureNum());
+ }
+
+ /**
+ * 查询指定城市的天气。
+ *
+ * @param city 城市名称(中文),如:北京、丰台
+ * @param futureNum 需要查询的未来天气天数(1~7),为 null 或不大于 0 时不查询
+ */
+ public JuheWeatherResponse.Result query(String city, Integer futureNum) {
+ validate(city);
+ return execute(uri(city, futureNum), JuheWeatherResponse.class).getResult();
+ }
+
+ /**
+ * 天气接口的原始 JSON,方便排查问题。
+ */
+ public String queryRaw(String city, Integer futureNum) {
+ validate(city);
+ return executeRaw(uri(city, futureNum));
+ }
+
+ private void validate(String city) {
+ if (!StringUtils.hasText(city)) {
+ throw new IllegalArgumentException("城市名称不能为空");
+ }
+ checkKeyConfigured(properties.getWeather().getKey(), "juhe.weather.key");
+ }
+
+ private Function uri(String city, Integer futureNum) {
+ return uriBuilder -> {
+ uriBuilder.queryParam("key", properties.getWeather().getKey());
+ uriBuilder.queryParam("city", city);
+ if (futureNum != null && futureNum > 0) {
+ uriBuilder.queryParam("future_num", futureNum);
+ }
+ return uriBuilder.build();
+ };
+ }
+}
diff --git a/src/main/java/com/cyf/devToolBox/juhe/weather/model/JuheWeatherResponse.java b/src/main/java/com/cyf/devToolBox/juhe/weather/model/JuheWeatherResponse.java
new file mode 100644
index 0000000..bbaee24
--- /dev/null
+++ b/src/main/java/com/cyf/devToolBox/juhe/weather/model/JuheWeatherResponse.java
@@ -0,0 +1,123 @@
+package com.cyf.devToolBox.juhe.weather.model;
+
+import com.cyf.devToolBox.juhe.JuheResponse;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 聚合数据天气接口的原始响应模型。
+ *
+ *
+ * {
+ * "reason": "查询成功",
+ * "result": {
+ * "city": "苏州",
+ * "realtime": { ... },
+ * "future": [ ... ]
+ * },
+ * "error_code": 0
+ * }
+ *
+ *
+ * @author cyf
+ */
+@Data
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class JuheWeatherResponse implements JuheResponse {
+
+ /** 返回说明,如:查询成功 */
+ private String reason;
+
+ /** 返回结果集 */
+ private Result result;
+
+ /** 返回码,0 表示成功 */
+ @JsonProperty("error_code")
+ private Integer errorCode;
+
+ /**
+ * 天气数据主体。
+ */
+ @Data
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Result {
+
+ /** 城市名 */
+ private String city;
+
+ /** 实时天气 */
+ private Realtime realtime;
+
+ /** 未来天气,未查询 future_num 时可能为空 */
+ private List future;
+ }
+
+ /**
+ * 实时天气。
+ */
+ @Data
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Realtime {
+
+ /** 温度(℃) */
+ private String temperature;
+
+ /** 湿度(%) */
+ private String humidity;
+
+ /** 天气现象,如:阴 */
+ private String info;
+
+ /** 天气标识,注意此处是字符串,如:"02" */
+ private String wid;
+
+ /** 风向,如:西北风 */
+ private String direct;
+
+ /** 风力,如:3级 */
+ private String power;
+
+ /** 空气质量指数 */
+ private String aqi;
+ }
+
+ /**
+ * 未来某天的天气预报。
+ */
+ @Data
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Future {
+
+ /** 日期,如:2019-02-22 */
+ private String date;
+
+ /** 温度区间,如:1/7℃ */
+ private String temperature;
+
+ /** 天气,如:小雨转多云 */
+ private String weather;
+
+ /** 天气标识,注意此处是对象(day / night) */
+ private Wid wid;
+
+ /** 风向,如:北风转西北风 */
+ private String direct;
+ }
+
+ /**
+ * 未来天气的 wid 结构,仅 future 中是对象,与 realtime.wid 的字符串类型不同。
+ */
+ @Data
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Wid {
+
+ /** 白天天气标识 */
+ private String day;
+
+ /** 夜间天气标识 */
+ private String night;
+ }
+}
diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml
new file mode 100644
index 0000000..117c19b
--- /dev/null
+++ b/src/main/resources/application.yml
@@ -0,0 +1,66 @@
+server:
+ port: 8077
+
+spring:
+ application:
+ name: dev-toolbox
+
+ # 默认使用 H2 内存库,本地无需任何外部依赖即可启动
+ datasource:
+ driver-class-name: org.h2.Driver
+ url: jdbc:h2:mem:dev_toolbox;MODE=MySQL;DB_CLOSE_DELAY=-1;DATABASE_TO_LOWER=TRUE
+ username: sa
+ password: ""
+
+ h2:
+ console:
+ enabled: true
+ path: /h2-console
+
+ jackson:
+ date-format: yyyy-MM-dd HH:mm:ss
+ time-zone: GMT+8
+
+mybatis-plus:
+ configuration:
+ map-underscore-to-camel-case: true
+ log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+ global-config:
+ banner: false
+ db-config:
+ id-type: auto
+
+# 聚合数据(每个接口使用各自的 key)
+# 天气文档:https://www.juhe.cn/docs/api/id/73
+# 每日一句文档:https://www.juhe.cn/docs/api/id/86
+juhe:
+ connect-timeout: 3s
+ read-timeout: 5s
+ # 天气查询
+ weather:
+ base-url: https://apis.juhe.cn/simpleWeather/query
+ # 建议改用环境变量 JUHE_WEATHER_KEY 覆盖,避免明文提交到仓库
+ key: ${JUHE_WEATHER_KEY:ca38eda68ea7242a3601e4e98afa8499}
+ # 查询未来天气天数(1~7),不填写则只返回实时天气
+ future-num: 5
+ # 每日一句
+ soup:
+ base-url: https://apis.juhe.cn/fapig/soup/query
+ # 每日一句接口的 key,请填入并建议用环境变量 JUHE_SOUP_KEY 覆盖
+ key: ${JUHE_SOUP_KEY:cf8695f97027c22b346a468697ba11db}
+
+---
+# 切换为 MySQL 时使用该 profile:mvn spring-boot:run -Dspring-boot.run.profiles=mysql
+spring:
+ config:
+ activate:
+ on-profile: mysql
+ datasource:
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ url: jdbc:mysql://localhost:3306/dev_toolbox?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
+ username: root
+ password: root
+
+logging:
+ level:
+ com.cyf.devToolBox: debug