微信开发-素材/消息管理接口

开始

本文是 微信公众号开发者模式介绍及接入 的后续,如没看过前文的话,可能看本文会有些懵逼。本文主要介绍微信公众平台的素材、消息管理接口的开发。由于个人的订阅号是没有大多数接口的权限的,所以我们需要使用微信官方提供的测试号来进行开发。测试号的申请可参考下文:


图文消息

本小节我们来开发回复图文消息的功能,官方文档地址如下:

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端的微信,是可以正常播放的,但实际手机端很有可能无法播放,这也是微信的一个小坑

原文地址:http://blog.51cto.com/zero01/2136341

时间: 2024-11-10 01:08:59

微信开发-素材/消息管理接口的相关文章

微信开发回复消息收不到的最大罪魁祸首,微信公众账号为什么回复消息不成功的原因解决办法

上一篇文章讲到如何配置微信开发,以及.NET平台使用MVC做一个小DEMO的流程,还有常见收不到消息的问题,本篇文章讲的是一个很容易让人忽略但又是很重要的问题导致用户收不到响应的消息原因. 上一篇文章链接:[C#开发微信.NET平台MVC微信开发 发送普通消息Demo以及收不到消息的问题] (如果你是通过搜索引擎进来的建议你看下上篇文章,说不定就解决你的问题了) 这个问题出在代码 首先我们的C#代码是这么写的: 1 //接收/回复 消息接口 2 [HttpPost] 3 public Actio

C#开发微信门户及应用(23)-微信小店商品管理接口的封装和测试

在上篇<C#开发微信门户及应用(22)-微信小店的开发和使用>里面介绍了一些微信小店的基础知识,以及对应的对象模型,本篇继续微信小店的主题,介绍其中API接口的封装和测试使用.微信小店的相关对象模型,基本上包括了常规的商品.商品分组.货架.库存.订单这些模型,还有商品分类,商品分类属性.商品分类SKU.快递邮寄模板.图片管理等功能.本文介绍的接口封装也就是基于这些内容进行的,并针对接口的实现进行测试和使用. 1.商品管理接口的定义 前面文章介绍了微信小店的对象模型,如下所示. 这个图形基本上覆

Java微信公众平台开发_03_消息管理之被动回复消息

GitHub源码:https://github.com/shirayner/weixin_gz 一.本节要点 1.回调url 上一节,我们启用服务器配置的时候,填写了一个服务器地址(url),如下图,这个url就是回调url,是开发者用来接收微信消息和事件的接口URL .也就是说,用户在微信公众号中发送的消息会被推送到这个回调url,而我们可以接收用户的消息,并进行回复. 2.被动回复消息的流程 官方文档: 我们在上一节中设置的消息加解密方式是安全模式.因此在用户发给公众号的消息(接收消息)以及

微信开发之消息接收与回复--weixin-java-tools

一.前言 在上一篇文章<微信开发之如何使用开发工具--weixin-java-tools>中我给各位介绍了weixin-java-tools,并且介绍了如何使用weixin-java-tools接入我们微信开发者模式,本次就针对这个工具给大家介绍消息的接收与回复 二.消息接收与回复 先来说说接收消息, 当普通微信用户向公众账号发消息时,微信服务器会先接收到用户发送的消息,然后将用户消息按照指定的XML格式组装好数据,最后POST消息的XML数据包到开发者填写的URL上. 接收到的普通消息的消息

Java版微信公共号开发之分组管理接口

开发者可以使用接口,对公众平台的分组进行查询.创建.修改操作,也可以使用接口在需要时移动用户到某个分组. 创建分组 一个公众账号,最多支持创建500个分组. 接口调用请求说明 http请求方式: POST(请使用https协议) https://api.weixin.qq.com/cgi-bin/groups/create?access_token=ACCESS_TOKEN POST数据格式:json POST数据例子:{"group":{"name":"

微信开发:客服接口向用户发送消息

发送文本消息: $access_token = ""; $url='https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token='.$access_token;$content = '{    "touser":"'.$openid.'",    "msgtype":"text",    "text":    { 

微信开发之消息接收--视频消息(八)

一.消息格式 <xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1357290913</CreateTime> <MsgType><![CDATA[video]]></MsgType> <

夺命雷公狗---微信开发39----微信语言识别接口1

语音识别接口的基本介绍 注意: 由于客户端缓存,开发者开启或者关闭语音识别功能,对新关注者立即生效,对已关注用户需要24小时生效,开发者可以从新关注帐号进行测试. 我们可以在测试号下方的体验接口权限表里面找到“接收语音识别结果”,如下图所示 现在这里是显示的关闭,就是开启,如果想使用,那么就必“关闭”. 点击一下开启后,他就会问你是否关闭,所以这里的意思是相反的,如图所示: 然后再点击一下确定即可关闭. 不过我们现在要对他进行开发所以这里就要显示关闭这两个字,然后点击“接收语音识别结果”即可进入

微信开发(三)消息的自动回复和关注后自动回复

1.前置与微信接口对接成功后. 3.创建一个实体类消息类.里面设置的变量和属性参照微信开发者文档,https://mp.weixin.qq.com/wiki. 2.修改Servlet中dopost方法. 3.在消息操作类中增加方法,添加主菜单的方法,拼接字符串的方法,回复弹出信息的方法(1-5). 参考代码:消息操作类中的代码 1 package com.util; 2 import java.io.IOException; 3 import java.io.InputStream; 4 imp