spring boot 开发微信公众号

因为公司需要开发微信公众号模块,所以在网上看了很多关于spring boot微信公众号的开发,都感觉不能满足自己对代码简单处理。

按照我的思路,完成了一个微信公众号的业务功能的开发,并总结如下:

1,创建用于接收和返回的对象

import lombok.Data;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

@Data
@XmlRootElement(name="xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class WeChatMessageBo {
    private String ToUserName;
    private String FromUserName;
    private long CreateTime;

    private String MsgType;

    private String Event;
    // 消息id
    protected Long MsgId;
    // 文本内容
    private String Content;
    // 图片链接(由系统生成)
    private String PicUrl;
    // 图片消息媒体id,可以调用多媒体文件下载接口拉取数据
    private String MediaId;
  
    private String EventKey;
}
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import java.util.List;

@Data
@XmlRootElement(name="xml")
@XmlAccessorType(XmlAccessType.FIELD)
public class WeChatMessageVo {
    // 发送方的账号
    protected String FromUserName;
    // 接收方的账号(OpenID)
    protected String ToUserName;
    // 消息创建时间
    protected Long CreateTime;
    /**
     * 消息类型
     * text 文本消息
     * image 图片消息
     * voice 语音消息
     * video 视频消息
     * music 音乐消息
     * news 图文消息
     */
    protected String MsgType;
    // 语音
    @XmlElementWrapper(name="Voice")
    private String[] MediaId ;
    // 文本内容
    private String Content;

    private int ArticleCount;
    //图文消息
    @XmlElementWrapper(name="Articles")
    private List<NewItems> item;

}

  笔者把请求对象全部塞到bo里面,把返回的数据全部塞到vo里面,把数据简单处理,不再根据请求类型拆分不同的vo和bo

    2创建controller,直接采用jdk中原装的 unmarshaller 和 marshaller 对事件的报文进行解析和封装

 

 
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.IOException;


@RestController
@Api(tags = "微信公众号服务")
@RequestMapping(value = "/weChart")
@Slf4j
public class WeChartController {

    @Autowired(required = false)
    private WeChartService weChartService;

    private Unmarshaller unmarshaller;

    private Marshaller marshaller;

    @PostConstruct
    private void init(){
        JAXBContext context = null;
        try {
            context = JAXBContext.newInstance(WeChatMessageBo.class);
            unmarshaller = context.createUnmarshaller();
        } catch (JAXBException e) {
           log.info(e.toString());
        }
        try {
            context = JAXBContext.newInstance(WeChatMessageVo.class);

            marshaller = context.createMarshaller();

            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            marshaller.setProperty(Marshaller.JAXB_ENCODING,"UTF-8");

        } catch (JAXBException e) {
            log.info(e.toString());
        }


    }


    @ApiOperation(value = "验证")
    @RequestMapping(value = "auth",method = RequestMethod.GET)
    public String auth(  @RequestParam(name="signature") String signature,
                                            @RequestParam(name="timestamp") String timestamp,
                                            @RequestParam(name="nonce") String nonce,
                                            @RequestParam(name="echostr") String echostr){
      return  weChartService.auth(signature,timestamp,nonce,echostr);

    }

    @ApiOperation(value = "接收事件")
    @RequestMapping(value = "auth",method = RequestMethod.POST, produces = MediaType.APPLICATION_XML_VALUE)
    public void event(HttpServletRequest request,HttpServletResponse response  ){
        try {
            WeChatMessageBo weChatMessageBo = (WeChatMessageBo) unmarshaller.unmarshal(request.getInputStream());
            Object res = weChartService.response(weChatMessageBo);

            response.setCharacterEncoding("UTF-8");

            marshaller.marshal(res, response.getWriter());

        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}
 
 
3,建一个注解和一个对应接口,用来区分不同的请求类型,当然,其他使用if else也是可以实现的。这里为了代码更加简练
注解使用@Component,之后把引用该注解的类,就不需要再次添加该注解
import org.springframework.stereotype.Component;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Component
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Hkey {
        String name() default "";
}

 

创建一个接口,用来处理微信调用的不同类型
public interface Hander {
    public Object Hander(WeChatMessageBo bo);
}
 
 
4:service 层逻辑处理
public class WeChartService implements ApplicationContextAware {
    /*
     * 自定义token, 用作生成签名,从而验证安全性
     * */
    @Value("${weChart.token}")
    private String token ;

    @Value("${weChart.appid}")
    private   String appid;

    @Value("${weChart.secret}")
    private   String secret;

    @Autowired
    private WeChartApi weChartApi;

    @Autowired
    private CacheAsyncService cacheAsyncService;

    public String auth(String signature, String timestamp, String nonce, String echostr) {

        String sortStr = sort(token,timestamp,nonce);
        String mySignature = shal(sortStr);

        if(!"".equals(signature) && !"".equals(mySignature) && signature.equals(mySignature)){
          return echostr;
        }else {
            return null;
        }

    }


    
    public String sort(String token, String timestamp, String nonce) {
        String[] strArray = {token, timestamp, nonce};
        Arrays.sort(strArray);
        StringBuilder sb = new StringBuilder();
        for (String str : strArray) {
            sb.append(str);
        }
        return sb.toString();
    }

    public String shal(String str){
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-1");
            digest.update(str.getBytes());
            byte messageDigest[] = digest.digest();

            StringBuffer hexString = new StringBuffer();
            // 字节数组转换为 十六进制 数
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }


private HashMap<String, Hander> handers=new HashMap<>();


public Object response(WeChatMessageBo msg) {
    String msgType = msg.getMsgType();
    String Event = msg.getEvent();
    String EventKey = msg.getEventKey();
    Hander hander = handers.get(msgType + "." + Event + "." + EventKey);
    if (hander!=null){
        return  hander.Hander(msg);
    }
   return null;
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    Map<String, Hander> beansOfType = applicationContext.getBeansOfType(Hander.class);
    for ( Hander hander:  beansOfType.values()) {
        Hkey hkey = hander.getClass().getAnnotation(Hkey.class);
        handers.put(hkey.name(),hander);
    }

}

}
 
 
该步使用利用了spring的ApplicationContext,对之后使用的hander 进行处理,方便responer方法的使用(该service 为节选,功能都存在,拷贝时可能代码可能出现问题)

5具体方法的实现:
用户回复,回复用户图文消息或者文子消息
@Hkey(name="text.null.null")
public class Replay implements Hander {

    @Autowired
    private Mapper mapper;
    private String unfind= "对不起,没找到你搜索的数据。\n要不,联系一下客服?或者我给你笑一个?\uE057";
 
    @Override
    //回复用户和图文消息
    public Object Hander(WeChatMessageBo msg) {
        WeChatMessageVo out = new WeChatMessageVo();
        //把原来的发送方设置为接收方
        out.setToUserName(msg.getFromUserName());
        //把原来的接收方设置为发送方
        out.setFromUserName(msg.getToUserName());
        //获取接收的消息类型
        String msgType = msg.getMsgType();
        String content = msg.getContent();
        //设置消息创建时间
        out.setCreateTime(System.currentTimeMillis());
        out.setMsgType("news");
      //用户自定义数据
        List<Data> list = mapper.selectByNameLike(content);

        if(CollectionUtils.isEmpty(gameList)){
            out.setMsgType("text");
            out.setContent(unfind);
            return out;
        }else{

            ArrayList<NewItems> items = new ArrayList<NewItems>(list.size());
            list.stream().forEach(item->{
                NewItems newItems = new NewItems();
                newItems.setTitle(item.getName());
                newItems.setUrl(item.getUrl());
                newItems.setDescription(item.getDescription());
                newItems.setPicUrl(item.getBackgroundPicture());
                items.add(newItems);
            });
            out.setArticleCount(gameList.size());
            out.setItem(items);
            return out;
        }
    }
}

 


菜单按钮,作者菜单中有个按钮V31_HELP,其他使用可能会有很多按钮,就直接修改注解名字即可
@Hkey(name="event.CLICK.V31_HELP")
public class ForHelp implements Hander {
    private String  value= "你好,请拨打电话\n";

    //联系客服
    @Override
    public Object Hander(WeChatMessageBo msg) {
        WeChatMessageVo out = new WeChatMessageVo();
        //把原来的发送方设置为接收方
        out.setToUserName(msg.getFromUserName());
        //把原来的接收方设置为发送方
        out.setFromUserName(msg.getToUserName());
        //获取接收的消息类型
        String msgType = msg.getMsgType();
        //设置消息创建时间
        out.setCreateTime(System.currentTimeMillis());
        out.setMsgType("text");
        out.setContent(value);
        return out;
    }
}
 
 

 

 
 
用户订阅时代码,此处代码,笔者未做测试,使用的用户要看一下注解名字是否正确
@Hkey(name = "event.subscribe.null")
@Service
public class Subscribe implements Hander {
    //用户订阅

    @Autowired
    private Mapper mapper;
    @Override
    public Object Hander(WeChatMessageBo msg) {
        WeChatMessageVo out = new WeChatMessageVo();
        //把原来的发送方设置为接收方
        out.setToUserName(msg.getFromUserName());
        //把原来的接收方设置为发送方
        out.setFromUserName(msg.getToUserName());
        //获取接收的消息类型
        //String msgType = msg.getMsgType();
        //设置消息创建时间
        out.setCreateTime(System.currentTimeMillis());
         
        out.setMsgType("news");
        out.setArticleCount(4);
        ArrayList<NewItems> items = new ArrayList<NewItems>(4);
         

       //用户自定义数据
        List<Data> list = mapper.selectSubScribeData();
        list .stream().forEach(item->{
            NewItems newItems = new NewItems();
            newItems.setTitle(item.getTitle());
            newItems.setUrl(item.getUrl());
            newItems.setDescription(item.getDes());
            newItems.setPicUrl(item.getPicUrl());
            items.add(newItems);
        });
        out.setItem(items);
        return out;
    }
}

 查询数据库的就不再写了,大概工程结构就是这个样子的,我不想写一堆的xml解析,也不想拷贝的或者是引入各种包,既然如此,那就直接原生的marshaller 吧,用的时候,感觉也很不错。就分享给大家吧

转载于:https://www.cnblogs.com/see-saw/p/11243206.html

### 回答1: Spring Boot 是一个用于快速构建基于 Java 的应用程序的开源框架。微信公众号是微信平台提供的一种服务,允许开发者在微信上创建自己的公众号,从而能够与用户进行交互、提供信息、进行营销等。 使用 Spring Boot 构建微信公众号意味着我们可以利用 Spring Boot 的优势快速开发和部署我们的公众号应用。Spring Boot 提供了丰富的特性和功能,例如自动配置、快速启动、内嵌 Servlet 容器等,使得我们能够以更高效的方式开发微信公众号应用程序。 在使用 Spring Boot 构建微信公众号时,我们可以利用微信公众平台提供的开发接口和 SDK,与微信服务器进行交互。该接口和 SDK 提供了丰富的功能,例如接收和回复消息、获取用户信息、上传素材、推送模板消息等。我们可以通过编写相应的代码,与微信服务器进行通信,并实现自定义的业务逻辑。 使用 Spring Boot 构建微信公众号还可以结合其他技术和框架,例如 Spring MVC、Spring Data JPA 等,来实现更复杂的功能。我们可以利用 Spring MVC 来处理微信服务器的请求,使用 Spring Data JPA 来操作数据库存储用户信息等。这些技术和框架的集成可以提高开发效率和代码质量。 总之,使用 Spring Boot 构建微信公众号能够帮助我们快速开发和部署应用,同时利用 Spring Boot 提供的特性和功能,使得开发过程更加高效和简化。 ### 回答2: Spring Boot是一个用于构建独立的、生产级的Spring应用程序的框架。它简化了Spring应用程序的配置和部署过程,并提供了许多默认配置,让开发者可以专注于业务逻辑的实现。 微信公众号是基于微信开放平台的一种应用,开发者可以通过微信公众号来与用户进行交互。使用微信公众号可以实现发送消息、接收消息、拉取用户信息等功能。通过集成Spring Boot框架,可以更加方便地开发和管理微信公众号应用。 使用Spring Boot开发微信公众号,可以通过Spring框架的依赖注入和控制反转等功能,更加灵活地管理公众号的业务逻辑和组件。开发者可以定义不同的Controller来处理用户的请求,并通过注解来实现自动装配和依赖管理。此外,Spring Boot还提供了许多自动配置的功能,使得开发者可以快速构建、测试和部署微信公众号应用。 Spring Boot还支持与微信公众平台的API进行交互,开发者可以通过SDK或自定义开发来实现微信公众号的各种功能。例如,可以使用SDK提供的API来发送消息、获取用户信息、创建菜单等。 总之,通过使用Spring Boot框架,开发者可以更加高效地开发和管理微信公众号应用,减少了繁琐的配置和部署过程,提高了开发效率和代码质量。 ### 回答3: Spring Boot 是一个基于 Spring 框架的开源项目,用于快速构建、运行和部署应用程序。微信公众号是基于微信平台的一种社交媒体营销工具,被广泛用于企业的宣传推广、客户互动等方面。那么,Spring Boot 如何与微信公众号结合呢? 首先,我们可以通过 Spring Boot 提供的 Web 应用程序开发框架,结合微信公众号开发文档和接口,编写实现微信公众号功能的后端代码。通过使用 Spring Boot 的 MVC 架构,我们可以定义处理微信公众号各种请求的控制器,并利用 Spring 的依赖注入特性,调用微信公众号提供的接口实现相关功能。 其次,Spring Boot 提供了丰富的开箱即用的插件和组件,可以方便地集成第三方库和工具。我们可以利用 Spring Boot 的插件,快速集成微信公众号的 SDK 或 API,简化开发过程。同时,Spring Boot 还提供了很多自动配置选项,可以减少开发者的配置工作,提高开发效率。 另外,通过使用 Spring Boot 的自动化部署特性,我们可以将微信公众号的后端应用程序快速打包为可执行的 JAR 文件,并使用常用的部署或容器工具(如 Docker、Tomcat 等)进行部署和运行。这样,我们可以轻松地将微信公众号应用程序部署到云端,实现高可用性和扩展性。 总之,Spring Boot 提供了一个便捷、快速、可靠的开发框架,可以与微信公众号无缝结合,帮助开发者快速构建和部署功能丰富的微信公众号应用程序。无论是小型企业还是大型企业,利用 Spring Boot 可以极大地简化微信公众号开发和运维工作,提升开发效率和用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值