audio.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. package controllers
  2. import (
  3. "errors"
  4. "fmt"
  5. "github.com/360EntSecGroup-Skylar/excelize/v2"
  6. "github.com/gin-gonic/gin"
  7. "gorm.io/gorm"
  8. "io"
  9. "mime/multipart"
  10. "net/url"
  11. "os"
  12. "path"
  13. "path/filepath"
  14. "speechAnalysis/constvar"
  15. "speechAnalysis/extend/code"
  16. "speechAnalysis/extend/util"
  17. "speechAnalysis/models"
  18. "speechAnalysis/pkg/logx"
  19. "speechAnalysis/request"
  20. "speechAnalysis/response"
  21. "speechAnalysis/service"
  22. "speechAnalysis/utils/upload"
  23. "strings"
  24. "time"
  25. )
  26. type AudioCtl struct{}
  27. // Upload
  28. // @Tags 音频
  29. // @Summary 上传音频
  30. // @Accept multipart/form-data
  31. // @Produce application/json
  32. // @Param file formData []file false "多文件上传"
  33. // @Success 200 {object} util.Response "成功"
  34. // @Router /api-sa/v1/audio/upload [post]
  35. func (slf AudioCtl) Upload(c *gin.Context) {
  36. var headers []*multipart.FileHeader
  37. files, _ := c.MultipartForm()
  38. if len(files.File["file"]) > 1 {
  39. headers = files.File["file"]
  40. } else {
  41. util.ResponseFormat(c, code.RequestParamError, "文件需要一一对应")
  42. return
  43. }
  44. audio := &models.Audio{}
  45. for _, header := range headers {
  46. filename := path.Base(header.Filename)
  47. arr := strings.Split(filename, "_")
  48. if len(arr) != 6 {
  49. util.ResponseFormat(c, code.RequestParamError, "文件名称错误")
  50. return
  51. }
  52. _, err := models.NewAudioSearch().SetName(filename).First()
  53. if err != gorm.ErrRecordNotFound {
  54. util.ResponseFormat(c, code.RequestParamError, "重复上传")
  55. return
  56. }
  57. oss := upload.NewOss()
  58. filePath, filename, uploadErr := oss.UploadFile(header)
  59. if uploadErr != nil {
  60. logx.Errorf("upload audio err: %v", err)
  61. util.ResponseFormat(c, code.RequestParamError, "上传失败")
  62. return
  63. }
  64. if filepath.Ext(filename) == ".mp3" || filepath.Ext(filename) == ".wav" {
  65. timeStr := arr[4] + strings.Split(arr[5], ".")[0]
  66. t, err := time.ParseInLocation("20060102150405", timeStr, time.Local)
  67. if err != nil {
  68. util.ResponseFormat(c, code.RequestParamError, "时间格式不对")
  69. return
  70. }
  71. audio.Name = filename
  72. audio.Size = header.Size
  73. audio.FilePath = filePath
  74. audio.AudioStatus = constvar.AudioStatusUploadOk
  75. audio.LocomotiveNumber = arr[0]
  76. audio.TrainNumber = arr[1]
  77. audio.DriverNumber = arr[2]
  78. audio.Station = arr[3]
  79. audio.OccurrenceAt = t
  80. audio.IsFollowed = 0
  81. }
  82. if filepath.Ext(filename) == ".txt" {
  83. audio.TxtFilePath = filePath
  84. //读取filepath文件内容到bts
  85. bts, err := os.ReadFile(filePath)
  86. if err != nil {
  87. util.ResponseFormat(c, code.RequestParamError, "读取文件失败")
  88. return
  89. }
  90. //解析 交路号:123_公里标:321
  91. fileds := string(bts)
  92. //按照换行或者回车分割
  93. arr := strings.Split(fileds, "\n")
  94. if len(arr) != 2 {
  95. util.ResponseFormat(c, code.RequestParamError, "文件内容格式不对")
  96. return
  97. } else {
  98. RouteNumber := strings.Split(arr[0], ":")
  99. KilometerMarker := strings.Split(arr[1], ":")
  100. if len(RouteNumber) > 1 && len(KilometerMarker) > 1 {
  101. audio.RouteNumber = RouteNumber[1]
  102. audio.KilometerMarker = KilometerMarker[1]
  103. } else {
  104. util.ResponseFormat(c, code.RequestParamError, "文件内容格式不对")
  105. return
  106. }
  107. }
  108. }
  109. }
  110. if err := models.NewAudioSearch().Create(audio); err != nil {
  111. util.ResponseFormat(c, code.SaveFail, "上传失败")
  112. return
  113. } else {
  114. go func() {
  115. var trainInfoNames = []string{audio.LocomotiveNumber, audio.TrainNumber, audio.Station}
  116. var (
  117. info *models.TrainInfo
  118. err error
  119. parent models.TrainInfo
  120. )
  121. for i := 0; i < 3; i++ {
  122. name := trainInfoNames[i]
  123. class := constvar.Class(i + 1)
  124. info, err = models.NewTrainInfoSearch().SetName(name).SetClass(class).First()
  125. if err == gorm.ErrRecordNotFound {
  126. info = &models.TrainInfo{
  127. Name: name,
  128. Class: class,
  129. ParentID: parent.ID,
  130. }
  131. _ = models.NewTrainInfoSearch().Create(info)
  132. }
  133. parent = *info
  134. }
  135. }()
  136. util.ResponseFormat(c, code.Success, "上传成功")
  137. return
  138. }
  139. }
  140. func (slf AudioCtl) ParamsCheck(filename string) (err error) {
  141. arr := strings.Split(filename, "_")
  142. if len(arr) != 6 {
  143. return errors.New("文件格式错误")
  144. }
  145. return nil
  146. }
  147. // TrainInfoList
  148. // @Tags 音频
  149. // @Summary 获取火车信息
  150. // @Produce application/json
  151. // @Param object query request.GetTrainInfoList true "参数"
  152. // @Success 200 {object} util.ResponseList{data=[]models.TrainInfo} "成功"
  153. // @Router /api-sa/v1/audio/trainInfoList [get]
  154. func (slf AudioCtl) TrainInfoList(c *gin.Context) {
  155. var params request.GetTrainInfoList
  156. if err := c.ShouldBindQuery(&params); err != nil {
  157. util.ResponseFormat(c, code.RequestParamError, err.Error())
  158. return
  159. }
  160. if !params.PageInfo.Check() {
  161. util.ResponseFormat(c, code.RequestParamError, "分页参数错误")
  162. return
  163. }
  164. list, total, err := models.NewTrainInfoSearch().
  165. SetPage(params.Page, params.PageSize).
  166. SetClass(params.Class).
  167. SetParentId(params.ParentID).
  168. Find()
  169. if err != nil {
  170. util.ResponseFormat(c, code.RequestParamError, "查找失败")
  171. return
  172. }
  173. util.ResponseFormatList(c, code.Success, list, total)
  174. }
  175. // List
  176. // @Tags 音频
  177. // @Summary 音频分析检索
  178. // @Produce application/json
  179. // @Param object query request.GetAudioList true "参数"
  180. // @Success 200 {object} util.ResponseList{data=[]models.Audio} "成功"
  181. // @Router /api-sa/v1/audio/list [get]
  182. func (slf AudioCtl) List(c *gin.Context) {
  183. var params request.GetAudioList
  184. if err := c.ShouldBindQuery(&params); err != nil {
  185. util.ResponseFormat(c, code.RequestParamError, err.Error())
  186. return
  187. }
  188. if !params.PageInfo.Check() {
  189. util.ResponseFormat(c, code.RequestParamError, "分页参数错误")
  190. return
  191. }
  192. list, total, err := models.NewAudioSearch().
  193. SetPage(params.Page, params.PageSize).
  194. SetKeyword(params.Keyword).
  195. SetLocomotiveNumber(params.LocomotiveNumber).
  196. SetTrainNumber(params.TrainNumber).
  197. SetDriverNumber(params.DriverNumber).
  198. SetStation(params.StationNumber).
  199. SetBeginTime(params.BeginTime).
  200. SetEndTime(params.EndTime).
  201. SetIsFollowed(params.IsFollowed).
  202. SetAudioStatusList(params.StatusList).
  203. SetOrder("created_at desc").
  204. Find()
  205. if err != nil {
  206. util.ResponseFormat(c, code.RequestParamError, "查找失败")
  207. return
  208. }
  209. util.ResponseFormatList(c, code.Success, list, total)
  210. }
  211. // Export
  212. // @Tags 音频检索结果到处
  213. // @Summary 音频检索结果到处
  214. // @Produce application/json
  215. // @Param object query request.GetAudioList true "参数"
  216. // @Success 200 {object} util.ResponseList{data=[]models.Audio} "成功"
  217. // @Router /api-sa/v1/audio/export [get]
  218. func (slf AudioCtl) Export(c *gin.Context) {
  219. var params request.GetAudioList
  220. if err := c.ShouldBindQuery(&params); err != nil {
  221. util.ResponseFormat(c, code.RequestParamError, err.Error())
  222. return
  223. }
  224. list, _, err := models.NewAudioSearch().
  225. SetKeyword(params.Keyword).
  226. SetLocomotiveNumber(params.LocomotiveNumber).
  227. SetTrainNumber(params.TrainNumber).
  228. SetDriverNumber(params.DriverNumber).
  229. SetStation(params.StationNumber).
  230. SetBeginTime(params.BeginTime).
  231. SetEndTime(params.EndTime).
  232. SetIsFollowed(params.IsFollowed).
  233. SetAudioStatusList(params.StatusList).
  234. SetOrder("created_at desc").
  235. Find()
  236. if err != nil {
  237. util.ResponseFormat(c, code.RequestParamError, "查找失败")
  238. return
  239. }
  240. sheet := "auto_result"
  241. f := excelize.NewFile()
  242. index := f.NewSheet(sheet)
  243. //设置f的列名,车号,车次,司机号,车站号,日期
  244. f.SetCellValue(sheet, "A1", "机车号")
  245. f.SetCellValue(sheet, "B1", "车次")
  246. f.SetCellValue(sheet, "C1", "司机号")
  247. f.SetCellValue(sheet, "D1", "车站号")
  248. f.SetCellValue(sheet, "E1", "日期")
  249. f.SetCellValue(sheet, "F1", "分数")
  250. // 将数据写入工作表
  251. for i, auto := range list {
  252. row := i + 2
  253. cell1, _ := excelize.CoordinatesToCellName(1, row)
  254. f.SetCellValue(sheet, cell1, auto.LocomotiveNumber)
  255. cell2, _ := excelize.CoordinatesToCellName(2, row)
  256. f.SetCellValue(sheet, cell2, auto.TrainNumber)
  257. cell3, _ := excelize.CoordinatesToCellName(3, row)
  258. f.SetCellValue(sheet, cell3, auto.DriverNumber)
  259. cell4, _ := excelize.CoordinatesToCellName(4, row)
  260. f.SetCellValue(sheet, cell4, auto.Station)
  261. cell5, _ := excelize.CoordinatesToCellName(5, row)
  262. f.SetCellValue(sheet, cell5, auto.OccurrenceAt)
  263. cell6, _ := excelize.CoordinatesToCellName(6, row)
  264. f.SetCellValue(sheet, cell6, auto.Score)
  265. }
  266. // 设置默认打开的工作表
  267. f.SetActiveSheet(index)
  268. //// 保存文件
  269. //if err := f.SaveAs("result.xlsx"); err != nil {
  270. // fmt.Println(err)
  271. //}
  272. // Set the headers for the file download
  273. c.Header("Content-Description", "File Transfer")
  274. c.Header("Content-Disposition", "attachment; filename=result.xlsx")
  275. c.Header("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
  276. // Send the file to the client
  277. if err := f.Write(c.Writer); err != nil {
  278. util.ResponseFormat(c, code.InternalError, err.Error())
  279. return
  280. }
  281. util.ResponseFormat(c, code.Success, "success")
  282. }
  283. // Process
  284. // @Tags 音频
  285. // @Summary 处理音频
  286. // @Produce application/json
  287. // @Param object body request.ProcessAudio true "参数"
  288. // @Success 200 {object} util.Response "成功"
  289. // @Router /api-sa/v1/audio/process [post]
  290. func (slf AudioCtl) Process(c *gin.Context) {
  291. var params request.ProcessAudio
  292. if err := c.ShouldBind(&params); err != nil {
  293. util.ResponseFormat(c, code.RequestParamError, err.Error())
  294. return
  295. }
  296. err := service.Process(params.ID)
  297. if err != nil {
  298. util.ResponseFormat(c, code.InternalError, err.Error())
  299. return
  300. }
  301. util.ResponseFormat(c, code.UpdateSuccess, "成功")
  302. }
  303. // AudioInfo
  304. // @Tags 音频
  305. // @Summary 音频详情,含解析结果
  306. // @Produce application/json
  307. // @Param object query request.ProcessAudio true "参数"
  308. // @Success 200 {object} util.Response{data=models.Audio} "成功"
  309. // @Router /api-sa/v1/audio/info [get]
  310. func (slf AudioCtl) AudioInfo(c *gin.Context) {
  311. var params request.ProcessAudio
  312. if err := c.ShouldBindQuery(&params); err != nil {
  313. util.ResponseFormat(c, code.RequestParamError, err.Error())
  314. return
  315. }
  316. audio, err := models.NewAudioSearch().SetID(params.ID).First()
  317. if err != nil {
  318. util.ResponseFormat(c, code.InternalError, "请求失败")
  319. return
  320. }
  321. audioText, err := models.NewAudioTextSearch().SetAudioID(audio.ID).First()
  322. if err == nil {
  323. audio.AudioText = audioText.AudioText
  324. }
  325. util.ResponseFormat(c, code.UpdateSuccess, audio)
  326. }
  327. // AudioDownload
  328. // @Tags 音频
  329. // @Summary 音频下载
  330. // @Produce application/json
  331. // @Param object query request.ProcessAudio true "参数"
  332. // @Success 200 {object} util.Response{data=models.Audio} "成功"
  333. // @Router /api-sa/v1/audio/download [get]
  334. func (slf AudioCtl) AudioDownload(c *gin.Context) {
  335. var params request.ProcessAudio
  336. if err := c.ShouldBindQuery(&params); err != nil {
  337. util.ResponseFormat(c, code.RequestParamError, err.Error())
  338. return
  339. }
  340. audio, err := models.NewAudioSearch().SetID(params.ID).First()
  341. if err != nil {
  342. util.ResponseFormat(c, code.InternalError, "查询失败")
  343. return
  344. }
  345. filepath := ""
  346. if params.Filetype == 1 {
  347. filepath = audio.FilePath
  348. c.Header("Content-Type", "audio/mpeg") // 设置音频文件类型
  349. c.Header("Content-Disposition", "attachment; filename="+url.PathEscape(audio.Name)) // 在浏览器中直接打开
  350. }
  351. if params.Filetype == 2 {
  352. filepath = audio.TxtFilePath
  353. //设置Content-Type为txt文件类型,避免中文乱码
  354. c.Header("Content-Type", "text/plain; charset=utf-8")
  355. c.Header("Content-Transfer-Encoding", "binary")
  356. //去掉audio.Name中.之后的内容,只保留文件名称
  357. fileName := strings.Split(audio.Name, ".")[0]
  358. c.Header("Content-Disposition", "attachment; filename="+url.PathEscape(fmt.Sprintf("%s.txt", fileName))) // 在浏览器中直接打开
  359. }
  360. if filepath == "" {
  361. util.ResponseFormat(c, code.InternalError, "查询失败")
  362. return
  363. }
  364. file, err := os.Open(filepath)
  365. if err != nil {
  366. util.ResponseFormat(c, code.InternalError, "文件打开失败")
  367. return
  368. }
  369. defer file.Close()
  370. fileInfo, err := file.Stat()
  371. if err != nil {
  372. util.ResponseFormat(c, code.InternalError, "获取文件信息失败")
  373. return
  374. }
  375. c.Header("Content-Length", fmt.Sprint(fileInfo.Size()))
  376. if _, err := io.Copy(c.Writer, file); err != nil {
  377. util.ResponseFormat(c, code.InternalError, "文件传输失败")
  378. return
  379. }
  380. }
  381. // BatchProcess
  382. // @Tags 音频
  383. // @Summary 批量处理音频
  384. // @Produce application/json
  385. // @Param object body request.BatchProcessAudio true "参数"
  386. // @Success 200 {object} util.Response "成功"
  387. // @Router /api-sa/v1/audio/batchProcess [post]
  388. func (slf AudioCtl) BatchProcess(c *gin.Context) {
  389. var params request.BatchProcessAudio
  390. if err := c.ShouldBind(&params); err != nil {
  391. util.ResponseFormat(c, code.RequestParamError, err.Error())
  392. return
  393. }
  394. var failedNumber int
  395. for _, audioID := range params.IDs {
  396. err := service.Process(audioID)
  397. if err != nil {
  398. logx.Errorf("%v,编号: %v", err.Error(), audioID)
  399. failedNumber++
  400. continue
  401. }
  402. }
  403. if failedNumber == 0 {
  404. util.ResponseFormat(c, code.UpdateSuccess, "成功")
  405. return
  406. } else if failedNumber < len(params.IDs) {
  407. util.ResponseFormat(c, code.RequestParamError, "部分处理失败")
  408. return
  409. } else {
  410. util.ResponseFormat(c, code.RequestParamError, "全部处理失败")
  411. return
  412. }
  413. }
  414. // Delete
  415. // @Tags 音频
  416. // @Summary 删除音频
  417. // @Produce application/json
  418. // @Param object body request.ProcessAudio true "参数"
  419. // @Success 200 {object} util.Response "成功"
  420. // @Router /api-sa/v1/audio/delete [delete]
  421. func (slf AudioCtl) Delete(c *gin.Context) {
  422. var params request.ProcessAudio
  423. if err := c.ShouldBind(&params); err != nil {
  424. util.ResponseFormat(c, code.RequestParamError, err.Error())
  425. return
  426. }
  427. audio, err := models.NewAudioSearch().SetID(params.ID).First()
  428. if err != nil {
  429. util.ResponseFormat(c, code.RequestParamError, "音频不存在")
  430. return
  431. }
  432. if audio.AudioStatus == constvar.AudioStatusProcessing || audio.AudioStatus == constvar.AudioStatusFinish {
  433. util.ResponseFormat(c, code.RequestParamError, "音频正在处理或者处理完成,不可删除")
  434. return
  435. }
  436. err = service.DeleteAudio(params.ID)
  437. if err != nil {
  438. util.ResponseFormat(c, code.InternalError, err.Error())
  439. return
  440. }
  441. go func() {
  442. err = os.Remove(audio.FilePath)
  443. if err != nil {
  444. logx.Warnf("remove file err:%v, file:%v", err, audio.FilePath)
  445. }
  446. }()
  447. util.ResponseFormat(c, code.DeleteSuccess, "成功")
  448. }
  449. // BatchDelete
  450. // @Tags 音频
  451. // @Summary 批量删除音频
  452. // @Produce application/json
  453. // @Param object body request.BatchProcessAudio true "参数"
  454. // @Success 200 {object} util.Response "成功"
  455. // @Router /api-sa/v1/audio/batchDelete [delete]
  456. func (slf AudioCtl) BatchDelete(c *gin.Context) {
  457. var params request.BatchProcessAudio
  458. if err := c.ShouldBind(&params); err != nil {
  459. util.ResponseFormat(c, code.RequestParamError, err.Error())
  460. return
  461. }
  462. audioList, err := models.NewAudioSearch().SetIDs(params.IDs).FindNotTotal()
  463. if err != nil {
  464. util.ResponseFormat(c, code.InternalError, "内部错误")
  465. return
  466. }
  467. for _, audio := range audioList {
  468. if audio.AudioStatus == constvar.AudioStatusProcessing || audio.AudioStatus == constvar.AudioStatusFinish {
  469. util.ResponseFormat(c, code.RequestParamError, "音频正在处理或者处理完成,不可删除")
  470. return
  471. }
  472. }
  473. err = service.BatchDeleteAudio(params.IDs)
  474. if err != nil {
  475. util.ResponseFormat(c, code.InternalError, err.Error())
  476. return
  477. }
  478. go func() {
  479. for _, audio := range audioList {
  480. err = os.Remove(audio.FilePath)
  481. if err != nil {
  482. logx.Warnf("remove file err:%v, file:%v", err, audio.FilePath)
  483. }
  484. }
  485. }()
  486. util.ResponseFormat(c, code.DeleteSuccess, "成功")
  487. }
  488. // Follow
  489. // @Tags 音频
  490. // @Summary 关注/取消关注
  491. // @Produce application/json
  492. // @Param object body request.FollowReq true "参数"
  493. // @Success 200 {object} util.Response{data=response.FollowResp} "成功"
  494. // @Router /api-sa/v1/audio/follow [post]
  495. func (slf AudioCtl) Follow(c *gin.Context) {
  496. var params request.ProcessAudio
  497. if err := c.ShouldBind(&params); err != nil {
  498. util.ResponseFormat(c, code.RequestParamError, err.Error())
  499. return
  500. }
  501. followStatus, err := service.Follow(params.ID)
  502. if err != nil {
  503. util.ResponseFormat(c, code.InternalError, err.Error())
  504. return
  505. }
  506. resp := response.FollowResp{FollowStatus: followStatus}
  507. util.ResponseFormat(c, code.UpdateSuccess, resp)
  508. }
  509. // PreLoadPath
  510. // @Tags 音频自动加载路径
  511. // @Summary 音频自动加载路径
  512. // @Produce application/json
  513. // @Success 200 {object} util.Response{data=response.PreLoadPathResp} "成功"
  514. // @Router /api-sa/v1/audio/preLoadPath [get]
  515. func (slf AudioCtl) PreLoadPath(c *gin.Context) {
  516. //获取PRELOAD_PATH环境变量
  517. preLoadPath := os.Getenv("PRELOAD_PATH")
  518. if len(preLoadPath) == 0 {
  519. preLoadPath = "./preloads"
  520. }
  521. resp := response.PreLoadPathResp{PreLoadPath: preLoadPath}
  522. util.ResponseFormat(c, code.UpdateSuccess, resp)
  523. }