|
@@ -7,14 +7,18 @@ import cn.hutool.extra.qrcode.QrCodeUtil;
|
|
|
import cn.hutool.json.JSONUtil;
|
|
import cn.hutool.json.JSONUtil;
|
|
|
import com.alibaba.fastjson.JSON;
|
|
import com.alibaba.fastjson.JSON;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
|
|
+import com.fs.aiSoundReplication.VoiceCloneController;
|
|
|
|
|
+import com.fs.aiSoundReplication.util.FileToMultipartConverterUtil;
|
|
|
import com.fs.app.annotation.Login;
|
|
import com.fs.app.annotation.Login;
|
|
|
import com.fs.app.controller.AppBaseController;
|
|
import com.fs.app.controller.AppBaseController;
|
|
|
import com.fs.app.param.FsBindCompanyUserParam;
|
|
import com.fs.app.param.FsBindCompanyUserParam;
|
|
|
import com.fs.common.config.FSConfig;
|
|
import com.fs.common.config.FSConfig;
|
|
|
|
|
+import com.fs.common.core.domain.AjaxResult;
|
|
|
import com.fs.common.core.domain.R;
|
|
import com.fs.common.core.domain.R;
|
|
|
import com.fs.common.core.page.TableDataInfo;
|
|
import com.fs.common.core.page.TableDataInfo;
|
|
|
import com.fs.common.core.redis.RedisCache;
|
|
import com.fs.common.core.redis.RedisCache;
|
|
|
import com.fs.common.exception.CustomException;
|
|
import com.fs.common.exception.CustomException;
|
|
|
|
|
+import com.fs.common.exception.ServiceException;
|
|
|
import com.fs.common.utils.CloudHostUtils;
|
|
import com.fs.common.utils.CloudHostUtils;
|
|
|
import com.fs.common.utils.ServletUtils;
|
|
import com.fs.common.utils.ServletUtils;
|
|
|
import com.fs.common.utils.sign.Md5Utils;
|
|
import com.fs.common.utils.sign.Md5Utils;
|
|
@@ -24,6 +28,7 @@ import com.fs.company.domain.CompanyUserCard;
|
|
|
import com.fs.company.domain.CompanyUserUser;
|
|
import com.fs.company.domain.CompanyUserUser;
|
|
|
import com.fs.company.mapper.CompanyUserMapper;
|
|
import com.fs.company.mapper.CompanyUserMapper;
|
|
|
import com.fs.company.param.CompanyUserLoginParam;
|
|
import com.fs.company.param.CompanyUserLoginParam;
|
|
|
|
|
+import com.fs.company.param.VcCompanyUser;
|
|
|
import com.fs.company.param.companyUserAddPrintParam;
|
|
import com.fs.company.param.companyUserAddPrintParam;
|
|
|
import com.fs.company.service.ICompanyUserCardService;
|
|
import com.fs.company.service.ICompanyUserCardService;
|
|
|
import com.fs.company.service.ICompanyUserService;
|
|
import com.fs.company.service.ICompanyUserService;
|
|
@@ -34,7 +39,9 @@ import com.fs.fastGpt.mapper.FastgptChatVoiceHomoMapper;
|
|
|
import com.fs.fastgptApi.util.AudioUtils;
|
|
import com.fs.fastgptApi.util.AudioUtils;
|
|
|
import com.fs.fastgptApi.vo.AudioVO;
|
|
import com.fs.fastgptApi.vo.AudioVO;
|
|
|
import com.fs.framework.security.SecurityUtils;
|
|
import com.fs.framework.security.SecurityUtils;
|
|
|
|
|
+import com.fs.his.utils.ConfigUtil;
|
|
|
import com.fs.his.utils.PhoneUtil;
|
|
import com.fs.his.utils.PhoneUtil;
|
|
|
|
|
+import com.fs.hisStore.enums.SysConfigEnum;
|
|
|
import com.fs.sop.domain.QwSopTempVoice;
|
|
import com.fs.sop.domain.QwSopTempVoice;
|
|
|
import com.fs.sop.service.IQwSopTempVoiceService;
|
|
import com.fs.sop.service.IQwSopTempVoiceService;
|
|
|
import com.fs.system.oss.CloudStorageService;
|
|
import com.fs.system.oss.CloudStorageService;
|
|
@@ -52,15 +59,19 @@ import org.apache.http.util.EntityUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
import org.springframework.web.bind.annotation.*;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
|
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
-import java.io.File;
|
|
|
|
|
-import java.io.FileInputStream;
|
|
|
|
|
-import java.io.IOException;
|
|
|
|
|
-import java.io.InputStream;
|
|
|
|
|
|
|
+import javax.sound.sampled.AudioFormat;
|
|
|
|
|
+import javax.sound.sampled.AudioInputStream;
|
|
|
|
|
+import javax.sound.sampled.AudioSystem;
|
|
|
|
|
+import java.io.*;
|
|
|
|
|
+import java.net.HttpURLConnection;
|
|
|
|
|
+import java.net.URL;
|
|
|
import java.util.HashMap;
|
|
import java.util.HashMap;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
|
|
|
+import java.util.UUID;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
@@ -95,6 +106,14 @@ public class CompanyUserScrmController extends AppBaseController {
|
|
|
private FastgptChatVoiceHomoMapper fastgptChatVoiceHomoMapper;
|
|
private FastgptChatVoiceHomoMapper fastgptChatVoiceHomoMapper;
|
|
|
@Autowired
|
|
@Autowired
|
|
|
AiHostProper aiHostProper;
|
|
AiHostProper aiHostProper;
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private ConfigUtil configUtil;
|
|
|
|
|
+ @Autowired
|
|
|
|
|
+ private VoiceCloneController voiceCloneController;
|
|
|
|
|
+ private static final String VOICE_CLONE_CACHE_KEY = "voice:clone:company:user:id";
|
|
|
|
|
+ private static final Integer CACHE_TTL = 3600;
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
|
|
|
@PostMapping("/login")
|
|
@PostMapping("/login")
|
|
|
public R Login(@RequestBody CompanyUserLoginParam param, HttpServletRequest request){
|
|
public R Login(@RequestBody CompanyUserLoginParam param, HttpServletRequest request){
|
|
@@ -298,31 +317,43 @@ public class CompanyUserScrmController extends AppBaseController {
|
|
|
//更新销售员工声纹
|
|
//更新销售员工声纹
|
|
|
companyUser.setVoicePrintUrl(wavUrl);
|
|
companyUser.setVoicePrintUrl(wavUrl);
|
|
|
companyUserMapper.updateCompanyUser(companyUser);
|
|
companyUserMapper.updateCompanyUser(companyUser);
|
|
|
-
|
|
|
|
|
- try {
|
|
|
|
|
- CloseableHttpClient httpClient = HttpClients.createDefault();
|
|
|
|
|
- HttpPost httpPost = new HttpPost(aiHostProper.getCommonApi()+"/app/common/addCompanyAudio");
|
|
|
|
|
- String json = "{\"url\":\""+wavUrl+"\",\"id\":\""+userId+"\"}";
|
|
|
|
|
- StringEntity entity = new StringEntity(json);
|
|
|
|
|
- httpPost.setEntity(entity);
|
|
|
|
|
- httpPost.setHeader("Content-type", "application/json");
|
|
|
|
|
- HttpResponse response = httpClient.execute(httpPost);
|
|
|
|
|
-
|
|
|
|
|
- if (response.getStatusLine().getStatusCode() == 200) {
|
|
|
|
|
- String responseBody = EntityUtils.toString(response.getEntity());
|
|
|
|
|
- JSONObject jsonObject = JSON.parseObject(responseBody);
|
|
|
|
|
- Integer code = (Integer)jsonObject.get("code");
|
|
|
|
|
- if (code==200){
|
|
|
|
|
- voiceService.insertQwSopTempVoiceModel(userId);
|
|
|
|
|
- return R.ok();
|
|
|
|
|
- }
|
|
|
|
|
- } else {
|
|
|
|
|
- return R.error();
|
|
|
|
|
|
|
+ JSONObject vcConfig = configUtil.generateConfigByKey(SysConfigEnum.VS_CONFIG.getKey());
|
|
|
|
|
+ if (vcConfig != null && !vcConfig.isEmpty() &&
|
|
|
|
|
+// !vcConfig.equals(new JSONObject()) &&
|
|
|
|
|
+ "2".equals(vcConfig.getString("type"))) {
|
|
|
|
|
+ /*走豆包直接不走原逻辑*/
|
|
|
|
|
+ AjaxResult ajaxResult = uploadVoice(userId, wavUrl);
|
|
|
|
|
+ if (!ajaxResult.get("code").equals(200)) {
|
|
|
|
|
+ return R.error((String) ajaxResult.get("msg"));
|
|
|
}
|
|
}
|
|
|
|
|
+ voiceService.insertQwSopTempVoiceModel(userId);
|
|
|
|
|
+ return R.ok(ajaxResult.get("msg").toString());
|
|
|
|
|
+ } else {
|
|
|
|
|
+ try {
|
|
|
|
|
+ CloseableHttpClient httpClient = HttpClients.createDefault();
|
|
|
|
|
+ HttpPost httpPost = new HttpPost(aiHostProper.getCommonApi()+"/app/common/addCompanyAudio");
|
|
|
|
|
+ String json = "{\"url\":\""+wavUrl+"\",\"id\":\""+userId+"\"}";
|
|
|
|
|
+ StringEntity entity = new StringEntity(json);
|
|
|
|
|
+ httpPost.setEntity(entity);
|
|
|
|
|
+ httpPost.setHeader("Content-type", "application/json");
|
|
|
|
|
+ HttpResponse response = httpClient.execute(httpPost);
|
|
|
|
|
+
|
|
|
|
|
+ if (response.getStatusLine().getStatusCode() == 200) {
|
|
|
|
|
+ String responseBody = EntityUtils.toString(response.getEntity());
|
|
|
|
|
+ JSONObject jsonObject = JSON.parseObject(responseBody);
|
|
|
|
|
+ Integer code = (Integer)jsonObject.get("code");
|
|
|
|
|
+ if (code==200){
|
|
|
|
|
+ voiceService.insertQwSopTempVoiceModel(userId);
|
|
|
|
|
+ return R.ok();
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return R.error();
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- httpClient.close();
|
|
|
|
|
- } catch (Exception e) {
|
|
|
|
|
- e.printStackTrace();
|
|
|
|
|
|
|
+ httpClient.close();
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ e.printStackTrace();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return R.error();
|
|
return R.error();
|
|
@@ -442,5 +473,135 @@ public class CompanyUserScrmController extends AppBaseController {
|
|
|
return R.ok().put("data", audioVO);
|
|
return R.ok().put("data", audioVO);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 上传音频豆包
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param voicePrintUrl
|
|
|
|
|
+ * @return
|
|
|
|
|
+ * @throws Exception
|
|
|
|
|
+ */
|
|
|
|
|
+ @PostMapping("/uploadVoice")
|
|
|
|
|
+ public AjaxResult uploadVoice(
|
|
|
|
|
+ Long userId,
|
|
|
|
|
+ String voicePrintUrl) throws Exception {
|
|
|
|
|
+ if (userId == null) userId = 123L;
|
|
|
|
|
+ VcCompanyUser vcCompanyUser = companyUserMapper.selectVcCompanyUserByCompanyUserId(userId);
|
|
|
|
|
+ if (vcCompanyUser == null) {
|
|
|
|
|
+ return AjaxResult.error("用户没有声纹槽位,请联系管理员");
|
|
|
|
|
+ }
|
|
|
|
|
+ if (vcCompanyUser.getTimes() != null && vcCompanyUser.getTimes() >= 5)
|
|
|
|
|
+ return AjaxResult.error("用户已上传声纹达到上限");
|
|
|
|
|
+ vcCompanyUser.setUploadUrl(voicePrintUrl);
|
|
|
|
|
+ File file = downloadFileFromUrl(voicePrintUrl);
|
|
|
|
|
+ /*获取文件时长*/
|
|
|
|
|
+ AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(file);
|
|
|
|
|
+ AudioFormat format = audioInputStream.getFormat();
|
|
|
|
|
+ long frames = audioInputStream.getFrameLength();
|
|
|
|
|
+ // 计算时长(秒)= 总帧数 / 帧率
|
|
|
|
|
+ Double duration = (double) frames / format.getFrameRate();
|
|
|
|
|
+ audioInputStream.close();
|
|
|
|
|
+ MultipartFile convert = FileToMultipartConverterUtil.convert(file);
|
|
|
|
|
+ R r = voiceCloneController.uploadVoice(vcCompanyUser.getSpeakerId(), convert, vcCompanyUser.getVersionId(), 0);
|
|
|
|
|
+ if (!r.get("code").equals(200))
|
|
|
|
|
+ return AjaxResult.error("上传声纹失败", r.get("msg"));
|
|
|
|
|
+ vcCompanyUser.incrementTimes();
|
|
|
|
|
+ vcCompanyUser.setUploadTime(duration);
|
|
|
|
|
+ companyUserMapper.updateVcCompanyUser(vcCompanyUser);
|
|
|
|
|
+ return AjaxResult.success(String.format("您已上传%d次声纹,总共有5次录入次数", vcCompanyUser.getTimes()));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取文件扩展名
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param fileUrl 文件路径
|
|
|
|
|
+ * @return 文件扩展名
|
|
|
|
|
+ */
|
|
|
|
|
+ private static String getFileExtension(String fileUrl) {
|
|
|
|
|
+ return fileUrl.substring(fileUrl.lastIndexOf("."));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ private File downloadFileFromUrl(String fileUrl) throws IOException {
|
|
|
|
|
+ InputStream inputStream = null;
|
|
|
|
|
+ FileOutputStream outputStream = null;
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 创建 HTTP 连接
|
|
|
|
|
+ URL url = new URL(fileUrl);
|
|
|
|
|
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
|
|
|
|
+ // 设置Referer请求头
|
|
|
|
|
+// connection.setRequestProperty("Referer", "cos.his.cdwjyyh.com");
|
|
|
|
|
+ connection.setRequestMethod("GET");
|
|
|
|
|
+ connection.connect();
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否成功连接
|
|
|
|
|
+ if (connection.getResponseCode() != 200) {
|
|
|
|
|
+ throw new ServiceException("无法下载音频文件,HTTP 响应码:" + connection.getResponseCode());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取输入流
|
|
|
|
|
+ inputStream = connection.getInputStream();
|
|
|
|
|
+
|
|
|
|
|
+ // 创建临时文件,并指定存放地址
|
|
|
|
|
+ String tempFileName = "temp_" + UUID.randomUUID() + "_" + getFileExtension(fileUrl);
|
|
|
|
|
+ File destinationDirectory = new File("c:\\hook\\");
|
|
|
|
|
+
|
|
|
|
|
+ // 参照 transferAudioSilk 方法,同步确保目录创建的线程安全
|
|
|
|
|
+ synchronized (AudioUtils.class) {
|
|
|
|
|
+ if (!destinationDirectory.exists()) {
|
|
|
|
|
+ destinationDirectory.mkdirs();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 将文件保存到指定路径
|
|
|
|
|
+ File tempFile = new File(destinationDirectory, tempFileName);
|
|
|
|
|
+
|
|
|
|
|
+ // 写入文件
|
|
|
|
|
+ outputStream = new FileOutputStream(tempFile);
|
|
|
|
|
+ byte[] buffer = new byte[8192];
|
|
|
|
|
+ int bytesRead;
|
|
|
|
|
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
|
|
|
|
|
+ outputStream.write(buffer, 0, bytesRead);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return tempFile;
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ e.printStackTrace();
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (inputStream != null) inputStream.close();
|
|
|
|
|
+ if (outputStream != null) outputStream.close();
|
|
|
|
|
+ } catch (IOException e) {
|
|
|
|
|
+ e.printStackTrace();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public VcCompanyUser getVcCompanyUser(Long companyUserId) {
|
|
|
|
|
+ String cacheKey = VOICE_CLONE_CACHE_KEY + companyUserId;
|
|
|
|
|
+ VcCompanyUser vcCompanyUser = redisCache.getCacheObject(cacheKey);
|
|
|
|
|
+ if (vcCompanyUser != null) {
|
|
|
|
|
+ return vcCompanyUser;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 使用双重检查锁,防止缓存击穿
|
|
|
|
|
+ synchronized (this) {
|
|
|
|
|
+ // 再次检查缓存,防止在等待锁的过程中已经被其他线程加载
|
|
|
|
|
+ vcCompanyUser = redisCache.getCacheObject(cacheKey);
|
|
|
|
|
+ if (vcCompanyUser != null) {
|
|
|
|
|
+ return vcCompanyUser;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 查询数据库
|
|
|
|
|
+ vcCompanyUser = companyUserMapper.selectVcCompanyUserByCompanyUserId(companyUserId);
|
|
|
|
|
+ if (vcCompanyUser == null) {
|
|
|
|
|
+ // 缓存空对象,防止缓存穿透
|
|
|
|
|
+ redisCache.setCacheObject(cacheKey, new VcCompanyUser(), CACHE_TTL, TimeUnit.SECONDS);
|
|
|
|
|
+ throw new RuntimeException("用户不存在");
|
|
|
|
|
+ }
|
|
|
|
|
+ // 设置缓存
|
|
|
|
|
+ redisCache.setCacheObject(cacheKey, vcCompanyUser, CACHE_TTL,TimeUnit.SECONDS);
|
|
|
|
|
+ return vcCompanyUser;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
|
|
|
}
|
|
}
|