阶段性处理

This commit is contained in:
13405411873 2025-03-03 14:31:40 +08:00
parent 2331d694ad
commit 02373b97be
14 changed files with 680 additions and 120 deletions

View File

@ -1,11 +1,11 @@
<template>
<!-- 聊天界面展示https://www.bilibili.com/video/BV1hT4y1P75N?p=22 12 -->
<view class="content">
<view class="content" @click="clickContent">
<view class="top_po">
<view class="" @click="goback()"><u-icon name="arrow-left" color="#fff" size="20"></u-icon></view>
<view style="display: flex;">
<text>{{info.title}}</text>
<view v-if="info.conversation=='Translator'" class="sm-text" @click="chooseLang">{{lang}} <u-icon style="margin-top: 6rpx;margin-left: 5rpx;" name="arrow-down" color="#fff"
size="12"></u-icon></view>
</view>
@ -14,7 +14,7 @@
</view>
<!-- 聊天内容 -->
<scroll-view class="chat" scroll-y="true" scroll-with-animation="true" :scroll-into-view="scrollToView">
<view @click="show=!show" class="chat-main" :style="{paddingBottom:inputh+'px'}">
<view class="chat-main" :style="{paddingBottom:inputh+'px'}">
<view class="chat-ls" v-for="(item,index) in messagesList" :key="index" :id="'msg'+ index">
<view class="msg-m msg-right">
<image class="user-img" :src="imagesUrl+userAvatar"></image>
@ -24,7 +24,7 @@
<text>{{item.query}}</text>
</view>
</view>
<view class="message" v-if="item.inputs.type == 'image'" @tap="previewImg(item.query)">
<view class="message" v-if="item.inputs.type == 'image'" >
<!-- 图像 -->
<image :src="item.query" class="msg-img" mode="widthFix"></image>
</view>
@ -36,17 +36,19 @@
</view>
</view>
</view>
<view class="msg-m msg-left" @click="clickSprink()" @touchstart="touchstart" @touchend="touchend" v-if="item.answer">
<view class="msg-m msg-left" v-if="item.answer">
<image class="user-img" :src="info.icon"></image>
<view class="msg-text" v-if="item.inputs.type == 'image'">
<view class="msg-text" v-if="item.inputs.type == 'image'">
<image :src="item.answer" class="msg-img" mode="widthFix"></image>
</view>
<view class="msg-text" id="po_" v-else>
<view class="po_z" v-if="show">
<view class="size_">Voice</view>
<view class="size_">text </view>
</view> {{item.answer}}</view>
</view>
<view class="msg-text" @click="clickSprink(index)" id="po_" v-else>
<view class="po_z" v-if="show=='1'&&clickIdx==index">
<view class="size_" @click="voiceTxt(item.answer)">Voice</view>
</view>
{{item.answer}}</view>
</view>
</view>
<span id="bottomId"></span>
@ -70,6 +72,7 @@
} from './msgSocket'
import requestChat from '../../utils/requestChat'
import permision from "@/js_sdk/wa-permission/permission.js"
import request from '../../utils/request'
//
const innerAudioContext = uni.createInnerAudioContext();
//
@ -77,10 +80,11 @@
export default {
data() {
return {
audioSrc: '', // URL
clickIdx:null,
//
islongPress:false,
timer:null,//
show:false,
show:'0',
columns: [
['Arabic-阿拉伯语',
'German-德语',
@ -146,40 +150,45 @@
submit,
},
methods: {
longpress(){
this.islongPress = true;
this.show = true
console.log("长按事件");
},
//
clickSprink(){
async voiceTxt(text){
let res = await request({
url: 'youDaoApi/tts',
method: 'post',
data: {
q: text,
voiceName: this.lang ,
}
})
this.playBase64Mp3(res.data)
this.show = false
},
// base64 MP3
playBase64Mp3(base64Data) {
innerAudioContext.src = config.baseUrl+base64Data; //
innerAudioContext.play();
// 使使
},
clickContent(index){
if(this.info.conversation=='Translator'){
//
this.show = '0'
this.clickIdx = null
}
},
//
clickSprink(index){
if(this.info.conversation=='Translator'){
//
if(this.islongPress == false){
console.log("点击事件");
}else if(this.islongPress == true){
console.log("长按事件",this.show);
}
},
//
touchstart(){
this.timer = setTimeout(()=>{
this.longpress();
},1000)
},
//
touchend(){
// click() islongPress fasle
clearTimeout(this.timer);
setTimeout(() => {
this.islongPress = false
}, 200)
},
this.show = '1'
this.clickIdx = index
}, 10); // 2000.2
}
},
// columnIndexvaluevalues
langConfirm(e) {
console.log('confirm', e)
this.lang = e.value[0].split('-')[0]
this.langShow = false
},
@ -221,30 +230,25 @@
this.goBottom()
uni.setStorageSync(this.storeList + '_' + this.info.conversation, this.messagesList)
}else{
console.log('触发首页的消息回调', res);
if (res.data.indexOf("conversation_id") > -1) {
uni.setStorageSync(this.info.conversation, JSON.parse(res.data).conversation_id);
} else if (res.data.indexOf("workflow_finished") > -1) {
//
uni.setStorageSync(this.storeList + '_' + this.info.conversation, this.messagesList)
} else {
this.messagesList[this.messagesList.length - 1].answer = this.messagesList[this
.messagesList.length - 1].answer + res.data
this.goBottom()
}
}
})
}
},
//
getMessageByStore() {
if (!uni.getStorageSync(this.info.conversation)) {
return;
}
let tempList = uni.getStorageSync(this.storeList + '_' + this.info.conversation)||[]
if (tempList && tempList.length > 30) {
//30
@ -463,7 +467,7 @@
background: #3d3d3d;
display: flex;
align-items: center;
color: #fff;
border-radius: 8px;
}
@ -681,4 +685,4 @@
align-items: center;
line-height: 29px;
}
</style>
</style>

View File

@ -2,38 +2,40 @@
<view class="container">
<map id="map" :show-compass="true" :include-points="points" :polyline="polyline" lang="en" :longitude="center.longitude" :latitude="center.latitude"
:markers="markers" bindmarkertap="handleMarkerTap" show-location
:style="'width:' + windowWidth + 'px;' + 'height:'+ windowHeight+'px;' ">
:style="'width:' + windowWidth + 'px;' + 'height:'+ '540rpx;' ">
</map>
<view class="contenr_">
<image src="/static/chatImg/fy.png" @click="changeLan" mode="" class="fy_icon" ></image>
<view class="contenr_" v-if="lang=='en'" >
<view class="contenr_top">
<!-- 顶部 全程 时间-->
<view class="c_t_box">
<view>
<text class="text_1">1 h22min</text>
<text class="text_1">{{Number.parseInt(allData.duration/60)||0}}min</text>
</view>
<view class="ds_">
<text class="text_2">Arrival time</text>
<text class="text_1">1 h22min</text>
<text class="text_2">walk distance</text>
<text class="text_1">{{allData.walkingDistance?Number.parseFloat(allData.walkingDistance/1000).toFixed(2):0}}km</text>
</view>
</view>
<!-- -->
<view class="c_c_box">
<view class="">
<image src="/static/imgs/icon1.png" style="height: 40rpx;width: 32rpx;margin-left: 8rpx;margin-bottom: 5rpx;"></image>
</view>
<view class="ds_">
<view class="ds_" style="flex-wrap: wrap;">
<image src="/static/imgs/icon2.png" style="height: 48rpx;width: 48rpx;"></image>
<view class="c_c_title" :style="'background-color:' + colorList[index] " v-for="(item,index) in 2" :key="index" >
<text class="text_3">Rail Transit Line 2</text>
<view v-for="(item,index) in allData.segments">
<view class="c_c_title" :style="'background-color:' + colorList[index] " v-for="(it,idx) in item.bus" :key="idx" >
<text class="text_line">{{it.enName.split('(')[0]||''}}</text>
</view>
</view>
</view>
</view>
<!-- 下方注释 -->
<view class="ds_">
<text class="text_4">Walk 805 meters</text>
<text class="text_4">5$</text>
<text class="text_4">15 Stations</text>
<text class="text_4">all cost</text>
<text class="text_4">¥{{allData.cost=='[]'?0:allData.cost}}</text>
</view>
</view>
<scroll-view scroll-y :show-scrollbar="false" @scrolltoupper="upper" @scrolltolower="lower"
@ -43,20 +45,107 @@
<image src="/static/imgs/icon3.png" style="height: 40rpx;width: 40rpx;margin-right: 20rpx;"></image>
<text class="text_1">My Location</text>
</view>
<view class="c_b_box">
</view>
<!-- -- 我需要把循环包在这 这两块一起循环 下面的循环不需要 -->
<view class="c_box" v-for="(item,index) in allData.segments" :key="index">
<view class="c_b_box" v-if="item.walking">
<view class="ds_">
<view class="qiu_"></view>
<text class="text_4">Walk 621 meters (9 minutes)</text>
<text class="text_4">Walk {{item.walking.distance}} meters ({{Number.parseInt(item.walking.duration/60)}} minutes)</text>
</view>
<view class="b_walk">
<text class="text_3">Walk</text>
<text class="text_3" @click="walkLine(item.walking)">Walk</text>
</view>
</view>
<view class="contenr_bottom_d" v-if="item.bus&&item.bus.length>0" v-for="(it) in item.bus">
<view class="bz_left">
<view class="l_t_i" :style="'background-color:' + colorList[index] " >
<image src="/static/imgs/icon4.png" style="height: 32rpx;width: 32rpx;"></image>
</view>
<view class="l_t_z" :style="'background-color:' + colorList[index] " ></view>
<view class="l_t_i" :style="'background-color:' + colorList[index] "></view>
</view>
<view class="bz_right">
<text class="text_1" style="margin-bottom: 32rpx;">{{it.departureStop.enName}} station</text>
<view class="ds_">
<view class="bz_icon" :style="'background-color:' + colorList[index] ">
<text class="text_3">{{it.enName}}</text>
</view>
</view>
<view class="botton_size">
<text class="text_4">{{it.viaStops.length}} station ({{Number.parseInt(it.duration/60)}} min)</text>
</view>
<text class="text_1" style="margin-top: 39rpx;">{{it.arrivalStop.enName}} station</text>
</view>
</view>
</view>
<view class="contenr_bottom">
<view class="c_b_title">
<image src="/static/imgs/end.png" style="height: 50rpx;width: 45rpx;margin-right: 20rpx;"></image>
<text class="text_1">destination</text>
</view>
</view>
</scroll-view>
</view>
<view class="contenr_" v-else >
<view class="contenr_top">
<!-- 顶部 全程 时间-->
<view class="c_t_box">
<view>
<text class="text_1">{{Number.parseInt(allData.duration/60)||0}}分钟</text>
</view>
<view class="ds_">
<text class="text_2">步行距离</text>
<text class="text_1">{{allData.walkingDistance?Number.parseFloat(allData.walkingDistance/1000).toFixed(2):0}}km</text>
</view>
</view>
<!-- -->
<view class="c_c_box">
<view class="ds_" style="flex-wrap: wrap;">
<image src="/static/imgs/icon2.png" style="height: 48rpx;width: 48rpx;"></image>
<view v-for="(item,index) in allData.segments">
<view class="c_c_title" :style="'background-color:' + colorList[index] " v-for="(it,idx) in item.bus" :key="idx" >
<text class="text_line">{{it.name.split('(')[0]||''}}</text>
</view>
</view>
<view v-for="(item,index) in 2" :key="index" class="contenr_bottom_d">
</view>
</view>
<!-- 下方注释 -->
<view class="ds_">
<text class="text_4">总花费</text>
<text class="text_4">¥{{allData.cost}}</text>
</view>
</view>
<scroll-view scroll-y :show-scrollbar="false" @scrolltoupper="upper" @scrolltolower="lower"
:refresher-enabled="true" style="height: 620rpx;" lower-threshold="50">
<view class="contenr_bottom">
<view class="c_b_title">
<image src="/static/imgs/icon3.png" style="height: 40rpx;width: 40rpx;margin-right: 20rpx;"></image>
<text class="text_1">我的位置</text>
</view>
</view>
<!-- -- 我需要把循环包在这 这两块一起循环 下面的循环不需要 -->
<view class="c_box" v-for="(item,index) in allData.segments" :key="index">
<view class="c_b_box" v-if="item.walking">
<view class="ds_">
<view class="qiu_"></view>
<text class="text_4">步行 {{item.walking.distance}} 米 ({{Number.parseInt(item.walking.duration/60)}} 分钟)</text>
</view>
<view class="b_walk">
<text class="text_3" @click="walkLine(item.walking)">导航</text>
</view>
</view>
<view class="contenr_bottom_d" v-if="item.bus&&item.bus.length>0" v-for="(it) in item.bus">
<view class="bz_left">
<view class="l_t_i" :style="'background-color:' + colorList[index] " >
<image src="/static/imgs/icon4.png" style="height: 32rpx;width: 32rpx;"></image>
@ -65,31 +154,31 @@
<view class="l_t_i" :style="'background-color:' + colorList[index] "></view>
</view>
<view class="bz_right">
<text class="text_1" style="margin-bottom: 32rpx;">Zhudian Subway Station, Exit G</text>
<text class="text_1" style="margin-bottom: 32rpx;">{{it.departureStop.name}} 站点</text>
<view class="ds_">
<view class="bz_icon" :style="'background-color:' + colorList[index] ">
<text class="text_3">Line 2</text>
<text class="text_3">{{it.name}}</text>
</view>
<text class="text_5">Wangfuzhuang Direction</text>
</view>
<view class="botton_size">
<text class="text_4">13 stops (33 min)</text>
<text class="text_4">上车站 首06.02 末23.47</text>
<text class="text_4">{{it.viaStops.length}} 站 ({{Number.parseInt(it.duration/60)}} 分钟)</text>
</view>
<text class="text_1" style="margin-top: 18rpx;">Wangfuzhuang Direction</text>
<text class="text_1" style="margin-top: 39rpx;">{{it.arrivalStop.name}} 站点</text>
</view>
</view>
</scroll-view>
</view>
<view class="bottom_pr">
<view class="button_">
<text class="button_text">Start Navigation</text>
<view class="contenr_bottom">
<view class="c_b_title">
<image src="/static/imgs/end.png" style="height: 50rpx;width: 45rpx;margin-right: 20rpx;"></image>
<text class="text_1">终点</text>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
@ -100,13 +189,16 @@
export default {
data() {
return {
lang:'en',
translateY: 0,
longPressTimer: null, // 长按定时器
isDragging: false, // 是否正在拖动
colorList:[
"#fcd803",
"#009bc2"
"#f9d402",
"#009bc2",
"#000000",
"#c34100",
"#006a00",
],
//打车预估路线
polyline: [],
@ -126,7 +218,8 @@
distance: 0,
duration: 0,
cost:0,
points:[]
points:[],
allData:{}
}
},
@ -143,7 +236,7 @@
},
computed: {
windowHeight() {
return uni.getSystemInfoSync().windowHeight;
return uni.getSystemInfoSync().windowHeight/3;
},
windowWidth() {
return uni.getSystemInfoSync().windowWidth;
@ -151,6 +244,37 @@
},
methods: {
changeLan(){
if(this.lang == 'en'){
this.lang = 'zh'
}else{
this.lang = 'en'
}
},
walkLine(data){
let endInfo = {}
endInfo.location = [data.endLocationLng, data.endLocationLat]
uni.getLocation({
// 谷歌使用wgs84 其他使用gcj02
type: 'gcj02', // 使用国测局坐标系
geocode: true,
success: function(res) {
let staInfo = {}
staInfo.location = [res.longitude, res.latitude]
uni.navigateTo({
url: '/pages/index/PedestrianNavigation?startPoint=' + staInfo
.location +
'&endPoint=' + endInfo.location
})
},
fail: function(err) {
uni.showToast({
title: 'Failed to get location',
icon: 'none'
})
}
});
},
upper(e) {
console.log('Scroll to top');
},
@ -177,7 +301,7 @@
endLocationLat: this.endPoint[1],
}
}).then(res => {
that.allData = res.data
that.duration = res.data.duration/60
that.distance = res.data.walkingDistance/1000
that.cost = res.data.cost
@ -252,7 +376,7 @@
that.markers.push({
id:busIt.departureStop.id,
label:{
content:busIt.name,
content:busIt.enName,
fontSize:8,
color:'#145e07'
},
@ -391,8 +515,9 @@
z-index: 99999;
}
.bz_icon{
width: 100rpx;
height: 40rpx;
box-sizing: border-box;
padding: 18rpx 18rpx;
border-radius: 8rpx ;
display: flex;
@ -430,7 +555,7 @@
margin-right: 32rpx;
}
.c_b_box{
margin: 28rpx 0rpx;
margin: 10rpx 0rpx;
flex-direction: row;
align-items: center;
justify-content: space-between;
@ -447,7 +572,7 @@
.contenr_{
min-height: 160rpx;
position: fixed;
bottom: 160rpx;
bottom: 0rpx;
width: 750rpx;
background: #fff;
@ -474,6 +599,11 @@
.c_c_box{
margin-bottom: 16rpx;
}
.c_box{
box-sizing: border-box;
padding: 0px 32rpx;
}
.text_1{
font-size: 32rpx;
color: #333333;
@ -489,6 +619,11 @@
font-size: 28rpx;
color: #FFFFFF;
}
.text_line{
font-weight: 500;
font-size: 23rpx;
color: #FFFFFF;
}
.text_4{
font-weight: 400;
font-size: 24rpx;
@ -503,7 +638,7 @@
}
.c_c_title{
width: 280rpx;
width: 260rpx;
height: 48rpx;
border-radius: 8rpx 8rpx 8rpx 8rpx;
background: #fcd702;
@ -513,7 +648,7 @@
margin: 0px 5rpx;
}
.contenr_bottom{
margin-top: 40rpx;
// margin-top: 40rpx;
box-sizing: border-box;
padding: 32rpx;
padding-bottom: 0px;
@ -521,7 +656,7 @@
}
.contenr_bottom_d{
box-sizing: border-box;
padding: 32rpx;
padding: 11rpx;
padding-top: 0px;
flex-direction: row;
@ -561,4 +696,26 @@
.botton_size{
margin-top: 100rpx;
}
.fy_icon{
position: fixed;
right: 15px;
top: 15px;
width: 80rpx;
height: 80rpx;
}
::v-deep .amap-logo {
opacity: 0 !important;
}
::v-deep .amap-copyright {
opacity: 0 !important;
}
::v-deep .BMap_cpyCtrl {
display: none !important;
}
::v-deep .anchorBL {
display: none !important;
}
</style>

View File

@ -6,6 +6,7 @@ import java.util.List;
@Data
public class BusLinesResponse {
private String name; // 公交路线名称
private String enName;
private String id; // 公交路线ID
private String type; // 公交类型
private int distance; // 行驶距离
@ -27,6 +28,7 @@ public class BusLinesResponse {
@Data
public static class DepartureStop {
private String name;
private String enName;
private String id;
/**
* 经度不能为空
@ -43,6 +45,7 @@ public class BusLinesResponse {
@Data
public static class ArrivalStop {
private String name;
private String enName;
private String id;
/**
* 经度不能为空
@ -59,6 +62,7 @@ public class BusLinesResponse {
@Data
public static class ViaStop {
private String name;
private String enName;
private String id;
/**
* 经度不能为空

View File

@ -6,14 +6,13 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.TimeUnit;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.dc.app.controller.ChatApi;
import com.dc.system.mapper.SysConfigMapper;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
@ -22,11 +21,55 @@ import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import static com.dc.system.until.AsrV2.*;
@Service
@Slf4j
public class ChatService {
@Resource
private ChatApi chatApi;
@Resource
SysConfigMapper sysConfigMapper;
static Map<String, String> langMap = new HashMap<String, String>() {{
put("Arabic", "ar");
put("Bahasa (Indonesia)", "in");
put("Cantonese", "yue");
put("Catalan", "ca");
put("Czech", "cs");
put("Danish", "da");
put("Dutch", "nl");
put("Dutch (Belgium)", "nl-BEL");
put("English", "en");
put("Finnish", "fi");
put("French", "fr");
put("French (Canada)", "fr-CAN");
put("German", "de");
put("Greek", "el");
put("Hebrew", "he");
put("Hindi", "hi");
put("Hungarian", "hu");
put("Italian", "it");
put("Japanese", "ja");
put("Korean", "ko");
put("Mandarin (China)", "zh-CHS");
put("Mandarin (Taiwan)", "zh-TWN");
put("Norwegian", "no");
put("Polish", "pl");
put("Portuguese (Brazil)", "pt-BRA");
put("Portuguese (Portugal)", "pt");
put("Romanian", "ro");
put("Russian", "ru");
put("Slovak", "sk");
put("Spanish (Castilian)", "es-ESP");
put("Spanish (Columbia)", "es-COL");
put("Spanish (Mexico)", "es-MEX");
put("Spanish", "es"); // 修正重复项
put("Swedish", "sv");
put("Thai", "th");
put("Turkish", "tr");
}};
private static Map<String,String> sendFlagMap =new HashMap<>();
@Autowired
private RedisTemplate<String, String> redisTemplate;
@ -40,6 +83,39 @@ public class ChatService {
if (requestData.getJSONObject("inputs").getString("type").equals("image")||requestData.getJSONObject("inputs").getString("type").equals("voice")){
requestData.put("query",redisTemplate.opsForValue().get(requestData.getString("query")));
}
if (requestData.getJSONObject("inputs").getString("type").equals("voice")){
String appKey = sysConfigMapper.getcheckConfigKeyUnique("YOUDAO_APP_KEY");
String appSecret = sysConfigMapper.getcheckConfigKeyUnique("YOUDAO_APP_SECRET");
//如果是语言则直接走有道自己进行翻译 不需要走大模型
Map<String, String> params = new HashMap<String, String>();
String langType = langMap.get(requestData.getJSONObject("inputs").getString("lang"));
params.put("appKey", appKey);
String q = requestData.getString("query");
params.put("q", q);
params.put("format", "wav");
params.put("rate", "16000");
params.put("channel", "1");
params.put("docType", "json");
params.put("type", "1");
String salt = UUID.randomUUID().toString();
params.put("salt", salt);
params.put("langType", langType);
String curtime = String.valueOf(System.currentTimeMillis() / 1000);
params.put("curtime", curtime);
String signStr = appKey + truncate(q) + salt + curtime + appSecret;
String sign = getDigest(signStr);
params.put("sign", sign);
params.put("signType", "v2");
JSONObject res = JSONObject.parseObject(doRequest("https://openapi.youdao.com/asrapi", params));
if(res.getString("errorCode").equals("0")){
JSONArray jsonArray = res.getJSONArray("result");
requestData.put("query",jsonArray.get(0));
JSONObject temp =new JSONObject();
temp.put("type","text");
requestData.put("inputs",temp);
}
}
String url = "http://flintown.cn:8080/v1/chat-messages";
MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
RequestBody body = RequestBody.create(mediaType, requestData.toJSONString());

View File

@ -2,10 +2,12 @@ package com.dc.system.controller;
import com.alibaba.fastjson2.JSONObject;
import com.dc.common.core.domain.AjaxResult;
import com.dc.system.domain.TtsRequest;
import com.dc.system.service.YoudaoService;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
@ -21,5 +23,12 @@ public class YoudaoController {
List<String> res = youdaoService.textOcr(params.getString("base64"));
return AjaxResult.success(res);
}
@PostMapping("tts")
public AjaxResult tts(@RequestBody TtsRequest ttsRequest, HttpServletResponse response) throws IOException, NoSuchAlgorithmException {
String res = youdaoService.tts(ttsRequest,response);
return AjaxResult.success("ok",res);
}
}

View File

@ -0,0 +1,9 @@
package com.dc.system.domain;
import lombok.Data;
@Data
public class TtsRequest {
private String q;
private String voiceName;
}

View File

@ -1,9 +1,16 @@
package com.dc.system.service;
import com.dc.system.domain.TtsRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.List;
public interface YoudaoService {
List<String> textOcr(String base64) throws NoSuchAlgorithmException, IOException;
String tts(TtsRequest ttsRequest, HttpServletResponse response) throws IOException, NoSuchAlgorithmException;
}

View File

@ -15,10 +15,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
@Slf4j
@Service
@ -71,6 +68,40 @@ public class AmapServiceImpl implements AmapService {
}
res.setSegments(segments);
// 路线翻译
List<String> names = new ArrayList<>();
for (BusTransitResponse.Segment segment : res.getSegments()) {
List<BusLinesResponse> bus = segment.getBus();
for (BusLinesResponse busLinesResponse : bus) {
names.add(busLinesResponse.getName());
names.add(busLinesResponse.getDepartureStop().getName());
names.add(busLinesResponse.getArrivalStop().getName());
List<BusLinesResponse.ViaStop> viaStops = busLinesResponse.getViaStops();
for (BusLinesResponse.ViaStop viaStop : viaStops) {
names.add(viaStop.getName());
}
}
}
// 调用翻译接口逐个翻译每个字段
if (!names.isEmpty()) {
String translatedInstruction = mapAndFy.translateNames(names);
String tempStr = mapAndFy.extractTranslations(translatedInstruction);
List<String> tempList = Arrays.asList(tempStr.split("%&&"));
int i=0;
for (BusTransitResponse.Segment segment : res.getSegments()) {
List<BusLinesResponse> bus = segment.getBus();
for (BusLinesResponse busLinesResponse : bus) {
busLinesResponse.setEnName(tempList.get(i++));
busLinesResponse.getDepartureStop().setEnName(tempList.get(i++));
busLinesResponse.getArrivalStop().setEnName(tempList.get(i++));
List<BusLinesResponse.ViaStop> viaStops = busLinesResponse.getViaStops();
for (BusLinesResponse.ViaStop viaStop : viaStops) {
viaStop.setEnName(tempList.get(i++));
}
}
}
}
return res;
}
private AmapResponse dealWalk(JSONObject walking) throws Exception {

View File

@ -3,22 +3,22 @@ package com.dc.system.service.impl;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.dc.common.config.DCConfig;
import com.dc.common.utils.youdaoUtils.AuthV3Util;
import com.dc.common.utils.youdaoUtils.FileUtil;
import com.dc.system.domain.TtsRequest;
import com.dc.system.mapper.SysConfigMapper;
import com.dc.system.service.YoudaoService;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import com.dc.web.controller.tool.YOUDAOHttpUtil;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
@ -27,10 +27,12 @@ import java.util.List;
import java.util.Map;
import static com.dc.system.until.OcrUtil.createRequestParams;
import static com.dc.system.until.OcrUtil.createTTSParams;
@Service
public class YoudaoServiceServiceImpl implements YoudaoService {
private final String orcPath = "https://openapi.youdao.com/ocrapi";
private final String ttsPath = "https://openapi.youdao.com/ttsapi";
@Resource
SysConfigMapper sysConfigMapper;
@Override
@ -58,5 +60,23 @@ public class YoudaoServiceServiceImpl implements YoudaoService {
return result;
}
@Override
public String tts(TtsRequest ttsRequest, HttpServletResponse response) throws IOException, NoSuchAlgorithmException {
ttsRequest.setVoiceName("youxiaomei");
List<String> result = new ArrayList<>();
String appKey = sysConfigMapper.getcheckConfigKeyUnique("YOUDAO_APP_KEY");
String appSecret = sysConfigMapper.getcheckConfigKeyUnique("YOUDAO_APP_SECRET");
// 添加请求参数
Map<String, String[]> params = createTTSParams(ttsRequest.getQ(), ttsRequest.getVoiceName());
// 添加鉴权相关参数
AuthV3Util.addTTSAuthParams(appKey, appSecret, params);
byte[] post = YOUDAOHttpUtil.doPost(ttsPath, null, params, "audio");
// 上传文件路径
String tempPath = java.util.UUID.randomUUID().toString().replace("-", "");
String filePath = DCConfig.getUploadPath()+File.separator+"tts"+File.separator +tempPath +".mp3";
FileUtil.saveFile(filePath, post, false);
return "profile/upload/tts"+File.separator +tempPath +".mp3";
}
}

View File

@ -0,0 +1,101 @@
package com.dc.system.until;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
import java.util.Base64;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.nio.charset.StandardCharsets.UTF_8;
public class AsrV2 {
public static String truncate(String q) {
if (q == null) {
return null;
}
int len = q.length();
String result;
return len <= 20 ? q : (q.substring(0, 10) + len + q.substring(len - 10, len));
}
public static String loadAsBase64(String filename) {
InputStream in = null;
byte[] data = null;
try {
in = new FileInputStream(filename);
data = new byte[in.available()];
in.read(data);
in.close();
} catch (IOException e) {
e.printStackTrace();
}
return Base64.getEncoder().encodeToString(data);
}
public static String doRequest(String url, Map<String,String> requestParams) throws Exception{
String result = null;
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
List<NameValuePair> params = new ArrayList<NameValuePair>();
for (String key : requestParams.keySet()) {
params.add(new BasicNameValuePair(key, requestParams.get(key)));
}
httpPost.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
CloseableHttpResponse httpResponse = httpClient.execute(httpPost);
try {
HttpEntity httpEntity = httpResponse.getEntity();
result = EntityUtils.toString(httpEntity, "utf-8");
EntityUtils.consume(httpEntity);
} finally {
try {
if (httpResponse != null) {
httpResponse.close();
}
} catch(IOException e) {
System.out.println("## release resouce error ##" + e);
}
}
return result;
}
public static String getDigest(String string) {
if (string == null) {
return null;
}
char hexDigits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
byte[] btInput = string.getBytes(UTF_8);
try {
MessageDigest mdInst = MessageDigest.getInstance("SHA-256");
mdInst.update(btInput);
byte[] md = mdInst.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (byte byte0 : md) {
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (NoSuchAlgorithmException e) {
return null;
}
}
public static void main(String[] args) throws Exception {
}
}

View File

@ -31,4 +31,13 @@ public class OcrUtil {
put("imageType", imageType);
}};
}
public static Map<String, String[]> createTTSParams(String q, String voiceName) throws IOException {
return new HashMap<String, String[]>() {{
put("q", new String[]{q});
put("voiceName", new String[]{voiceName} );
}};
}
}

View File

@ -0,0 +1,98 @@
package com.dc.web.controller.tool;
import okhttp3.*;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.Objects;
public class YOUDAOHttpUtil {
private static OkHttpClient httpClient = new OkHttpClient.Builder().build();
public static byte[] doGet(String url, Map<String, String[]> header, Map<String, String[]> params, String expectContentType) {
Request.Builder builder = new Request.Builder();
addHeader(builder, header);
addUrlParam(builder, url, params);
return requestExec(builder.build(), expectContentType);
}
public static byte[] doPost(String url, Map<String, String[]> header, Map<String, String[]> body, String expectContentType) {
Request.Builder builder = new Request.Builder().url(url);
addHeader(builder, header);
addBodyParam(builder, body, "POST");
return requestExec(builder.build(), expectContentType);
}
private static void addHeader(Request.Builder builder, Map<String, String[]> header) {
if (header == null) {
return;
}
for (String key : header.keySet()) {
String[] values = header.get(key);
if (values != null) {
for (String value : values) {
builder.addHeader(key, value);
}
}
}
}
private static void addUrlParam(Request.Builder builder, String url, Map<String, String[]> params) {
if (params == null) {
return;
}
HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder();
for (String key : params.keySet()) {
String[] values = params.get(key);
if (values != null) {
for (String value : values) {
urlBuilder.addQueryParameter(key, value);
}
}
}
builder.url(urlBuilder.build());
}
private static void addBodyParam(Request.Builder builder, Map<String, String[]> body, String method) {
if (body == null) {
return;
}
FormBody.Builder formBodyBuilder = new FormBody.Builder(StandardCharsets.UTF_8);
for (String key : body.keySet()) {
String[] values = body.get(key);
if (values != null) {
for (String value : values) {
formBodyBuilder.add(key, value);
}
}
}
builder.method(method, formBodyBuilder.build());
}
private static byte[] requestExec(Request request, String expectContentType) {
Objects.requireNonNull(request, "okHttp request is null");
try (Response response = httpClient.newCall(request).execute()) {
if (response.code() == 200) {
ResponseBody body = response.body();
if (body != null) {
String contentType = response.header("Content-Type");
if (contentType != null && !contentType.contains(expectContentType)) {
String res = new String(body.bytes(), StandardCharsets.UTF_8);
System.out.println(res);
return null;
}
return body.bytes();
}
System.out.println("response body is null");
} else {
System.out.println("request failed, http code: " + response.code());
}
} catch (IOException ioException) {
System.out.println("request exec error: " + ioException.getMessage());
}
return null;
}
}

View File

@ -1,5 +1,8 @@
package com.dc.common.utils.youdaoUtils;
import cn.hutool.core.util.ObjectUtil;
import org.apache.commons.lang3.StringUtils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
@ -21,7 +24,14 @@ public class AuthV3Util {
*/
public static void addAuthParams(String appKey, String appSecret, Map<String, Object> paramsMap)
throws NoSuchAlgorithmException {
String qArray = paramsMap.get("img").toString();
String qArray="";
if(ObjectUtil.isNotEmpty(paramsMap.get("q"))){
qArray = paramsMap.get("q").toString();
}
if (StringUtils.isEmpty(qArray)) {
qArray = paramsMap.get("img").toString();
}
String salt = UUID.randomUUID().toString();
String curtime = String.valueOf(System.currentTimeMillis() / 1000);
String sign = calculateSign(appKey, appSecret, qArray, salt, curtime);
@ -32,6 +42,26 @@ public class AuthV3Util {
paramsMap.put("sign",sign);
}
public static void addTTSAuthParams(String appKey, String appSecret, Map<String, String[]> paramsMap)
throws NoSuchAlgorithmException {
String[] qArray = paramsMap.get("q");
if (qArray == null) {
qArray = paramsMap.get("img");
}
StringBuilder q = new StringBuilder();
for (String item : qArray) {
q.append(item);
}
String salt = UUID.randomUUID().toString();
String curtime = String.valueOf(System.currentTimeMillis() / 1000);
String sign = calculateSign(appKey, appSecret, q.toString(), salt, curtime);
paramsMap.put("appKey", new String[]{appKey});
paramsMap.put("salt", new String[]{salt});
paramsMap.put("curtime", new String[]{curtime});
paramsMap.put("signType", new String[]{"v3"});
paramsMap.put("sign", new String[]{sign});
}
/**
* 计算鉴权签名 -
* 计算方式 : sign = sha256(appKey + input(q) + salt + curtime + appSecret)

View File

@ -21,9 +21,14 @@ public class FileUtil {
public static String saveFile(String path, byte[] data, boolean needDecode) throws IOException {
File file = new File(path);
if (!file.exists()) {
file.createNewFile();
if (!file.exists())
{
if (!file.getParentFile().exists())
{
file.getParentFile().mkdirs();
}
}
file.createNewFile();
byte[] bytes = data;
if (needDecode) {
String base64 = new String(data, StandardCharsets.UTF_8);