蒲柳青 發表於 2021-1-24 16:48:00

微信个人公众号开发-java

<h2>一:申请公众号</h2>
<p>&nbsp; &nbsp;无聊的去申请一个个人公众号,试了下微信实现自定义菜单与自定义对话的一些基本功能,如果多花点时间,金钱(= =!)还是有不少功能可以使用,还是蛮有意思的。</p>
<p>废话不多说,先看一下申请的流程。首先进入微信公众平台申请一个个人的订阅号,其实它自带的功能里就有自动回复的设置,不过还是试试开发接口去实现,更有意思。</p>
<p><img src="https://img2020.cnblogs.com/blog/998887/202101/998887-20210123103530381-126994491.png" alt="" width="912" height="549" loading="lazy"></p>
<p>&nbsp;</p>
<h2>&nbsp;二:接口实现</h2>
<p>在开发-基本配置里,服务器配置就是当前公众号和我们自己服务器建立连接的配置。</p>
<p>controller:该方法的路径就是基本配置里的服务器地址(URL)</p>
<div class="cnblogs_Highlighter">
<pre class="brush:java;collapse:true;;gutter:true;">/**
   * 微信接入
   * @param
   * @return
   * @throws IOException
   */
    @RequestMapping(value="/wechat/connect",method = {RequestMethod.GET, RequestMethod.POST})
    @ResponseBody
    public void connectWeixin(HttpServletRequest request, HttpServletResponse response) throws IOException {
      // 将请求、响应的编码均设置为UTF-8(防止中文乱码)
      request.setCharacterEncoding("UTF-8");//微信服务器POST消息时用的是UTF-8编码,在接收时也要用同样的编码,否则中文会乱码;
      response.setCharacterEncoding("UTF-8"); //在响应消息(回复消息给用户)时,也将编码方式设置为UTF-8,原理同上;boolean isGet = request.getMethod().toLowerCase().equals("get");

      PrintWriter out = response.getWriter();

      try {
            if (RequestMethod.GET.name().equals(request.getMethod())) {
                String signature = request.getParameter("signature");// 微信加密签名
                String timestamp = request.getParameter("timestamp");// 时间戳
                String nonce = request.getParameter("nonce");// 随机数
                String echostr = request.getParameter("echostr");//随机字符串

                // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
                if (SignUtil.checkSignature(DNBX_TOKEN, signature, timestamp, nonce)) {
                  LOGGER.info("Connect the weixin server is successful.");
                  response.getWriter().write(echostr);
                } else {
                  LOGGER.error("Failed to verify the signature!");
                }
            }else{
                String respMessage = "";
                try {
                  respMessage = wechatService.weixinPost(request);
                  out.write(respMessage);
                  LOGGER.info("The request completed successfully");
                  LOGGER.info("to weixin server "+respMessage);
                } catch (Exception e) {
                  LOGGER.error("Failed to convert the message from weixin!", e);
                }
            }
      } catch (Exception e) {
            LOGGER.error("Connect the weixin server is error.", e);
      }finally{
            out.close();
      }
    }
</pre>
</div>
<p>  校验的代码如下:</p>
<div class="cnblogs_Highlighter">
<pre class="brush:java;gutter:true;">public class SignUtil {

    /**
   * 验证签名
   *
   * @param token 微信服务器token,在env.properties文件中配置的和在开发者中心配置的必须一致
   * @param signature 微信服务器传过来sha1加密的证书签名
   * @param timestamp 时间戳
   * @param nonce 随机数
   * @return
   */
    public static boolean checkSignature(String token,String signature, String timestamp, String nonce) {
      String[] arr = new String[] { token, timestamp, nonce };
      // 将token、timestamp、nonce三个参数进行字典序排序
      Arrays.sort(arr);

      // 将三个参数字符串拼接成一个字符串进行sha1加密
      String tmpStr = SHA1.encode(arr + arr + arr);

      // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
      return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
    }

}
</pre>
</div>
<p>  </p>
<div class="cnblogs_Highlighter">
<pre class="brush:java;gutter:true;">import java.security.MessageDigest;

/**
* 微信公众平台(JAVA) SDK
*
* SHA1算法
*
* @author helijun 2016/06/15 19:49
*/
public final class SHA1 {

    private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',
            '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

    /**
   * Takes the raw bytes from the digest and formats them correct.
   *
   * @param bytes the raw bytes from the digest.
   * @return the formatted bytes.
   */
    private static String getFormattedText(byte[] bytes) {
      int len = bytes.length;
      StringBuilder buf = new StringBuilder(len * 2);
      // 把密文转换成十六进制的字符串形式
      for (int j = 0; j &lt; len; j++) {
            buf.append(HEX_DIGITS[(bytes &gt;&gt; 4) &amp; 0x0f]);
            buf.append(HEX_DIGITS &amp; 0x0f]);
      }
      return buf.toString();
    }

    public static String encode(String str) {
      if (str == null) {
            return null;
      }
      try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
            messageDigest.update(str.getBytes());
            return getFormattedText(messageDigest.digest());
      } catch (Exception e) {
            throw new RuntimeException(e);
      }
    }
}
</pre>
</div>
<p>  其中加密字段分别为:signature:微信加密签名&nbsp;  timestamp:时间戳  nonce:随机数  echostr:随机字符串,</p>
<p>而代码中的DNBX_TOKEN 就是公众号里配置的&nbsp;<label for="">令牌(Token)。如果校验成功就把echostr返回给微信端即可。</label></p>
<p>&nbsp;</p>
<p>再来看看如何处理具体的post请求:</p>
<p>&nbsp;</p>
<div class="cnblogs_Highlighter">
<pre class="brush:java;gutter:true;">import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yxy.nova.bean.wechat.TextMessage;
import com.yxy.nova.util.MessageUtil;
import com.yxy.nova.util.SimpleHttpClient;
import com.yxy.nova.util.wechat.weather.TianQiCityID;
import com.yxy.nova.util.wechat.weather.TianQiWeatherHelper;
import com.yxy.nova.web.util.WebUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Map;

/**
* @date 2021/1/22 上午11:38
* @Description
*/
@Service
public class WechatServiceImpl implements WechatService {

    private final Logger LOGGER = LoggerFactory.getLogger(getClass());

    private static JSONObject accessToken = new JSONObject();

    @Value("${wechat-AppId}")
    private String appleId;
    @Value("${wechat-AppSecret}")
    private String appSecret;
    @Autowired
    private SimpleHttpClient simpleHttpClient;



    /**
   * 处理微信发来的请求
   *
   * @param request
   * @return
   */
    @Override
    public String weixinPost(HttpServletRequest request) {
      String respMessage = null;
      try {

            // xml请求解析
            Map&lt;String, String&gt; requestMap = MessageUtil.xmlToMap(request);

            // 发送方帐号(open_id)
            String fromUserName = requestMap.get("FromUserName");
            // 公众帐号
            String toUserName = requestMap.get("ToUserName");
            // 消息类型
            String msgType = requestMap.get("MsgType");
            // 消息内容
            String content = requestMap.get("Content");

            LOGGER.info("FromUserName is:" + fromUserName + ", ToUserName is:" + toUserName + ", MsgType is:" + msgType);

            // 文本消息
            if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
                //这里根据关键字执行相应的逻辑,只有你想不到的,没有做不到的

                TianQiCityID ci = new TianQiCityID();
                if(ci.getCityIDMap().get(content) !=null){
                  TextMessage text = new TextMessage();
                  TianQiWeatherHelper tianQiWeatherHelper = new TianQiWeatherHelper();
                  tianQiWeatherHelper.setSimpleHttpClient(simpleHttpClient);
                  text.setContent(tianQiWeatherHelper.getWeatherReportByCityName(content));
                  text.setToUserName(fromUserName);
                  text.setFromUserName(toUserName);
                  text.setCreateTime(System.currentTimeMillis() + "");
                  text.setMsgType(msgType);
                  respMessage = MessageUtil.textMessageToXml(text);
                }
                if ("天气".equals(content)) {
                  TextMessage text = new TextMessage();
                  TianQiWeatherHelper tianQiWeatherHelper = new TianQiWeatherHelper();
                  tianQiWeatherHelper.setSimpleHttpClient(simpleHttpClient);
                  text.setContent(tianQiWeatherHelper.getWeatherReportByIP(WebUtil.getRemoteAddr(request)));
                  text.setToUserName(fromUserName);
                  text.setFromUserName(toUserName);
                  text.setCreateTime(System.currentTimeMillis() + "");
                  text.setMsgType(msgType);
                  respMessage = MessageUtil.textMessageToXml(text);
                }

                //自动回复
//                TextMessage text = new TextMessage();
//                text.setContent("the text is" + content);
//                text.setToUserName(fromUserName);
//                text.setFromUserName(toUserName);
//                text.setCreateTime(System.currentTimeMillis() + "");
//                text.setMsgType(msgType);
//                respMessage = MessageUtil.textMessageToXml(text);

            }
//            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {// 事件推送
//                String eventType = requestMap.get("Event");// 事件类型
//
//                if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {// 订阅
//                  respContent = "欢迎关注xxx公众号!";
//                  return MessageResponse.getTextMessage(fromUserName , toUserName , respContent);
//                } else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {// 自定义菜单点击事件
//                  String eventKey = requestMap.get("EventKey");// 事件KEY值,与创建自定义菜单时指定的KEY值对应
//                  logger.info("eventKey is:" +eventKey);
//                  return xxx;
//                }
//            }
            //开启微信声音识别测试 2015-3-30
//            else if(msgType.equals("voice"))
//            {
//                String recvMessage = requestMap.get("Recognition");
//                //respContent = "收到的语音解析结果:"+recvMessage;
//                if(recvMessage!=null){
//                  respContent = TulingApiProcess.getTulingResult(recvMessage);
//                }else{
//                  respContent = "您说的太模糊了,能不能重新说下呢?";
//                }
//                return MessageResponse.getTextMessage(fromUserName , toUserName , respContent);
//            }
            //拍照功能
//            else if(msgType.equals("pic_sysphoto"))
//            {
//
//            }
//            else
//            {
//                return MessageResponse.getTextMessage(fromUserName , toUserName , "返回为空");
//            }
            // 事件推送
            else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
                String eventType = requestMap.get("Event");// 事件类型
                // 订阅
                if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {

                  TextMessage text = new TextMessage();
                  text.setContent("欢迎关注,xxx");
                  text.setToUserName(fromUserName);
                  text.setFromUserName(toUserName);
                  text.setCreateTime(System.currentTimeMillis() + "");
                  text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);

                  respMessage = MessageUtil.textMessageToXml(text);
                }
                // TODO 取消订阅后用户再收不到公众号发送的消息,因此不需要回复消息
                else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {// 取消订阅


                }
                // 自定义菜单点击事件
                else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {
                  String eventKey = requestMap.get("EventKey");// 事件KEY值,与创建自定义菜单时指定的KEY值对应
                  if (eventKey.equals("weather")) {
                        TextMessage text = new TextMessage();
                        text.setContent("请输入城市名称");
                        text.setToUserName(fromUserName);
                        text.setFromUserName(toUserName);
                        text.setCreateTime(System.currentTimeMillis() + "");
                        text.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);

                        respMessage = MessageUtil.textMessageToXml(text);
                  }
                }
            }
      }
      catch (Exception e) {
            LOGGER.error("error......");
      }
      return respMessage;
    }
}</pre>
</div>
<p>如上面的代码,我在文本消息中,简单的实现了根据城市名称查询天气情况。接口的实现就不贴出来了,就是一个简单的接口对接。</p>
<p>&nbsp;</p>
<h2>三:第三方接口</h2>
<p>网上有关于&nbsp;www.weather.com.cn网站获取天气情况的接口,但是本人测试使用发现返回字段比较少,而且刷新的不是很及时,</p>
<p>现在推荐一个免费的第三方接口:https://www.tianqiapi.com/,在api接口中有三个免费的接口,可以实现一些简单的功能了。</p>
<p>我在demo中使用的就是:免费实况天气接口:https://www.tianqiapi.com/index/doc?version=v6,用完感受还是不错的。</p>
<p>&nbsp;</p>
<p>至此,已经实现了公众号的部分功能,其实微信的接口文档还有很多,还有很多更好玩的功能,比如微信对话开放平台,有兴趣的可以申请开发权限,去探索一下吧。<em style="font-family: &quot;Courier New&quot;; font-size: 12px"><br></em></p>
<p>&nbsp;</p>
<div class="weui-desktop-form__controls weui-desktop-form__control-offset">&nbsp;</div>

</div>
<div id="MySignature" role="contentinfo">
    <div style="font-size: 18px">
<br><br>
================================================================== <br><br>
勇气是,尽管你感到害怕,但仍能迎难而上。<br>尽管你感觉痛苦,但仍能直接面对。<br>向前一步,也许一切都会不同。<br><br>
================================================================== <br><br></div><br><br>
来源:https://www.cnblogs.com/yxy-ngu/p/14316814.html
頁: [1]
查看完整版本: 微信个人公众号开发-java