mirror of
				https://gitee.com/binary/weixin-java-tools.git
				synced 2025-11-01 03:25:35 +08:00 
			
		
		
		
	#532 实现微信AI开放接口的三个接口:语音上传、查询识别结果和微信翻译功能
This commit is contained in:
		| @ -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; | ||||
|   } | ||||
| } | ||||
| @ -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<o=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; | ||||
| } | ||||
| @ -408,6 +408,13 @@ public interface WxMpService { | ||||
|    */ | ||||
|   WxMpMassMessageService getMassMessageService(); | ||||
|  | ||||
|   /** | ||||
|    * 返回AI开放接口方法的实现类对象,以方便调用其各个接口 | ||||
|    * | ||||
|    * @return WxMpAiOpenService | ||||
|    */ | ||||
|   WxMpAiOpenService getAiOpenService(); | ||||
|  | ||||
|   void setKefuService(WxMpKefuService kefuService); | ||||
|  | ||||
|   void setMaterialService(WxMpMaterialService materialService); | ||||
| @ -437,4 +444,6 @@ public interface WxMpService { | ||||
|   void setMemberCardService(WxMpMemberCardService memberCardService); | ||||
|  | ||||
|   void setMassMessageService(WxMpMassMessageService massMessageService); | ||||
|  | ||||
|   void setAiOpenService(WxMpAiOpenService aiOpenService); | ||||
| } | ||||
|  | ||||
| @ -26,11 +26,14 @@ import org.slf4j.LoggerFactory; | ||||
| 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); | ||||
| @ -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; | ||||
| @ -487,4 +491,14 @@ public abstract class WxMpServiceBaseImpl<H, P> implements WxMpService, RequestH | ||||
|   public void setMassMessageService(WxMpMassMessageService massMessageService) { | ||||
|     this.massMessageService = massMessageService; | ||||
|   } | ||||
| 
 | ||||
|   @Override | ||||
|   public WxMpAiOpenService getAiOpenService() { | ||||
|     return this.aiOpenService; | ||||
|   } | ||||
| 
 | ||||
|   @Override | ||||
|   public void setAiOpenService(WxMpAiOpenService aiOpenService) { | ||||
|     this.aiOpenService = aiOpenService; | ||||
|   } | ||||
| } | ||||
| @ -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<o=%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)); | ||||
|   } | ||||
| } | ||||
| @ -22,7 +22,7 @@ import java.util.concurrent.locks.Lock; | ||||
| /** | ||||
|  * apache http client方式实现. | ||||
|  */ | ||||
| public class WxMpServiceHttpClientImpl extends WxMpServiceBaseImpl<CloseableHttpClient, HttpHost> { | ||||
| public class WxMpServiceHttpClientImpl extends BaseWxMpServiceImpl<CloseableHttpClient, HttpHost> { | ||||
|   private CloseableHttpClient httpClient; | ||||
|   private HttpHost httpProxy; | ||||
|  | ||||
|  | ||||
| @ -15,7 +15,7 @@ import java.util.concurrent.locks.Lock; | ||||
| /** | ||||
|  * jodd-http方式实现 | ||||
|  */ | ||||
| public class WxMpServiceJoddHttpImpl extends WxMpServiceBaseImpl<HttpConnectionProvider, ProxyInfo> { | ||||
| public class WxMpServiceJoddHttpImpl extends BaseWxMpServiceImpl<HttpConnectionProvider, ProxyInfo> { | ||||
|   private HttpConnectionProvider httpClient; | ||||
|   private ProxyInfo httpProxy; | ||||
|  | ||||
|  | ||||
| @ -15,7 +15,7 @@ import java.util.concurrent.locks.Lock; | ||||
| /** | ||||
|  * okhttp实现 | ||||
|  */ | ||||
| public class WxMpServiceOkHttpImpl extends WxMpServiceBaseImpl<OkHttpClient, OkHttpProxyInfo> { | ||||
| public class WxMpServiceOkHttpImpl extends BaseWxMpServiceImpl<OkHttpClient, OkHttpProxyInfo> { | ||||
|   private OkHttpClient httpClient; | ||||
|   private OkHttpProxyInfo httpProxy; | ||||
|  | ||||
|  | ||||
| @ -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(); | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -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; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| @ -0,0 +1,47 @@ | ||||
| package me.chanjar.weixin.mp.api.impl; | ||||
|  | ||||
| import com.google.inject.Inject; | ||||
| import me.chanjar.weixin.common.error.WxErrorException; | ||||
| import me.chanjar.weixin.mp.AiLangType; | ||||
| import me.chanjar.weixin.mp.api.WxMpService; | ||||
| import me.chanjar.weixin.mp.api.test.ApiTestModule; | ||||
| import org.testng.annotations.Guice; | ||||
| import org.testng.annotations.Test; | ||||
|  | ||||
| import java.io.File; | ||||
|  | ||||
| /** | ||||
|  * <pre> | ||||
|  *  Created by BinaryWang on 2018/6/10. | ||||
|  * </pre> | ||||
|  * | ||||
|  * @author <a href="https://github.com/binarywang">Binary Wang</a> | ||||
|  */ | ||||
| @Test | ||||
| @Guice(modules = ApiTestModule.class) | ||||
| public class WxMpAiOpenServiceImplTest { | ||||
|   @Inject | ||||
|   protected WxMpService wxService; | ||||
|  | ||||
|   @Test | ||||
|   public void testUploadVoice() throws WxErrorException { | ||||
|     String voiceId = System.currentTimeMillis() + "a"; | ||||
|     AiLangType lang = AiLangType.zh_CN; | ||||
|     this.wxService.getAiOpenService().uploadVoice(voiceId, lang, new File("d:\\t.mp3")); | ||||
|   } | ||||
|  | ||||
|   @Test | ||||
|   public void testRecogniseVoice() throws WxErrorException { | ||||
|     String voiceId = System.currentTimeMillis() + "a"; | ||||
|     AiLangType lang = AiLangType.zh_CN; | ||||
|     final String result = this.wxService.getAiOpenService().recogniseVoice(voiceId, lang, new File("d:\\t.mp3")); | ||||
|     System.out.println(result); | ||||
|   } | ||||
|  | ||||
|   @Test | ||||
|   public void testTranslate() throws WxErrorException { | ||||
|     final String responseContent = this.wxService.getAiOpenService() | ||||
|       .translate(AiLangType.zh_CN, AiLangType.en_US, "微信文档很坑爹"); | ||||
|     System.out.println(responseContent); | ||||
|   } | ||||
| } | ||||
| @ -4,6 +4,7 @@ import java.io.IOException; | ||||
| import java.io.InputStream; | ||||
| import java.util.concurrent.locks.ReentrantLock; | ||||
|  | ||||
| import me.chanjar.weixin.mp.api.impl.WxMpServiceHttpClientImpl; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| @ -28,7 +29,7 @@ public class ApiTestModule implements Module { | ||||
|  | ||||
|       TestConfigStorage config = this.fromXml(TestConfigStorage.class, inputStream); | ||||
|       config.setAccessTokenLock(new ReentrantLock()); | ||||
|       WxMpService wxService = new WxMpServiceOkHttpImpl(); | ||||
|       WxMpService wxService = new WxMpServiceHttpClientImpl(); | ||||
|       wxService.setWxMpConfigStorage(config); | ||||
|  | ||||
|       binder.bind(WxMpService.class).toInstance(wxService); | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Binary Wang
					Binary Wang