diff --git a/src/main/java/com/newyetai/ai/controller/AiController.java b/src/main/java/com/newyetai/ai/controller/AiController.java new file mode 100644 index 0000000..b2bcde8 --- /dev/null +++ b/src/main/java/com/newyetai/ai/controller/AiController.java @@ -0,0 +1,33 @@ +package com.newyetai.ai.controller; + +import com.newyetai.common.AlitaResult; +import com.newyetai.sdk.xf.model.ChatReq; +import com.newyetai.sdk.xf.model.TranslateReq; +import com.newyetai.sdk.xf.util.XfUtils; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = "ai") +@RestController +@RequestMapping("/ai") +public class AiController { + + @PostMapping("chat") + @ApiOperation("ai对话") + public AlitaResult chat(@Validated @RequestBody ChatReq req) { + return AlitaResult.success(XfUtils.chat(req)); + } + + @PostMapping("translate") + @ApiOperation("ai翻译") + public AlitaResult chat(@Validated @RequestBody TranslateReq req) { + return AlitaResult.success(XfUtils.translate(req)); + } + + +} diff --git a/src/main/java/com/newyetai/sdk/xf/config/XfProperties.java b/src/main/java/com/newyetai/sdk/xf/config/XfProperties.java index 41c53f6..827027c 100644 --- a/src/main/java/com/newyetai/sdk/xf/config/XfProperties.java +++ b/src/main/java/com/newyetai/sdk/xf/config/XfProperties.java @@ -34,5 +34,8 @@ public class XfProperties implements Serializable{ private String iatHostUrl; /** 语音合成地址 **/ private String ttsHostUrl; + /** 机器翻译niutrans **/ + private String otsHostUrl; + } diff --git a/src/main/java/com/newyetai/sdk/xf/model/ChatReq.java b/src/main/java/com/newyetai/sdk/xf/model/ChatReq.java index 284c43d..cd9c244 100644 --- a/src/main/java/com/newyetai/sdk/xf/model/ChatReq.java +++ b/src/main/java/com/newyetai/sdk/xf/model/ChatReq.java @@ -3,6 +3,7 @@ package com.newyetai.sdk.xf.model; import java.io.Serializable; import java.util.List; +import javax.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -19,6 +20,7 @@ public class ChatReq implements Serializable{ @Default private String model = "4.0Ultra"; @Singular + @NotNull(message = "消息内容不能为空") private List messages; diff --git a/src/main/java/com/newyetai/sdk/xf/model/TranslateReq.java b/src/main/java/com/newyetai/sdk/xf/model/TranslateReq.java new file mode 100644 index 0000000..7f79bdc --- /dev/null +++ b/src/main/java/com/newyetai/sdk/xf/model/TranslateReq.java @@ -0,0 +1,67 @@ +package com.newyetai.sdk.xf.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.newyetai.sdk.common.dto.ISdkRequest; +import com.newyetai.sdk.xf.model.TranslateReq.TranslateRes; +import javax.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Builder.Default; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.core.ParameterizedTypeReference; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class TranslateReq implements ISdkRequest> { + + private static ParameterizedTypeReference> type = new ParameterizedTypeReference>() { + }; + /** 中文 */ + private static final String CN = "cn"; + /** 维语 */ + private static final String UY = "uy"; + + @NotBlank(message = "待翻译文本不能为空") + private String text; + @Default + private String from = CN; + @Default + private String to = UY; + + @Override + public ParameterizedTypeReference getResponseType() { + return type; + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + @Builder + public static class TranslateRes { + private TranslateResDTO result; + } + @Data + @AllArgsConstructor + @NoArgsConstructor + @Builder + public static class TranslateResDTO { + private String from; + private String to; + @JsonProperty("trans_result") + private TranslateResultDTO transResult; + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + @Builder + public static class TranslateResultDTO { + private String src; + private String dst; + } + + +} diff --git a/src/main/java/com/newyetai/sdk/xf/model/XfRes.java b/src/main/java/com/newyetai/sdk/xf/model/XfRes.java new file mode 100644 index 0000000..4a45f85 --- /dev/null +++ b/src/main/java/com/newyetai/sdk/xf/model/XfRes.java @@ -0,0 +1,19 @@ +package com.newyetai.sdk.xf.model; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class XfRes { + + private Integer code; + private String message; + private String sid; + private T data; + +} diff --git a/src/main/java/com/newyetai/sdk/xf/service/XfSparkProvider.java b/src/main/java/com/newyetai/sdk/xf/service/XfSparkProvider.java index b4a654f..fd32ca0 100644 --- a/src/main/java/com/newyetai/sdk/xf/service/XfSparkProvider.java +++ b/src/main/java/com/newyetai/sdk/xf/service/XfSparkProvider.java @@ -1,5 +1,24 @@ package com.newyetai.sdk.xf.service; +import com.google.gson.JsonObject; +import com.newyetai.common.constant.CommonConstants; +import com.newyetai.sdk.xf.model.TranslateReq; +import com.newyetai.sdk.xf.model.TranslateReq.TranslateRes; +import com.newyetai.sdk.xf.model.XfRes; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.Charset; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.SimpleDateFormat; +import java.util.Base64; +import java.util.Date; +import java.util.Locale; +import java.util.TimeZone; +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -67,6 +86,149 @@ public class XfSparkProvider { public static String dealJson(String content) { return content.replaceAll("(```json\n)|(\n```)|(\n)", ""); } + + public static AlitaResult translate(TranslateReq translateReq){ + return translate(translateReq, xfProperties); + } + + public static AlitaResult translate(TranslateReq translateReq, XfProperties xfProperties){ + + ResponseEntity> response = null; + try { + try { + String body = buildHttpBody(translateReq, xfProperties); + + HttpHeaders header = buildHttpHeader(body, xfProperties); + // 发送请求 + HttpEntity requestEntity = new HttpEntity(body, header); + response = + HttpUtils.restTemplate.exchange(xfProperties.getOtsHostUrl(), + HttpMethod.POST, requestEntity, translateReq.getResponseType()); + } catch (Exception e) { + log.error("xfSpark http error:{}", e.getMessage()); + return AlitaResult.error(SdkConstants.SDK_HTTP_INVOKE_ERROR_CODE, e.getMessage()); + } + try { + XfRes res = response.getBody(); + if (!res.getCode().equals(0)) { + return AlitaResult.error(SdkConstants.SDK_HTTP_INVOKE_RESPONSE_FAIL_CODE, + res.getCode() + CommonConstants.COLON + res.getMessage()); + } + return AlitaResult.success(res.getData().getResult().getTransResult().getDst()); + } catch (Exception e) { + return AlitaResult.error(SdkConstants.SDK_RESPONSE_CONVERT_ERROR_CODE, "xf返回结果获取content失败"); + } + + } finally { + log.info("xfSpark invoke:{}", + JsonUtils + .toJsonString( + DefaultSdkInvokeLogDTO.builder().requestData(translateReq).responseData(response) + .build())); + } + + } + /** + * 组装http请求头 + */ + public static HttpHeaders buildHttpHeader(String body, XfProperties xfProperties) + throws MalformedURLException, NoSuchAlgorithmException, InvalidKeyException { + HttpHeaders header = new HttpHeaders(); + URL url = new URL(xfProperties.getOtsHostUrl()); + + //时间戳 + SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + Date dateD = new Date(); + String date = format.format(dateD); + //System.out.println("【OTS WebAPI date】\n" + date); + + //对body进行sha256签名,生成digest头部,POST请求必须对body验证 + String digestBase64 = "SHA-256=" + signBody(body); + //System.out.println("【OTS WebAPI digestBase64】\n" + digestBase64); + + //hmacsha256加密原始字符串 + StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n").// + append("date: ").append(date).append("\n").// + append("POST ").append(url.getPath()).append(" HTTP/1.1").append("\n").// + append("digest: ").append(digestBase64); + //System.out.println("【OTS WebAPI builder】\n" + builder); + String sha = hmacsign(builder.toString(), xfProperties.getApiSecret()); + //System.out.println("【OTS WebAPI sha】\n" + sha); + + //组装authorization + String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", xfProperties.getApiKey(), "hmac-sha256", "host date request-line digest", sha); + //System.out.println("【OTS WebAPI authorization】\n" + authorization); + + header.add("Authorization", authorization); + header.add("Content-Type", "application/json"); + header.add("Accept", "application/json,version=1.0"); + header.add("Host", url.getHost()); + header.add("Date", date); + header.add("Digest", digestBase64); + //System.out.println("【OTS WebAPI header】\n" + header); + return header; + } + + + /** + * 组装http请求体 + */ + public static String buildHttpBody(TranslateReq translateReq, XfProperties xfProperties) + throws UnsupportedEncodingException { + JsonObject body = new JsonObject(); + JsonObject business = new JsonObject(); + JsonObject common = new JsonObject(); + JsonObject data = new JsonObject(); + //填充common + common.addProperty("app_id", xfProperties.getAppId()); + //填充business + business.addProperty("from", translateReq.getFrom()); + business.addProperty("to", translateReq.getTo()); + //填充data + //System.out.println("【OTS WebAPI TEXT字个数:】\n" + TEXT.length()); + byte[] textByte = translateReq.getText().getBytes("UTF-8"); + String textBase64 = new String(Base64.getEncoder().encodeToString(textByte)); + //System.out.println("【OTS WebAPI textBase64编码后长度:】\n" + textBase64.length()); + data.addProperty("text", textBase64); + //填充body + body.add("common", common); + body.add("business", business); + body.add("data", data); + return body.toString(); + } + + + /** + * 对body进行SHA-256加密 + */ + private static String signBody(String body) { + MessageDigest messageDigest; + String encodestr = ""; + try { + messageDigest = MessageDigest.getInstance("SHA-256"); + messageDigest.update(body.getBytes("UTF-8")); + encodestr = Base64.getEncoder().encodeToString(messageDigest.digest()); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return encodestr; + } + + /** + * hmacsha256加密 + */ + private static String hmacsign(String signature, String apiSecret) + throws NoSuchAlgorithmException, InvalidKeyException { + Charset charset = Charset.forName("UTF-8"); + Mac mac = Mac.getInstance("hmacsha256"); + SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(charset), "hmacsha256"); + mac.init(spec); + byte[] hexDigits = mac.doFinal(signature.getBytes(charset)); + return Base64.getEncoder().encodeToString(hexDigits); + } } diff --git a/src/main/java/com/newyetai/sdk/xf/util/XfUtils.java b/src/main/java/com/newyetai/sdk/xf/util/XfUtils.java index 043ee77..1c8dc5c 100644 --- a/src/main/java/com/newyetai/sdk/xf/util/XfUtils.java +++ b/src/main/java/com/newyetai/sdk/xf/util/XfUtils.java @@ -1,5 +1,6 @@ package com.newyetai.sdk.xf.util; +import com.newyetai.sdk.xf.model.TranslateReq; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -229,4 +230,16 @@ public class XfUtils { return result.getData(); } + public static String chat(ChatReq req) { + AlitaResult result = XfSparkProvider.httpChat(req); + Assert.isTrue(result.getSuccess(), result.getMsg()); + return result.getData(); + } + + public static String translate(TranslateReq req) { + AlitaResult result = XfSparkProvider.translate(req); + Assert.isTrue(result.getSuccess(), result.getMsg()); + return result.getData(); + } + } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 2bc89e2..5a9a171 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -156,13 +156,15 @@ ali.idcard.appcode=728e8c05167a482093418707ca22822b #讯飞配置 xf.httpAPIPassword=EiORVeNtBjFOcUSKXanT:oIVlTRNhWcEDpksviZaG #在控制台-我的应用获取 -xf.appId=a2847f13 -xf.apiSecret=ZGYzYjY5N2E2ODJhYzJkZWNjMjBmMWU5 -xf.apiKey=a232b25616c1359d15c30711c1976de6 +xf.appId=83b8ddca +xf.apiSecret=ZWE2MjkzY2VmZDkwM2EwZGJhNGRhOWJm +xf.apiKey=e092f93e28ebe6d27f756bb2d884dcff #语音听写地址 xf.iatHostUrl=https://iat-api.xfyun.cn/v2/iat #语音合成地址 xf.ttsHostUrl=https://tts-api.xfyun.cn/v2/tts +#机器翻译niutrans +xf.otsHostUrl=https://ntrans.xfyun.cn/v2/ots #腾讯地图 tencent.map.key=TS7BZ-GOVCB-CUZUL-JUUAV-WTYT3-Z2B4Z