package controllers import ( "errors" "fmt" "github.com/gin-gonic/gin" "gorm.io/gorm" "io" "mime/multipart" "net/url" "os" "path" "path/filepath" "speechAnalysis/constvar" "speechAnalysis/extend/code" "speechAnalysis/extend/util" "speechAnalysis/models" "speechAnalysis/pkg/logx" "speechAnalysis/request" "speechAnalysis/response" "speechAnalysis/service" "speechAnalysis/utils/upload" "strings" "time" ) type AudioCtl struct{} // Upload // @Tags 音频 // @Summary 上传音频 // @Accept multipart/form-data // @Produce application/json // @Param file formData []file false "多文件上传" // @Success 200 {object} util.Response "成功" // @Router /api-sa/v1/audio/upload [post] func (slf AudioCtl) Upload(c *gin.Context) { var headers []*multipart.FileHeader files, _ := c.MultipartForm() if len(files.File["file"]) > 1 { headers = files.File["file"] } else { util.ResponseFormat(c, code.RequestParamError, "文件需要一一对应") return } audio := &models.Audio{} for _, header := range headers { filename := path.Base(header.Filename) arr := strings.Split(filename, "_") if len(arr) != 6 { util.ResponseFormat(c, code.RequestParamError, "文件名称错误") return } _, err := models.NewAudioSearch().SetName(filename).First() if err != gorm.ErrRecordNotFound { util.ResponseFormat(c, code.RequestParamError, "重复上传") return } oss := upload.NewOss() filePath, filename, uploadErr := oss.UploadFile(header) if uploadErr != nil { logx.Errorf("upload audio err: %v", err) util.ResponseFormat(c, code.RequestParamError, "上传失败") return } if filepath.Ext(filename) == ".mp3" || filepath.Ext(filename) == ".wav" { timeStr := arr[4] + strings.Split(arr[5], ".")[0] t, err := time.ParseInLocation("20060102150405", timeStr, time.Local) if err != nil { util.ResponseFormat(c, code.RequestParamError, "时间格式不对") return } audio.Name = filename audio.Size = header.Size audio.FilePath = filePath audio.AudioStatus = constvar.AudioStatusUploadOk audio.LocomotiveNumber = arr[0] audio.TrainNumber = arr[1] audio.DriverNumber = arr[2] audio.Station = arr[3] audio.OccurrenceAt = t audio.IsFollowed = 0 } if filepath.Ext(filename) == ".txt" { audio.TxtFilePath = filePath //读取filepath文件内容到bts bts, err := os.ReadFile(filePath) if err != nil { util.ResponseFormat(c, code.RequestParamError, "读取文件失败") return } //解析 交路号:123_公里标:321 fileds := string(bts) arr = strings.Split(fileds, "_") if len(arr) != 2 { util.ResponseFormat(c, code.RequestParamError, "文件内容格式不对") return } else { RouteNumber := strings.Split(arr[0], ":") KilometerMarker := strings.Split(arr[1], ":") if len(RouteNumber) > 1 && len(KilometerMarker) > 1 { audio.RouteNumber = RouteNumber[1] audio.KilometerMarker = KilometerMarker[1] } else { util.ResponseFormat(c, code.RequestParamError, "文件内容格式不对") return } } } } if err := models.NewAudioSearch().Create(audio); err != nil { util.ResponseFormat(c, code.SaveFail, "上传失败") return } go func() { var trainInfoNames = []string{audio.LocomotiveNumber, audio.TrainNumber, audio.Station} var ( info *models.TrainInfo err error parent models.TrainInfo ) for i := 0; i < 3; i++ { name := trainInfoNames[i] class := constvar.Class(i + 1) info, err = models.NewTrainInfoSearch().SetName(name).SetClass(class).First() if err == gorm.ErrRecordNotFound { info = &models.TrainInfo{ Name: name, Class: class, ParentID: parent.ID, } _ = models.NewTrainInfoSearch().Create(info) } parent = *info } }() } func (slf AudioCtl) ParamsCheck(filename string) (err error) { arr := strings.Split(filename, "_") if len(arr) != 6 { return errors.New("文件格式错误") } return nil } // TrainInfoList // @Tags 音频 // @Summary 获取火车信息 // @Produce application/json // @Param object query request.GetTrainInfoList true "参数" // @Success 200 {object} util.ResponseList{data=[]models.TrainInfo} "成功" // @Router /api-sa/v1/audio/trainInfoList [get] func (slf AudioCtl) TrainInfoList(c *gin.Context) { var params request.GetTrainInfoList if err := c.ShouldBindQuery(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } if !params.PageInfo.Check() { util.ResponseFormat(c, code.RequestParamError, "分页参数错误") return } list, total, err := models.NewTrainInfoSearch(). SetPage(params.Page, params.PageSize). SetClass(params.Class). SetParentId(params.ParentID). Find() if err != nil { util.ResponseFormat(c, code.RequestParamError, "查找失败") return } util.ResponseFormatList(c, code.Success, list, total) } // List // @Tags 音频 // @Summary 音频分析检索 // @Produce application/json // @Param object query request.GetAudioList true "参数" // @Success 200 {object} util.ResponseList{data=[]models.Audio} "成功" // @Router /api-sa/v1/audio/list [get] func (slf AudioCtl) List(c *gin.Context) { var params request.GetAudioList if err := c.ShouldBindQuery(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } if !params.PageInfo.Check() { util.ResponseFormat(c, code.RequestParamError, "分页参数错误") return } list, total, err := models.NewAudioSearch(). SetPage(params.Page, params.PageSize). SetKeyword(params.Keyword). SetLocomotiveNumber(params.LocomotiveNumber). SetTrainNumber(params.TrainNumber). SetDriverNumber(params.DriverNumber). SetStation(params.StationNumber). SetBeginTime(params.BeginTime). SetEndTime(params.EndTime). SetIsFollowed(params.IsFollowed). SetAudioStatusList(params.StatusList). SetOrder("created_at desc"). Find() if err != nil { util.ResponseFormat(c, code.RequestParamError, "查找失败") return } util.ResponseFormatList(c, code.Success, list, total) } // Process // @Tags 音频 // @Summary 处理音频 // @Produce application/json // @Param object body request.ProcessAudio true "参数" // @Success 200 {object} util.Response "成功" // @Router /api-sa/v1/audio/process [post] func (slf AudioCtl) Process(c *gin.Context) { var params request.ProcessAudio if err := c.ShouldBind(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } err := service.Process(params.ID) if err != nil { util.ResponseFormat(c, code.InternalError, err.Error()) return } util.ResponseFormat(c, code.UpdateSuccess, "成功") } // AudioInfo // @Tags 音频 // @Summary 音频详情,含解析结果 // @Produce application/json // @Param object query request.ProcessAudio true "参数" // @Success 200 {object} util.Response{data=models.Audio} "成功" // @Router /api-sa/v1/audio/info [get] func (slf AudioCtl) AudioInfo(c *gin.Context) { var params request.ProcessAudio if err := c.ShouldBindQuery(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } audio, err := models.NewAudioSearch().SetID(params.ID).First() if err != nil { util.ResponseFormat(c, code.InternalError, "请求失败") return } audioText, err := models.NewAudioTextSearch().SetAudioID(audio.ID).First() if err == nil { audio.AudioText = audioText.AudioText } util.ResponseFormat(c, code.UpdateSuccess, audio) } // AudioDownload // @Tags 音频 // @Summary 音频下载 // @Produce application/json // @Param object query request.ProcessAudio true "参数" // @Success 200 {object} util.Response{data=models.Audio} "成功" // @Router /api-sa/v1/audio/download [get] func (slf AudioCtl) AudioDownload(c *gin.Context) { var params request.ProcessAudio if err := c.ShouldBindQuery(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } audio, err := models.NewAudioSearch().SetID(params.ID).First() if err != nil { util.ResponseFormat(c, code.InternalError, "查询失败") return } filepath := "" if params.Filetype == 1 { filepath = audio.FilePath c.Header("Content-Type", "audio/mpeg") // 设置音频文件类型 } if params.Filetype == 2 { filepath = audio.TxtFilePath //设置Content-Type为txt文件类型,避免中文乱码 c.Header("Content-Type", "text/plain; charset=utf-8") c.Header("Content-Transfer-Encoding", "binary") } if filepath == "" { util.ResponseFormat(c, code.InternalError, "查询失败") return } file, err := os.Open(filepath) if err != nil { util.ResponseFormat(c, code.InternalError, "文件打开失败") return } defer file.Close() fileInfo, err := file.Stat() if err != nil { util.ResponseFormat(c, code.InternalError, "获取文件信息失败") return } c.Header("Content-Disposition", "inline; filename="+url.PathEscape(audio.Name)) // 在浏览器中直接打开 c.Header("Content-Length", fmt.Sprint(fileInfo.Size())) if _, err := io.Copy(c.Writer, file); err != nil { util.ResponseFormat(c, code.InternalError, "文件传输失败") return } } // BatchProcess // @Tags 音频 // @Summary 批量处理音频 // @Produce application/json // @Param object body request.BatchProcessAudio true "参数" // @Success 200 {object} util.Response "成功" // @Router /api-sa/v1/audio/batchProcess [post] func (slf AudioCtl) BatchProcess(c *gin.Context) { var params request.BatchProcessAudio if err := c.ShouldBind(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } var failedNumber int for _, audioID := range params.IDs { err := service.Process(audioID) if err != nil { logx.Errorf("%v,编号: %v", err.Error(), audioID) failedNumber++ continue } } if failedNumber == 0 { util.ResponseFormat(c, code.UpdateSuccess, "成功") return } else if failedNumber < len(params.IDs) { util.ResponseFormat(c, code.RequestParamError, "部分处理失败") return } else { util.ResponseFormat(c, code.RequestParamError, "全部处理失败") return } } // Delete // @Tags 音频 // @Summary 删除音频 // @Produce application/json // @Param object body request.ProcessAudio true "参数" // @Success 200 {object} util.Response "成功" // @Router /api-sa/v1/audio/delete [delete] func (slf AudioCtl) Delete(c *gin.Context) { var params request.ProcessAudio if err := c.ShouldBind(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } audio, err := models.NewAudioSearch().SetID(params.ID).First() if err != nil { util.ResponseFormat(c, code.RequestParamError, "音频不存在") return } if audio.AudioStatus == constvar.AudioStatusProcessing || audio.AudioStatus == constvar.AudioStatusFinish { util.ResponseFormat(c, code.RequestParamError, "音频正在处理或者处理完成,不可删除") return } err = service.DeleteAudio(params.ID) if err != nil { util.ResponseFormat(c, code.InternalError, err.Error()) return } go func() { err = os.Remove(audio.FilePath) if err != nil { logx.Warnf("remove file err:%v, file:%v", err, audio.FilePath) } }() util.ResponseFormat(c, code.DeleteSuccess, "成功") } // BatchDelete // @Tags 音频 // @Summary 批量删除音频 // @Produce application/json // @Param object body request.BatchProcessAudio true "参数" // @Success 200 {object} util.Response "成功" // @Router /api-sa/v1/audio/batchDelete [delete] func (slf AudioCtl) BatchDelete(c *gin.Context) { var params request.BatchProcessAudio if err := c.ShouldBind(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } audioList, err := models.NewAudioSearch().SetIDs(params.IDs).FindNotTotal() if err != nil { util.ResponseFormat(c, code.InternalError, "内部错误") return } for _, audio := range audioList { if audio.AudioStatus == constvar.AudioStatusProcessing || audio.AudioStatus == constvar.AudioStatusFinish { util.ResponseFormat(c, code.RequestParamError, "音频正在处理或者处理完成,不可删除") return } } err = service.BatchDeleteAudio(params.IDs) if err != nil { util.ResponseFormat(c, code.InternalError, err.Error()) return } go func() { for _, audio := range audioList { err = os.Remove(audio.FilePath) if err != nil { logx.Warnf("remove file err:%v, file:%v", err, audio.FilePath) } } }() util.ResponseFormat(c, code.DeleteSuccess, "成功") } // Follow // @Tags 音频 // @Summary 关注/取消关注 // @Produce application/json // @Param object body request.FollowReq true "参数" // @Success 200 {object} util.Response{data=response.FollowResp} "成功" // @Router /api-sa/v1/audio/follow [post] func (slf AudioCtl) Follow(c *gin.Context) { var params request.ProcessAudio if err := c.ShouldBind(¶ms); err != nil { util.ResponseFormat(c, code.RequestParamError, err.Error()) return } followStatus, err := service.Follow(params.ID) if err != nil { util.ResponseFormat(c, code.InternalError, err.Error()) return } resp := response.FollowResp{FollowStatus: followStatus} util.ResponseFormat(c, code.UpdateSuccess, resp) }