speech_recognition.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. package services
  2. import (
  3. "baliance.com/gooxml/document"
  4. "baliance.com/gooxml/measurement"
  5. "baliance.com/gooxml/schema/soo/wml"
  6. "bufio"
  7. "eta/eta_api/models"
  8. "eta/eta_api/models/speech_recognition"
  9. "eta/eta_api/services/alarm_msg"
  10. "eta/eta_api/utils"
  11. "fmt"
  12. "github.com/jung-kurt/gofpdf"
  13. "os"
  14. "strconv"
  15. "sync"
  16. "time"
  17. )
  18. const (
  19. SpeechRecognitionExportTypeTxt = 1
  20. SpeechRecognitionExportTypeDocx = 2
  21. SpeechRecognitionExportTypePdf = 3
  22. )
  23. // GetSpeechRecognitionTagMenuTreeRecursive 递归获取标签目录树
  24. func GetSpeechRecognitionTagMenuTreeRecursive(list []*speech_recognition.SpeechRecognitionTagMenu, parentId int) []*speech_recognition.SpeechRecognitionTagMenuItem {
  25. res := make([]*speech_recognition.SpeechRecognitionTagMenuItem, 0)
  26. for _, v := range list {
  27. if v.ParentId == parentId {
  28. t := new(speech_recognition.SpeechRecognitionTagMenuItem)
  29. t.MenuId = v.SpeechRecognitionTagMenuId
  30. t.MenuName = v.MenuName
  31. t.ParentId = v.ParentId
  32. t.Level = v.Level
  33. t.Sort = v.Sort
  34. t.CreateTime = utils.TimeTransferString(utils.FormatDateTime, v.CreateTime)
  35. t.Children = GetSpeechRecognitionTagMenuTreeRecursive(list, v.SpeechRecognitionTagMenuId)
  36. res = append(res, t)
  37. }
  38. }
  39. return res
  40. }
  41. // BatchConvertSpeech 批量转写语音
  42. func BatchConvertSpeech(speeches []*speech_recognition.SpeechRecognition) {
  43. var err error
  44. defer func() {
  45. if err != nil {
  46. tips := fmt.Sprintf("批量转写语音失败, ErrMsg: %s", err.Error())
  47. utils.FileLog.Info(tips)
  48. go alarm_msg.SendAlarmMsg(tips, 1)
  49. }
  50. }()
  51. conf, e := models.GetBusinessConf()
  52. if e != nil {
  53. err = fmt.Errorf("获取配置失败, Err: %s", e.Error())
  54. return
  55. }
  56. if conf[models.BusinessConfTencentApiSecretId] == "" || conf[models.BusinessConfTencentApiSecretKey] == "" || conf[models.BusinessConfTencentApiRecTaskCallbackUrl] == "" {
  57. err = fmt.Errorf("API配置有误, SecretId: %s, SecretKey: %s, Callback: %s", conf[models.BusinessConfTencentApiSecretId], conf[models.BusinessConfTencentApiSecretKey], conf[models.BusinessConfTencentApiRecTaskCallbackUrl])
  58. return
  59. }
  60. // 限制接口请求频率
  61. apiLimit := make(chan struct{}, 20)
  62. var wg sync.WaitGroup
  63. for _, v := range speeches {
  64. wg.Add(1)
  65. go func(speech *speech_recognition.SpeechRecognition) {
  66. defer func() {
  67. wg.Done()
  68. <-apiLimit
  69. }()
  70. apiLimit <- struct{}{}
  71. // 发起请求
  72. var errMsg string
  73. var r TencentRecTaskReq
  74. r.FileUrl = speech.ResourceUrl
  75. r.SecretId = conf[models.BusinessConfTencentApiSecretId]
  76. r.SecretKey = conf[models.BusinessConfTencentApiSecretKey]
  77. r.CallbackUrl = conf[models.BusinessConfTencentApiRecTaskCallbackUrl]
  78. taskId, e := TencentCreateRecTask(r)
  79. if e != nil {
  80. errMsg = "创建语音识别任务失败"
  81. utils.FileLog.Info("TencentCreateRecTask创建语音识别任务失败, ErrMsg: %s", e.Error())
  82. }
  83. if errMsg == "" {
  84. apiLog := new(speech_recognition.SpeechRecognitionApiLog)
  85. apiLog.SpeechRecognitionId = speech.SpeechRecognitionId
  86. apiLog.RequestId = strconv.Itoa(taskId)
  87. apiLog.RequestCode = -1
  88. apiLog.CreateTime = time.Now().Local()
  89. apiLog.ModifyTime = time.Now().Local()
  90. if e = apiLog.Create(); e != nil {
  91. errMsg = "生成API请求失败"
  92. utils.FileLog.Info("CreateApiLog生成API请求记录失败, ErrMsg: %s", e.Error())
  93. return
  94. }
  95. }
  96. // 有报错则更新对应语音识别状态
  97. if errMsg == "" {
  98. return
  99. }
  100. speech.State = speech_recognition.SpeechRecognitionStateFail
  101. speech.ConvertRemark = errMsg
  102. speech.ModifyTime = time.Now().Local()
  103. updateCols := []string{speech_recognition.SpeechRecognitionCols.State, speech_recognition.SpeechRecognitionCols.ConvertRemark, speech_recognition.SpeechRecognitionCols.ModifyTime}
  104. if e = speech.Update(updateCols); e != nil {
  105. utils.FileLog.Info("UpdateSpeech更新语音识别状态失败, ErrMsg: %s", e.Error())
  106. return
  107. }
  108. }(v)
  109. }
  110. wg.Wait()
  111. return
  112. }
  113. // SpeechRecognitionContentExport 导出语音识别内容
  114. func SpeechRecognitionContentExport(exportType int, exportTimestamp bool, fileName string, contents []*speech_recognition.SpeechRecognitionContent) (result string, err error) {
  115. defer func() {
  116. if err != nil {
  117. fmt.Println(err)
  118. }
  119. }()
  120. if len(contents) == 0 {
  121. return
  122. }
  123. // 整理内容
  124. exportText := ""
  125. exportArr := make([]string, 0)
  126. exportPdfArr := make([]string, 0)
  127. for _, v := range contents {
  128. if v.Content == "" {
  129. continue
  130. }
  131. sec := ""
  132. secPdf := ""
  133. if exportTimestamp {
  134. // 毫秒转时间格式
  135. sec = fmt.Sprintf("%s\n%s\n\n", utils.MillisecondsToHHMMSS(v.StartMs), v.Content)
  136. secPdf = fmt.Sprintf("%s %s", utils.MillisecondsToHHMMSS(v.StartMs), v.Content)
  137. } else {
  138. sec = fmt.Sprintf("%s\n\n", v.Content)
  139. secPdf = v.Content
  140. }
  141. exportText += sec
  142. exportArr = append(exportArr, sec)
  143. exportPdfArr = append(exportPdfArr, secPdf)
  144. }
  145. // 导出doc
  146. if exportType == SpeechRecognitionExportTypeDocx {
  147. doc := document.New()
  148. for _, v := range exportArr {
  149. p := doc.AddParagraph()
  150. prop := p.Properties()
  151. prop.Spacing().SetLineSpacing(measurement.Distance(1.5*15*measurement.Point), wml.ST_LineSpacingRuleAuto)
  152. prop.SetAlignment(wml.ST_JcLeft)
  153. run := p.AddRun()
  154. runProp := run.Properties()
  155. runProp.SetSize(measurement.Distance(15 * measurement.Point))
  156. runProp.SetFontFamily("宋体")
  157. run.AddText(v)
  158. run.AddBreak()
  159. }
  160. filePath := fmt.Sprintf("%s.docx", fileName)
  161. if e := doc.SaveToFile(filePath); e != nil {
  162. err = fmt.Errorf("生成docx失败, Err: %s", e.Error())
  163. return
  164. }
  165. result = filePath
  166. return
  167. }
  168. // 导出pdf
  169. if exportType == SpeechRecognitionExportTypePdf {
  170. pdf := gofpdf.New("P", "mm", "A4", "")
  171. pdf.AddPage()
  172. pdf.AddUTF8Font("SimHei", "", "static/SimHei.ttf") // 此处字体文件只能用本地的
  173. pdf.SetFont("SimHei", "", 14)
  174. // 计算可用内容区域宽度
  175. w, _ := pdf.GetPageSize()
  176. marginLeft := 10.0
  177. marginRight := 10.0
  178. availableWidth := w - marginLeft - marginRight
  179. for _, v := range exportPdfArr {
  180. pdf.MultiCell(availableWidth, 10, v, "", "L", false)
  181. pdf.MultiCell(availableWidth, 5, "", "", "L", false) // 单纯的换行
  182. }
  183. filePath := fmt.Sprintf("%s.pdf", fileName)
  184. if e := pdf.OutputFileAndClose(filePath); e != nil {
  185. err = fmt.Errorf("生成pdf失败, Err: %s", e.Error())
  186. return
  187. }
  188. result = filePath
  189. return
  190. }
  191. // 默认导出txt
  192. filePath := fmt.Sprintf("%s.txt", fileName)
  193. file, e := os.Create(filePath)
  194. if e != nil {
  195. err = fmt.Errorf("生成txt文件失败, err: %s", e.Error())
  196. return
  197. }
  198. defer file.Close()
  199. // 写入txt
  200. writer := bufio.NewWriter(file)
  201. _, e = writer.WriteString(exportText)
  202. if e != nil {
  203. err = fmt.Errorf("写入txt文件失败, err: %s", e.Error())
  204. return
  205. }
  206. if e = writer.Flush(); e != nil {
  207. err = fmt.Errorf("刷新txt缓存失败, err: %s", e.Error())
  208. return
  209. }
  210. result = filePath
  211. return
  212. }