1. 程式人生 > >微信開發-素材/消息管理接口

微信開發-素材/消息管理接口

expires 都在 stream load 如何獲取 getname 5.5 tpi uil

開始

本文是 微信公眾號開發者模式介紹及接入 的後續,如沒看過前文的話,可能看本文會有些懵逼。本文主要介紹微信公眾平臺的素材、消息管理接口的開發。由於個人的訂閱號是沒有大多數接口的權限的,所以我們需要使用微信官方提供的測試號來進行開發。測試號的申請可參考下文:

  • 使用微信測試賬號對網頁進行授權

圖文消息

本小節我們來開發回復圖文消息的功能,官方文檔地址如下:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140543

回復圖文消息所需傳遞的參數如下:
技術分享圖片

註:多圖文消息不會顯示Description參數的信息

官方的圖文消息示例數據結構如下:

<xml>
    <ToUserName>
        <![CDATA[toUser]]>
    </ToUserName>
    <FromUserName>
        <![CDATA[fromUser]]>
    </FromUserName>
    <CreateTime>12345678</CreateTime>
    <MsgType>
        <![CDATA[news]]>
    </MsgType>
    <ArticleCount>2</ArticleCount>
    <Articles>
        <item>
            <Title>
                <![CDATA[title1]]>
            </Title>
            <Description>
                <![CDATA[description1]]>
            </Description>
            <PicUrl>
                <![CDATA[picurl]]>
            </PicUrl>
            <Url>
                <![CDATA[url]]>
            </Url>
        </item>
        <item>
            <Title>
                <![CDATA[title]]>
            </Title>
            <Description>
                <![CDATA[description]]>
            </Description>
            <PicUrl>
                <![CDATA[picurl]]>
            </PicUrl>
            <Url>
                <![CDATA[url]]>
            </Url>
        </item>
    </Articles>
</xml>

圖文消息都在Articles標簽內,而每個item標簽都包含一條圖文消息,有多少個item標簽就代表有多少條圖文消息。

在開發回復圖文消息的時候,我們需要使用到一張圖片來作為圖文消息的封面,找一個圖片文件放在工程的resources/static目錄下即可,並確保能夠在外網上訪問:
技術分享圖片

看完了官方的示例數據及文檔,那麽我們就來開發一下圖文消息的回復吧。首先是創建一個基類,封裝通用的字段,代碼如下:

package org.zero01.weixin.mqdemo.vo;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Getter;
import lombok.Setter;

/**
 * @program: mq-demo
 * @description: 圖文消息基類
 * @author: 01
 * @create: 2018-07-02 20:24
 **/
@Getter
@Setter
public class BaseMassage {

    /**
     * 接收方賬號
     */
    @XStreamAlias("ToUserName")
    private String toUserName;

    /**
     * 發送方賬號
     */
    @XStreamAlias("FromUserName")
    private String fromUserName;

    /**
     * 消息創建時間 (整型)
     */
    @XStreamAlias("CreateTime")
    private long createTime;

    /**
     * 消息類型
     */
    @XStreamAlias("MsgType")
    private String msgType;
}

然後是具體的封裝每條圖文消息字段的對象,代碼如下:

package org.zero01.weixin.mqdemo.vo;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Getter;
import lombok.Setter;

/**
 * @program: mq-demo
 * @description: 圖文消息對象
 * @author: 01
 * @create: 2018-07-02 20:19
 **/
@Getter
@Setter
public class NewsItem{
    @XStreamAlias("Title")
    private String title;

    @XStreamAlias("Description")
    private String description;

    @XStreamAlias("PicUrl")
    private String picUrl;

    @XStreamAlias("Url")
    private String url;
}

接著是包含每條圖文消息的容器對象,代碼如下:

package org.zero01.weixin.mqdemo.vo;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Getter;
import lombok.Setter;

import java.util.List;

/**
 * @program: mq-demo
 * @description: 圖文消息容器對象
 * @author: 01
 * @create: 2018-07-02 20:29
 **/
@Getter
@Setter
public class NewsMessage extends BaseMassage{
    @XStreamAlias("ArticleCount")
    private int articleCount;

    @XStreamAlias("Articles")
    private List<NewsItem> articles;
}

將圖文消息結構都封裝成一個個的類後,就是需要組裝圖文消息以及將組裝好的圖文消息轉換成xml格式的數據,發送給微信服務器了。所以我們需要在MessageUtil類中,新增如下兩個方法:

/**
 * 圖文消息轉換為xml
 *
 * @param newsMessage
 * @return
 */
public static String newsMessageToXml(NewsMessage newsMessage) {
    XStream xStream = new XStream();
    xStream.processAnnotations(new Class[]{NewsItem.class, NewsMessage.class});
    xStream.alias("xml", newsMessage.getClass());
    xStream.alias("item", NewsItem.class);

    return xStream.toXML(newsMessage);
}

/**
 * 組裝圖文消息
 *
 * @param toUserName
 * @param fromUserName
 * @return
 */
public static String initNewsMessage(String toUserName, String fromUserName) {
    List<NewsItem> newsItemList = new ArrayList<>();
    NewsMessage newsMessage = new NewsMessage();

    NewsItem newsItem = new NewsItem();
    newsItem.setTitle("圖文消息");
    newsItem.setDescription("這是一個圖文消息");
    newsItem.setPicUrl("http://zero.mynatapp.cc/code.jpg");
    newsItem.setUrl("www.baidu.com");
    newsItemList.add(newsItem);

    newsMessage.setToUserName(fromUserName);
    newsMessage.setFromUserName(toUserName);
    newsMessage.setCreateTime(System.currentTimeMillis());
    newsMessage.setMsgType(MessageTypeEnum.MSG_NEWS.getMsgType());
    newsMessage.setArticles(newsItemList);
    newsMessage.setArticleCount(newsItemList.size());

    return newsMessageToXml(newsMessage);
}

最後修改WeChatMqController中的text方法,增加一條判斷,判斷當用戶輸入數字1時,則回復圖文消息。代碼如下:

@PostMapping("/common")
public String text(@RequestBody String xmlStr) {
    // 將xml格式的數據,轉換為 AllMessage 對象
    AllMessage allMessage = MessageUtil.xmlToAllMessage(xmlStr);

    // 是否是文本消息類型
    if (allMessage.getMsgType().equals(MessageTypeEnum.MSG_TEXT.getMsgType())) {
        // 用戶輸入數字1時,回復圖文消息
        if ("1".equals(allMessage.getContent())) {
            return MessageUtil.initNewsMessage(allMessage.getToUserName(), allMessage.getFromUserName());
        }
        // 自動回復用戶所發送的文本消息
        return MessageUtil.autoReply(allMessage, ContentEnum.CONTENT_PREFIX.getContent() + allMessage.getContent());
    }
    // 是否是事件推送類型
    else if (allMessage.getMsgType().equals(MessageTypeEnum.MSG_EVENT.getMsgType())) {
        // 是否為訂閱事件
        if (EventType.EVENT_SUBSCRIBE.getEventType().equals(allMessage.getEvent())) {
            // 自動回復歡迎語
            return MessageUtil.autoReply(allMessage, ContentEnum.CONTENT_SUBSCRIBE.getContent());
        }
    } else {
        // 暫不支持文本以外的消息回復
        return MessageUtil.autoReply(allMessage, ContentEnum.CONTENT_NONSUPPORT.getContent());
    }
    return MessageUtil.autoReply(allMessage, ContentEnum.CONTENT_NONSUPPORT.getContent());
}

完成以上代碼的編寫後,啟動SpringBoot,打開微信公眾號,測試結果如下:
技術分享圖片


access_token的獲取

本小節我們來看看如何獲取access_token,官方文檔地址如下:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140183

access_token是什麽?官方的定義如下:

access_token是公眾號的全局唯一接口調用憑據,公眾號調用各接口時都需使用access_token。開發者需要進行妥善保存。access_token的存儲至少要保留512個字符空間。access_token的有效期目前為2個小時,需定時刷新,重復獲取將導致上次獲取的access_token失效。

調用接口獲取access_token需要傳遞的參數說明如下:
技術分享圖片

獲取access_token成功後,接口所返回的參數說明如下:
技術分享圖片

從文檔中我們可以看到,調用接口獲取access_token時需要傳遞appid和secret,appid和secret可以在公眾號的基本配置頁面中獲取,如下:
技術分享圖片

然後我們還需要安裝提示,設置一下白名單的ip,即你機器的ip,不然是無法調用接口獲取access_token的,如下:
技術分享圖片

將appid、secret以及獲取access_token的接口url,配置到SpringBoot的配置文件中,如下:

wechat:
  mpAppid: wx8ed1xxxxxx9513dd
  mpAppSecret: 0c1b5b7ea5xxxxxxxxx14cb5b61258
  accessTokenUrl: https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

在工程中新建一個config包,在該包下新建一個 WeXinConfig 配置類,用於加載配置文件中所配置的appid和secret:

package org.zero01.weixin.mqdemo.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @program: mq-demo
 * @description: 微信公眾號配置類
 * @author: 01
 * @create: 2018-07-03 20:50
 **/
@Data
@Configuration
@ConfigurationProperties(prefix = "wechat")
public class WeXinConfig {
    private String mpAppid;
    private String mpAppSecret;
    private String accessTokenUrl;
}

因為我們需要序列化json數據以及發送http請求給微信服務器,所以需要使用到一些工具包,在maven的pom.xml文件中,加入如下依賴:

<!-- https://mvnrepository.com/artifact/org.json/json -->
<dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20160810</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.5</version>
</dependency>

在util包下新建一個 WeiXinUtil 工具類,在該類中封裝get、post請求方法,以及獲取access_token的方法。代碼如下:

package org.zero01.weixin.mqdemo.util;

import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.zero01.weixin.mqdemo.config.WeXinConfig;
import org.zero01.weixin.mqdemo.vo.AccessToken;

import java.io.IOException;

/**
 * @program: mq-demo
 * @description: 
 * @author: 01
 * @create: 2018-07-03 21:04
 **/
@Component
public class WeiXinUtil {

    private static WeXinConfig wxConfig;

    public WeXinConfig getWeXinConfig() {
        return wxConfig;
    }

    @Autowired
    public void setWeXinConfig(WeXinConfig wxConfig) {
        WeiXinUtil.wxConfig = wxConfig;
    }

    /**
     * get請求
     *
     * @param url
     * @return
     */
    public static JSONObject doGet(String url) throws IOException {
        CloseableHttpClient client = HttpClientBuilder.create().build();
        HttpGet httpGet = new HttpGet(url);

        HttpResponse response = client.execute(httpGet);
        String result = EntityUtils.toString(response.getEntity(), "utf-8");

        return new JSONObject(result);
    }

    /**
     * post請求
     *
     * @param url
     * @param outStr
     * @return
     */
    public static JSONObject doPost(String url, String outStr) throws IOException {
        CloseableHttpClient client = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(url);
        httpPost.setEntity(new StringEntity(outStr, "utf-8"));

        HttpResponse response = client.execute(httpPost);
        String result = EntityUtils.toString(response.getEntity(), "utf-8");

        return new JSONObject(result);
    }

    /**
     * 獲取access_token
     *
     * @return
     * @throws IOException
     */
    public static AccessToken getAccessToken() throws IOException {
        AccessToken token = new AccessToken();
        // 替換appid和secret
        String url = wxConfig.getAccessTokenUrl()
                .replace("APPID", wxConfig.getMpAppid())
                .replace("APPSECRET", wxConfig.getMpAppSecret());

        JSONObject jsonObject = doGet(url);
        token.setToken(jsonObject.getString("access_token"));
        token.setExpiresIn(jsonObject.getInt("expires_in"));

        return token;
    }
}

完成以上代碼的編寫後,新建一個測試類,測試一下是否能正常獲取到access_token。測試代碼如下:

package org.zero01.weixin.mqdemo.util;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.zero01.weixin.mqdemo.vo.AccessToken;

import java.io.IOException;

import static org.junit.Assert.*;

@RunWith(SpringRunner.class)
@SpringBootTest
public class WeiXinUtilTest {

    @Test
    public void getAccessToken() throws IOException {
        AccessToken accessToken = WeiXinUtil.getAccessToken();
        System.out.println("access_token: " + accessToken.getToken());
        System.out.println("有效時間: " + accessToken.getExpiresIn());
    }
}

運行以上測試用例後,控制臺輸出如下:

access_token: 11_AMxhxO9soXndEc6XI-0hG0CWQ_oVQjaiPol6P2eMDLrSYpIrbiNMjHEDFwoOiKwG-ckgwPTHCiWypzK_reZohT7H5UdEYUmdlU_qq-oGQefv9q9A4mEkFV5WyiEFK5q5SsvsLR5QIKcjf1BhLDEfAIAAST
有效時間: 7200

從測試結果中,可以看到,成功獲取到了access_token,並且這個access_token的有效期是7200秒,也就是兩個小時,和官方文檔描述的一致。

一般在實際的項目開發中,我們都會把這個access_token緩存起來,緩存到本地或者nosql數據庫中,然後每隔1.5個小時或2個小時的時候,就重新獲取一次access_token,刷新緩存。這樣做是為了避免在每個邏輯點都去重新獲取access_token,因為這樣會導致服務的不穩定,而且微信也規定了獲取access_token的接口每天只能調用2000次,如果每個邏輯點都去重新獲取access_token的話,不僅會導致服務不穩定,還容易把調用次數給花完。如下:
技術分享圖片


圖片消息回復

本小節我們來看看如何進行圖片消息的回復,官方文檔地址如下:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140543

回復圖片消息所需傳遞的參數如下:
技術分享圖片

官方的圖文消息示例數據結構如下:

<xml>
    <ToUserName>
        <![CDATA[toUser]]>
    </ToUserName>
    <FromUserName>
        <![CDATA[fromUser]]>
    </FromUserName>
    <CreateTime>12345678</CreateTime>
    <MsgType>
        <![CDATA[image]]>
    </MsgType>
    <Image>
        <MediaId>
            <![CDATA[media_id]]>
        </MediaId>
    </Image>
</xml>

從所需傳遞的參數列表中可以看到,回復圖片消息時需要傳遞一個MediaId,這是通過素材管理中的接口上傳多媒體文件,得到的id。所以在開發回復圖片消息的接口前,我們還需要開發一個上傳多媒體文件的接口,以此來獲得MediaId。關於素材管理接口的官方文檔地址如下:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738726

新增臨時素材接口調用說明如下:
技術分享圖片

上傳素材成功後,返回的參數如下:
技術分享圖片

有一點要說明的是,個人的訂閱號是沒有素材管理接口的權限的,所以我們需要將之前配置的appid和AppSecret配置為測試號的,不然接口會調用失敗,如果是已認證的服務號就可以直接使用。

由於需要上傳圖片素材才能發送圖片消息,所以首先需要在 WexinUtil 中,新增一個 upload 方法,用於上傳臨時圖片素材並返回素材的media_id。

但是在寫代碼前,需要先將上傳臨時素材的接口url地址配置到SpringBoot的配置文件中,如下:

wechat:
  ...
  uploadUrl: https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE

然後在配置類裏加上這個配置的字段,如下:

...
public class WeXinConfig {
    ...
    private String uploadUrl;
}

upload 方法代碼如下:

/**
 * 上傳臨時素材
 *
 * @param filePath    需要上傳的文件所在路徑
 * @param accessToken access_token
 * @param type        素材類型
 * @return media_id
 * @throws IOException
 */
public static String upload(String filePath, String accessToken, String type, String key) throws IOException {
    File file = new File(filePath);
    if (!(file.exists() || file.isFile())) {
        throw new IOException("文件不存在");
    }

    String url = wxConfig.getUploadUrl()
            .replace("ACCESS_TOKEN", accessToken)
            .replace("TYPE", type);

    URL urlObj = new URL(url);
    // 打開連接
    HttpURLConnection connection = (HttpURLConnection) urlObj.openConnection();

    // 設置屬性
    connection.setRequestMethod("POST");
    connection.setDoInput(true);
    connection.setDoOutput(true);
    connection.setUseCaches(false);

    // 設置頭信息
    connection.setRequestProperty("Connection", "Keep-Alive");
    connection.setRequestProperty("Charset", "UTF-8");

    // 設置邊界
    String boundary = "----------" + System.currentTimeMillis();
    connection.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);

    StringBuilder sb = new StringBuilder();
    sb.append("--").append(boundary).append("\r\n")
            .append("Content-Disposition;form-data;name=\"file\";filename=\"")
            .append(file.getName())
            .append("\"\r\n")
            .append("Content-Type:application/octet-stream\r\n\r\n");

    byte[] head = sb.toString().getBytes("UTF-8");

    // 獲得輸出流
    OutputStream output = new DataOutputStream(connection.getOutputStream());
    // 輸出表頭
    output.write(head);

    // 文件正文部分
    // 把文件以流文件的方式,推入到url中
    DataInputStream input = new DataInputStream(new FileInputStream(file));
    int bytes = 0;
    byte[] bufferOutput = new byte[1024];
    while ((bytes = input.read(bufferOutput)) != -1) {
        output.write(bufferOutput, 0, bytes);
    }
    input.close();

    // 結尾部分,定義最後數據分割線
    byte[] foot = ("\r\n--" + boundary + "--\r\n").getBytes("utf-8");
    output.write(foot);
    output.flush();
    output.close();

    StringBuilder buffer = new StringBuilder();
    String result = null;

    try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
        String line = null;
        while ((line = reader.readLine()) != null) {
            buffer.append(line);
        }

        result = buffer.toString();
    } catch (IOException e) {
        e.printStackTrace();
    }

    JSONObject jsonObject = new JSONObject(result);
    log.info("response data: {}", jsonObject);

    return jsonObject.getString(key);
}

在測試類中新增一個測試方法,測試代碼如下:

@Test
public void upload() throws IOException {
    String filePath = "Z:/v2-9b17df91629f842edd472d7cfcaa9c4b_hd.jpg";
    AccessToken accessToken = WeiXinUtil.getAccessToken();
    String mediaId = WeiXinUtil.upload(filePath, accessToken.getToken(), "image");

    System.out.println(mediaId);
}

控制臺輸出結果如下:

mediaId: 5_PCrofX1_KIpSfWzJE-tu7AxQjxw6zlQ44oBuUkM_PZ6FiPeDY0a7vcWU2zdap9

獲取到media_id後,就可以開始開發回復圖片消息功能了,首先根據官方給出的數據結構,封裝好各個實體類。Image類代碼如下:

package org.zero01.weixin.mqdemo.vo;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;

@Data
public class Image {
    @XStreamAlias("MediaId")
    private String mediaId;
}

ImageMessage類代碼如下:

package org.zero01.weixin.mqdemo.vo;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;

@Data
public class ImageMessage extends BaseMassage {
    @XStreamAlias("Image")
    private Image image;
}

然後在MessageUtil中新增如下兩個方法:

/**
 * 將圖片消息轉換為xml
 *
 * @param imageMessage
 * @return
 */
public static String imageMessageToXml(ImageMessage imageMessage) {
    XStream xStream = new XStream();
    xStream.processAnnotations(new Class[]{ImageMessage.class, Image.class});
    xStream.alias("xml", imageMessage.getClass());

    return xStream.toXML(imageMessage);
}

/**
 * 組裝圖片消息對象
 *
 * @param toUserName
 * @param fromUserName
 * @return
 */
public static String initImageMessage(String toUserName, String fromUserName) {
    Image image = new Image();
    image.setMediaId("5_PCrofX1_KIpSfWzJE-tu7AxQjxw6zlQ44oBuUkM_PZ6FiPeDY0a7vcWU2zdap9");
    ImageMessage imageMessage = new ImageMessage();
    imageMessage.setFromUserName(toUserName);
    imageMessage.setToUserName(fromUserName);
    imageMessage.setMsgType(MessageTypeEnum.MSG_IMAGE.getMsgType());
    imageMessage.setCreateTime(System.currentTimeMillis());
    imageMessage.setImage(image);

    return imageMessageToXml(imageMessage);
}

最後修改WeChatMqController中的text方法,增加一條判斷,判斷當用戶輸入數字2時,則回復圖片消息。代碼如下:

...

    if ("1".equals(allMessage.getContent())) {
        return MessageUtil.initNewsMessage(allMessage.getToUserName(), allMessage.getFromUserName());
    } else if ("2".equals(allMessage.getContent())) {
        return MessageUtil.initImageMessage(allMessage.getToUserName(), allMessage.getFromUserName());
    }

...

完成以上代碼的編寫後,重啟SpringBoot,打開微信公眾號,測試結果如下:
技術分享圖片


音樂消息回復

在上一小節中,我們介紹了如何開發回復圖片消息的功能,而其他類似的消息回復都是差不多的,這裏就不一一去贅述了。本小節我們來看看如何進行音樂消息回復的開發,官方文檔地址如下:

https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140543

回復音樂消息所需傳遞的參數如下:
技術分享圖片

官方的圖文消息示例數據結構如下:

<xml>
    <ToUserName>
        <![CDATA[toUser]]>
    </ToUserName>
    <FromUserName>
        <![CDATA[fromUser]]>
    </FromUserName>
    <CreateTime>12345678</CreateTime>
    <MsgType>
        <![CDATA[music]]>
    </MsgType>
    <Music>
        <Title>
            <![CDATA[TITLE]]>
        </Title>
        <Description>
            <![CDATA[DESCRIPTION]]>
        </Description>
        <MusicUrl>
            <![CDATA[MUSIC_Url]]>
        </MusicUrl>
        <HQMusicUrl>
            <![CDATA[HQ_MUSIC_Url]]>
        </HQMusicUrl>
        <ThumbMediaId>
            <![CDATA[media_id]]>
        </ThumbMediaId>
    </Music>
</xml>

開發音樂消息回復,我們需要一個音樂文件,找一個mp3文件放在工程的resources/static目錄下即可,並確保能夠在外網上訪問:
技術分享圖片

根據官方給出的數據結構,封裝好各個實體類。Music 實體類代碼如下:

package org.zero01.weixin.mqdemo.vo;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;

@Data
public class Music {

    @XStreamAlias("Title")
    private String title;

    @XStreamAlias("Description")
    private String description;

    @XStreamAlias("MusicUrl")
    private String musicUrl;

    @XStreamAlias("HQMusicUrl")
    private String hQMusicUrl;

    @XStreamAlias("ThumbMediaId")
    private String thumbMediaId;
}

MusicMessage 實體類代碼如下:

package org.zero01.weixin.mqdemo.vo;

import com.thoughtworks.xstream.annotations.XStreamAlias;
import lombok.Data;

@Data
public class MusicMessage extends BaseMassage {

    @XStreamAlias("Music")
    private Music music;
}

由於音樂消息需要傳遞一個ThumbMediaId,也就是縮略圖的媒體id。所以我們需要修改之前的測試代碼,以此獲取thumb_media_id,如下:

@Test
public void upload() throws IOException {
    String filePath = "Z:/v2-9b17df91629f842edd472d7cfcaa9c4b_hd.jpg";
    AccessToken accessToken = WeiXinUtil.getAccessToken();
    String thumbMediaId = WeiXinUtil.upload(filePath, accessToken.getToken(), "thumb","thumb_media_id");

    System.out.println("thumb_media_id: " + thumbMediaId);
}

執行以上測試方法,控制臺輸出的結果如下:

thumb_media_id: Iu9puUGeFcS8HWyBGepJfeGoLDV_sWg8vJTeG-akMhcSGrqFjvoimMhCfjWw8F53

復制好thumb_media_id,然後在MessageUtil中新增如下兩個方法:

/**
 * 將音樂消息轉換為xml
 *
 * @param musicMessage
 * @return
 */
public static String musicMessageToXml(MusicMessage musicMessage) {
    XStream xStream = new XStream();
    xStream.processAnnotations(new Class[]{MusicMessage.class, Music.class});
    xStream.alias("xml", musicMessage.getClass());

    return xStream.toXML(musicMessage);
}

/**
 * 組裝音樂消息對象
 *
 * @param toUserName
 * @param fromUserName
 * @return
 */
public static String initMusicMessage(String toUserName, String fromUserName) {
    Music music = new Music();
    music.setTitle("音樂消息");
    music.setDescription("這是一個音樂消息");
    music.setThumbMediaId("Iu9puUGeFcS8HWyBGepJfeGoLDV_sWg8vJTeG-akMhcSGrqFjvoimMhCfjWw8F53");
    music.setMusicUrl("http://zero.mynatapp.cc/Unravel.mp3");
    music.setHQMusicUrl("http://zero.mynatapp.cc/Unravel.mp3");

    MusicMessage musicMessage = new MusicMessage();
    musicMessage.setFromUserName(toUserName);
    musicMessage.setToUserName(fromUserName);
    musicMessage.setMsgType(MessageTypeEnum.MSG_MUSIC.getMsgType());
    musicMessage.setCreateTime(System.currentTimeMillis());
    musicMessage.setMusic(music);

    return musicMessageToXml(musicMessage);
}

最後修改WeChatMqController中的text方法,增加一條判斷,判斷當用戶輸入數字3時,則回復音樂消息。代碼如下:

...

    if ("1".equals(allMessage.getContent())) {
        return MessageUtil.initNewsMessage(allMessage.getToUserName(), allMessage.getFromUserName());
    } else if ("2".equals(allMessage.getContent())) {
        return MessageUtil.initImageMessage(allMessage.getToUserName(), allMessage.getFromUserName());
    } else if ("3".equals(allMessage.getContent())) {
        return MessageUtil.initMusicMessage(allMessage.getToUserName(), allMessage.getFromUserName());
    }

...

完成以上代碼的編寫後,重啟SpringBoot,打開微信測試公眾號進行測試,測試結果如下:
技術分享圖片

點擊音樂消息,打開後效果如下:
技術分享圖片

註:我這用的是pc端的微信,是可以正常播放的,但實際手機端很有可能無法播放,這也是微信的一個小坑

微信開發-素材/消息管理接口