From acdf092de317deed172596b5c5ff8bac705ae869 Mon Sep 17 00:00:00 2001 From: bamanker <27054792@qq.com> Date: Wed, 14 Jan 2026 00:06:59 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=BA=86=E9=83=A8=E5=88=86?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- README.md | 201 +++++++++- pom.xml | 10 +- .../dailylove/DailyLoveApplication.java | 20 +- .../dailylove/config/ClassReflectConfig.java | 7 +- .../dailylove/config/DailyLoveConfigure.java | 261 ++++--------- .../dailylove/config/DailyLoveProperties.java | 128 ++++++ .../bamanker/dailylove/config/PushTask.java | 12 +- .../dailylove/config/WebClientConfig.java | 7 +- .../controller/PushDailyController.java | 368 +++++++----------- .../bamanker/dailylove/domain/DataItem.java | 2 + .../dailylove/domain/ErrorResponse.java | 8 + .../bamanker/dailylove/domain/ResultVo.java | 8 +- .../dailylove/domain/TianXinReqParam.java | 6 + .../bamanker/dailylove/domain/Weather.java | 5 + .../dailylove/domain/WechatTokenParam.java | 5 + .../exception/GlobalExceptionHandler.java | 57 ++- .../dailylove/exception/PushException.java | 16 + .../dailylove/service/DataRemoteService.java | 5 + .../service/WechatRequestService.java | 5 + .../bamanker/dailylove/utils/DataUtils.java | 52 +-- src/main/resources/application.yml | 90 +++-- 22 files changed, 743 insertions(+), 533 deletions(-) create mode 100644 src/main/java/com/bamanker/dailylove/config/DailyLoveProperties.java create mode 100644 src/main/java/com/bamanker/dailylove/exception/PushException.java diff --git a/.gitignore b/.gitignore index d74ac63..79ac447 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,5 @@ build/ !**/src/test/**/build/ ### VS Code ### -.vscode/ \ No newline at end of file +.vscode/ +/mvn.log diff --git a/README.md b/README.md index b927a86..0ade51e 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,194 @@ # dailyLove -#### 介绍 -公众号每日问候推送 +## 公众号每日问候推送系统 -#### 软件架构 -软件架构说明 +一个基于 Spring Boot 4 和响应式编程的微信公众号每日问候推送系统,可定时推送天气、节日、纪念日等个性化信息。 + +## 功能特性 + +- **定时推送**:每日早上7:30推送早安问候,晚上22:00推送晚安问候 +- **天气信息**:实时获取并推送当地天气情况 +- **纪念日提醒**:自动计算恋爱纪念日、结婚纪念日及生日倒计时 +- **节日祝福**:支持农历节日和节气提醒 +- **个性化内容**:彩虹话、每日一言、贴心小提示等 +- **响应式架构**:使用 WebFlux 和 WebClient 实现高性能异步处理 +- **配置灵活**:支持外部配置和环境变量 + +## 技术栈 + +- **后端框架**:Spring Boot 4.x +- **响应式编程**:Spring WebFlux + WebClient +- **JSON处理**:Jackson 3 +- **工具库**:Hutool +- **构建工具**:Maven +- **JDK版本**:JDK 25 +- **原生镜像**:GraalVM Native Image + +## 快速开始 + +### 环境要求 + +- JDK 25+ +- Maven 3.6+ +- 微信公众平台账号 +- 天行数据API密钥 + +### 配置说明 + +在 `application.yml` 中配置以下参数: + +```yaml +# 微信相关配置 +wechat: + app-id: "your-app-id" # 微信公众号AppID + app-secret: "your-app-secret" # 微信公众号AppSecret + open-id: "user-open-id" # 接收推送的用户OpenID + template-id-morning: "template-id-for-morning" # 早安模板消息ID + template-id-night: "template-id-for-night" # 晚安模板消息ID + +# 天行数据API配置 +daily-love: + data: + tianxin-key: "your-tianxin-api-key" # 天行数据API密钥 + city-id: "101270106" # 城市ID + girl-birthday: "1995-06-28" # 女方生日 + boy-birthday: "1995-03-30" # 男方生日 + cat-birthday: "2022-10-23" # 宠物生日 + love-day: "2022-07-16" # 恋爱纪念日 + wedding-day: "2025-10-08" # 结婚纪念日 +``` + +### 启动应用 + +```bash +# 编译项目 +mvn clean package + +# 运行应用 +java -jar target/dailylove.jar + +# 或使用Maven运行 +mvn spring-boot:run +``` + +## 项目结构 + +``` +src/ +├── main/ +│ ├── java/com/bamanker/dailylove/ +│ │ ├── config/ # 配置类 +│ │ ├── controller/ # 控制器 +│ │ ├── domain/ # 数据模型 +│ │ ├── service/ # 业务服务 +│ │ ├── utils/ # 工具类 +│ │ └── DailyLoveApplication.java +│ └── resources/ +│ └── application.yml # 应用配置 +└── test/ +``` + +## 核心功能模块 + +### 1. 推送控制器 + +- [PushDailyController](file:///D:/myWork/dailyLove/src/main/java/com/bamanker/dailylove/controller/PushDailyController.java):处理早安/晚安推送逻辑 +- 支持 `/pushMorning` 和 `/pushNight` 接口手动触发推送 + +### 2. 定时任务 + +- [PushTask](file:///D:/myWork/dailyLove/src/main/java/com/bamanker/dailylove/config/PushTask.java):配置定时推送任务 +- Cron表达式:`0 30 7 * * ?` (早上7:30) 和 `0 0 22 * * ?` (晚上22:00) + +### 3. 外部服务调用 + +- 使用 WebClient 调用微信API获取访问令牌 +- 调用天行数据API获取天气、彩虹话、每日一言等信息 + +## 配置项说明 -#### 安装教程 +| 配置项 | 描述 | 示例 | +| --------------------------- | -------------------- | ----------- | +| wechat.app-id | 微信公众号AppID | wx123456789 | +| wechat.app-secret | 微信公众号AppSecret | abc123... | +| wechat.open-id | 接收消息的用户OpenID | oABC123... | +| daily-love.data.tianxin-key | 天行数据API密钥 | 123abc... | +| daily-love.data.city-id | 城市ID | 101270106 | -1. xxxx -2. xxxx -3. xxxx +## 自定义推送内容 -#### 使用说明 +系统会根据以下情况进行智能提醒: -1. xxxx -2. xxxx -3. xxxx +- **恋爱纪念日**:显示恋爱天数及特殊纪念日提醒 +- **生日提醒**:推送生日倒计时 +- **节日祝福**:农历节日、节气等 +- **天气预报**:当日/次日天气情况 +- **个性化内容**:彩虹话、每日一言等 -#### 参与贡献 +## 部署方式 -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request +### Docker部署 + +```bash +# 构建Docker镜像 +docker build -t dailylove . + +# 运行容器 +docker run -d -p 13145:13145 --name dailylove dailylove +``` + +### 原生镜像部署 + +项目支持GraalVM原生镜像构建,启动速度更快: + +```bash +# 构建原生镜像 +./mvnw native:compile -Pnative + +# 运行原生镜像 +./target/dailylove +``` + +## 更新日志 + +### **v3.1.1-native** + +- 优化了响应式编程实现,避免阻塞操作 + · 移除了 PushDailyController 中的 .block() 阻塞操作 + · 重构了异步数据处理逻辑,使用 Mono.zip 并发获取多个数据源 + · 改善了错误处理机制 + +- 重构了配置管理,使用@ConfigurationProperties替代静态字段 + · 创建了 DailyLoveProperties 类使用 @ConfigurationProperties 注解 + · 更新了配置文件结构,使用更清晰的层次结构 + · 添加了环境变量支持,提高了配置的灵活性 +- 修复了定时任务失效问题 + · 启用了 PushTask 定时任务类,使用 @Component 注解 + · 添加了日志记录以便跟踪任务执行 +- 改进了错误处理,增加全局异常处理器 + · 创建了专门的 PushException 异常类 + · 更新了全局异常处理器 GlobalExceptionHandler + · 在关键位置添加了错误映射和处理 +- 增强了配置文件,支持环境变量 + · 更新了 application.yml 文件,采用新的配置结构 + · 添加了日志级别配置 + · 为配置项提供了默认值和环境变量支持 + +### **v2.4.0-native** + +- 升级到Spring Boot 4 +- 使用WebClient替代OpenFeign +- 支持原生镜像构建 + +## 贡献指南 + +1. Fork 本仓库 +2. 创建功能分支 (`git checkout -b feature/AmazingFeature`) +3. 提交更改 (`git commit -m 'Add some AmazingFeature'`) +4. 推送到分支 (`git push origin feature/AmazingFeature`) +5. 开启 Pull Request + +## 许可证 + +此项目采用 MIT 许可证。 diff --git a/pom.xml b/pom.xml index f345550..5604906 100644 --- a/pom.xml +++ b/pom.xml @@ -32,8 +32,10 @@ 25 25 25 + 4.0.1 + 2023.0.0 - + 5.8.25 registry.cn-chengdu.aliyuncs.com/bamanker @@ -74,6 +76,12 @@ org.springframework.boot spring-boot-starter-web + + + org.springframework.boot + spring-boot-configuration-processor + true + org.springframework.boot spring-boot-starter-tomcat diff --git a/src/main/java/com/bamanker/dailylove/DailyLoveApplication.java b/src/main/java/com/bamanker/dailylove/DailyLoveApplication.java index 931e5cb..ebf2d86 100644 --- a/src/main/java/com/bamanker/dailylove/DailyLoveApplication.java +++ b/src/main/java/com/bamanker/dailylove/DailyLoveApplication.java @@ -1,21 +1,35 @@ package com.bamanker.dailylove; +import com.bamanker.dailylove.config.DailyLoveConfigure; +import com.bamanker.dailylove.config.DailyLoveProperties; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; import org.springframework.scheduling.annotation.EnableScheduling; /** + * @descriptions 启动类 * @author bamanker + * @date 2026/1/13 23:42 */ @SpringBootApplication -//@EnableFeignClients //开启定时任务 @EnableScheduling -//@ImportAutoConfiguration({FeignAutoConfiguration.class}) -public class DailyLoveApplication { +public class DailyLoveApplication implements ApplicationListener { public static void main(String[] args) { SpringApplication.run(DailyLoveApplication.class, args); } + /** + * 应用启动完成后执行 + */ + @Override + public void onApplicationEvent(ApplicationReadyEvent event) { + ConfigurableApplicationContext applicationContext = event.getApplicationContext(); + DailyLoveProperties properties = applicationContext.getBean(DailyLoveProperties.class); + DailyLoveConfigure.initialize(properties); + } } diff --git a/src/main/java/com/bamanker/dailylove/config/ClassReflectConfig.java b/src/main/java/com/bamanker/dailylove/config/ClassReflectConfig.java index c13c243..ef96211 100644 --- a/src/main/java/com/bamanker/dailylove/config/ClassReflectConfig.java +++ b/src/main/java/com/bamanker/dailylove/config/ClassReflectConfig.java @@ -27,8 +27,11 @@ public class ClassReflectConfig { @Value("${scanclass}") private Boolean scanclass; - @Autowired - private ThreadPoolTaskExecutor executorService; + private final ThreadPoolTaskExecutor executorService; + + public ClassReflectConfig(ThreadPoolTaskExecutor executorService) { + this.executorService = executorService; + } @PostConstruct public void init() { diff --git a/src/main/java/com/bamanker/dailylove/config/DailyLoveConfigure.java b/src/main/java/com/bamanker/dailylove/config/DailyLoveConfigure.java index 2adcb24..7ae861b 100644 --- a/src/main/java/com/bamanker/dailylove/config/DailyLoveConfigure.java +++ b/src/main/java/com/bamanker/dailylove/config/DailyLoveConfigure.java @@ -1,18 +1,36 @@ package com.bamanker.dailylove.config; -import org.springframework.beans.factory.annotation.Value; +import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; /** - * @descriptions + * @descriptions 配置类适配器,用于兼容旧代码 * @author bamanker * @date 2026/1/8 12:12 - * @return */ @Component +@RequiredArgsConstructor public class DailyLoveConfigure { - // public static String Access_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}"; - // public static String Send_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token={0}"; + + private final DailyLoveProperties properties; + + // 微信相关配置 + public static String App_ID; + public static String App_Secret; + public static String Open_ID; + public static String Template_ID_Morning; + public static String Template_ID_Night; + + // 数据相关配置 + public static String City_ID; + public static String TianXin_Key; + public static String Boy_Birthday; + public static String Girl_Birthday; + public static String Cat_Birthday; + public static String Love_Day; + public static String Wedding_Day; + + // 颜色配置 public static String Color_quality; public static String Color_morning; public static String Color_chineseDate; @@ -33,195 +51,48 @@ public class DailyLoveConfigure { public static String Color_loveDay; public static String Color_weddingDay; public static String Color_remark; - - @Value("${wechat.color.tomorrow:null}") - public void setColor_tomorrow(String color_tomorrow) { - Color_tomorrow = color_tomorrow; - } - - @Value("${wechat.color.quality:null}") - public void setColor_quality(String color_quality) { - Color_quality = color_quality; - } - - @Value("${wechat.color.morning:null}") - public void setColor_morning(String color_morning) { - Color_morning = color_morning; - } - - @Value("${wechat.color.chineseDate:null}") - public void setColor_chineseDate(String color_chineseDate) { - Color_chineseDate = color_chineseDate; - } - - @Value("${wechat.color.festival:null}") - public void setColor_festival(String color_festival) { - Color_festival = color_festival; - } - - @Value("${wechat.color.night:null}") - public void setColor_night(String color_night) { - Color_night = color_night; - } - - @Value("${wechat.color.city:null}") - public void setColor_city(String color_city) { - Color_city = color_city; - } - - @Value("${wechat.color.weather:null}") - public void setColor_weather(String color_weather) { - Color_weather = color_weather; - } - - @Value("${wechat.color.minTem:null}") - public void setColor_minTem(String color_minTem) { - Color_minTem = color_minTem; - } - - @Value("${wechat.color.maxTem:null}") - public void setColor_maxTem(String color_maxTem) { - Color_maxTem = color_maxTem; - } - - @Value("${wechat.color.tips:null}") - public void setColor_tips(String color_tips) { - Color_tips = color_tips; - } - - @Value("${wechat.color.dailyCn:null}") - public void setColor_dailyCn(String color_dailyCn) { - Color_dailyCn = color_dailyCn; - } - - @Value("${wechat.color.dailyEn:null}") - public void setColor_dailyEn(String color_dailyEn) { - Color_dailyEn = color_dailyEn; - } - - @Value("${wechat.color.gbir:null}") - public void setColor_gbir(String color_gbir) { - Color_gbir = color_gbir; - } - - @Value("${wechat.color.bbir:null}") - public void setColor_bbir(String color_bbir) { - Color_bbir = color_bbir; - } - - @Value("${wechat.color.cbir:null}") - public void setColor_cbir(String color_cbir) { - Color_cbir = color_cbir; - } - - @Value("${wechat.color.loveDay:null}") - public void setColor_loveDay(String color_loveDay) { - Color_loveDay = color_loveDay; - } - - @Value("${wechat.color.weddingDay:null}") - public void setColor_weddingDay(String color_weddingDay) { - Color_loveDay = color_weddingDay; - } - - @Value("${wechat.color.remark:null}") - public void setColor_remark(String color_remark) { - Color_remark = color_remark; - } - - @Value("${wechat.color.now:null}") - public void setColor_Now(String color_Now) { - Color_Now = color_Now; - } - - public static String App_ID; - - @Value("${wechat.app-id}") - public void setAppID(String AppID) { - App_ID = AppID; - } - - public static String App_Secret; - - @Value("${wechat.app-secret}") - public void setAppSecret(String AppSecret) { - App_Secret = AppSecret; - } - - public static String Open_ID; - - @Value("${wechat.open-id}") - public void setOpenID(String OpenID) { - Open_ID = OpenID; - } - - public static String Template_ID_Morning; - - @Value("${wechat.template-id-morning}") - public void setTemplateIDMorning(String templateIDMorning) { - Template_ID_Morning = templateIDMorning; - } - - public static String Template_ID_Night; - - @Value("${wechat.template-id-night}") - public void setTemplateIDNight(String templateIDNight) { - Template_ID_Night = templateIDNight; - } - - public static String City_ID; - - @Value("${DL.city-id}") - public void setCity_ID(String city_ID) { - City_ID = city_ID; - } - - public static String Color_Top = null; - - @Value("${wechat.color.top}") - public void setColor_Top(String colorTop) { - Color_Top = colorTop; - } - - public static String TianXin_Key; - - @Value("${DL.tianxin-key}") - public void setTianXin_Key(String tianXin_Key) { - TianXin_Key = tianXin_Key; - } - - public static String Boy_Birthday; - - @Value("${DL.boy-birthday}") - public void setBoyBirthday(String boyBirthday) { - Boy_Birthday = boyBirthday; - } - - public static String Girl_Birthday; - - @Value("${DL.girl-birthday}") - public void setGirlBirthday(String girlBirthday) { - Girl_Birthday = girlBirthday; - } - - public static String Cat_Birthday; - - @Value("${DL.cat-birthday}") - public void setCatBirthday(String catBirthday) { - Cat_Birthday = catBirthday; - } - - public static String Love_Day; - - @Value("${DL.love-day}") - public void setLoveDay(String loveDay) { - Love_Day = loveDay; - } - - public static String Wedding_Day; - - @Value("${DL.wedding-day}") - public void setWeddingDay(String weddingDay) { - Wedding_Day = weddingDay; + public static String Color_Top; + + // 初始化静态变量 + public static void initialize(DailyLoveProperties properties) { + // 微信相关配置 + App_ID = properties.getWechat().getAppId(); + App_Secret = properties.getWechat().getAppSecret(); + Open_ID = properties.getWechat().getOpenId(); + Template_ID_Morning = properties.getWechat().getTemplateIdMorning(); + Template_ID_Night = properties.getWechat().getTemplateIdNight(); + + // 数据相关配置 + City_ID = properties.getData().getCityId(); + TianXin_Key = properties.getData().getTianxinKey(); + Boy_Birthday = properties.getData().getBoyBirthday(); + Girl_Birthday = properties.getData().getGirlBirthday(); + Cat_Birthday = properties.getData().getCatBirthday(); + Love_Day = properties.getData().getLoveDay(); + Wedding_Day = properties.getData().getWeddingDay(); + + // 颜色配置 + DailyLoveProperties.Wechat.Colors colors = properties.getWechat().getColors(); + Color_quality = colors.getQuality(); + Color_morning = colors.getMorning(); + Color_chineseDate = colors.getChineseDate(); + Color_festival = colors.getFestival(); + Color_night = colors.getNight(); + Color_tomorrow = colors.getTomorrow(); + Color_Now = colors.getNow(); + Color_city = colors.getCity(); + Color_weather = colors.getWeather(); + Color_minTem = colors.getMinTem(); + Color_maxTem = colors.getMaxTem(); + Color_tips = colors.getTips(); + Color_dailyCn = colors.getDailyCn(); + Color_dailyEn = colors.getDailyEn(); + Color_gbir = colors.getGbir(); + Color_bbir = colors.getBbir(); + Color_cbir = colors.getCbir(); + Color_loveDay = colors.getLoveDay(); + Color_weddingDay = colors.getWeddingDay(); + Color_remark = colors.getRemark(); + Color_Top = colors.getTop(); } } \ No newline at end of file diff --git a/src/main/java/com/bamanker/dailylove/config/DailyLoveProperties.java b/src/main/java/com/bamanker/dailylove/config/DailyLoveProperties.java new file mode 100644 index 0000000..367fee9 --- /dev/null +++ b/src/main/java/com/bamanker/dailylove/config/DailyLoveProperties.java @@ -0,0 +1,128 @@ +package com.bamanker.dailylove.config; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +/** + * @descriptions 配置类 + * @author bamanker + * @date 2026/1/13 23:31 + */ +@Setter +@Getter +@Component +@ConfigurationProperties(prefix = "daily-love") +public class DailyLoveProperties { + + // Getters and Setters for DailyLoveProperties + private Wechat wechat = new Wechat(); + private Data data = new Data(); + + public static class Wechat { + private String appId; + private String appSecret; + private String openId; + private String templateIdMorning; + private String templateIdNight; + private Colors colors = new Colors(); + + public static class Colors { + private String quality = "#000000"; + private String morning = "#FF69B4"; + private String chineseDate = "#FF69B4"; + private String festival = "#FF69B4"; + private String night = "#FF69B4"; + private String tomorrow = "#000000"; + private String now = "#000000"; + private String city = "#000000"; + private String weather = "#000000"; + private String minTem = "#000000"; + private String maxTem = "#000000"; + private String tips = "#000000"; + private String dailyCn = "#000000"; + private String dailyEn = "#000000"; + private String gbir = "#FF69B4"; + private String bbir = "#FF69B4"; + private String cbir = "#FF69B4"; + private String loveDay = "#FF69B4"; + private String weddingDay = "#FF69B4"; + private String remark = "#FF69B4"; + private String top = "#FF69B4"; + + // Getters and Setters for Colors + public String getQuality() { return quality; } + public void setQuality(String quality) { this.quality = quality; } + public String getMorning() { return morning; } + public void setMorning(String morning) { this.morning = morning; } + public String getChineseDate() { return chineseDate; } + public void setChineseDate(String chineseDate) { this.chineseDate = chineseDate; } + public String getFestival() { return festival; } + public void setFestival(String festival) { this.festival = festival; } + public String getNight() { return night; } + public void setNight(String night) { this.night = night; } + public String getTomorrow() { return tomorrow; } + public void setTomorrow(String tomorrow) { this.tomorrow = tomorrow; } + public String getNow() { return now; } + public void setNow(String now) { this.now = now; } + public String getCity() { return city; } + public void setCity(String city) { this.city = city; } + public String getWeather() { return weather; } + public void setWeather(String weather) { this.weather = weather; } + public String getMinTem() { return minTem; } + public void setMinTem(String minTem) { this.minTem = minTem; } + public String getMaxTem() { return maxTem; } + public void setMaxTem(String maxTem) { this.maxTem = maxTem; } + public String getTips() { return tips; } + public void setTips(String tips) { this.tips = tips; } + public String getDailyCn() { return dailyCn; } + public void setDailyCn(String dailyCn) { this.dailyCn = dailyCn; } + public String getDailyEn() { return dailyEn; } + public void setDailyEn(String dailyEn) { this.dailyEn = dailyEn; } + public String getGbir() { return gbir; } + public void setGbir(String gbir) { this.gbir = gbir; } + public String getBbir() { return bbir; } + public void setBbir(String bbir) { this.bbir = bbir; } + public String getCbir() { return cbir; } + public void setCbir(String cbir) { this.cbir = cbir; } + public String getLoveDay() { return loveDay; } + public void setLoveDay(String loveDay) { this.loveDay = loveDay; } + public String getWeddingDay() { return weddingDay; } + public void setWeddingDay(String weddingDay) { this.weddingDay = weddingDay; } + public String getRemark() { return remark; } + public void setRemark(String remark) { this.remark = remark; } + public String getTop() { return top; } + public void setTop(String top) { this.top = top; } + } + + // Getters and Setters for Wechat + public String getAppId() { return appId; } + public void setAppId(String appId) { this.appId = appId; } + public String getAppSecret() { return appSecret; } + public void setAppSecret(String appSecret) { this.appSecret = appSecret; } + public String getOpenId() { return openId; } + public void setOpenId(String openId) { this.openId = openId; } + public String getTemplateIdMorning() { return templateIdMorning; } + public void setTemplateIdMorning(String templateIdMorning) { this.templateIdMorning = templateIdMorning; } + public String getTemplateIdNight() { return templateIdNight; } + public void setTemplateIdNight(String templateIdNight) { this.templateIdNight = templateIdNight; } + public Colors getColors() { return colors; } + public void setColors(Colors colors) { this.colors = colors; } + } + + @Setter + @Getter + public static class Data { + // Getters and Setters for Data + private String cityId; + private String tianxinKey; + private String boyBirthday; + private String girlBirthday; + private String catBirthday; + private String loveDay; + private String weddingDay; + + } + +} \ No newline at end of file diff --git a/src/main/java/com/bamanker/dailylove/config/PushTask.java b/src/main/java/com/bamanker/dailylove/config/PushTask.java index b72712c..b65a57f 100644 --- a/src/main/java/com/bamanker/dailylove/config/PushTask.java +++ b/src/main/java/com/bamanker/dailylove/config/PushTask.java @@ -1,9 +1,17 @@ package com.bamanker.dailylove.config; import com.bamanker.dailylove.controller.PushDailyController; +import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; -//@Component +/** + * @descriptions 定时任务类 + * @author bamanker + * @date 2026/1/13 23:33 + */ +@Component +@Slf4j public class PushTask { final @@ -16,11 +24,13 @@ public class PushTask { //每日 早上7.30,晚上22点 定时推送 @Scheduled(cron = "0 30 7 * * ?") public void scheduledPushMorning(){ + log.info("开始执行早安推送任务..."); pushDailyController.pushMorning(); } @Scheduled(cron = "0 0 22 * * ?") public void scheduledPushNight(){ + log.info("开始执行晚安推送任务..."); pushDailyController.pushNight(); } } diff --git a/src/main/java/com/bamanker/dailylove/config/WebClientConfig.java b/src/main/java/com/bamanker/dailylove/config/WebClientConfig.java index 483f16c..67f2ea0 100644 --- a/src/main/java/com/bamanker/dailylove/config/WebClientConfig.java +++ b/src/main/java/com/bamanker/dailylove/config/WebClientConfig.java @@ -13,7 +13,6 @@ import reactor.netty.transport.logging.AdvancedByteBufFormat; * @author bamanker * @descriptions webclent 配置类 * @date 2026/1/12 17:33 - * @return */ @Configuration @Slf4j @@ -34,8 +33,10 @@ public class WebClientConfig { log.info("wechatRequest: {}", request.url()); return next.exchange(request); }) - .baseUrl("https://api.weixin.qq.com/cgi-bin") // 基础URL - .defaultHeader("User-Agent", "WebFlux-Client") // 默认请求头 + // 基础URL + .baseUrl("https://api.weixin.qq.com/cgi-bin") + // 默认请求头 + .defaultHeader("User-Agent", "WebFlux-Client") .build(); } diff --git a/src/main/java/com/bamanker/dailylove/controller/PushDailyController.java b/src/main/java/com/bamanker/dailylove/controller/PushDailyController.java index 51d5431..274f913 100644 --- a/src/main/java/com/bamanker/dailylove/controller/PushDailyController.java +++ b/src/main/java/com/bamanker/dailylove/controller/PushDailyController.java @@ -4,6 +4,7 @@ import cn.hutool.core.date.ChineseDate; import cn.hutool.core.date.DateUtil; import com.bamanker.dailylove.config.DailyLoveConfigure; import com.bamanker.dailylove.domain.*; +import com.bamanker.dailylove.exception.PushException; import com.bamanker.dailylove.service.DataRemoteService; import com.bamanker.dailylove.service.WechatRequestService; import com.bamanker.dailylove.utils.DataUtils; @@ -12,23 +13,20 @@ import org.springframework.aot.hint.annotation.RegisterReflectionForBinding; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import reactor.core.publisher.Mono; -import reactor.util.function.Tuple5; import tools.jackson.databind.JsonNode; import tools.jackson.databind.json.JsonMapper; import java.text.SimpleDateFormat; import java.time.LocalDate; import java.util.Date; -import java.util.function.Function; /** * @author bamanker - * @descriptions + * @descriptions 推送控制器 * @date 2026/1/8 12:04 - * @return */ -@Slf4j @RestController +@Slf4j public class PushDailyController { final @@ -46,108 +44,36 @@ public class PushDailyController { this.wechatRequestService = wechatRequestService; } - /** * 推送晚安 - * - * @return */ @GetMapping("/pushNight") @RegisterReflectionForBinding(Weather.class) public Mono pushNight() { - ResultVo resultVo = ResultVo.initializeResultVo(DailyLoveConfigure.Open_ID, - DailyLoveConfigure.Template_ID_Night, - DailyLoveConfigure.Color_Top); - + // 封装获取天气信息请求参数 TianXinReqParam param1 = new TianXinReqParam(); param1.setKey(DailyLoveConfigure.TianXin_Key); param1.setCity(DailyLoveConfigure.City_ID); param1.setType("7"); - Mono weatherRespMono = dataRemoteService.getWeather(param1.getKey(), param1.getCity(), param1.getType()); - Mono resultVoMono1 = weatherRespMono - .map(respJson -> mapper.readTree(respJson) - .get("result") - .get("area") - .toString()) - .flatMap(city -> { - resultVo.setAttribute("city", new DataItem(city, DailyLoveConfigure.Color_city)); - return Mono.just(resultVo); - }); - Mono resultVoMono2 = weatherRespMono - .map(respJson -> mapper.readTree(respJson) - .get("result") - .get("list") - .get(1)) - .map(respJson -> mapper.treeToValue(respJson, Weather.class)) - .flatMap(weather -> { - resultVo.setAttribute("tomorrow", new DataItem(weather.getDate() + " " + weather.getWeek(), DailyLoveConfigure.Color_tomorrow)); - resultVo.setAttribute("weather", new DataItem(weather.getWeather(), DailyLoveConfigure.Color_weather)); - resultVo.setAttribute("min_temperature", new DataItem(weather.getLowest(), DailyLoveConfigure.Color_minTem)); - resultVo.setAttribute("max_temperature", new DataItem(weather.getHighest(), DailyLoveConfigure.Color_maxTem)); - resultVo.setAttribute("quality", new DataItem(weather.getVis(), DailyLoveConfigure.Color_quality)); - return Mono.just(resultVo); - }); -// Mono> zip = Mono.zip(resultVoMono1, resultVoMono); - - - TianXinReqParam param2 = new TianXinReqParam(); - param2.setKey(DailyLoveConfigure.TianXin_Key); - - Mono resultVoMono3 = dataRemoteService.getTips(param2.getKey(), param2.getCity(), param2.getType()) - .map(respJson -> mapper.readTree(respJson) - .get("result") - .get("content").asString()) - .flatMap(tips -> { - resultVo.setAttribute("tips", new DataItem(tips, DailyLoveConfigure.Color_tips)); - return Mono.just(resultVo); - }); - Mono resultVoMono4 = dataRemoteService.getNight(param2.getKey(), param2.getCity(), param2.getType()) - .map(respJson -> mapper.readTree(respJson) - .get("result") - .get("content").asString()) - .flatMap(night -> { - resultVo.setAttribute("night", new DataItem(night, DailyLoveConfigure.Color_night)); - return Mono.just(resultVo); - }); - Mono resultVoMono5 = dataRemoteService.getRainbow(param2.getKey(), param2.getCity(), param2.getType()) - .map(respJson -> mapper.readTree(respJson) - .get("result") - .get("content").asString()) - .flatMap(rainbow -> { - resultVo.setAttribute("rainbow", new DataItem(rainbow, DailyLoveConfigure.Color_dailyCn)); - return Mono.just(resultVo); - }); - - Mono resultVoMono = Mono.zip(resultVoMono1, resultVoMono2, resultVoMono3, resultVoMono4, resultVoMono5) - .flatMap(new Function, Mono>() { - @Override - public Mono apply(Tuple5 tuple) { - return Mono.just(tuple.getT1()); - } - }); + // 获取农历和节日信息 ChineseDate chineseDate = new ChineseDate(DateUtil.parseDate(LocalDate.now().toString())); String festival = chineseDate.getFestivals(); String term = chineseDate.getTerm(); - resultVo.setAttribute("lunar", new DataItem(chineseDate.toString(), DailyLoveConfigure.Color_chineseDate)); - resultVo.setAttribute("festival", new DataItem(festival + " " + term, DailyLoveConfigure.Color_festival)); - + // 计算各种日期 int girlBirthdays = DataUtils.getBirthdays(DailyLoveConfigure.Girl_Birthday); - log.info("gbir_days:{}", girlBirthdays); - resultVo.setAttribute("gbir_day", new DataItem(girlBirthdays - 1 + "", DailyLoveConfigure.Color_gbir)); - int boyBirthdays = DataUtils.getChineseBirthdays(DailyLoveConfigure.Boy_Birthday); - log.info("bbir_days:{}", boyBirthdays); - resultVo.setAttribute("bbir_day", new DataItem(boyBirthdays - 1 + "", DailyLoveConfigure.Color_bbir)); - int catBirthdays = DataUtils.getBirthdays(DailyLoveConfigure.Cat_Birthday); - log.info("cbir_days:{}", catBirthdays); - resultVo.setAttribute("cbir_day", new DataItem(catBirthdays - 1 + "", DailyLoveConfigure.Color_cbir)); + int loveDays = DataUtils.getDayDiff(DailyLoveConfigure.Love_Day); + int weddingDays = DataUtils.getDayDiff(DailyLoveConfigure.Wedding_Day); - String words = "普通的一天"; + log.info("gbir_days:{}, bbir_days:{}, cbir_days:{}, love_days:{}, wedding_days:{}", + girlBirthdays, boyBirthdays, catBirthdays, loveDays, weddingDays); + // 设置提醒文字 + String words; if (DataUtils.getBirthdays(DailyLoveConfigure.Love_Day) == 1) { words = "明天是恋爱周年纪念日!永远爱你~mua"; } else if ((DataUtils.getDayDiff(DailyLoveConfigure.Love_Day)) % 100 == 99) { @@ -160,36 +86,73 @@ public class PushDailyController { words = "明天是菘菘的生日!别忘了哦~"; } else if (catBirthdays == 1) { words = "明天是小离谱的生日!别忘了小鱼干!"; + } else { + words = "普通的一天"; } - resultVo.setAttribute("words", new DataItem(words, DailyLoveConfigure.Color_remark)); + // 同时获取多个数据源 + Mono weatherRespMono = dataRemoteService.getWeather(param1.getKey(), param1.getCity(), param1.getType()); + Mono tipsMono = dataRemoteService.getTips(param1.getKey(), param1.getCity(), param1.getType()); + Mono nightMono = dataRemoteService.getNight(param1.getKey(), param1.getCity(), param1.getType()); + Mono rainbowMono = dataRemoteService.getRainbow(param1.getKey(), param1.getCity(), param1.getType()); - int loveDays = DataUtils.getDayDiff(DailyLoveConfigure.Love_Day); - log.info("love_days:{}", loveDays); - resultVo.setAttribute("love_day", new DataItem(loveDays + 1 + "", DailyLoveConfigure.Color_loveDay)); + // 组合所有异步操作 + return Mono.zip(weatherRespMono, tipsMono, nightMono, rainbowMono) + .onErrorMap(throwable -> new PushException("获取晚安推送数据失败: " + throwable.getMessage(), throwable)) + .flatMap(tuple -> { + String weatherResp = tuple.getT1(); + String tipsResp = tuple.getT2(); + String nightResp = tuple.getT3(); + String rainbowResp = tuple.getT4(); - int weddingDays = DataUtils.getDayDiff(DailyLoveConfigure.Wedding_Day); - log.info("wedding_days:{}", weddingDays); - resultVo.setAttribute("wedding_day", new DataItem(weddingDays + 1 + "", DailyLoveConfigure.Color_weddingDay)); + try { + // 解析天气数据 + String city = mapper.readTree(weatherResp).get("result").get("area").asString(); + var weatherData = mapper.readTree(weatherResp).get("result").get("list").get(1); + Weather weather = mapper.treeToValue(weatherData, Weather.class); - resultVoMono.flatMap(new Function>() { - @Override - public Mono apply(ResultVo resultVo) { - return Mono.just(resultVo); - } - }).log().block(); + // 解析其他数据 + String tips = mapper.readTree(tipsResp).get("result").get("content").asString(); + String nightContent = mapper.readTree(nightResp).get("result").get("content").asString(); + String rainbowContent = mapper.readTree(rainbowResp).get("result").get("content").asString(); - WechatTokenParam param3 = new WechatTokenParam(); - param3.setAppid(DailyLoveConfigure.App_ID); - param3.setSecret(DailyLoveConfigure.App_Secret); + // 创建结果对象 + ResultVo resultVo = ResultVo.initializeResultVo(DailyLoveConfigure.Open_ID, + DailyLoveConfigure.Template_ID_Night, + DailyLoveConfigure.Color_Top); - String accessToken = wechatRequestService.getAccessToken(param3.getGrant_type(), param3.getAppid(), param3.getSecret()) - .map(respJson -> { - return mapper.readTree(respJson) - .get("access_token").asString(); - }).log().block(); - return wechatRequestService.sendMsg(accessToken, resultVo).log(); + // 设置各项数据 + resultVo.setAttribute("city", new DataItem(city, DailyLoveConfigure.Color_city)); + resultVo.setAttribute("tomorrow", new DataItem(weather.getDate() + " " + weather.getWeek(), DailyLoveConfigure.Color_tomorrow)); + resultVo.setAttribute("weather", new DataItem(weather.getWeather(), DailyLoveConfigure.Color_weather)); + resultVo.setAttribute("min_temperature", new DataItem(weather.getLowest(), DailyLoveConfigure.Color_minTem)); + resultVo.setAttribute("max_temperature", new DataItem(weather.getHighest(), DailyLoveConfigure.Color_maxTem)); + resultVo.setAttribute("quality", new DataItem(weather.getVis(), DailyLoveConfigure.Color_quality)); + resultVo.setAttribute("tips", new DataItem(tips, DailyLoveConfigure.Color_tips)); + resultVo.setAttribute("night", new DataItem(nightContent, DailyLoveConfigure.Color_night)); + resultVo.setAttribute("rainbow", new DataItem(rainbowContent, DailyLoveConfigure.Color_dailyCn)); + resultVo.setAttribute("lunar", new DataItem(chineseDate.toString(), DailyLoveConfigure.Color_chineseDate)); + resultVo.setAttribute("festival", new DataItem(festival + " " + term, DailyLoveConfigure.Color_festival)); + resultVo.setAttribute("gbir_day", new DataItem((girlBirthdays - 1) + "", DailyLoveConfigure.Color_gbir)); + resultVo.setAttribute("bbir_day", new DataItem((boyBirthdays - 1) + "", DailyLoveConfigure.Color_bbir)); + resultVo.setAttribute("cbir_day", new DataItem((catBirthdays - 1) + "", DailyLoveConfigure.Color_cbir)); + resultVo.setAttribute("words", new DataItem(words, DailyLoveConfigure.Color_remark)); + resultVo.setAttribute("love_day", new DataItem((loveDays + 1) + "", DailyLoveConfigure.Color_loveDay)); + resultVo.setAttribute("wedding_day", new DataItem((weddingDays + 1) + "", DailyLoveConfigure.Color_weddingDay)); + // 获取微信访问令牌并发送消息 + return wechatRequestService.getAccessToken("client_credential", DailyLoveConfigure.App_ID, DailyLoveConfigure.App_Secret) + .onErrorMap(throwable -> new PushException("获取微信访问令牌失败: " + throwable.getMessage(), throwable)) + .map(tokenResp -> { + return mapper.readTree(tokenResp).get("access_token").asString(); + }) + .flatMap(accessToken -> wechatRequestService.sendMsg(accessToken, resultVo) + .onErrorMap(throwable -> new PushException("发送微信消息失败: " + throwable.getMessage(), throwable))); + } catch (Exception e) { + log.error("处理晚安推送数据时发生错误", e); + return Mono.error(new PushException("处理晚安推送数据时发生错误", e)); + } + }); } /** @@ -198,104 +161,29 @@ public class PushDailyController { @GetMapping("/pushMorning") @RegisterReflectionForBinding(Weather.class) public Mono pushMorning() { - - ResultVo resultVo = ResultVo.initializeResultVo(DailyLoveConfigure.Open_ID, - DailyLoveConfigure.Template_ID_Morning, - DailyLoveConfigure.Color_Top); - + // 获取天气信息 TianXinReqParam param1 = new TianXinReqParam(); param1.setKey(DailyLoveConfigure.TianXin_Key); param1.setCity(DailyLoveConfigure.City_ID); param1.setType("7"); - Mono weatherRespMono = dataRemoteService.getWeather(param1.getKey(), param1.getCity(), param1.getType()); - Mono resultVoMono1 = weatherRespMono - .map(respJson -> mapper.readTree(respJson) - .get("result") - .get("area") - .toString()) - .flatMap(city -> { - resultVo.setAttribute("city", new DataItem(city, DailyLoveConfigure.Color_city)); - return Mono.just(resultVo); - }); - Mono resultVoMono2 = weatherRespMono - .map(respJson -> mapper.readTree(respJson) - .get("result") - .get("list") - .get(0)) - .map(respJson -> mapper.treeToValue(respJson, Weather.class)) - .flatMap(weather -> { - resultVo.setAttribute("now", new DataItem(weather.getDate() + " " + weather.getWeek(), DailyLoveConfigure.Color_Now)); - resultVo.setAttribute("weather", new DataItem(weather.getWeather(), DailyLoveConfigure.Color_weather)); - resultVo.setAttribute("min_temperature", new DataItem(weather.getLowest(), DailyLoveConfigure.Color_minTem)); - resultVo.setAttribute("max_temperature", new DataItem(weather.getHighest(), DailyLoveConfigure.Color_maxTem)); - resultVo.setAttribute("quality", new DataItem(weather.getVis(), DailyLoveConfigure.Color_quality)); - return Mono.just(resultVo); - }); -// Mono> zip = Mono.zip(resultVoMono1, resultVoMono); - - - TianXinReqParam param2 = new TianXinReqParam(); - param2.setKey(DailyLoveConfigure.TianXin_Key); - - Mono resultVoMono3 = dataRemoteService.getTips(param2.getKey(), param2.getCity(), param2.getType()) - .map(respJson -> mapper.readTree(respJson) - .get("result") - .get("content").asString()) - .flatMap(tips -> { - resultVo.setAttribute("tips", new DataItem(tips, DailyLoveConfigure.Color_tips)); - return Mono.just(resultVo); - }); - Mono resultVoMono4 = dataRemoteService.getMorning(param2.getKey(), param2.getCity(), param2.getType()) - .map(respJson -> mapper.readTree(respJson) - .get("result") - .get("content").asString()) - .flatMap(morning -> { - resultVo.setAttribute("morning", new DataItem(morning, DailyLoveConfigure.Color_morning)); - return Mono.just(resultVo); - }); - Mono resultVoMono5 = dataRemoteService.getRainbow(param2.getKey(), param2.getCity(), param2.getType()) - .map(respJson -> mapper.readTree(respJson) - .get("result") - .get("content").asString()) - .flatMap(rainbow -> { - resultVo.setAttribute("rainbow", new DataItem(rainbow, DailyLoveConfigure.Color_dailyCn)); - return Mono.just(resultVo); - }); - - Mono resultVoMono = Mono.zip(resultVoMono1, resultVoMono2, resultVoMono3, resultVoMono4, resultVoMono5) - .flatMap(new Function, Mono>() { - @Override - public Mono apply(Tuple5 tuple) { - return Mono.just(tuple.getT1()); - } - }); - - -// String englishResp = dataRemoteClient.getDailyEnglish(param2); -// String english = JSONObject.parseObject(englishResp).getJSONArray("result").getJSONObject(0).getString("en"); -// resultVo.setAttribute("daily_english_en", new DataItem(english, DailyLoveConfigure.Color_dailyEn)); + // 获取农历和节日信息 ChineseDate chineseDate = new ChineseDate(DateUtil.parseDate(LocalDate.now().toString())); String festival = chineseDate.getFestivals(); String term = chineseDate.getTerm(); - resultVo.setAttribute("lunar", new DataItem(chineseDate.toString(), DailyLoveConfigure.Color_chineseDate)); - resultVo.setAttribute("festival", new DataItem(festival + " " + term, DailyLoveConfigure.Color_festival)); - + // 计算各种日期 int girlBirthdays = DataUtils.getBirthdays(DailyLoveConfigure.Girl_Birthday); - log.info("gbir_days:{}", girlBirthdays); - resultVo.setAttribute("gbir_day", new DataItem(girlBirthdays + "", DailyLoveConfigure.Color_gbir)); - int boyBirthdays = DataUtils.getChineseBirthdays(DailyLoveConfigure.Boy_Birthday); - log.info("bbir_days:{}", boyBirthdays); - resultVo.setAttribute("bbir_day", new DataItem(boyBirthdays + "", DailyLoveConfigure.Color_bbir)); - int catBirthdays = DataUtils.getBirthdays(DailyLoveConfigure.Cat_Birthday); - log.info("cbir_days:{}", catBirthdays); - resultVo.setAttribute("cbir_day", new DataItem(catBirthdays + "", DailyLoveConfigure.Color_cbir)); + int loveDays = DataUtils.getDayDiff(DailyLoveConfigure.Love_Day); + int weddingDays = DataUtils.getDayDiff(DailyLoveConfigure.Wedding_Day); - String words = "普通的一天"; + log.info("gbir_days:{}, bbir_days:{}, cbir_days:{}, love_days:{}, wedding_days:{}", + girlBirthdays, boyBirthdays, catBirthdays, loveDays, weddingDays); + // 设置提醒文字 + String words; if (DataUtils.getBirthdays(DailyLoveConfigure.Love_Day) == 0) { words = "今天是恋爱周年纪念日!永远爱你~mua"; } else if ((DataUtils.getDayDiff(DailyLoveConfigure.Love_Day)) % 100 == 0) { @@ -308,52 +196,86 @@ public class PushDailyController { words = "今天是菘菘的生日!别忘了好好爱他~"; } else if (catBirthdays == 0) { words = "今天是小离谱的生日!别忘了小鱼干!"; + } else { + words = "普通的一天"; } - resultVo.setAttribute("words", new DataItem(words, DailyLoveConfigure.Color_remark)); + // 同时获取多个数据源 + Mono weatherRespMono = dataRemoteService.getWeather(param1.getKey(), param1.getCity(), param1.getType()); + Mono tipsMono = dataRemoteService.getTips(param1.getKey(), param1.getCity(), param1.getType()); + Mono morningMono = dataRemoteService.getMorning(param1.getKey(), param1.getCity(), param1.getType()); + Mono rainbowMono = dataRemoteService.getRainbow(param1.getKey(), param1.getCity(), param1.getType()); - int loveDays = DataUtils.getDayDiff(DailyLoveConfigure.Love_Day); - log.info("love_days:{}", loveDays); - resultVo.setAttribute("love_day", new DataItem(loveDays + "", DailyLoveConfigure.Color_loveDay)); + // 组合所有异步操作 + return Mono.zip(weatherRespMono, tipsMono, morningMono, rainbowMono) + .onErrorMap(throwable -> new PushException("获取早安推送数据失败: " + throwable.getMessage(), throwable)) + .flatMap(tuple -> { + String weatherResp = tuple.getT1(); + String tipsResp = tuple.getT2(); + String morningResp = tuple.getT3(); + String rainbowResp = tuple.getT4(); - int weddingDays = DataUtils.getDayDiff(DailyLoveConfigure.Wedding_Day); - log.info("wedding_days:{}", weddingDays); - resultVo.setAttribute("wedding_day", new DataItem(weddingDays + "", DailyLoveConfigure.Color_weddingDay)); + try { + // 解析天气数据 + String city = mapper.readTree(weatherResp).get("result").get("area").asText(); + var weatherData = mapper.readTree(weatherResp).get("result").get("list").get(0); + Weather weather = mapper.treeToValue(weatherData, Weather.class); - log.debug("resultVo:{}", resultVo); - WechatTokenParam wechatTokenParam = new WechatTokenParam(); - wechatTokenParam.setAppid(DailyLoveConfigure.App_ID); - wechatTokenParam.setSecret(DailyLoveConfigure.App_Secret); + // 解析其他数据 + String tips = mapper.readTree(tipsResp).get("result").get("content").asText(); + String morningContent = mapper.readTree(morningResp).get("result").get("content").asText(); + String rainbowContent = mapper.readTree(rainbowResp).get("result").get("content").asText(); - resultVoMono.flatMap((Function>) resultVo1 -> Mono.just(resultVo1)).log().block(); + // 创建结果对象 + ResultVo resultVo = ResultVo.initializeResultVo(DailyLoveConfigure.Open_ID, + DailyLoveConfigure.Template_ID_Morning, + DailyLoveConfigure.Color_Top); - WechatTokenParam param3 = new WechatTokenParam(); - param3.setAppid(DailyLoveConfigure.App_ID); - param3.setSecret(DailyLoveConfigure.App_Secret); - - String accessToken = wechatRequestService.getAccessToken(param3.getGrant_type(), param3.getAppid(), param3.getSecret()) - .map(respJson -> { - return mapper.readTree(respJson) - .get("access_token").asString(); - }).log().block(); - return wechatRequestService.sendMsg(accessToken, resultVo).log(); + // 设置各项数据 + resultVo.setAttribute("city", new DataItem(city, DailyLoveConfigure.Color_city)); + resultVo.setAttribute("now", new DataItem(weather.getDate() + " " + weather.getWeek(), DailyLoveConfigure.Color_Now)); + resultVo.setAttribute("weather", new DataItem(weather.getWeather(), DailyLoveConfigure.Color_weather)); + resultVo.setAttribute("min_temperature", new DataItem(weather.getLowest(), DailyLoveConfigure.Color_minTem)); + resultVo.setAttribute("max_temperature", new DataItem(weather.getHighest(), DailyLoveConfigure.Color_maxTem)); + resultVo.setAttribute("quality", new DataItem(weather.getVis(), DailyLoveConfigure.Color_quality)); + resultVo.setAttribute("tips", new DataItem(tips, DailyLoveConfigure.Color_tips)); + resultVo.setAttribute("morning", new DataItem(morningContent, DailyLoveConfigure.Color_morning)); + resultVo.setAttribute("rainbow", new DataItem(rainbowContent, DailyLoveConfigure.Color_dailyCn)); + resultVo.setAttribute("lunar", new DataItem(chineseDate.toString(), DailyLoveConfigure.Color_chineseDate)); + resultVo.setAttribute("festival", new DataItem(festival + " " + term, DailyLoveConfigure.Color_festival)); + resultVo.setAttribute("gbir_day", new DataItem(girlBirthdays + "", DailyLoveConfigure.Color_gbir)); + resultVo.setAttribute("bbir_day", new DataItem(boyBirthdays + "", DailyLoveConfigure.Color_bbir)); + resultVo.setAttribute("cbir_day", new DataItem(catBirthdays + "", DailyLoveConfigure.Color_cbir)); + resultVo.setAttribute("words", new DataItem(words, DailyLoveConfigure.Color_remark)); + resultVo.setAttribute("love_day", new DataItem(loveDays + "", DailyLoveConfigure.Color_loveDay)); + resultVo.setAttribute("wedding_day", new DataItem(weddingDays + "", DailyLoveConfigure.Color_weddingDay)); + // 获取微信访问令牌并发送消息 + return wechatRequestService.getAccessToken("client_credential", DailyLoveConfigure.App_ID, DailyLoveConfigure.App_Secret) + .onErrorMap(throwable -> new PushException("获取微信访问令牌失败: " + throwable.getMessage(), throwable)) + .map(tokenResp -> { + return mapper.readTree(tokenResp).get("access_token").asString(); + }) + .flatMap(accessToken -> wechatRequestService.sendMsg(accessToken, resultVo) + .onErrorMap(throwable -> new PushException("发送微信消息失败: " + throwable.getMessage(), throwable))); + } catch (Exception e) { + log.error("处理早安推送数据时发生错误", e); + return Mono.error(new PushException("处理早安推送数据时发生错误", e)); + } + }); } /** - * 打印 response log - * - * @param responseStr + * @descriptions 打印 error日志 + * @author bamanker + * @date 2026/1/13 23:47 */ private void printPushLog(String responseStr) { JsonNode jsonNode = mapper.readTree(responseStr); - String msgCode = jsonNode.get("errcode").asString(); - String msgContent = jsonNode.get("errmsg").asString(); -// JSONObject jsonObject = JSONObject.parseObject(responseStr); -// String msgCode1 = jsonObject.getString("errcode"); -// String msgContent1 = jsonObject.getString("errmsg"); + String msgCode = jsonNode.get("errcode").asText(); + String msgContent = jsonNode.get("errmsg").asText(); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); System.out.println("[ " + dateFormat.format(new Date()) + " ] : messageCode=" + msgCode + ",messageContent=" + msgContent); } -} +} \ No newline at end of file diff --git a/src/main/java/com/bamanker/dailylove/domain/DataItem.java b/src/main/java/com/bamanker/dailylove/domain/DataItem.java index 85e7e95..554a11d 100644 --- a/src/main/java/com/bamanker/dailylove/domain/DataItem.java +++ b/src/main/java/com/bamanker/dailylove/domain/DataItem.java @@ -5,7 +5,9 @@ import lombok.Data; import lombok.NoArgsConstructor; /** + * @descriptions 数据项 * @author bamanker + * @date 2026/1/13 23:36 */ @Data @AllArgsConstructor diff --git a/src/main/java/com/bamanker/dailylove/domain/ErrorResponse.java b/src/main/java/com/bamanker/dailylove/domain/ErrorResponse.java index b02327d..5a6b064 100644 --- a/src/main/java/com/bamanker/dailylove/domain/ErrorResponse.java +++ b/src/main/java/com/bamanker/dailylove/domain/ErrorResponse.java @@ -1,10 +1,18 @@ package com.bamanker.dailylove.domain; import lombok.Data; +import org.springframework.web.bind.annotation.ResponseStatus; +/** + * @descriptions 错误响应类 + * @author bamanker + * @date 2026/1/13 23:36 + */ @Data public class ErrorResponse { private Integer code; private String message; + private Long timestamp; + } diff --git a/src/main/java/com/bamanker/dailylove/domain/ResultVo.java b/src/main/java/com/bamanker/dailylove/domain/ResultVo.java index 2dd9d48..9b6ca5d 100644 --- a/src/main/java/com/bamanker/dailylove/domain/ResultVo.java +++ b/src/main/java/com/bamanker/dailylove/domain/ResultVo.java @@ -6,6 +6,11 @@ import lombok.NoArgsConstructor; import java.util.HashMap; +/** + * @descriptions 结果集 + * @author bamanker + * @date 2026/1/13 23:36 + */ @Data @AllArgsConstructor @NoArgsConstructor @@ -24,11 +29,10 @@ public class ResultVo { return new ResultVo(_touser,_template_id,_topcolor,_data); } - public ResultVo setAttribute(String key, DataItem item){ + public void setAttribute(String key, DataItem item){ if(this.data==null) { this.data = new HashMap(); } this.data.put(key,item); - return this; } } diff --git a/src/main/java/com/bamanker/dailylove/domain/TianXinReqParam.java b/src/main/java/com/bamanker/dailylove/domain/TianXinReqParam.java index d8fe6dc..b99196d 100644 --- a/src/main/java/com/bamanker/dailylove/domain/TianXinReqParam.java +++ b/src/main/java/com/bamanker/dailylove/domain/TianXinReqParam.java @@ -4,6 +4,12 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; + +/** + * @descriptions 天行请求参数 + * @author bamanker + * @date 2026/1/13 23:37 + */ @Builder @Data @AllArgsConstructor diff --git a/src/main/java/com/bamanker/dailylove/domain/Weather.java b/src/main/java/com/bamanker/dailylove/domain/Weather.java index 8400c87..7f29755 100644 --- a/src/main/java/com/bamanker/dailylove/domain/Weather.java +++ b/src/main/java/com/bamanker/dailylove/domain/Weather.java @@ -4,6 +4,11 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +/** + * @descriptions 天气实体类 + * @author bamanker + * @date 2026/1/13 23:30 + */ @Data @AllArgsConstructor @NoArgsConstructor diff --git a/src/main/java/com/bamanker/dailylove/domain/WechatTokenParam.java b/src/main/java/com/bamanker/dailylove/domain/WechatTokenParam.java index a269009..e8364f6 100644 --- a/src/main/java/com/bamanker/dailylove/domain/WechatTokenParam.java +++ b/src/main/java/com/bamanker/dailylove/domain/WechatTokenParam.java @@ -4,6 +4,11 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +/** + * @descriptions 微信 token 参数 + * @author bamanker + * @date 2026/1/13 23:37 + */ @Data @AllArgsConstructor @NoArgsConstructor diff --git a/src/main/java/com/bamanker/dailylove/exception/GlobalExceptionHandler.java b/src/main/java/com/bamanker/dailylove/exception/GlobalExceptionHandler.java index bd7adad..024d668 100644 --- a/src/main/java/com/bamanker/dailylove/exception/GlobalExceptionHandler.java +++ b/src/main/java/com/bamanker/dailylove/exception/GlobalExceptionHandler.java @@ -1,34 +1,71 @@ package com.bamanker.dailylove.exception; import com.bamanker.dailylove.domain.ErrorResponse; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; -import reactor.core.publisher.Mono; +import org.springframework.web.bind.annotation.RestControllerAdvice; -@ControllerAdvice +/** + * @descriptions 全局异常处理器 + * @author bamanker + * @date 2026/1/13 23:38 + */ +@Slf4j +@RestControllerAdvice public class GlobalExceptionHandler { + /** + * 处理推送异常 + */ + @ExceptionHandler(PushException.class) + public ResponseEntity handlePushException(PushException e) { + log.error("推送服务异常: {}", e.getMessage(), e); + ErrorResponse error = new ErrorResponse(); + error.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); + error.setMessage(e.getMessage()); + error.setTimestamp(System.currentTimeMillis()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error); + } + /** * 处理运行时异常 */ @ExceptionHandler(RuntimeException.class) - public Mono> handleRuntimeException(RuntimeException e) { + public ResponseEntity handleRuntimeException(RuntimeException e) { + log.error("运行时异常: {}", e.getMessage(), e); ErrorResponse error = new ErrorResponse(); - error.setCode(500); + error.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); error.setMessage(e.getMessage()); - return Mono.just(ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error)); + error.setTimestamp(System.currentTimeMillis()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error); } + + /** + * 处理一般异常 + */ + @ExceptionHandler(Exception.class) + public ResponseEntity handleGeneralException(Exception e) { + log.error("系统异常: {}", e.getMessage(), e); + ErrorResponse error = new ErrorResponse(); + error.setCode(HttpStatus.INTERNAL_SERVER_ERROR.value()); + error.setMessage("系统内部错误,请稍后重试"); + error.setTimestamp(System.currentTimeMillis()); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error); + } + /** * 处理参数异常 */ @ExceptionHandler(IllegalArgumentException.class) - public Mono> handleIllegalArgumentException(IllegalArgumentException e) { + public ResponseEntity handleIllegalArgumentException(IllegalArgumentException e) { + log.warn("参数错误: {}", e.getMessage(), e); ErrorResponse error = new ErrorResponse(); - error.setCode(400); + error.setCode(HttpStatus.BAD_REQUEST.value()); error.setMessage("参数错误: " + e.getMessage()); - return Mono.just(ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error)); + error.setTimestamp(System.currentTimeMillis()); + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error); } -} +} \ No newline at end of file diff --git a/src/main/java/com/bamanker/dailylove/exception/PushException.java b/src/main/java/com/bamanker/dailylove/exception/PushException.java new file mode 100644 index 0000000..ac0a8f5 --- /dev/null +++ b/src/main/java/com/bamanker/dailylove/exception/PushException.java @@ -0,0 +1,16 @@ +package com.bamanker.dailylove.exception; + +/** + * @descriptions 推送异常 + * @author bamanker + * @date 2026/1/13 23:38 + */ +public class PushException extends RuntimeException { + public PushException(String message) { + super(message); + } + + public PushException(String message, Throwable cause) { + super(message, cause); + } +} \ No newline at end of file diff --git a/src/main/java/com/bamanker/dailylove/service/DataRemoteService.java b/src/main/java/com/bamanker/dailylove/service/DataRemoteService.java index 53dfc48..43ad23a 100644 --- a/src/main/java/com/bamanker/dailylove/service/DataRemoteService.java +++ b/src/main/java/com/bamanker/dailylove/service/DataRemoteService.java @@ -5,6 +5,11 @@ import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; +/** + * @descriptions 远程数据服务 + * @author bamanker + * @date 2026/1/13 23:38 + */ @Service public class DataRemoteService { diff --git a/src/main/java/com/bamanker/dailylove/service/WechatRequestService.java b/src/main/java/com/bamanker/dailylove/service/WechatRequestService.java index 918ca4e..25cb52d 100644 --- a/src/main/java/com/bamanker/dailylove/service/WechatRequestService.java +++ b/src/main/java/com/bamanker/dailylove/service/WechatRequestService.java @@ -6,6 +6,11 @@ import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.client.WebClient; import reactor.core.publisher.Mono; +/** + * @descriptions 微信请求服务 + * @author bamanker + * @date 2026/1/13 23:38 + */ @Service public class WechatRequestService { diff --git a/src/main/java/com/bamanker/dailylove/utils/DataUtils.java b/src/main/java/com/bamanker/dailylove/utils/DataUtils.java index 8a0ed5d..3f22ce0 100644 --- a/src/main/java/com/bamanker/dailylove/utils/DataUtils.java +++ b/src/main/java/com/bamanker/dailylove/utils/DataUtils.java @@ -13,14 +13,20 @@ import java.time.temporal.ChronoUnit; import java.util.Calendar; import java.util.Date; +/** + * @author bamanker + * @descriptions 日期工具类 + * @date 2026/1/13 23:39 + */ @Slf4j public class DataUtils { /** - * 计算农历生日天数 - * * @param birthday - * @return + * @return int + * @descriptions 计算农历生日天数 + * @author bamanker + * @date 2026/1/13 23:39 */ public static int getChineseBirthdays(String birthday) { //获取输入的生日 @@ -46,15 +52,16 @@ public class DataUtils { } /** - * 需要递归计算日期差 - * * @param chineseMonth 农历月 * @param chineseDay 农历日 * @param dateToday 今天的日期类 * @param todayYear 当前的年 * @param chineseDate 组装的待计算的新日期 * @param gregorianDay 判断是否存在农历日期的参数,-1代表今年不存在这个农历日期 - * @return 计算好的天数 + * @return long 计算好的天数 + * @descriptions 递归计算日期差 + * @author bamanker + * @date 2026/1/13 23:39 */ private static long haveThisDay(int chineseMonth, int chineseDay, Calendar dateToday, int todayYear, ChineseDate chineseDate, int gregorianDay) { //判断当前年份是否存在农历日对应的阳历日 @@ -90,10 +97,11 @@ public class DataUtils { } /** - * 计算生日天数 days - * * @param birthday - * @return + * @return int + * @descriptions 计算生日天数 + * @author bamanker + * @date 2026/1/13 23:40 */ public static int getBirthdays(String birthday) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); @@ -109,8 +117,7 @@ public class DataUtils { dateBirth.set(Calendar.YEAR, dateToday.get(Calendar.YEAR)); if (dateBirth.get(Calendar.DAY_OF_YEAR) < dateToday.get(Calendar.DAY_OF_YEAR)) { // 生日已经过了,要算明年的了 - days = (dateToday.getActualMaximum(Calendar.DAY_OF_YEAR) - dateToday.get(Calendar.DAY_OF_YEAR)) - + dateBirth.get(Calendar.DAY_OF_YEAR); + days = (dateToday.getActualMaximum(Calendar.DAY_OF_YEAR) - dateToday.get(Calendar.DAY_OF_YEAR)) + dateBirth.get(Calendar.DAY_OF_YEAR); } else { // 生日还没过 days = dateBirth.get(Calendar.DAY_OF_YEAR) - dateToday.get(Calendar.DAY_OF_YEAR); @@ -122,31 +129,12 @@ public class DataUtils { return days; } -// /** -// * 计算恋爱天数 days -// * -// * @param loveDay -// * @return -// */ -// public static int getLoveDays(String loveDay) { -// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); -// int days = 0; -// try { -// long time = System.currentTimeMillis() - dateFormat.parse(loveDay).getTime(); -// days = (int) (time / (24 * 60 * 60 * 1000)); -// -// } catch (ParseException e) { -// e.printStackTrace(); -// } -// return days; -// } - /** * @param startDay * @return int - * @descriptions 计算日期茶 days + * @descriptions 计算日期差 * @author bamanker - * @date 2026/1/8 12:38 + * @date 2026/1/13 23:41 */ public static int getDayDiff(String startDay) { diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0ae7370..46fa031 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -8,53 +8,57 @@ spring: refresh: enabled: false -DL: - tianxin-key: 72fbbb9e75e338ea6a240e83972f287c - city-id: 101270106 - girl-birthday: 1995-06-28 - boy-birthday: 1995-3-30 - love-day: 2022-07-16 - cat-birthday: 2022-10-23 - wedding-day: 2025-10-08 +# 新的配置结构 +daily-love: + wechat: + app-id: ${WECHAT_APP_ID:wxba68702957f8b93e} + app-secret: ${WECHAT_APP_SECRET:834078bb149409bfca4fe693ea7c4c1c} + # ME: oo5bL6bK_4TC0tb-Wa5oiugTPVeQ + #LILI: oo5bL6QafHJa9zQNYKS0fIhFC0zM + open-id: ${WECHAT_OPEN_ID:oo5bL6bK_4TC0tb-Wa5oiugTPVeQ} + template-id-morning: ${WECHAT_TEMPLATE_ID_MORNING:dWNAL-ZOzpBhnByFoTamt9DlJQYLB5z3ldKLvQstyU4} + template-id-night: ${WECHAT_TEMPLATE_ID_NIGHT:oraLiXC-8740stYc1a7mpzUFHiAIRaM3JikqibZ2grE} + colors: + quality: '#99CC66' + morning: '#FFFF99' + chineseDate: '#99CC66' + festival: '#FF6666' + night: '#006699' + tomorrow: '#99CCCC' + now: '#99CCCC' + city: "" + weather: '#66CCCC' + minTem: '#0066CC' + maxTem: '#FF0033' + tips: "" + dailyCn: '#993366' + dailyEn: '#CC99CC' + gbir: '#FF3399' + bbir: '#FF3399' + cbir: '#FF3399' + loveDay: '#FF3399' + weddingDay: '#FF3399' + remark: '#FF6666' + top: '#FF0000' + data: + city-id: ${CITY_ID:101270106} + tianxin-key: ${TIANXIN_KEY:72fbbb9e75e338ea6a240e83972f287c} + girl-birthday: ${GIRL_BIRTHDAY:1995-06-28} + boy-birthday: ${BOY_BIRTHDAY:1995-03-30} + cat-birthday: ${CAT_BIRTHDAY:2022-10-23} + love-day: ${LOVE_DAY:2022-07-16} + wedding-day: ${WEDDING_DAY:2025-10-08} -wechat: - app-id: wxba68702957f8b93e - app-secret: 834078bb149409bfca4fe693ea7c4c1c -# ME: oo5bL6bK_4TC0tb-Wa5oiugTPVeQ - #LILI: oo5bL6QafHJa9zQNYKS0fIhFC0zM - open-id: oo5bL6bK_4TC0tb-Wa5oiugTPVeQ - template-id-morning: dWNAL-ZOzpBhnByFoTamt9DlJQYLB5z3ldKLvQstyU4 - template-id-night: oraLiXC-8740stYc1a7mpzUFHiAIRaM3JikqibZ2grE - - color: - now: '#99CCCC' - tomorrow: '#99CCCC' - chineseDate: '#99CC66' - top: '#FF0000' - weather: '#66CCCC' - minTem: '#0066CC' - maxTem: '#FF0033' - dailyCn: '#993366' - dailyEn: '#CC99CC' - gbir: '#FF3399' - bbir: '#FF3399' - cbir: '#FF3399' - loveDay: '#FF3399' - weddingDay: '#FF3399' - remark: '#FF6666' - city: "" - tips: "" - quality: '#99CC66' - festival: '#FF6666' - morning: '#FFFF99' - night: '#006699' - -tianxin: - server: https://apis.tianapi.com +# 外部服务配置 +apis: + tianxin: + server: https://apis.tianapi.com logging: level: - reactor.netty.http.client: debug + com.bamanker.dailylove: INFO + reactor.netty.http.client: DEBUG + org.springframework.web.reactive.function.client: DEBUG scanclass: false # Actuator 配置