Skip to content

Commit

Permalink
#532 实现微信AI开放接口的三个接口:语音上传、查询识别结果和微信翻译功能
Browse files Browse the repository at this point in the history
  • Loading branch information
binarywang committed Jun 10, 2018
1 parent 0daaa01 commit 5b5dada
Show file tree
Hide file tree
Showing 12 changed files with 377 additions and 6 deletions.
29 changes: 29 additions & 0 deletions weixin-java-mp/src/main/java/me/chanjar/weixin/mp/AiLangType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package me.chanjar.weixin.mp;

import lombok.Getter;

/**
* <pre>
* AI开放接口里的语言类型,目前只支持两种:中文和英文
* Created by BinaryWang on 2018/6/10.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
@Getter
public enum AiLangType {
/**
* 中文 汉语
*/
zh_CN("zh_CN"),
/**
* 英文 英语
*/
en_US("en_US");

private String code;

AiLangType(String code) {
this.code = code;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package me.chanjar.weixin.mp.api;

import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.AiLangType;

import java.io.File;

/**
* <pre>
* 微信AI开放接口(语音识别,微信翻译).
* https://mp.weixin.qq.com/wiki?t=resource/res_main&id=21516712282KzWVE
* Created by BinaryWang on 2018/6/9.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public interface WxMpAiOpenService {
String VOICE_UPLOAD_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/addvoicetorecofortext?format=%s&voice_id=%s&lang=%s";
String VOICE_QUERY_RESULT_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/queryrecoresultfortext";

/**
* <pre>
* 提交语音.
* 接口调用请求说明
*
* http请求方式: POST
* http://api.weixin.qq.com/cgi-bin/media/voice/addvoicetorecofortext?access_token=ACCESS_TOKEN&format=&voice_id=xxxxxx&lang=zh_CN
* 参数说明
*
* 参数 是否必须 说明
* access_token 是 接口调用凭证
* format 是 文件格式 (只支持mp3,16k,单声道,最大1M)
* voice_id 是 语音唯一标识
* lang 否 语言,zh_CN 或 en_US,默认中文
* 语音内容放body里或者上传文件的形式
* </pre>
*
* @param lang 语言,zh_CN 或 en_US,默认中文
* @param voiceFile 语音文件
* @param voiceId 语音唯一标识
*/
void uploadVoice(String voiceId, AiLangType lang, File voiceFile) throws WxErrorException;

/**
* <pre>
* 获取语音识别结果.
* 接口调用请求说明
*
* http请求方式: POST
* http://api.weixin.qq.com/cgi-bin/media/voice/queryrecoresultfortext?access_token=ACCESS_TOKEN&voice_id=xxxxxx&lang=zh_CN
* 请注意,添加完文件之后10s内调用这个接口
*
* 参数说明
*
* 参数 是否必须 说明
* access_token 是 接口调用凭证
* voice_id 是 语音唯一标识
* lang 否 语言,zh_CN 或 en_US,默认中文
* </pre>
*
* @param lang 语言,zh_CN 或 en_US,默认中文
* @param voiceId 语音唯一标识
*/
String queryRecognitionResult(String voiceId, AiLangType lang) throws WxErrorException;

/**
* 识别指定语音文件内容.
* 此方法揉合了前两两个方法:uploadVoice 和 queryRecognitionResult
*
* @param lang 语言,zh_CN 或 en_US,默认中文
* @param voiceFile 语音文件
* @param voiceId 语音唯一标识
*/
String recogniseVoice(String voiceId, AiLangType lang, File voiceFile) throws WxErrorException;

/**
* <pre>
* 微信翻译.
* 接口调用请求说明
*
* http请求方式: POST
* http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?access_token=ACCESS_TOKEN&lfrom=xxx&lto=xxx
* 参数说明
*
* 参数 是否必须 说明
* access_token 是 接口调用凭证
* lfrom 是 源语言,zh_CN 或 en_US
* lto 是 目标语言,zh_CN 或 en_US
* 源内容放body里或者上传文件的形式(utf8格式,最大600Byte)
* </pre>
*
* @param langFrom 源语言,zh_CN 或 en_US
* @param langTo 目标语言,zh_CN 或 en_US
* @param content 要翻译的文本内容
*/
String translate(AiLangType langFrom, AiLangType langTo, String content) throws WxErrorException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,13 @@ public interface WxMpService {
*/
WxMpMassMessageService getMassMessageService();

/**
* 返回AI开放接口方法的实现类对象,以方便调用其各个接口
*
* @return WxMpAiOpenService
*/
WxMpAiOpenService getAiOpenService();

void setKefuService(WxMpKefuService kefuService);

void setMaterialService(WxMpMaterialService materialService);
Expand Down Expand Up @@ -437,4 +444,6 @@ public interface WxMpService {
void setMemberCardService(WxMpMemberCardService memberCardService);

void setMassMessageService(WxMpMassMessageService massMessageService);

void setAiOpenService(WxMpAiOpenService aiOpenService);
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@
import java.io.IOException;
import java.util.concurrent.locks.Lock;

public abstract class WxMpServiceBaseImpl<H, P> implements WxMpService, RequestHttp<H, P> {

/**
* @author someone
*/
public abstract class BaseWxMpServiceImpl<H, P> implements WxMpService, RequestHttp<H, P> {
private static final JsonParser JSON_PARSER = new JsonParser();

protected final Logger log = LoggerFactory.getLogger(this.getClass());

protected WxSessionManager sessionManager = new StandardSessionManager();
protected WxMpConfigStorage wxMpConfigStorage;
private WxMpKefuService kefuService = new WxMpKefuServiceImpl(this);
Expand All @@ -49,6 +52,7 @@ public abstract class WxMpServiceBaseImpl<H, P> implements WxMpService, RequestH
private WxMpShakeService shakeService = new WxMpShakeServiceImpl(this);
private WxMpMemberCardService memberCardService = new WxMpMemberCardServiceImpl(this);
private WxMpMassMessageService massMessageService = new WxMpMassMessageServiceImpl(this);
private WxMpAiOpenService aiOpenService = new WxMpAiOpenServiceImpl(this);

private int retrySleepMillis = 1000;
private int maxRetryTimes = 5;
Expand Down Expand Up @@ -487,4 +491,14 @@ public void setMemberCardService(WxMpMemberCardService memberCardService) {
public void setMassMessageService(WxMpMassMessageService massMessageService) {
this.massMessageService = massMessageService;
}

@Override
public WxMpAiOpenService getAiOpenService() {
return this.aiOpenService;
}

@Override
public void setAiOpenService(WxMpAiOpenService aiOpenService) {
this.aiOpenService = aiOpenService;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package me.chanjar.weixin.mp.api.impl;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import me.chanjar.weixin.common.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.mp.AiLangType;
import me.chanjar.weixin.mp.api.WxMpAiOpenService;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.util.requestexecuter.voice.VoiceUploadRequestExecutor;

import java.io.File;

/**
* <pre>
* Created by BinaryWang on 2018/6/9.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class WxMpAiOpenServiceImpl implements WxMpAiOpenService {

private static final JsonParser JSON_PARSER = new JsonParser();
public static final String TRANSLATE_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?lfrom=%s&lto=%s";
private WxMpService wxMpService;

public WxMpAiOpenServiceImpl(WxMpService wxMpService) {
this.wxMpService = wxMpService;
}

@Override
public void uploadVoice(String voiceId, AiLangType lang, File voiceFile) throws WxErrorException {
if (lang == null) {
lang = AiLangType.zh_CN;
}

this.wxMpService.execute(VoiceUploadRequestExecutor.create(this.wxMpService.getRequestHttp()),
String.format(VOICE_UPLOAD_URL, "mp3", voiceId, lang.getCode()),
voiceFile);
}

@Override
public String recogniseVoice(String voiceId, AiLangType lang, File voiceFile) throws WxErrorException {
this.uploadVoice(voiceId, lang, voiceFile);
return this.queryRecognitionResult(voiceId, lang);
}

@Override
public String translate(AiLangType langFrom, AiLangType langTo, String content) throws WxErrorException {
final String responseContent = this.wxMpService.post(String.format(TRANSLATE_URL, langFrom.getCode(), langTo.getCode()),
content);
final JsonObject jsonObject = new JsonParser().parse(responseContent).getAsJsonObject();
if (jsonObject.get("errcode") == null || jsonObject.get("errcode").getAsInt() == 0) {
return jsonObject.get("to_content").getAsString();
}

throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP));
}

@Override
public String queryRecognitionResult(String voiceId, AiLangType lang) throws WxErrorException {
if (lang == null) {
lang = AiLangType.zh_CN;
}

final String responseContent = this.wxMpService.get(VOICE_QUERY_RESULT_URL,
String.format("voice_id=%s&lang=%s", voiceId, lang.getCode()));
final JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject();
if (jsonObject.get("errcode") == null || jsonObject.get("errcode").getAsInt() == 0) {
return jsonObject.get("result").getAsString();
}

throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
/**
* apache http client方式实现.
*/
public class WxMpServiceHttpClientImpl extends WxMpServiceBaseImpl<CloseableHttpClient, HttpHost> {
public class WxMpServiceHttpClientImpl extends BaseWxMpServiceImpl<CloseableHttpClient, HttpHost> {
private CloseableHttpClient httpClient;
private HttpHost httpProxy;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
/**
* jodd-http方式实现
*/
public class WxMpServiceJoddHttpImpl extends WxMpServiceBaseImpl<HttpConnectionProvider, ProxyInfo> {
public class WxMpServiceJoddHttpImpl extends BaseWxMpServiceImpl<HttpConnectionProvider, ProxyInfo> {
private HttpConnectionProvider httpClient;
private ProxyInfo httpProxy;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
/**
* okhttp实现
*/
public class WxMpServiceOkHttpImpl extends WxMpServiceBaseImpl<OkHttpClient, OkHttpProxyInfo> {
public class WxMpServiceOkHttpImpl extends BaseWxMpServiceImpl<OkHttpClient, OkHttpProxyInfo> {
private OkHttpClient httpClient;
private OkHttpProxyInfo httpProxy;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package me.chanjar.weixin.mp.util.requestexecuter.voice;

import me.chanjar.weixin.common.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.RequestHttp;
import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;

import java.io.File;
import java.io.IOException;

/**
* <pre>
* Created by BinaryWang on 2018/6/9.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class VoiceUploadApacheHttpRequestExecutor extends VoiceUploadRequestExecutor<CloseableHttpClient, HttpHost> {
public VoiceUploadApacheHttpRequestExecutor(RequestHttp requestHttp) {
super(requestHttp);
}

@Override
public Boolean execute(String uri, File data) throws WxErrorException, IOException {
if (data == null) {
throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("文件对象为空").build());
}

HttpPost httpPost = new HttpPost(uri);
if (requestHttp.getRequestHttpProxy() != null) {
RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build();
httpPost.setConfig(config);
}

HttpEntity entity = MultipartEntityBuilder
.create()
.addBinaryBody("media", data)
.setMode(HttpMultipartMode.RFC6532)
.build();
httpPost.setEntity(entity);
httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString());

try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) {
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
WxError error = WxError.fromJson(responseContent, WxType.MP);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}

return true;
} finally {
httpPost.releaseConnection();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package me.chanjar.weixin.mp.util.requestexecuter.voice;

import me.chanjar.weixin.common.util.http.RequestExecutor;
import me.chanjar.weixin.common.util.http.RequestHttp;

import java.io.File;

/**
* <pre>
* Created by BinaryWang on 2018/6/9.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public abstract class VoiceUploadRequestExecutor<H, P> implements RequestExecutor<Boolean, File> {
protected RequestHttp<H, P> requestHttp;

public VoiceUploadRequestExecutor(RequestHttp requestHttp) {
this.requestHttp = requestHttp;
}

public static RequestExecutor<Boolean, File> create(RequestHttp requestHttp) {
switch (requestHttp.getRequestType()) {
case APACHE_HTTP:
return new VoiceUploadApacheHttpRequestExecutor(requestHttp);
case JODD_HTTP:
case OK_HTTP:
default:
return null;
}
}

}
Loading

0 comments on commit 5b5dada

Please sign in to comment.