语音质检系统api

QcResultServiceImpl.java 83KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627
  1. package api.service.quality.impl;
  2. import api.entity.database.call.Translate;
  3. import api.entity.database.quality.*;
  4. import api.entity.input.PageInput;
  5. import api.entity.input.quality.QcResultPush;
  6. import api.entity.input.quality.RepTranslate;
  7. import api.entity.view.quality.ExcelSentiment;
  8. import api.entity.view.quality.HitRules;
  9. import api.mapper.quality.QcResultMapper;
  10. import api.service.call.ITranslateService;
  11. import api.service.quality.*;
  12. import api.service.BaseServiceImpl;
  13. import api.service.system.IConfigService;
  14. import api.util.annotation.Log;
  15. import api.util.enums.RulesCondition;
  16. import api.util.helper.DateHelper;
  17. import api.util.helper.StringHelper;
  18. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  19. import com.baomidou.mybatisplus.core.metadata.IPage;
  20. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  21. import com.fasterxml.jackson.core.JsonProcessingException;
  22. import com.fasterxml.jackson.databind.ObjectMapper;
  23. //import edu.stanford.nlp.ling.CoreAnnotations;
  24. //import edu.stanford.nlp.neural.rnn.RNNCoreAnnotations;
  25. //import edu.stanford.nlp.pipeline.Annotation;
  26. //import edu.stanford.nlp.pipeline.StanfordCoreNLP;
  27. //import edu.stanford.nlp.sentiment.SentimentCoreAnnotations;
  28. //import edu.stanford.nlp.util.CoreMap;
  29. //import com.hankcs.hanlp.HanLP;
  30. import lombok.var;
  31. import opennlp.tools.doccat.*;
  32. import opennlp.tools.tokenize.SimpleTokenizer;
  33. import opennlp.tools.util.CollectionObjectStream;
  34. import opennlp.tools.util.ObjectStream;
  35. import opennlp.tools.util.model.ModelUtil;
  36. import org.apache.poi.ss.usermodel.Cell;
  37. import org.apache.poi.ss.usermodel.Row;
  38. import org.apache.poi.ss.usermodel.Sheet;
  39. import org.apache.poi.ss.usermodel.Workbook;
  40. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  41. import org.slf4j.Logger;
  42. import org.springframework.beans.factory.annotation.Autowired;
  43. import org.springframework.stereotype.Service;
  44. import org.springframework.transaction.annotation.Transactional;
  45. import javax.sound.sampled.*;
  46. import java.io.*;
  47. import java.net.HttpURLConnection;
  48. import java.net.URL;
  49. import java.nio.ByteBuffer;
  50. import java.nio.ByteOrder;
  51. import java.nio.MappedByteBuffer;
  52. import java.nio.channels.FileChannel;
  53. import java.nio.charset.StandardCharsets;
  54. import java.nio.file.Files;
  55. import java.nio.file.Path;
  56. import java.nio.file.Paths;
  57. import java.time.Duration;
  58. import java.time.Instant;
  59. import java.time.LocalDate;
  60. import java.util.*;
  61. @Transactional
  62. @Service
  63. public class QcResultServiceImpl extends BaseServiceImpl<QcResultMapper, QcResult> implements IQcResultService{
  64. public QcResultServiceImpl(){ super(false); }
  65. //质检规则主体
  66. @Autowired
  67. private IInspectionRulesService inspectionrulesService;
  68. //通话翻译主体
  69. @Autowired
  70. private ITranslateService translateService;
  71. //评分标准主体
  72. @Autowired
  73. private IScoringCriteriaService scoringCriteriaService;
  74. //系统配置主体
  75. @Autowired
  76. private IConfigService configService;
  77. //触发规则主体
  78. @Autowired
  79. private ITriggerRulesService triggerRulesService;
  80. //词汇表
  81. @Autowired
  82. private ISearchLexiconService searchLexiconService;
  83. @Autowired
  84. private IQualityModelService modelService;
  85. private <T> Page<T> GetPage(PageInput page) {
  86. if (page.getPageNum() != null && page.getPageSize() != null) {
  87. Page<T> ipage = new Page<>();
  88. ipage.setCurrent(page.getPageNum());
  89. ipage.setSize(page.getPageSize());
  90. return ipage;
  91. } else {
  92. return null;
  93. }
  94. }
  95. //分隔音频获取音量音速
  96. @Override
  97. public List<String> GetAudio(String callId) throws UnsupportedAudioFileException, IOException {
  98. //获取通话记录
  99. LambdaQueryWrapper<QcResult> qw =new LambdaQueryWrapper<>();
  100. //qw.apply("IFNULL(user_name,'') != ''");
  101. qw.apply("IFNULL(file_path,'') != ''");
  102. qw.eq(QcResult::getIsQuality,0);
  103. // if (!Objects.equals(callId, ""))
  104. // {
  105. // qw.eq(QcResult::getCallid,callId);
  106. // }
  107. // qw.eq(QcResult::getUserName,"8002");
  108. // qw.ge (QcResult::getCallid,580);
  109. qw.orderByAsc(QcResult::getQcId);
  110. PageInput pageInput=new PageInput();
  111. pageInput.setPageNum(1);
  112. pageInput.setPageSize(100);
  113. Page<QcResult> page = GetPage(pageInput);
  114. IPage<QcResult> iPage = this.getListPage(page, qw);
  115. // var calls=this.getList(qw);
  116. var calls=iPage.getRecords();
  117. List<String> audio=new ArrayList<>();
  118. if (calls!=null&&calls.size()>0)
  119. {
  120. for (QcResult call : calls )
  121. {
  122. //获取质检规则
  123. LambdaQueryWrapper<InspectionRules> qe =new LambdaQueryWrapper<>();
  124. qe.le ( InspectionRules::getStartTime, new Date());
  125. qe.ge ( InspectionRules::getEndTime, new Date());
  126. qe.eq(InspectionRules::getIsDelete,0);
  127. qe.like(InspectionRules::getApplySeats,call.getUserName());
  128. var Rules=inspectionrulesService.getList(qe);
  129. new InspectionRules();
  130. InspectionRules rule;
  131. if (Rules!=null&&Rules.size()>0)
  132. {
  133. rule=Rules.get(0);
  134. }
  135. else
  136. {
  137. //当前时间无质检规则直接抛出
  138. continue;
  139. }
  140. String FileUrl="";String URL="";
  141. //获取录音路径
  142. if (!StringHelper.isEmpty(call.getFilePath())) {
  143. URL="http://1.194.161.64:9000/"+call.getFilePath();
  144. FileUrl= downloadFile(URL,"files/luYin/"+ DateHelper.getDate());
  145. }
  146. if (Objects.equals(FileUrl, ""))
  147. {
  148. continue;
  149. }
  150. call.setRuleId(rule.getId());
  151. LambdaQueryWrapper<Translate> qt=new LambdaQueryWrapper<>();
  152. //获取语音翻译
  153. qt.eq(Translate::getCallid,call.getCallid());
  154. var mobile=translateService.getList(qt);
  155. if (mobile!=null&&mobile.size()>0)
  156. {
  157. ObjectMapper mapper = new ObjectMapper();
  158. try
  159. {
  160. if (StringHelper.isNotEmpty(mobile.get(0).getTranslate()))
  161. {
  162. //解析同声翻译
  163. RepTranslate [] translate = mapper.readValue(mobile.get(0).getTranslate(), RepTranslate[].class);
  164. StringBuilder customerSpeech= new StringBuilder();StringBuilder seatSpeech= new StringBuilder();
  165. int duration=0;int Time=0;
  166. for (RepTranslate item :translate)
  167. {
  168. if(item.getNumber().length()>4)
  169. {
  170. customerSpeech.append(item.getSpeech());
  171. }
  172. else
  173. {
  174. seatSpeech.append(item.getSpeech());
  175. }
  176. int[] Millisecond=GetMillisecond(item.getTime());
  177. if (Millisecond!=null&&Millisecond.length>0)
  178. {
  179. duration+=Millisecond[0]-Time;
  180. Time=Millisecond[1];
  181. }
  182. }
  183. double decibel = calculateRMSVolume(new File(FileUrl));//获取音量
  184. call.setVolume(String.format("%.2f", decibel));
  185. int wordCount=returnCharacters(customerSpeech.toString()).length()+returnCharacters(seatSpeech.toString()).length();
  186. double speechRate=calculateSpeechRate( wordCount, new File(FileUrl));//获取语速
  187. call.setSpeed(String.format("%.2f", speechRate));
  188. //静音时长
  189. call.setMute((long) (duration/1000));
  190. //客户情绪
  191. call.setCustomerEmotion(modelService.OpenNLPDetection(customerSpeech.toString()));
  192. //坐席情绪
  193. call.setSeatEmotion(modelService.OpenNLPDetection(seatSpeech.toString()));
  194. //分段录音解析音量音速
  195. try {
  196. GetRepTranslates(translate, FileUrl);
  197. } catch (Exception e) {
  198. e.printStackTrace();
  199. }
  200. LambdaQueryWrapper<ScoringCriteria> qs =new LambdaQueryWrapper<>();
  201. qs.eq(ScoringCriteria::getIsDelete,0);//是否确认
  202. qs.eq(ScoringCriteria::getRulesId,rule.getId());//规则id
  203. var scoring=scoringCriteriaService.getList(qs);
  204. long score=100;int index=0;List<TriggerRules> AllTriggerRules=new ArrayList<>();
  205. int start=0;int validity = 0;
  206. if (scoring!=null&&scoring.size()>0)
  207. {
  208. LinkedHashMap<Long,List<String>> keyWorlds=keyWorlds(scoring);
  209. var filterKeyWorlds=filterKeyWorlds();
  210. for (RepTranslate item :translate)
  211. {
  212. index=index+1; List<TriggerRules> triggerRules=new ArrayList<>();
  213. StringBuilder trigger= new StringBuilder();
  214. if(item.getNumber().length()>4)
  215. {
  216. //只质检话务员,客户语音跳过
  217. if ( index!=translate.length)
  218. {
  219. continue;
  220. }
  221. }
  222. else
  223. {
  224. start=start+1;
  225. }
  226. for (ScoringCriteria scoringCriteria :scoring)
  227. {
  228. String value= RulesCondition.getTypeValue(scoringCriteria.getConditionName());
  229. TriggerRules triggerRules1=new TriggerRules();
  230. triggerRules1.setRulesId(rule.getId());//规则id
  231. triggerRules1.setRulesName(rule.getRuleName());//触发规则名称
  232. triggerRules1.setScoringId(scoringCriteria.getId());//触发标准id
  233. triggerRules1.setScoringName(value);//触发标准名称
  234. triggerRules1.setQualityId(call.getQcId());
  235. triggerRules1.setSoundName(item.getFilePath());
  236. HitRules hit =new HitRules();
  237. hit.setId(scoringCriteria.getId());
  238. hit.setName(value);
  239. hit.setScore(scoringCriteria.getScore());
  240. if (Objects.equals(scoringCriteria.getConditionName(), RulesCondition.prologue.name())
  241. &&start!=1)
  242. {
  243. //检测开场白非第一条跳过
  244. continue;
  245. }
  246. else if (Objects.equals(scoringCriteria.getConditionName(), RulesCondition.conclusion.name())&&
  247. index!=translate.length)
  248. {
  249. //检测结束语非不是最后一条跳过
  250. continue;
  251. }
  252. if(item.getNumber().length()>4&&Objects.equals(scoringCriteria.getConditionName(), RulesCondition.conclusion.name()))
  253. {
  254. for (int i=translate.length-1;i>=0;i--)
  255. {
  256. if (translate[i].getNumber().length()<=4)
  257. {
  258. item=translate[i];
  259. break;
  260. }
  261. }
  262. }
  263. else if(item.getNumber().length()>4)
  264. {
  265. continue;
  266. }
  267. if(Objects.equals(scoringCriteria.getConditionName(), RulesCondition.ringing.name())
  268. &&start==1)
  269. {
  270. if (call.getRingTime()!=null&& call.getAnswerTime()!=null)
  271. {
  272. // 将Date转换为Instant
  273. Instant instant1 = call.getRingTime().toInstant();
  274. Instant instant2 = call.getAnswerTime().toInstant();
  275. // 计算时间差
  276. long seconds = Duration.between(instant1, instant2).getSeconds() ;
  277. if (scoringCriteria.getMaxValue()!=null &&seconds>scoringCriteria.getMaxValue())
  278. {
  279. triggerRules.add(triggerRules1);
  280. // trigger.append(",振铃时长").append(seconds).append("命中规则条件超过")
  281. // .append(value);
  282. }
  283. }
  284. }
  285. else if(Objects.equals(scoringCriteria.getConditionName(), RulesCondition.duration.name())
  286. &&start==1)
  287. {
  288. if (call.getAnswerTime()!=null&& call.getHangupTime()!=null)
  289. {
  290. // 将Date转换为Instant
  291. Instant instant1 = call.getAnswerTime().toInstant();
  292. Instant instant2 = call.getHangupTime().toInstant();
  293. // 计算时间差
  294. long seconds = Duration.between(instant1, instant2).getSeconds() ;
  295. if (scoringCriteria.getMaxValue()!=null &&scoringCriteria.getMaxValue()>0&&
  296. scoringCriteria.getMinValue()!=null&&scoringCriteria.getMinValue()>0
  297. )
  298. {
  299. if ( seconds>scoringCriteria.getMaxValue() ||seconds<scoringCriteria.getMinValue())
  300. {
  301. triggerRules.add(triggerRules1);
  302. // trigger.append(",通话时长异常").append("命中规则条件")
  303. // .append(value);
  304. }
  305. }
  306. else if (scoringCriteria.getMaxValue()!=null &&scoringCriteria.getMaxValue()>0 &&seconds>scoringCriteria.getMaxValue())
  307. {
  308. triggerRules.add(triggerRules1);
  309. // trigger.append(",通话时长异常").append("命中规则条件")
  310. // .append(value);
  311. }
  312. else if (scoringCriteria.getMinValue()!=null&&scoringCriteria.getMinValue()>0 &&seconds<scoringCriteria.getMinValue())
  313. {
  314. triggerRules.add(triggerRules1);
  315. //trigger.append(",通话时长异常").append("命中规则条件")
  316. // .append(value);
  317. }
  318. }
  319. }
  320. else if(Objects.equals(scoringCriteria.getConditionName(), RulesCondition.mute.name())
  321. &&start==1)
  322. {
  323. if (scoringCriteria.getMaxValue()!=null &&(long) (duration/1000)>scoringCriteria.getMaxValue())
  324. {
  325. triggerRules.add(triggerRules1);
  326. // trigger.append(",静音分析").append((long) (duration/1000)).append("命中规则条件超过")
  327. // .append(value);
  328. }
  329. }
  330. else if(Objects.equals(scoringCriteria.getConditionName(), RulesCondition.satisfaction.name())
  331. &&start==1)
  332. {
  333. if (call.getMyd()!=null&&call.getMyd()==3)
  334. {
  335. triggerRules.add(triggerRules1);
  336. // trigger.append(",用户满意度").append("不满意").append("命中规则条件")
  337. // .append(value);
  338. }
  339. }
  340. else if(Objects.equals(scoringCriteria.getConditionName(), RulesCondition.emotional.name())
  341. )
  342. {
  343. if (StringHelper.isNotEmpty(item.getSpeech())
  344. &&item.getSpeech().length()>10)
  345. {
  346. if(Objects.equals(modelService.OpenNLPDetection(item.getSpeech()), "消极"))
  347. {
  348. triggerRules.add(triggerRules1);
  349. hit.setHit("消极");
  350. trigger.append(",").append(hit.toJsonString());
  351. }
  352. }
  353. }
  354. else if(Objects.equals(scoringCriteria.getConditionName(), RulesCondition.response.name())
  355. &&start==1)
  356. {
  357. if (scoringCriteria.getMaxValue()!=null)
  358. {
  359. if (index==1)
  360. {
  361. int[] Millisecond=GetMillisecond(item.getTime());
  362. if(Millisecond!=null)
  363. {
  364. validity=Millisecond[0]/1000;
  365. if (validity>scoringCriteria.getMaxValue())
  366. {
  367. triggerRules.add(triggerRules1);
  368. hit.setHit(String.valueOf(validity));
  369. trigger.append(",").append(hit.toJsonString());
  370. }
  371. }
  372. }
  373. else
  374. {
  375. int[] Millisecond=GetMillisecond(item.getTime());
  376. if(Millisecond!=null)
  377. {
  378. int[] Millisecond1=GetMillisecond(translate[index-2].getTime());
  379. validity=(Millisecond[0]-Millisecond1[1])/1000;
  380. if (validity>scoringCriteria.getMaxValue())
  381. {
  382. triggerRules.add(triggerRules1);
  383. hit.setHit(String.valueOf(validity));
  384. trigger.append(",").append(hit.toJsonString());
  385. }
  386. }
  387. }
  388. }
  389. }
  390. else
  391. {
  392. //声音过短不质检
  393. if (StringHelper.isNotEmpty(item.getSpeech())
  394. &&(item.getSpeech().length()>2||Objects.equals(scoringCriteria.getConditionName(), RulesCondition.prologue.name())
  395. ||Objects.equals(scoringCriteria.getConditionName(), RulesCondition.conclusion.name()))) {
  396. //当前评分为音量
  397. if (Objects.equals(scoringCriteria.getConditionName(), RulesCondition.volume.name())) {
  398. if (scoringCriteria.getMaxValue()!=null &&scoringCriteria.getMinValue()!=null
  399. ){
  400. if(Double.parseDouble(item.getVolume()) < scoringCriteria.getMinValue()
  401. ||Double.parseDouble(item.getVolume())> scoringCriteria.getMaxValue())
  402. {
  403. triggerRules.add(triggerRules1);
  404. hit.setHit(String.valueOf(item.getVolume()));
  405. trigger.append(",").append(hit.toJsonString());
  406. }
  407. }
  408. else if (scoringCriteria.getMaxValue()!=null &&
  409. Double.parseDouble(item.getVolume())> scoringCriteria.getMaxValue())
  410. {
  411. triggerRules.add(triggerRules1);
  412. hit.setHit(String.valueOf(item.getVolume()));
  413. trigger.append(",").append(hit.toJsonString());
  414. }
  415. else if (scoringCriteria.getMinValue()!=null&&
  416. Double.parseDouble(item.getVolume()) < scoringCriteria.getMinValue())
  417. {
  418. triggerRules.add(triggerRules1);
  419. hit.setHit(String.valueOf(item.getVolume()));
  420. trigger.append(",").append(hit.toJsonString());
  421. }
  422. }
  423. //当前评分为语速
  424. else if (Objects.equals(scoringCriteria.getConditionName(), RulesCondition.speed.name())) {
  425. //语速
  426. if (scoringCriteria.getMaxValue()!=null &&scoringCriteria.getMinValue()!=null
  427. ){
  428. if (Double.parseDouble(item.getSpeed()) < scoringCriteria.getMinValue()
  429. ||Double.parseDouble(item.getSpeed())> scoringCriteria.getMaxValue())
  430. {
  431. triggerRules.add(triggerRules1);
  432. hit.setHit(String.valueOf(item.getSpeed()));
  433. trigger.append(",").append(hit.toJsonString());
  434. }
  435. }
  436. else if (scoringCriteria.getMaxValue()!=null &&
  437. Double.parseDouble(item.getSpeed())> scoringCriteria.getMaxValue())
  438. {
  439. triggerRules.add(triggerRules1);
  440. hit.setHit(String.valueOf(item.getSpeed()));
  441. trigger.append(",").append(hit.toJsonString());
  442. }
  443. else if (scoringCriteria.getMinValue()!=null&&
  444. Double.parseDouble(item.getSpeed()) < scoringCriteria.getMinValue())
  445. {
  446. triggerRules.add(triggerRules1);
  447. hit.setHit(String.valueOf(item.getSpeed()));
  448. trigger.append(",").append(hit.toJsonString());
  449. }
  450. }
  451. else if (Objects.equals(scoringCriteria.getConditionName(), RulesCondition.interrupt.name()))
  452. {
  453. if (index>1)
  454. {
  455. if(translate[index-2].getNumber().length()>4&&translate[index-1].getNumber().length()<=4)
  456. {
  457. int[] Millisecond=GetMillisecond(item.getTime());
  458. int[] Millisecond1=GetMillisecond(translate[index-2].getTime());
  459. if (Millisecond[0]<Millisecond1[1])
  460. {
  461. triggerRules.add(triggerRules1);
  462. // hit.setHit(String.valueOf(item.getSpeed()));
  463. trigger.append(",").append(hit.toJsonString());
  464. }
  465. }
  466. }
  467. }
  468. else
  469. {
  470. //获取关键词
  471. List<String> keywords=keyWorlds.get(scoringCriteria.getId());
  472. if(keywords!=null&&keywords.size()>0)
  473. {
  474. if (scoringCriteria.getIsContain()!=null&&scoringCriteria.getIsContain()>1)
  475. {
  476. if (!searchLexiconService.containsAnyParallel(item.getSpeech(),keywords))
  477. {
  478. triggerRules.add(triggerRules1);
  479. trigger.append(",").append(hit.toJsonString());
  480. }
  481. }
  482. else
  483. {
  484. if (searchLexiconService.containsAnyParallel(item.getSpeech(),keywords))
  485. {
  486. if (Objects.equals(scoringCriteria.getConditionName(), RulesCondition.swearing.name()))
  487. {
  488. if (!searchLexiconService.containsAnyParallel(item.getSpeech(),filterKeyWorlds))
  489. {
  490. triggerRules.add(triggerRules1);
  491. trigger.append(",").append(hit.toJsonString());
  492. }
  493. }
  494. else
  495. {
  496. triggerRules.add(triggerRules1);
  497. trigger.append(",").append(hit.toJsonString());
  498. }
  499. }
  500. }
  501. }
  502. }
  503. }
  504. }
  505. }
  506. if (triggerRules.size()>0)
  507. {
  508. item.setTrigger("["+trigger.deleteCharAt(0).toString()+"]");
  509. AllTriggerRules.addAll(triggerRules);
  510. }
  511. }
  512. }
  513. List<LinkedHashMap<String,Object>> criteria=new ArrayList<>();
  514. if (scoring != null) {
  515. for (ScoringCriteria scoringCriteria :scoring) {
  516. long count=AllTriggerRules.stream().filter(x-> Objects.equals(x.getScoringId(),
  517. scoringCriteria.getId()))
  518. .count();
  519. Long frequency=scoringCriteria.getFrequency();
  520. if (scoringCriteria.getFrequency()==null||scoringCriteria.getFrequency()<=0)
  521. {
  522. frequency=1L;
  523. }
  524. if (count>=frequency)
  525. {
  526. long multiple=count/frequency*scoringCriteria.getScore();
  527. if (Objects.equals(scoringCriteria.getConditionName(), RulesCondition.volume.name()))
  528. {
  529. //音量只扣一次分
  530. multiple=scoringCriteria.getScore();
  531. }
  532. LinkedHashMap<String,Object> map=new LinkedHashMap<>();
  533. map.put("conditionName",RulesCondition.getTypeValue(scoringCriteria.getConditionName()));
  534. // if (scoringCriteria.getScoreType()==0)
  535. // {
  536. // map.put("score",scoringCriteria.getScore());
  537. // score+=multiple;
  538. // }
  539. // else
  540. // {
  541. // map.put("score",-(scoringCriteria.getScore()));
  542. // score-=multiple;
  543. // }
  544. map.put("score",-multiple);
  545. score-=multiple;
  546. map.put("isCritical",scoringCriteria.getIsCritical());
  547. if (scoringCriteria.getIsCritical()==1)
  548. {
  549. //是否致命
  550. call.setIsCritical(1L);
  551. }
  552. map.put("scoring",scoringCriteria);
  553. criteria.add(map);
  554. }
  555. }
  556. }
  557. if(AllTriggerRules.size()>0)
  558. {
  559. triggerRulesService.insert(AllTriggerRules);
  560. }
  561. if (criteria.size()>0)
  562. {
  563. //评分标准
  564. call.setScoringCriteria(mapper.writeValueAsString(criteria));
  565. }
  566. call.setValidity((long) validity);
  567. //质检是否成功
  568. call.setIsSuccess(1L);
  569. //AI质检分值
  570. call.setScore(score);
  571. //合格分数
  572. call.setPassingScore(rule.getPassingScore());
  573. //是否合格
  574. if (score>=rule.getPassingScore())
  575. {
  576. call.setIsQualified(1L);
  577. call.setAiResult("合格");
  578. }
  579. else
  580. {
  581. call.setIsQualified(2L);
  582. call.setAiResult("不合格");
  583. }
  584. //保存json文件
  585. call.setTextJson(mapper.writeValueAsString(translate));
  586. }
  587. else
  588. {
  589. call.setIsSuccess(2L);
  590. }
  591. }
  592. catch (Exception e)
  593. {
  594. call.setIsSuccess(2L);
  595. }
  596. }
  597. else
  598. {
  599. try
  600. {
  601. ObjectMapper mapper = new ObjectMapper();
  602. //没有翻译只检测音量音速
  603. double decibel = calculateRMSVolume(new File(FileUrl));//获取音量
  604. call.setVolume(String.format("%.2f", decibel));
  605. //double speechRate = analyzeSpeechSpeed(new File(FileUrl), -40.0);;//获取语速
  606. // call.setSpeed(String.format("%.2f", speechRate));
  607. LambdaQueryWrapper<ScoringCriteria> qs =new LambdaQueryWrapper<>();
  608. qs.eq(ScoringCriteria::getIsDelete,0);//是否确认
  609. qs.eq(ScoringCriteria::getRulesId,rule.getId());//规则id
  610. var scoring=scoringCriteriaService.getList(qs);
  611. List<TriggerRules> triggerRules=new ArrayList<>();
  612. StringBuilder trigger= new StringBuilder();long score=100;
  613. List<LinkedHashMap<String,Object>> criteria=new ArrayList<>();
  614. if (scoring != null) {
  615. Long isSuccess=1L;
  616. for (ScoringCriteria scoringCriteria :scoring) {
  617. if (RulesCondition.getTypeIsCharacters(scoringCriteria.getConditionName()))
  618. {
  619. isSuccess=2L;
  620. break;
  621. }
  622. }
  623. if (isSuccess==1L)
  624. {
  625. for (ScoringCriteria scoringCriteria :scoring)
  626. {
  627. String value= RulesCondition.getTypeValue(scoringCriteria.getConditionName());
  628. TriggerRules triggerRules1=new TriggerRules();
  629. triggerRules1.setRulesId(rule.getId());//规则id
  630. triggerRules1.setRulesName(rule.getRuleName());//触发规则名称
  631. triggerRules1.setScoringId(scoringCriteria.getId());//触发标准id
  632. triggerRules1.setScoringName(value);//触发标准名称
  633. triggerRules1.setQualityId(call.getQcId());
  634. triggerRules1.setSoundName(FileUrl);
  635. HitRules hit =new HitRules();
  636. hit.setId(scoringCriteria.getId());
  637. hit.setName(value);
  638. hit.setScore(scoringCriteria.getScore());
  639. long count=0;
  640. if(Objects.equals(scoringCriteria.getConditionName(), RulesCondition.ringing.name())
  641. )
  642. {
  643. if (call.getRingTime()!=null&& call.getAnswerTime()!=null)
  644. {
  645. // 将Date转换为Instant
  646. Instant instant1 = call.getRingTime().toInstant();
  647. Instant instant2 = call.getAnswerTime().toInstant();
  648. // 计算时间差
  649. long seconds = Duration.between(instant1, instant2).getSeconds() ;
  650. if (scoringCriteria.getMaxValue()!=null &&seconds>scoringCriteria.getMaxValue())
  651. {
  652. triggerRules.add(triggerRules1);
  653. // trigger.append(",振铃时长").append("命中规则条件")
  654. // .append(value);
  655. }
  656. }
  657. }
  658. else if(Objects.equals(scoringCriteria.getConditionName(), RulesCondition.duration.name())
  659. )
  660. {
  661. if (call.getAnswerTime()!=null&& call.getHangupTime()!=null)
  662. {
  663. // 将Date转换为Instant
  664. Instant instant1 = call.getAnswerTime().toInstant();
  665. Instant instant2 = call.getHangupTime().toInstant();
  666. // 计算时间差
  667. long seconds = Duration.between(instant1, instant2).getSeconds() ;
  668. if (scoringCriteria.getMaxValue()!=null&& scoringCriteria.getMaxValue()>0&&
  669. scoringCriteria.getMinValue()!=null&& scoringCriteria.getMinValue()>0
  670. )
  671. {
  672. if ( seconds>scoringCriteria.getMaxValue() ||seconds<scoringCriteria.getMinValue())
  673. {
  674. triggerRules.add(triggerRules1);
  675. // trigger.append(",通话时长异常").append("命中规则条件")
  676. // .append(value);
  677. }
  678. }
  679. else if (scoringCriteria.getMaxValue()!=null&& scoringCriteria.getMaxValue()>0 &&seconds>scoringCriteria.getMaxValue())
  680. {
  681. triggerRules.add(triggerRules1);
  682. // trigger.append(",通话时长异常").append("命中规则条件")
  683. // .append(value);
  684. }
  685. else if (scoringCriteria.getMinValue()!=null && scoringCriteria.getMinValue()>0
  686. &&seconds<scoringCriteria.getMinValue())
  687. {
  688. triggerRules.add(triggerRules1);
  689. //trigger.append(",通话时长异常").append("命中规则条件")
  690. // .append(value);
  691. }
  692. }
  693. }
  694. else
  695. if (Objects.equals(scoringCriteria.getConditionName(), RulesCondition.volume.name())) {
  696. if (scoringCriteria.getMaxValue()!=null &&scoringCriteria.getMinValue()!=null
  697. ){
  698. if (decibel < scoringCriteria.getMinValue()
  699. ||decibel> scoringCriteria.getMaxValue())
  700. {
  701. triggerRules.add(triggerRules1);
  702. // trigger.append(",音量").append(decibel).append("命中规则条件")
  703. // .append(value);
  704. hit.setHit(String.valueOf(decibel));
  705. trigger.append(",").append(hit.toJsonString());
  706. count=1;
  707. }
  708. }
  709. else if (scoringCriteria.getMaxValue()!=null &&
  710. decibel> scoringCriteria.getMaxValue())
  711. {
  712. triggerRules.add(triggerRules1);
  713. // trigger.append(",音量").append(decibel).append("命中规则条件")
  714. // .append(value);
  715. hit.setHit(String.valueOf(decibel));
  716. trigger.append(",").append(hit.toJsonString());
  717. count=1;
  718. }
  719. else if (scoringCriteria.getMinValue()!=null&&
  720. decibel < scoringCriteria.getMinValue())
  721. {
  722. triggerRules.add(triggerRules1);
  723. // trigger.append(",音量").append(decibel).append("命中规则条件")
  724. // .append(value);
  725. hit.setHit(String.valueOf(decibel));
  726. trigger.append(",").append(hit.toJsonString());
  727. count=1;
  728. }
  729. }
  730. //当前评分为语速
  731. // else if (Objects.equals(scoringCriteria.getConditionName(), RulesCondition.speed.getValue())) {
  732. // //音量音速固定为减分项
  733. // if (scoringCriteria.getIsContain()!=null&&scoringCriteria.getIsContain()>1)
  734. // {
  735. // if (speechRate < scoringCriteria.getMinValue()
  736. // &&speechRate> scoringCriteria.getMaxValue()){
  737. // triggerRules.add(triggerRules1);
  738. // trigger.append(",语速").append(speechRate).append("命中规则条件")
  739. // .append(scoringCriteria.getConditionName());
  740. // }
  741. // else if (scoringCriteria.getMaxValue()!=null &&
  742. // speechRate> scoringCriteria.getMaxValue())
  743. // {
  744. // triggerRules.add(triggerRules1);
  745. // trigger.append(",语速").append(speechRate).append("命中规则条件")
  746. // .append(scoringCriteria.getConditionName());
  747. // }
  748. // else if (scoringCriteria.getMinValue()!=null&&
  749. // speechRate < scoringCriteria.getMinValue())
  750. // {
  751. // triggerRules.add(triggerRules1);
  752. // trigger.append(",语速").append(speechRate).append("命中规则条件")
  753. // .append(scoringCriteria.getConditionName());
  754. // }
  755. // }
  756. // else
  757. // {
  758. // if (speechRate>= scoringCriteria.getMinValue()
  759. // &&speechRate<= scoringCriteria.getMaxValue()){
  760. // triggerRules.add(triggerRules1);
  761. // trigger.append(",语速").append(speechRate).append("命中规则条件")
  762. // .append(scoringCriteria.getConditionName());
  763. // }
  764. // else if (scoringCriteria.getMaxValue()!=null &&
  765. // speechRate<= scoringCriteria.getMaxValue())
  766. // {
  767. // triggerRules.add(triggerRules1);
  768. // trigger.append(",语速").append(speechRate).append("命中规则条件")
  769. // .append(scoringCriteria.getConditionName());
  770. // }
  771. // else if (scoringCriteria.getMinValue()!=null&&
  772. // speechRate >= scoringCriteria.getMinValue())
  773. // {
  774. // triggerRules.add(triggerRules1);
  775. // trigger.append(",语速").append(speechRate).append("命中规则条件")
  776. // .append(scoringCriteria.getConditionName());
  777. // }
  778. // }
  779. // }
  780. if (count==1)
  781. {
  782. LinkedHashMap<String,Object> map=new LinkedHashMap<>();
  783. map.put("conditionName",value);
  784. // if (scoringCriteria.getScoreType()==0)
  785. // {
  786. // map.put("score",scoringCriteria.getScore());
  787. // score+=scoringCriteria.getScore();
  788. // }
  789. // else
  790. // {
  791. // map.put("score",-(scoringCriteria.getScore()));
  792. // score-=scoringCriteria.getScore();
  793. // }
  794. map.put("score",-(scoringCriteria.getScore()));
  795. score-=scoringCriteria.getScore();
  796. map.put("isCritical",scoringCriteria.getIsCritical());
  797. map.put("scoring",scoringCriteria);
  798. if (scoringCriteria.getIsCritical()==1)
  799. {
  800. //是否致命
  801. call.setIsCritical(1L);
  802. }
  803. map.put("Id",scoringCriteria.getId());
  804. criteria.add(map);
  805. }
  806. }
  807. }
  808. if(triggerRules.size()>0)
  809. {
  810. triggerRulesService.insert(triggerRules);
  811. }
  812. if (criteria.size()>0)
  813. {
  814. //评分标准
  815. call.setScoringCriteria(mapper.writeValueAsString(criteria));
  816. }
  817. call.setValidity(0L);
  818. //质检是否成功
  819. call.setIsSuccess(isSuccess);
  820. //AI质检分值
  821. call.setScore(score);
  822. //合格分数
  823. call.setPassingScore(rule.getPassingScore());
  824. //是否合格
  825. if (score>=rule.getPassingScore())
  826. {
  827. call.setIsQualified(1L);
  828. call.setAiResult("合格");
  829. }
  830. else
  831. {
  832. call.setIsQualified(2L);
  833. call.setAiResult("不合格");
  834. }
  835. RepTranslate item=new RepTranslate();
  836. if (triggerRules.size()>0)
  837. {
  838. item.setTrigger("["+trigger.deleteCharAt(0).toString()+"]");
  839. }
  840. item.setVolume(String.format("%.2f", decibel));
  841. // item.setSpeed(String.format("%.2f", speechRate));
  842. item.setScore(score);
  843. item.setFilePath(FileUrl);
  844. //保存json文件
  845. call.setTextJson(mapper.writeValueAsString(item));
  846. }
  847. }
  848. catch (Exception e)
  849. {
  850. call.setIsSuccess(2L);
  851. }
  852. }
  853. call.setQcTime(new Date());
  854. call.setIsQuality(1L);
  855. this.update(call);
  856. try {
  857. // 延时1秒(1000毫秒)
  858. Thread.sleep(3000);
  859. } catch (InterruptedException e) {
  860. // 处理中断异常
  861. e.printStackTrace();
  862. // 恢复中断状态
  863. Thread.currentThread().interrupt();
  864. }
  865. deleteFiles(FileUrl) ;
  866. }
  867. }
  868. return audio;
  869. }
  870. private static String returnCharacters(String str)
  871. {
  872. String result = str.replaceAll("[^\\p{L}\\p{N} ]", "");
  873. return result;
  874. }
  875. private void deleteFiles(String files)
  876. {
  877. Path path = Paths.get(files);
  878. try {
  879. System.gc();
  880. Thread.sleep(100); // 短暂等待
  881. // 删除文件
  882. Files.delete(path);
  883. System.out.println("文件删除成功!");
  884. } catch (IOException e) {
  885. System.err.println("删除失败: " + e.getMessage());
  886. // 可根据具体异常类型处理不同情况
  887. if (e instanceof java.nio.file.NoSuchFileException) {
  888. System.err.println("文件不存在!");
  889. } else if (e instanceof java.nio.file.AccessDeniedException) {
  890. System.err.println("权限不足!");
  891. } else {
  892. e.printStackTrace();
  893. }
  894. } catch (InterruptedException e) {
  895. throw new RuntimeException(e);
  896. }
  897. }
  898. public static double calculateRMSVolume(File audioFile)
  899. throws UnsupportedAudioFileException, IOException {
  900. // 使用 try-with-resources 确保流自动关闭
  901. try (AudioInputStream originalStream = AudioSystem.getAudioInputStream(audioFile)) {
  902. AudioInputStream audioStream = originalStream;
  903. AudioFormat format = audioStream.getFormat();
  904. // 检查是否需要转换为 PCM 格式
  905. if (format.getEncoding() != AudioFormat.Encoding.PCM_SIGNED
  906. && format.getEncoding() != AudioFormat.Encoding.PCM_UNSIGNED) {
  907. // 定义目标格式(转换为带符号16位PCM)
  908. AudioFormat targetFormat = new AudioFormat(
  909. AudioFormat.Encoding.PCM_SIGNED,
  910. format.getSampleRate(),
  911. 16,
  912. format.getChannels(),
  913. format.getChannels() * 2,
  914. format.getSampleRate(),
  915. false);
  916. // 获取转换后的流,并包裹在 try-with-resources 中
  917. try (AudioInputStream convertedStream =
  918. AudioSystem.getAudioInputStream(targetFormat, originalStream)) {
  919. return processAudioStream(convertedStream, targetFormat);
  920. }
  921. } else {
  922. // 直接处理原始流
  923. return processAudioStream(audioStream, format);
  924. }
  925. }
  926. }
  927. private static double processAudioStream(AudioInputStream audioStream, AudioFormat format)
  928. throws IOException {
  929. // 读取所有音频数据
  930. byte[] buffer = new byte[(int) (audioStream.getFrameLength() * format.getFrameSize())];
  931. audioStream.read(buffer);
  932. // 将字节转换为样本值
  933. double[] samples = convertBytesToSamples(buffer, format);
  934. // 计算RMS
  935. double sumSquares = 0.0;
  936. for (double sample : samples) {
  937. sumSquares += sample * sample;
  938. }
  939. double rms = Math.sqrt(sumSquares / samples.length);
  940. // 转换为分贝(避免除以0)
  941. return rms > 0 ? 20 * Math.log10(rms) : Double.NEGATIVE_INFINITY;
  942. }
  943. private static double[] convertBytesToSamples(byte[] bytes, AudioFormat format) {
  944. int sampleSize = format.getSampleSizeInBits() / 8;
  945. int numSamples = bytes.length / sampleSize;
  946. double[] samples = new double[numSamples];
  947. ByteOrder byteOrder = format.isBigEndian() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
  948. for (int i = 0; i < numSamples; i++) {
  949. int offset = i * sampleSize;
  950. int value = 0;
  951. // 根据位深度解析样本
  952. switch (sampleSize) {
  953. case 1: // 8位
  954. value = bytes[offset] & 0xFF; // 转换为无符号
  955. samples[i] = (value - 128) / 128.0; // 归一化到[-1, 1]
  956. break;
  957. case 2: // 16位
  958. value = ByteBuffer.wrap(bytes, offset, 2)
  959. .order(byteOrder)
  960. .getShort();
  961. samples[i] = value / 32768.0; // 归一化到[-1, 1]
  962. break;
  963. default:
  964. throw new UnsupportedOperationException("不支持的位深度: " + format.getSampleSizeInBits());
  965. }
  966. }
  967. return samples;
  968. }
  969. public static String downloadFile(String fileUrl, String savePath) throws IOException {
  970. URL url = new URL(fileUrl);
  971. HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  972. conn.setRequestMethod("GET");
  973. conn.setConnectTimeout(5000);
  974. String file="";
  975. if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
  976. // 解析文件名
  977. String disposition = conn.getHeaderField("Content-Disposition");
  978. String fileName = (disposition != null)
  979. ? disposition.substring(disposition.indexOf("filename=") + 10, disposition.length() - 1)
  980. : fileUrl.substring(fileUrl.lastIndexOf("/") + 1);
  981. // 创建目录
  982. File saveDir = new File(savePath);
  983. if (!saveDir.exists()) saveDir.mkdirs();
  984. file=savePath + File.separator + fileName;
  985. // 读写数据
  986. try (InputStream in = new BufferedInputStream(conn.getInputStream());
  987. FileOutputStream out = new FileOutputStream(savePath + File.separator + fileName)) {
  988. byte[] buffer = new byte[4096];
  989. int bytesRead;
  990. while ((bytesRead = in.read(buffer)) != -1) {
  991. out.write(buffer, 0, bytesRead);
  992. }
  993. }
  994. finally {
  995. conn.disconnect();
  996. }
  997. }
  998. return file;
  999. }
  1000. @Override
  1001. public List<ExcelSentiment> excelSentiment() throws JsonProcessingException {
  1002. var list =translateService.getList();
  1003. ObjectMapper mapper = new ObjectMapper();
  1004. List<ExcelSentiment> sentiments=new ArrayList<>();
  1005. for (Translate item:list)
  1006. {
  1007. if (StringHelper.isNotEmpty(item.getTranslate()))
  1008. {
  1009. RepTranslate [] translate = mapper.readValue(item.getTranslate(), RepTranslate[].class);
  1010. int index=0;
  1011. StringBuilder message= new StringBuilder();
  1012. for (RepTranslate i :translate)
  1013. {
  1014. index=index+1;
  1015. if (translate.length>index)
  1016. {
  1017. if (i.getNumber().length()==translate[index].getNumber().length())
  1018. {
  1019. message.append(i.getSpeech());
  1020. }
  1021. else
  1022. {
  1023. ExcelSentiment excelSentiment=new ExcelSentiment();
  1024. message.append(i .getSpeech());
  1025. if (message.toString().length()<10)
  1026. {
  1027. message= new StringBuilder();
  1028. continue;
  1029. }
  1030. excelSentiment.setContent(message.toString());
  1031. if(i.getNumber().length()>4)
  1032. {
  1033. excelSentiment.setSeat( "客户");
  1034. }
  1035. else
  1036. {
  1037. excelSentiment.setSeat( "坐席");
  1038. }
  1039. sentiments.add(excelSentiment);
  1040. message= new StringBuilder();
  1041. }
  1042. }
  1043. else
  1044. {
  1045. message.append(i .getSpeech());
  1046. if (message.toString().length()<10)
  1047. {
  1048. message= new StringBuilder();
  1049. continue;
  1050. }
  1051. ExcelSentiment excelSentiment=new ExcelSentiment();
  1052. excelSentiment.setContent(message.toString());
  1053. if(i.getNumber().length()>4)
  1054. {
  1055. excelSentiment.setSeat( "客户");
  1056. }
  1057. else
  1058. {
  1059. excelSentiment.setSeat( "坐席");
  1060. }
  1061. sentiments.add(excelSentiment);
  1062. message= new StringBuilder();
  1063. }
  1064. }
  1065. }
  1066. }
  1067. return sentiments;
  1068. }
  1069. @Override
  1070. public boolean QcResultPush(QcResultPush push)
  1071. {
  1072. QcResult result=new QcResult();
  1073. result.setIsQuality(0L);
  1074. result.setCallid(push.getCallid());
  1075. result.setTel(push.getTel());
  1076. result.setTalkStartTime(push.getTalkStartTime());
  1077. result.setWaitTime(push.getWaitTime());
  1078. result.setRingTime(push.getRingTime());
  1079. result.setAnswerTime(push.getAnswerTime());
  1080. result.setHangupTime(push.getHangupTime());
  1081. result.setFilePath(push.getFilePath());
  1082. result.setCallType(push.getCallType());
  1083. result.setUserName(push.getUserName());
  1084. result.setMyd(push.getMyd());
  1085. result.setNickName(push.getNickName());
  1086. boolean n=this.insert(result);
  1087. Translate translate=new Translate();
  1088. if (StringHelper.isNotEmpty(push.getTranslate()))
  1089. {
  1090. translate.setCallid(push.getCallid());
  1091. translate.setTranslate(push.getTranslate());
  1092. translate.setTranslateJson(push.getTranslateJson());
  1093. translateService.insert(translate);
  1094. }
  1095. return n;
  1096. }
  1097. private List<String> filterKeyWorlds()
  1098. {
  1099. List<String> linke =new ArrayList<>();
  1100. linke.add("妈妈的");
  1101. return linke;
  1102. }
  1103. private LinkedHashMap<Long,List<String>> keyWorlds(List<ScoringCriteria> scoring)
  1104. {
  1105. LinkedHashMap<Long, List<String>> linkedMap = new LinkedHashMap<>();
  1106. for (ScoringCriteria scoringCriteria :scoring)
  1107. {
  1108. List<Long> result = new ArrayList<>();
  1109. if (StringHelper.isEmpty(scoringCriteria.getContent()))
  1110. {
  1111. continue;
  1112. }
  1113. String[] parts = scoringCriteria.getContent().split(",");
  1114. try
  1115. {
  1116. for (String part : parts) {
  1117. String trimmed = part.trim();
  1118. if (!trimmed.isEmpty()) {
  1119. result.add(Long.parseLong(trimmed));
  1120. }
  1121. }
  1122. }
  1123. catch (Exception E)
  1124. {
  1125. //关键词中存在汉字直接抛出
  1126. continue;
  1127. }
  1128. LambdaQueryWrapper<SearchLexicon> qw = new LambdaQueryWrapper<>();
  1129. qw.in(SearchLexicon::getCategory,result);
  1130. qw.in(SearchLexicon::getType,2);
  1131. List<SearchLexicon> search=searchLexiconService.getList(qw);
  1132. List<String> keywords =new ArrayList<>();
  1133. if(search!=null&&search.size()>0)
  1134. {
  1135. for (SearchLexicon item :search)
  1136. {
  1137. keywords.add(item.getTerm());
  1138. }
  1139. }
  1140. linkedMap.put(scoringCriteria.getId(),keywords);
  1141. }
  1142. return linkedMap;
  1143. }
  1144. //解析同声翻译
  1145. private void GetRepTranslates(RepTranslate [] translate, String inputFile) throws Exception {
  1146. LocalDate currentDate = LocalDate.now();
  1147. //获取当前年月日
  1148. int year = currentDate.getYear();
  1149. int month = currentDate.getMonthValue();
  1150. int day = currentDate.getDayOfMonth();
  1151. //设置保存路径
  1152. String File= "files/luYin/" +year+"/"+month+"/"+day+"/";
  1153. //下载录音文件到指定路径
  1154. // String fileName= DownloadHelper.downloadFile(File,FileUrl);
  1155. //获取录音文件
  1156. //String inputFile = FileUrl;
  1157. for (RepTranslate item :translate)
  1158. {
  1159. if (StringHelper.isNotEmpty(item.getSpeech())
  1160. &&item.getSpeech().length()>2)
  1161. {
  1162. if(item.getNumber().length()>4)
  1163. {
  1164. //只质检话务员,客户语音跳过
  1165. continue;
  1166. }
  1167. if (StringHelper.isNotEmpty(item.getTime()))
  1168. {
  1169. int[] Millisecond=GetMillisecond(item.getTime());
  1170. if (Millisecond!=null)
  1171. {
  1172. String newFile=File+Millisecond[0]+"-"+new File(inputFile).getName();
  1173. //分割wav
  1174. splitWav(new File(inputFile),
  1175. new File(newFile), Millisecond[0], Millisecond[1]);
  1176. double decibel = calculateRMSVolume(new File(newFile));
  1177. //double speechRate = analyzeSpeechSpeed(new File(newFile), -40.0);
  1178. double speechRate=calculateSpeechRate(returnCharacters(item.getSpeech()).length(), new File(newFile));//获取语速
  1179. //分割路径
  1180. item.setFilePath(newFile);
  1181. //音量
  1182. item.setVolume( String.format("%.2f", decibel));
  1183. //音速
  1184. item.setSpeed( String.format("%.2f", speechRate));
  1185. //质检完毕删除附件
  1186. deleteFiles(newFile);
  1187. }
  1188. }
  1189. }
  1190. }
  1191. }
  1192. //获取音量
  1193. public static double calculateDecibel(File wavFile) throws Exception {
  1194. try (DataInputStream dis = new DataInputStream(Files.newInputStream(wavFile.toPath()))) {
  1195. // 跳过WAV头(假设头长度为44字节)
  1196. dis.skipBytes(44);
  1197. // 读取PCM数据
  1198. List<Short> samples = new ArrayList<>();
  1199. while (dis.available() > 0) {
  1200. samples.add(dis.readShort());
  1201. }
  1202. // 计算RMS
  1203. double sum = 0;
  1204. for (short sample : samples) {
  1205. sum += (sample * sample);
  1206. }
  1207. double rms = Math.sqrt(sum / samples.size());
  1208. // 计算分贝(参考值取16位最大值32767)
  1209. return 20 * Math.log10(rms / 32767.0);
  1210. }
  1211. }
  1212. //获取语速
  1213. public static double analyzeSpeechSpeed(File wavFile, double silenceThresholdDb) throws Exception {
  1214. try (DataInputStream dis = new DataInputStream(Files.newInputStream(wavFile.toPath()))) {
  1215. dis.skipBytes(44); // 跳过WAV头
  1216. int sampleRate = 16000; // 需从WAV头读取实际值
  1217. List<Boolean> isSpeech = new ArrayList<>();
  1218. List<Short> buffer = new ArrayList<>();
  1219. // 分帧检测静音(假设帧长度100ms)
  1220. int frameSize = sampleRate / 10;
  1221. while (dis.available() >= 2) {
  1222. short sample = dis.readShort();
  1223. buffer.add(sample);
  1224. if (buffer.size() >= frameSize) {
  1225. double rms = calculateFrameRms(buffer);
  1226. double db = 20 * Math.log10(rms / 32767.0);
  1227. isSpeech.add(db > silenceThresholdDb);
  1228. buffer.clear();
  1229. }
  1230. }
  1231. // 统计语音段数量
  1232. int speechSegments = 0;
  1233. boolean lastState = false;
  1234. for (boolean state : isSpeech) {
  1235. if (state && !lastState) speechSegments++;
  1236. lastState = state;
  1237. }
  1238. // 计算语速(假设每个语音段对应一个词)
  1239. double totalTime = (double) isSpeech.size() * 0.1; // 总时间(秒)
  1240. return (speechSegments / totalTime) * 60; // 词/分钟
  1241. }
  1242. }
  1243. // 主计算方法
  1244. public static double calculateSpeechRate(int wordCount, File wavFile) throws Exception {
  1245. long durationMs = getAudioDuration(wavFile);
  1246. if (durationMs == 0) throw new IllegalArgumentException("音频时长为零");
  1247. return (wordCount * 60000.0) / durationMs; // 60,000 = 1000ms * 60s
  1248. }
  1249. // 安全的头解析方法
  1250. private static long parseDurationFromHeader(ByteBuffer buffer) throws IOException {
  1251. try {
  1252. buffer.order(ByteOrder.LITTLE_ENDIAN);
  1253. // 验证RIFF头
  1254. byte[] riff = new byte[4];
  1255. buffer.get(riff);
  1256. if (!"RIFF".equals(new String(riff, StandardCharsets.US_ASCII))) {
  1257. throw new IOException("Invalid WAV file header");
  1258. }
  1259. // 跳过文件总大小
  1260. buffer.position(buffer.position() + 4);
  1261. // 验证WAVE标识
  1262. byte[] wave = new byte[4];
  1263. buffer.get(wave);
  1264. if (!"WAVE".equals(new String(wave, StandardCharsets.US_ASCII))) {
  1265. throw new IOException("Not a WAVE file");
  1266. }
  1267. // 查找fmt和data块
  1268. long sampleRate = -1;
  1269. long dataSize = -1;
  1270. while (buffer.remaining() > 8) {
  1271. byte[] chunkIdBytes = new byte[4];
  1272. buffer.get(chunkIdBytes);
  1273. String chunkId = new String(chunkIdBytes, StandardCharsets.US_ASCII);
  1274. int chunkSize = buffer.getInt();
  1275. if (chunkId.equals("fmt ")) {
  1276. // 解析采样率
  1277. buffer.position(buffer.position() + 8); // 跳过格式和声道
  1278. sampleRate = buffer.getInt() & 0xFFFFFFFFL;
  1279. buffer.position(buffer.position() + 6); // 跳过其他参数
  1280. } else if (chunkId.equals("data")) {
  1281. dataSize = chunkSize & 0xFFFFFFFFL;
  1282. break;
  1283. } else {
  1284. // 跳过未知块(处理补位)
  1285. int padding = chunkSize % 2;
  1286. buffer.position(buffer.position() + chunkSize + padding);
  1287. }
  1288. }
  1289. if (sampleRate == -1 || dataSize == -1) {
  1290. throw new IOException("Missing required chunks");
  1291. }
  1292. return (dataSize * 1000L) / (sampleRate * 2); // 假设16位单声道
  1293. } finally {
  1294. // 显式释放内存映射(Java 9+)
  1295. if (buffer instanceof sun.nio.ch.DirectBuffer) {
  1296. ((sun.nio.ch.DirectBuffer) buffer).cleaner().clean();
  1297. }
  1298. }
  1299. }
  1300. // 语速计算核心
  1301. private static double calculateRate(int wordCount, long durationMs) {
  1302. if (durationMs == 0) throw new IllegalArgumentException("Audio duration cannot be zero");
  1303. return (wordCount * 60000.0) / durationMs;
  1304. }
  1305. // 获取音频时长(毫秒)
  1306. private static long getAudioDuration(File wavFile) throws Exception {
  1307. try (RandomAccessFile raf = new RandomAccessFile(wavFile, "r")) {
  1308. // 验证RIFF头
  1309. if (!readString(raf).equals("RIFF")) {
  1310. throw new IllegalArgumentException("无效的WAV文件");
  1311. }
  1312. // 2. 跳过RIFF总大小(不需要使用)
  1313. raf.skipBytes(4);
  1314. // 3. 验证格式类型必须是"WAVE"
  1315. String format = readString(raf);
  1316. if (!format.equals("WAVE")) {
  1317. throw new IllegalArgumentException("非WAVE格式文件");
  1318. }
  1319. // 查找fmt和data块
  1320. long sampleRate = -1;
  1321. long dataSize = -1;
  1322. while (true) {
  1323. String chunkId = readString(raf);
  1324. long chunkSize = readLittleEndianInt(raf) & 0xFFFFFFFFL;
  1325. long chunkStart = raf.getFilePointer();
  1326. if (chunkId.equals("fmt ")) {
  1327. // 读取采样率
  1328. raf.skipBytes(4); // 跳过音频格式和声道数
  1329. sampleRate = readLittleEndianInt(raf) & 0xFFFFFFFFL;
  1330. raf.skipBytes(6); // 跳过其他fmt参数
  1331. } else if (chunkId.equals("data")) {
  1332. dataSize = chunkSize;
  1333. break;
  1334. }
  1335. if (chunkStart + chunkSize >= raf.length()) break;
  1336. raf.seek(chunkStart + chunkSize);
  1337. }
  1338. if (sampleRate == -1 || dataSize == -1) {
  1339. throw new IllegalArgumentException("缺少关键数据块");
  1340. }
  1341. // 计算时长:数据大小(字节) / (声道数 * 位深度/8) / 采样率
  1342. // 简化计算(假设声道数=1,位深度=16)
  1343. // 实际应根据fmt块参数动态计算
  1344. long totalSamples = dataSize / 2; // 16位=2字节/样本
  1345. return (totalSamples * 1000L) / sampleRate;
  1346. }
  1347. }
  1348. private static double calculateFrameRms(List<Short> buffer) {
  1349. double sum = 0;
  1350. for (short sample : buffer) {
  1351. sum += sample * sample;
  1352. }
  1353. return Math.sqrt(sum / buffer.size());
  1354. }
  1355. //获取分段语音开始毫秒数和结束毫秒数
  1356. private static int[] GetMillisecond(String input)
  1357. {
  1358. // 去除外层的中括号
  1359. String content = input.substring(2, input.length() - 2);
  1360. // 分割各个子区间
  1361. String[] intervals = content.split("],\\[");
  1362. if (intervals.length == 0) {
  1363. System.out.println("无有效区间");
  1364. return null;
  1365. }
  1366. // 提取第一个区间的第一个值
  1367. String[] firstParts = intervals[0].split(",");
  1368. int firstValue = Integer.parseInt(firstParts[0]);
  1369. // 提取最后一个区间的最后一个值
  1370. String[] lastParts = intervals[intervals.length - 1].split(",");
  1371. int lastValue = Integer.parseInt(lastParts[1]);
  1372. return new int[]{firstValue,lastValue};
  1373. }
  1374. //分割音频
  1375. private static void splitWav(File inputFile, File outputFile, long startMs, long endMs) throws Exception {
  1376. try (RandomAccessFile raf = new RandomAccessFile(inputFile, "r")) {
  1377. // 读取RIFF头
  1378. if (!readString(raf).equals("RIFF")) {
  1379. throw new IllegalArgumentException("不是有效的RIFF文件");
  1380. }
  1381. long riffSize = readLittleEndianInt(raf) & 0xFFFFFFFFL;
  1382. String format = readString(raf);
  1383. if (!format.equals("WAVE")) {
  1384. throw new IllegalArgumentException("不是WAVE文件");
  1385. }
  1386. // 查找fmt和data块
  1387. int numChannels = -1;
  1388. long sampleRate = -1;
  1389. int bitsPerSample = -1;
  1390. long dataStart = -1;
  1391. long dataSize = -1;
  1392. while (true) {
  1393. String chunkId;
  1394. try {
  1395. chunkId = readString(raf);
  1396. } catch (EOFException e) {
  1397. break; // 所有块处理完毕
  1398. }
  1399. long chunkSize = readLittleEndianInt(raf) & 0xFFFFFFFFL;
  1400. long chunkDataStart = raf.getFilePointer();
  1401. if (chunkId.equals("fmt ")) {
  1402. // 解析fmt块
  1403. int audioFormat = readLittleEndianShort(raf);
  1404. numChannels = readLittleEndianShort(raf);
  1405. sampleRate = readLittleEndianInt(raf) & 0xFFFFFFFFL;
  1406. long byteRate = readLittleEndianInt(raf);
  1407. int blockAlign = readLittleEndianShort(raf);
  1408. bitsPerSample = readLittleEndianShort(raf);
  1409. } else if (chunkId.equals("data")) {
  1410. dataStart = chunkDataStart;
  1411. dataSize = chunkSize;
  1412. break; // 找到data块后退出循环
  1413. }
  1414. // 跳到下一个块
  1415. raf.seek(chunkDataStart + chunkSize);
  1416. }
  1417. if (numChannels == -1 || dataStart == -1) {
  1418. throw new IllegalArgumentException("缺少fmt或data块");
  1419. }
  1420. // 计算参数
  1421. int bytesPerFrame = numChannels * (bitsPerSample / 8);
  1422. long totalSamples = dataSize / bytesPerFrame;
  1423. long durationMs = (totalSamples * 1000L) / sampleRate;
  1424. // 调整时间范围
  1425. if (startMs < 0) startMs = 0;
  1426. if (endMs > durationMs) endMs = durationMs;
  1427. if (startMs >= endMs) {
  1428. throw new IllegalArgumentException("起始时间必须小于结束时间");
  1429. }
  1430. // 计算样本位置
  1431. long startSample = (startMs * sampleRate) / 1000L;
  1432. long endSample = (endMs * sampleRate) / 1000L;
  1433. long startByte = startSample * bytesPerFrame;
  1434. long endByte = endSample * bytesPerFrame;
  1435. long newDataSize = endByte - startByte;
  1436. // 转换为Path对象并获取父目录
  1437. Path outputPath = outputFile.toPath();
  1438. Path parentDir = outputPath.getParent();
  1439. // 如果父目录存在则创建所有必要目录
  1440. if (parentDir != null) {
  1441. Files.createDirectories(parentDir);
  1442. }
  1443. // 写入新文件
  1444. try (DataOutputStream dos = new DataOutputStream(Files.newOutputStream(outputFile.toPath()))) {
  1445. // RIFF头
  1446. writeString(dos, "RIFF");
  1447. writeLittleEndianInt(dos, (int) (36 + newDataSize)); // RIFF大小
  1448. writeString(dos, "WAVE");
  1449. // fmt块
  1450. writeString(dos, "fmt ");
  1451. writeLittleEndianInt(dos, 16); // fmt块大小
  1452. writeLittleEndianShort(dos, (short) 1); // PCM格式
  1453. writeLittleEndianShort(dos, (short) numChannels);
  1454. writeLittleEndianInt(dos, (int) sampleRate);
  1455. writeLittleEndianInt(dos, (int) (sampleRate * numChannels * bitsPerSample / 8));
  1456. writeLittleEndianShort(dos, (short) (numChannels * bitsPerSample / 8));
  1457. writeLittleEndianShort(dos, (short) bitsPerSample);
  1458. // data块头
  1459. writeString(dos, "data");
  1460. writeLittleEndianInt(dos, (int) newDataSize);
  1461. // 复制音频数据
  1462. raf.seek(dataStart + startByte);
  1463. byte[] buffer = new byte[4096];
  1464. long remaining = newDataSize;
  1465. while (remaining > 0) {
  1466. int read = raf.read(buffer, 0, (int) Math.min(buffer.length, remaining));
  1467. if (read == -1) break;
  1468. dos.write(buffer, 0, read);
  1469. remaining -= read;
  1470. }
  1471. }
  1472. }
  1473. }
  1474. // 辅助方法:读取小端整数
  1475. private static int readLittleEndianInt(DataInput in) throws IOException {
  1476. int b1 = in.readUnsignedByte();
  1477. int b2 = in.readUnsignedByte();
  1478. int b3 = in.readUnsignedByte();
  1479. int b4 = in.readUnsignedByte();
  1480. return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
  1481. }
  1482. private static short readLittleEndianShort(DataInput in) throws IOException {
  1483. int b1 = in.readUnsignedByte();
  1484. int b2 = in.readUnsignedByte();
  1485. return (short) ((b2 << 8) | b1);
  1486. }
  1487. private static String readString(DataInput in) throws IOException {
  1488. byte[] bytes = new byte[4];
  1489. in.readFully(bytes);
  1490. return new String(bytes, StandardCharsets.US_ASCII);
  1491. }
  1492. private static void writeLittleEndianInt(DataOutput out, int value) throws IOException {
  1493. out.write(value & 0xFF);
  1494. out.write((value >> 8) & 0xFF);
  1495. out.write((value >> 16) & 0xFF);
  1496. out.write((value >> 24) & 0xFF);
  1497. }
  1498. private static void writeLittleEndianShort(DataOutput out, short value) throws IOException {
  1499. out.write(value & 0xFF);
  1500. out.write((value >> 8) & 0xFF);
  1501. }
  1502. private static void writeString(DataOutput out, String s) throws IOException {
  1503. out.write(s.getBytes(StandardCharsets.US_ASCII));
  1504. }
  1505. }