Skip to content

Commit 5b5dada

Browse files
committed
binarywang#532 实现微信AI开放接口的三个接口:语音上传、查询识别结果和微信翻译功能
1 parent 0daaa01 commit 5b5dada

File tree

12 files changed

+377
-6
lines changed

12 files changed

+377
-6
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package me.chanjar.weixin.mp;
2+
3+
import lombok.Getter;
4+
5+
/**
6+
* <pre>
7+
* AI开放接口里的语言类型,目前只支持两种:中文和英文
8+
* Created by BinaryWang on 2018/6/10.
9+
* </pre>
10+
*
11+
* @author <a href="https://github.com/binarywang">Binary Wang</a>
12+
*/
13+
@Getter
14+
public enum AiLangType {
15+
/**
16+
* 中文 汉语
17+
*/
18+
zh_CN("zh_CN"),
19+
/**
20+
* 英文 英语
21+
*/
22+
en_US("en_US");
23+
24+
private String code;
25+
26+
AiLangType(String code) {
27+
this.code = code;
28+
}
29+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package me.chanjar.weixin.mp.api;
2+
3+
import me.chanjar.weixin.common.error.WxErrorException;
4+
import me.chanjar.weixin.mp.AiLangType;
5+
6+
import java.io.File;
7+
8+
/**
9+
* <pre>
10+
* 微信AI开放接口(语音识别,微信翻译).
11+
* https://mp.weixin.qq.com/wiki?t=resource/res_main&id=21516712282KzWVE
12+
* Created by BinaryWang on 2018/6/9.
13+
* </pre>
14+
*
15+
* @author <a href="https://github.com/binarywang">Binary Wang</a>
16+
*/
17+
public interface WxMpAiOpenService {
18+
String VOICE_UPLOAD_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/addvoicetorecofortext?format=%s&voice_id=%s&lang=%s";
19+
String VOICE_QUERY_RESULT_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/queryrecoresultfortext";
20+
21+
/**
22+
* <pre>
23+
* 提交语音.
24+
* 接口调用请求说明
25+
*
26+
* http请求方式: POST
27+
* http://api.weixin.qq.com/cgi-bin/media/voice/addvoicetorecofortext?access_token=ACCESS_TOKEN&format=&voice_id=xxxxxx&lang=zh_CN
28+
* 参数说明
29+
*
30+
* 参数 是否必须 说明
31+
* access_token 是 接口调用凭证
32+
* format 是 文件格式 (只支持mp3,16k,单声道,最大1M)
33+
* voice_id 是 语音唯一标识
34+
* lang 否 语言,zh_CN 或 en_US,默认中文
35+
* 语音内容放body里或者上传文件的形式
36+
* </pre>
37+
*
38+
* @param lang 语言,zh_CN 或 en_US,默认中文
39+
* @param voiceFile 语音文件
40+
* @param voiceId 语音唯一标识
41+
*/
42+
void uploadVoice(String voiceId, AiLangType lang, File voiceFile) throws WxErrorException;
43+
44+
/**
45+
* <pre>
46+
* 获取语音识别结果.
47+
* 接口调用请求说明
48+
*
49+
* http请求方式: POST
50+
* http://api.weixin.qq.com/cgi-bin/media/voice/queryrecoresultfortext?access_token=ACCESS_TOKEN&voice_id=xxxxxx&lang=zh_CN
51+
* 请注意,添加完文件之后10s内调用这个接口
52+
*
53+
* 参数说明
54+
*
55+
* 参数 是否必须 说明
56+
* access_token 是 接口调用凭证
57+
* voice_id 是 语音唯一标识
58+
* lang 否 语言,zh_CN 或 en_US,默认中文
59+
* </pre>
60+
*
61+
* @param lang 语言,zh_CN 或 en_US,默认中文
62+
* @param voiceId 语音唯一标识
63+
*/
64+
String queryRecognitionResult(String voiceId, AiLangType lang) throws WxErrorException;
65+
66+
/**
67+
* 识别指定语音文件内容.
68+
* 此方法揉合了前两两个方法:uploadVoice 和 queryRecognitionResult
69+
*
70+
* @param lang 语言,zh_CN 或 en_US,默认中文
71+
* @param voiceFile 语音文件
72+
* @param voiceId 语音唯一标识
73+
*/
74+
String recogniseVoice(String voiceId, AiLangType lang, File voiceFile) throws WxErrorException;
75+
76+
/**
77+
* <pre>
78+
* 微信翻译.
79+
* 接口调用请求说明
80+
*
81+
* http请求方式: POST
82+
* http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?access_token=ACCESS_TOKEN&lfrom=xxx&lto=xxx
83+
* 参数说明
84+
*
85+
* 参数 是否必须 说明
86+
* access_token 是 接口调用凭证
87+
* lfrom 是 源语言,zh_CN 或 en_US
88+
* lto 是 目标语言,zh_CN 或 en_US
89+
* 源内容放body里或者上传文件的形式(utf8格式,最大600Byte)
90+
* </pre>
91+
*
92+
* @param langFrom 源语言,zh_CN 或 en_US
93+
* @param langTo 目标语言,zh_CN 或 en_US
94+
* @param content 要翻译的文本内容
95+
*/
96+
String translate(AiLangType langFrom, AiLangType langTo, String content) throws WxErrorException;
97+
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,13 @@ public interface WxMpService {
408408
*/
409409
WxMpMassMessageService getMassMessageService();
410410

411+
/**
412+
* 返回AI开放接口方法的实现类对象,以方便调用其各个接口
413+
*
414+
* @return WxMpAiOpenService
415+
*/
416+
WxMpAiOpenService getAiOpenService();
417+
411418
void setKefuService(WxMpKefuService kefuService);
412419

413420
void setMaterialService(WxMpMaterialService materialService);
@@ -437,4 +444,6 @@ public interface WxMpService {
437444
void setMemberCardService(WxMpMemberCardService memberCardService);
438445

439446
void setMassMessageService(WxMpMassMessageService massMessageService);
447+
448+
void setAiOpenService(WxMpAiOpenService aiOpenService);
440449
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceBaseImpl.java renamed to weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/BaseWxMpServiceImpl.java

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,14 @@
2626
import java.io.IOException;
2727
import java.util.concurrent.locks.Lock;
2828

29-
public abstract class WxMpServiceBaseImpl<H, P> implements WxMpService, RequestHttp<H, P> {
30-
29+
/**
30+
* @author someone
31+
*/
32+
public abstract class BaseWxMpServiceImpl<H, P> implements WxMpService, RequestHttp<H, P> {
3133
private static final JsonParser JSON_PARSER = new JsonParser();
3234

3335
protected final Logger log = LoggerFactory.getLogger(this.getClass());
36+
3437
protected WxSessionManager sessionManager = new StandardSessionManager();
3538
protected WxMpConfigStorage wxMpConfigStorage;
3639
private WxMpKefuService kefuService = new WxMpKefuServiceImpl(this);
@@ -49,6 +52,7 @@ public abstract class WxMpServiceBaseImpl<H, P> implements WxMpService, RequestH
4952
private WxMpShakeService shakeService = new WxMpShakeServiceImpl(this);
5053
private WxMpMemberCardService memberCardService = new WxMpMemberCardServiceImpl(this);
5154
private WxMpMassMessageService massMessageService = new WxMpMassMessageServiceImpl(this);
55+
private WxMpAiOpenService aiOpenService = new WxMpAiOpenServiceImpl(this);
5256

5357
private int retrySleepMillis = 1000;
5458
private int maxRetryTimes = 5;
@@ -487,4 +491,14 @@ public void setMemberCardService(WxMpMemberCardService memberCardService) {
487491
public void setMassMessageService(WxMpMassMessageService massMessageService) {
488492
this.massMessageService = massMessageService;
489493
}
494+
495+
@Override
496+
public WxMpAiOpenService getAiOpenService() {
497+
return this.aiOpenService;
498+
}
499+
500+
@Override
501+
public void setAiOpenService(WxMpAiOpenService aiOpenService) {
502+
this.aiOpenService = aiOpenService;
503+
}
490504
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package me.chanjar.weixin.mp.api.impl;
2+
3+
import com.google.gson.JsonObject;
4+
import com.google.gson.JsonParser;
5+
import me.chanjar.weixin.common.WxType;
6+
import me.chanjar.weixin.common.error.WxError;
7+
import me.chanjar.weixin.common.error.WxErrorException;
8+
import me.chanjar.weixin.mp.AiLangType;
9+
import me.chanjar.weixin.mp.api.WxMpAiOpenService;
10+
import me.chanjar.weixin.mp.api.WxMpService;
11+
import me.chanjar.weixin.mp.util.requestexecuter.voice.VoiceUploadRequestExecutor;
12+
13+
import java.io.File;
14+
15+
/**
16+
* <pre>
17+
* Created by BinaryWang on 2018/6/9.
18+
* </pre>
19+
*
20+
* @author <a href="https://github.com/binarywang">Binary Wang</a>
21+
*/
22+
public class WxMpAiOpenServiceImpl implements WxMpAiOpenService {
23+
24+
private static final JsonParser JSON_PARSER = new JsonParser();
25+
public static final String TRANSLATE_URL = "http://api.weixin.qq.com/cgi-bin/media/voice/translatecontent?lfrom=%s&lto=%s";
26+
private WxMpService wxMpService;
27+
28+
public WxMpAiOpenServiceImpl(WxMpService wxMpService) {
29+
this.wxMpService = wxMpService;
30+
}
31+
32+
@Override
33+
public void uploadVoice(String voiceId, AiLangType lang, File voiceFile) throws WxErrorException {
34+
if (lang == null) {
35+
lang = AiLangType.zh_CN;
36+
}
37+
38+
this.wxMpService.execute(VoiceUploadRequestExecutor.create(this.wxMpService.getRequestHttp()),
39+
String.format(VOICE_UPLOAD_URL, "mp3", voiceId, lang.getCode()),
40+
voiceFile);
41+
}
42+
43+
@Override
44+
public String recogniseVoice(String voiceId, AiLangType lang, File voiceFile) throws WxErrorException {
45+
this.uploadVoice(voiceId, lang, voiceFile);
46+
return this.queryRecognitionResult(voiceId, lang);
47+
}
48+
49+
@Override
50+
public String translate(AiLangType langFrom, AiLangType langTo, String content) throws WxErrorException {
51+
final String responseContent = this.wxMpService.post(String.format(TRANSLATE_URL, langFrom.getCode(), langTo.getCode()),
52+
content);
53+
final JsonObject jsonObject = new JsonParser().parse(responseContent).getAsJsonObject();
54+
if (jsonObject.get("errcode") == null || jsonObject.get("errcode").getAsInt() == 0) {
55+
return jsonObject.get("to_content").getAsString();
56+
}
57+
58+
throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP));
59+
}
60+
61+
@Override
62+
public String queryRecognitionResult(String voiceId, AiLangType lang) throws WxErrorException {
63+
if (lang == null) {
64+
lang = AiLangType.zh_CN;
65+
}
66+
67+
final String responseContent = this.wxMpService.get(VOICE_QUERY_RESULT_URL,
68+
String.format("voice_id=%s&lang=%s", voiceId, lang.getCode()));
69+
final JsonObject jsonObject = JSON_PARSER.parse(responseContent).getAsJsonObject();
70+
if (jsonObject.get("errcode") == null || jsonObject.get("errcode").getAsInt() == 0) {
71+
return jsonObject.get("result").getAsString();
72+
}
73+
74+
throw new WxErrorException(WxError.fromJson(responseContent, WxType.MP));
75+
}
76+
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceHttpClientImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
/**
2323
* apache http client方式实现.
2424
*/
25-
public class WxMpServiceHttpClientImpl extends WxMpServiceBaseImpl<CloseableHttpClient, HttpHost> {
25+
public class WxMpServiceHttpClientImpl extends BaseWxMpServiceImpl<CloseableHttpClient, HttpHost> {
2626
private CloseableHttpClient httpClient;
2727
private HttpHost httpProxy;
2828

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceJoddHttpImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
/**
1616
* jodd-http方式实现
1717
*/
18-
public class WxMpServiceJoddHttpImpl extends WxMpServiceBaseImpl<HttpConnectionProvider, ProxyInfo> {
18+
public class WxMpServiceJoddHttpImpl extends BaseWxMpServiceImpl<HttpConnectionProvider, ProxyInfo> {
1919
private HttpConnectionProvider httpClient;
2020
private ProxyInfo httpProxy;
2121

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/impl/WxMpServiceOkHttpImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
/**
1616
* okhttp实现
1717
*/
18-
public class WxMpServiceOkHttpImpl extends WxMpServiceBaseImpl<OkHttpClient, OkHttpProxyInfo> {
18+
public class WxMpServiceOkHttpImpl extends BaseWxMpServiceImpl<OkHttpClient, OkHttpProxyInfo> {
1919
private OkHttpClient httpClient;
2020
private OkHttpProxyInfo httpProxy;
2121

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package me.chanjar.weixin.mp.util.requestexecuter.voice;
2+
3+
import me.chanjar.weixin.common.WxType;
4+
import me.chanjar.weixin.common.error.WxError;
5+
import me.chanjar.weixin.common.error.WxErrorException;
6+
import me.chanjar.weixin.common.util.http.RequestHttp;
7+
import me.chanjar.weixin.common.util.http.apache.Utf8ResponseHandler;
8+
import org.apache.http.HttpEntity;
9+
import org.apache.http.HttpHost;
10+
import org.apache.http.client.config.RequestConfig;
11+
import org.apache.http.client.methods.CloseableHttpResponse;
12+
import org.apache.http.client.methods.HttpPost;
13+
import org.apache.http.entity.ContentType;
14+
import org.apache.http.entity.mime.HttpMultipartMode;
15+
import org.apache.http.entity.mime.MultipartEntityBuilder;
16+
import org.apache.http.impl.client.CloseableHttpClient;
17+
18+
import java.io.File;
19+
import java.io.IOException;
20+
21+
/**
22+
* <pre>
23+
* Created by BinaryWang on 2018/6/9.
24+
* </pre>
25+
*
26+
* @author <a href="https://github.com/binarywang">Binary Wang</a>
27+
*/
28+
public class VoiceUploadApacheHttpRequestExecutor extends VoiceUploadRequestExecutor<CloseableHttpClient, HttpHost> {
29+
public VoiceUploadApacheHttpRequestExecutor(RequestHttp requestHttp) {
30+
super(requestHttp);
31+
}
32+
33+
@Override
34+
public Boolean execute(String uri, File data) throws WxErrorException, IOException {
35+
if (data == null) {
36+
throw new WxErrorException(WxError.builder().errorCode(-1).errorMsg("文件对象为空").build());
37+
}
38+
39+
HttpPost httpPost = new HttpPost(uri);
40+
if (requestHttp.getRequestHttpProxy() != null) {
41+
RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build();
42+
httpPost.setConfig(config);
43+
}
44+
45+
HttpEntity entity = MultipartEntityBuilder
46+
.create()
47+
.addBinaryBody("media", data)
48+
.setMode(HttpMultipartMode.RFC6532)
49+
.build();
50+
httpPost.setEntity(entity);
51+
httpPost.setHeader("Content-Type", ContentType.MULTIPART_FORM_DATA.toString());
52+
53+
try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpPost)) {
54+
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
55+
WxError error = WxError.fromJson(responseContent, WxType.MP);
56+
if (error.getErrorCode() != 0) {
57+
throw new WxErrorException(error);
58+
}
59+
60+
return true;
61+
} finally {
62+
httpPost.releaseConnection();
63+
}
64+
}
65+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package me.chanjar.weixin.mp.util.requestexecuter.voice;
2+
3+
import me.chanjar.weixin.common.util.http.RequestExecutor;
4+
import me.chanjar.weixin.common.util.http.RequestHttp;
5+
6+
import java.io.File;
7+
8+
/**
9+
* <pre>
10+
* Created by BinaryWang on 2018/6/9.
11+
* </pre>
12+
*
13+
* @author <a href="https://github.com/binarywang">Binary Wang</a>
14+
*/
15+
public abstract class VoiceUploadRequestExecutor<H, P> implements RequestExecutor<Boolean, File> {
16+
protected RequestHttp<H, P> requestHttp;
17+
18+
public VoiceUploadRequestExecutor(RequestHttp requestHttp) {
19+
this.requestHttp = requestHttp;
20+
}
21+
22+
public static RequestExecutor<Boolean, File> create(RequestHttp requestHttp) {
23+
switch (requestHttp.getRequestType()) {
24+
case APACHE_HTTP:
25+
return new VoiceUploadApacheHttpRequestExecutor(requestHttp);
26+
case JODD_HTTP:
27+
case OK_HTTP:
28+
default:
29+
return null;
30+
}
31+
}
32+
33+
}

0 commit comments

Comments
 (0)