基于 SpringBoot 实现微信消息推送

基于SpringBoot实现微信消息推送

  • 版本
  • POM文件
  • 初始化项目
  • 代码
    • 1 启动类
    • 2 配置类
    • 实体类
    • 工具类
      • 纪念日工具类
      • 天气工具类
      • 彩虹屁工具类
      • 微信推送工具类
    • 定时任务
    • 手动调用接口
    • 遇到问题:ConfigurableWebBindingInitializer为空
    • 常量
    • 配置文件
  • 对应权限申请
  • 效果
  • 注意
  • 参考

版本

1 Spring Boot 3.1.5
2 JDK 21
3 weixin-java-mp 4.5.0
4 fastjson 2.0.43

POM文件

版本

    <properties>
        <java.version>21java.version>
        <snakeyaml.version>2.2snakeyaml.version>
        <jakarta.validation-api.version>3.0.2jakarta.validation-api.version>
        <hibernate-validator.version>8.0.1.Finalhibernate-validator.version>
        <weixin-java-mp.version>4.5.0weixin-java-mp.version>
        <fastjson.version>2.0.43fastjson.version>
    properties>

导入

<dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-configuration-processorartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

        <dependency>
            <groupId>jakarta.validationgroupId>
            <artifactId>jakarta.validation-apiartifactId>
            <version>${jakarta.validation-api.version}version>
        dependency>

        
        <dependency>
            <groupId>org.hibernate.validatorgroupId>
            <artifactId>hibernate-validatorartifactId>
            <version>${hibernate-validator.version}version>
        dependency>
        
        <dependency>
            <groupId>com.github.binarywanggroupId>
            <artifactId>weixin-java-mpartifactId>
            <version>${weixin-java-mp.version}version>
        dependency>
        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>${fastjson.version}version>
        dependency>
    dependencies>

初始化项目

按照上面的版本初始化即可
项目目录
基于 SpringBoot 实现微信消息推送_第1张图片

代码

1 启动类

@Slf4j
@SpringBootApplication
public class BaseWeixinMsgApplication {
    public static void main(String[] args) {
        SpringApplication.run(BaseWeixinMsgApplication.class, args);
        log.info("启动成功!");
        log.info(PushConfigure.getSecret());
        log.info(PushConfigure.getAppId());
    }
}

2 配置类

@Component
@ConfigurationProperties("wechat")
public class PushConfigure {
    /**
     * 微信公众平台的appID
     */
    private static String appId;
    /**
     * 微信公众平台的appSecret
     */
    private static String secret;
    /**
     * 天气查询的城市ID
     */
    private static String districtId;
    /**
     * 应用AK
     */
    private static String ak;
    /**
     * 纪念日
     */
    private static String loveDate;
    /**
     * 生日
     */
    private static String birthday;
    /**
     * 关注公众号的用户ID
     */
    private static String userId;
    /**
     * 模板ID
     */
    private static String templateId;

    /**
     * 天行数据apiKey
     */
    private static String rainbowKey;

    public static String getAppId() {
        return appId;
    }

    public void setAppId(String appId) {
        PushConfigure.appId = appId;
    }

    public static String getSecret() {
        return secret;
    }

    public void setSecret(String secret) {
        PushConfigure.secret = secret;
    }

    public static String getDistrictId() {
        return districtId;
    }

    public void setDistrict_id(String district_id) {
        PushConfigure.districtId = district_id;
    }

    public static String getAk() {
        return ak;
    }

    public void setAk(String ak) {
        PushConfigure.ak = ak;
    }

    public static String getLoveDate() {
        return loveDate;
    }

    public void setLoveDate(String loveDate) {
        PushConfigure.loveDate = loveDate;
    }

    public static String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        PushConfigure.birthday = birthday;
    }

    public static String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        PushConfigure.userId = userId;
    }

    public static String getTemplateId() {
        return templateId;
    }

    public void setTemplateId(String templateId) {
        PushConfigure.templateId = templateId;
    }

    public static String getRainbowKey() {
        return rainbowKey;
    }

    public void setRainbowKey(String rainbowKey) {
        PushConfigure.rainbowKey = rainbowKey;
    }
}

实体类

@Data
public class Weather {
    private String textDay;
    private String textNight;
    private String high;
    private String low;
    private String wcDay;
    private String wdDay;
    private String wcNight;
    private String wdNight;
    private String date;
    private String week;
    private String textNow;
    private String temp;
    private String city;
}

工具类

纪念日工具类

public class MemoryDayUtil {
    private static final ThreadLocal<SimpleDateFormat> THREAD_LOCAL = new ThreadLocal<>();

    /**
     * 获取SimpleDateFormat
     */
    private static SimpleDateFormat get() {
        SimpleDateFormat sdf = THREAD_LOCAL.get();
        if (sdf == null) {
            sdf = new SimpleDateFormat("yyyy-MM-dd");
            THREAD_LOCAL.set(sdf);
        }
        return sdf;
    }

    /**
     * 计算两个时间差
     */
    public static long getDatePoor(Date endDate, Date nowDate) {
        long nd = 1000 * 24 * 60 * 60;
        // 获得两个时间的毫秒时间差异
        long diff = endDate.getTime() - nowDate.getTime();
        // 计算差多少天
        long day = diff / nd;
        return day;
    }

    /**
     * 计算天数
     */
    public static long calculationLianAi(String date) {
        SimpleDateFormat simpleDateFormat = get();
        Date startDate = new Date();
        try {
            startDate = simpleDateFormat.parse(date);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return getDatePoor(new Date(), startDate);
    }

    /**
     * 计算生日
     */
    public static long calculationBirthday(String birthday) {
        SimpleDateFormat simpleDateFormat = get();
        Calendar cToday = Calendar.getInstance();
        Calendar cBirth = Calendar.getInstance();
        Date now = new Date();
        try {
            now = simpleDateFormat.parse(birthday);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        cBirth.setTime(now);
        cBirth.set(Calendar.YEAR, cToday.get(Calendar.YEAR));
        int days;
        if (cBirth.get(Calendar.DAY_OF_YEAR) < cToday.get(Calendar.DAY_OF_YEAR)) {
            days = cToday.getActualMaximum(Calendar.DAY_OF_YEAR) - cToday.get(Calendar.DAY_OF_YEAR);
            days += cBirth.get(Calendar.DAY_OF_YEAR);
        } else {
            days = cBirth.get(Calendar.DAY_OF_YEAR) - cToday.get(Calendar.DAY_OF_YEAR);
        }
        return days;
    }
}

天气工具类

@Component
public class WeatherUtil {
    public static Weather getWeather() {
        RestTemplate restTemplate = new RestTemplate();
        Map<String, String> map = new HashMap<>();
        map.put("district_id", PushConfigure.getDistrictId());
        map.put("ak", PushConfigure.getAk());
        String res = restTemplate.getForObject("https://api.map.baidu.com/weather/v1/?district_id={district_id}&data_type=all&ak={ak}", String.class, map);
        JSONObject json = JSONObject.parseObject(res);
        if (json == null) {
            //接口地址有误或者接口没调通
            return null;
        }
        JSONArray forecasts = json.getJSONObject("result").getJSONArray("forecasts");
        List<Weather> weathers = forecasts.toJavaList(Weather.class);
        Weather weather = weathers.get(0);
        JSONObject now = json.getJSONObject("result").getJSONObject("now");
        JSONObject location = json.getJSONObject("result").getJSONObject("location");
        weather.setTextNow(now.getString("text"));
        weather.setTemp(now.getString("temp"));
        weather.setCity(location.getString("city"));
        return weather;
    }
}

彩虹屁工具类

public class RainbowUtil {
    public static String getRainbow() {
        String httpUrl = "http://api.tianapi.com/caihongpi/index?key=" + PushConfigure.getRainbowKey();
        BufferedReader reader = null;
        String result = null;
        StringBuilder stringBuilder = new StringBuilder();

        try {
            URL url = URL.of(URI.create(httpUrl), null);
            // JDK 21 过期 
            // URL url = new URL(httpUrl); 
            HttpURLConnection connection = (HttpURLConnection) url
                    .openConnection();
            connection.setRequestMethod("GET");
            InputStream is = connection.getInputStream();
            reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
            String strRead = null;
            while ((strRead = reader.readLine()) != null) {
                stringBuilder.append(strRead);
                stringBuilder.append("\r\n");
            }
            reader.close();
            result = stringBuilder.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        JSONObject jsonObject = JSONObject.parseObject(result);
        if (jsonObject == null) {
            return "";
        }
        JSONArray newslist = jsonObject.getJSONArray("newslist");
        return  newslist.getJSONObject(0).getString("content");
    }
}

微信推送工具类

public class PushUtil {

    /**
     * 消息推送主要业务代码
     */
    public static String push() {
        //1,配置 // weixin-java-mp 4.5.0 版本使用该Config
        WxMpDefaultConfigImpl wxStorage = new WxMpDefaultConfigImpl();
        // weixin-java-mp 3.3.0 版本使用该Config
        // WxMpInMemoryConfigStorage wxStorage = new WxMpInMemoryConfigStorage();
        wxStorage.setAppId(PushConfigure.getAppId());
        wxStorage.setSecret(PushConfigure.getSecret());
        WxMpService wxMpService = new WxMpServiceImpl();
        wxMpService.setWxMpConfigStorage(wxStorage);
        // 推送消息
        WxMpTemplateMessage templateMessage = WxMpTemplateMessage.builder()
                .toUser(PushConfigure.getUserId())
                .templateId(PushConfigure.getTemplateId())
                .build();
        // 配置你的信息
        long loveDays = MemoryDayUtil.calculationLianAi(PushConfigure.getLoveDate());
        long birthdays = MemoryDayUtil.calculationBirthday(PushConfigure.getBirthday());
        Weather weather = WeatherUtil.getWeather();
        if (weather == null) {
            templateMessage.addData(new WxMpTemplateData("weather", "***", "#00FFFF"));
        } else {
            templateMessage.addData(new WxMpTemplateData("date", weather.getDate() + "  " + weather.getWeek(), "#00BFFF"));
            templateMessage.addData(new WxMpTemplateData("weather", weather.getTextNow(), "#00FFFF"));
            templateMessage.addData(new WxMpTemplateData("low", weather.getLow() + "", "#173177"));
            templateMessage.addData(new WxMpTemplateData("temp", weather.getTemp() + "", "#EE212D"));
            templateMessage.addData(new WxMpTemplateData("high", weather.getHigh() + "", "#FF6347"));
            templateMessage.addData(new WxMpTemplateData("city", weather.getCity() + "", "#173177"));
        }

        templateMessage.addData(new WxMpTemplateData("loveDays", loveDays + "", "#FF1493"));
        templateMessage.addData(new WxMpTemplateData("birthdays", birthdays + "", "#FFA500"));

        String remark = "亲爱的乖乖宝贝,早上好!记得要吃早餐哦,今天也要开心哦 =^_^= ";
        if (loveDays % Constant.YEARDAYS == 0) {
            remark = "\n今天是恋爱" + (loveDays / 365) + "周年纪念日!";
        }
        if (birthdays == 0) {
            remark = "\n今天是生日,生日快乐呀!";
        }
        if (loveDays % Constant.YEARDAYS == 0 && birthdays == 0) {
            remark = "\n今天是生日,也是恋爱" + (loveDays / 365) + "周年纪念日!";
        }

        templateMessage.addData(new WxMpTemplateData("remark", remark, "#FF1493"));
        templateMessage.addData(new WxMpTemplateData("rainbow", RainbowUtil.getRainbow(), "#FF69B4"));
        System.out.println(templateMessage.toJson());
        try {
            wxMpService.getTemplateMsgService().sendTemplateMsg(templateMessage);
        } catch (Exception e) {
            System.out.println("推送失败:" + e.getMessage());
            return "推送失败:" + e.getMessage();
        }
        return "推送成功!";
    }
}

定时任务


@EnableScheduling
@Configuration
public class Task {
    // 定时 早8点推送  0秒 0分 8时
    //@Scheduled(cron = "0 0 8 * * ?")
    // 定时 一个小时推送一次
    @Scheduled(cron = "0 0 0/1 * * ?")
    public void goodMorning() {
        PushUtil.push();
    }
}

手动调用接口

@RestController
public class TestController {
    @GetMapping("test")
    public String test() {
        return PushUtil.push();
    }
}

遇到问题:ConfigurableWebBindingInitializer为空

这里自定义引入一下

@Configuration
public class WebDataBindConfig {
    @Bean
    ConfigurableWebBindingInitializer configurableWebBindingInitializer(){
        return new ConfigurableWebBindingInitializer();
    }
}

常量

public class Constant {
    public static final int YEARDAYS = 365;
}

配置文件

#测试号信息中的appID
wechat.appId=
#测试号信息中的appsecret
wechat.secret=
#用户列表中的微信号id
wechat.userId=
#模板消息接口中模板ID
wechat.templateId=
#你查询天气城市的邮编
wechat.district_id=430300
#百度地图开放平台的AK
wechat.ak=
#在一起的时间
wechat.loveDate=2022-01-01
#生日
wechat.birthday=2003-12-05
#彩虹屁平台apiKey
wechat.rainbowKey=

对应权限申请

参考文章 申请对应接口平台的权限
这里贴一下各个申请平台的地址:
微信公众平台: 微信公众平台
基于 SpringBoot 实现微信消息推送_第2张图片
基于 SpringBoot 实现微信消息推送_第3张图片
基于 SpringBoot 实现微信消息推送_第4张图片
模板内容:
注意格式

:{{date.DATA}}
:{{remark.DATA}}
:{{city.DATA}} 的天气: {{weather.DATA}}
:最低气温: {{low.DATA }} 度
:最高气温: {{high.DATA }} 度
:今天是我们恋爱的第 {{loveDays.DATA}} 天
:距离宝宝的生日还有 {{birthdays.DATA}} 天 
:{{rainbow.DATA}}

百度地图开放平台: 百度地图开放平台
天气服务接口:天气服务接口
创建应用:创建应用
基于 SpringBoot 实现微信消息推送_第5张图片
注意:在 src/main/resources/application.properties 里面填写"应用AK"和"城市ID/行政代码",如上海浦东新区行政代码是310115

彩虹屁平台:彩虹屁平台
搜索一下彩虹屁申请对应的apiKey即可。

效果

注意

1 spring版本问题
需要自定义引入 ConfigurableWebBindingInitializer见上面的问题
2 展示效果无彩色字体
微信公众号平台 2023年3月30日去除尾部/备注内容、自定义颜色、表情符号
关于规范公众号模板消息的再次公告
3 模板注意空格
可参考:模板消息

参考

  • 本文参考自 https://blog.csdn.net/m0_63823719/article/details/127935329 欢迎大家交流学习
  • 参考网站连接汇总
    • https://www.tianapi.com/
    • https://lbsyun.baidu.com/index.php?title=%E9%A6%96%E9%A1%B5
    • https://lbs.baidu.com/faq/api?title=webapi/weather
    • https://lbsyun.baidu.com/apiconsole/center#/home
    • https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
    • https://mp.weixin.qq.com/debug/cgi-bin/readtmpl?t=tmplmsg/faq_tmpl
    • https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=1151313677&lang=zh_CN
    • https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html
    • https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/mp-access-token/getAccessToken.html#%E8%B0%83%E7%94%A8%E7%A4%BA%E4%BE%8B
    • https://developers.weixin.qq.com/community/search?query=40125&page=1&block=1&random=1701861386186&type=1
    • https://developers.weixin.qq.com/community/develop/doc/000260386388b09712bf4540f51000
    • https://mp.weixin.qq.com/cgi-bin/filepage?type=2&begin=0&count=12&token=1151313677&lang=zh_CN
    • https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Template_Message_Interface.html

你可能感兴趣的:(Spring,Boot,spring,boot,微信,java)