Browse Source

Merge branch 'feature/eta_1.6.9' into debug

hsun 1 year ago
parent
commit
e2b449619b

+ 80 - 12
controllers/speech_recognition/speech_recognition.go

@@ -10,6 +10,8 @@ import (
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
+	"os"
+	"strconv"
 	"strings"
 	"time"
 )
@@ -189,7 +191,8 @@ func (this *SpeechRecognitionController) Convert() {
 		t.FileName = v.FileName
 		t.ResourceUrl = v.ResourceUrl
 		t.MenuId = req.MenuId
-		// TODO:所属目录位置
+		timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+		t.UniqueCode = utils.MD5(fmt.Sprintf("%s_%s", t.TableName(), timestamp))
 		t.SysUserId = sysUser.AdminId
 		t.SysUserName = sysUser.RealName
 		t.State = speech_recognition.SpeechRecognitionStateWait
@@ -520,8 +523,10 @@ func (this *SpeechRecognitionController) Remove() {
 		return
 	}
 
-	// 清除标签关联
+	// 清除关联
 	go func() {
+		contentOb := new(speech_recognition.SpeechRecognitionContent)
+		_ = contentOb.ClearContentBySpeechId(req.SpeechRecognitionId)
 		mappingOb := new(speech_recognition.SpeechRecognitionTagMapping)
 		_ = mappingOb.ClearMappingBySpeechId(req.SpeechRecognitionId)
 	}()
@@ -617,6 +622,7 @@ func (this *SpeechRecognitionController) SaveTag() {
 // @Param   EndTime  query  string  false  "结束时间"
 // @Param   CreateUserId  query  int  false  "创建人ID"
 // @Param   TagId  query  int  false  "标签ID"
+// @Param   MenuId  query  int  false  "目录ID"
 // @Success 200 {object} speech_recognition.SpeechRecognitionListResp
 // @router /list [get]
 func (this *SpeechRecognitionController) List() {
@@ -673,6 +679,10 @@ func (this *SpeechRecognitionController) List() {
 			cond += fmt.Sprintf(` AND %s = ?`, speech_recognition.SpeechRecognitionCols.SysUserId)
 			pars = append(pars, params.CreateUserId)
 		}
+		if params.MenuId > 0 {
+			cond += fmt.Sprintf(` AND %s = ?`, speech_recognition.SpeechRecognitionCols.MenuId)
+			pars = append(pars, params.MenuId)
+		}
 
 		// 标签筛选
 		if params.TagId > 0 {
@@ -810,6 +820,9 @@ func (this *SpeechRecognitionController) Detail() {
 			return
 		}
 		for _, v := range list {
+			if v.Content == "" {
+				continue
+			}
 			contents = append(contents, speech_recognition.FormatSpeechRecognitionContent2Item(v))
 		}
 	}
@@ -829,13 +842,13 @@ func (this *SpeechRecognitionController) Detail() {
 	br.Msg = "获取成功"
 }
 
-// TODO:Move
-// @Title 移动语音识别/目录
-// @Description 移动标签/目录
-// @Param	request	body speech_recognition.SpeechRecognitionTagRemoveReq true "type json string"
+// Export
+// @Title 导出内容
+// @Description 导出内容
+// @Param	request	body speech_recognition.SpeechRecognitionContentExportReq true "type json string"
 // @Success 200 string "操作成功"
-// @router /move [post]
-func (this *SpeechRecognitionController) Move() {
+// @router /export [post]
+func (this *SpeechRecognitionController) Export() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
 		if br.ErrMsg == "" {
@@ -851,10 +864,65 @@ func (this *SpeechRecognitionController) Move() {
 		br.Ret = 408
 		return
 	}
+	var req speech_recognition.SpeechRecognitionContentExportReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	if req.SpeechRecognitionId <= 0 {
+		br.Msg = "参数有误"
+		br.ErrMsg = fmt.Sprintf("参数有误, SpeechRecognitionId: %d", req.SpeechRecognitionId)
+		return
+	}
 
-	// TODO:移动语音识别/目录
+	speechOb := new(speech_recognition.SpeechRecognition)
+	speechItem, e := speechOb.GetItemById(req.SpeechRecognitionId)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "转写文件不存在,请刷新页面"
+			return
+		}
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取转写文件失败, Err: " + e.Error()
+		return
+	}
 
-	br.Ret = 200
-	br.Success = true
-	br.Msg = "操作成功"
+	contentOb := new(speech_recognition.SpeechRecognitionContent)
+	cond := fmt.Sprintf(` AND %s = ?`, speech_recognition.SpeechRecognitionContentCols.SpeechRecognitionId)
+	pars := make([]interface{}, 0)
+	pars = append(pars, req.SpeechRecognitionId)
+	contents, e := contentOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", speech_recognition.SpeechRecognitionContentCols.Sort))
+	if e != nil {
+		br.Msg = "导出失败"
+		br.ErrMsg = "获取语音识别内容失败, Err: " + e.Error()
+		return
+	}
+	if len(contents) == 0 {
+		br.Msg = "无内容导出"
+		return
+	}
+	if req.ExportType != services.SpeechRecognitionExportTypeTxt && req.ExportType != services.SpeechRecognitionExportTypeDocx && req.ExportType != services.SpeechRecognitionExportTypePdf {
+		br.Msg = "导出类型有误"
+		return
+	}
+
+	suffixMap := map[int]string{services.SpeechRecognitionExportTypeTxt: ".txt", services.SpeechRecognitionExportTypeDocx: ".docx", services.SpeechRecognitionExportTypePdf: ".pdf"}
+	suffix := suffixMap[req.ExportType]
+	if suffix == "" {
+		suffix = ".txt"
+	}
+	downloadPath, e := services.SpeechRecognitionContentExport(req.ExportType, req.Timestamp, speechItem.FileName, contents)
+	if e != nil {
+		br.Msg = "导出文件失败"
+		br.ErrMsg = "导出语音识别内容文件失败, Err: " + e.Error()
+		_ = os.Remove(downloadPath)
+		return
+	}
+	defer func() {
+		_ = os.Remove(downloadPath)
+	}()
+
+	downloadName := fmt.Sprintf("%s%s", speechItem.FileName, suffix)
+	this.Ctx.Output.Download(downloadPath, downloadName)
 }

+ 93 - 46
controllers/speech_recognition/speech_recognition_menu.go

@@ -7,6 +7,8 @@ import (
 	"eta/eta_api/models/speech_recognition"
 	"eta/eta_api/utils"
 	"fmt"
+	"sort"
+	"strconv"
 	"strings"
 	"time"
 )
@@ -69,6 +71,7 @@ func (this *SpeechRecognitionMenuController) Add() {
 
 	// 获取目录层级
 	level := 1
+	rootId := 0
 	{
 		if req.ParentId > 0 {
 			parentMenu, e := menuOb.GetItemById(req.ParentId)
@@ -78,12 +81,16 @@ func (this *SpeechRecognitionMenuController) Add() {
 				return
 			}
 			level += parentMenu.Level
+			rootId = parentMenu.RootId
 		}
 	}
 
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	menuOb.UniqueCode = utils.MD5(fmt.Sprintf("%s_%s", menuOb.TableName(), timestamp))
 	menuOb.MenuName = req.MenuName
 	menuOb.ParentId = req.ParentId
 	menuOb.Level = level
+	menuOb.RootId = rootId
 	menuOb.CreateTime = time.Now().Local()
 	menuOb.ModifyTime = time.Now().Local()
 	e := menuOb.Create()
@@ -296,13 +303,11 @@ func (this *SpeechRecognitionMenuController) Tree() {
 	// 前端采用懒加载, 所以只查询目录及当前目录下的语音识别
 	parentId, _ := this.GetInt("ParentId")
 
+	// 获取所有目录
 	menus := make([]*speech_recognition.SpeechRecognitionMenu, 0)
 	{
 		menuOb := new(speech_recognition.SpeechRecognitionMenu)
-		cond := fmt.Sprintf(` AND %s = ?`, speech_recognition.SpeechRecognitionMenuCols.ParentId)
-		pars := make([]interface{}, 0)
-		pars = append(pars, parentId)
-		list, e := menuOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", speech_recognition.SpeechRecognitionMenuCols.Sort))
+		list, e := menuOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, fmt.Sprintf("%s ASC", speech_recognition.SpeechRecognitionMenuCols.Sort))
 		if e != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取目录列表失败, Err: " + e.Error()
@@ -311,50 +316,44 @@ func (this *SpeechRecognitionMenuController) Tree() {
 		menus = list
 	}
 
+	topMenus := make([]*speech_recognition.SpeechRecognitionMenu, 0)                  // 顶部节点
+	menuChildren := make(map[int][]*speech_recognition.SpeechRecognitionMenuNodeItem) // 子目录节点
+	menuIdLevel := make(map[int]int)                                                  // 目录对应层级
+	for _, v := range menus {
+		menuIdLevel[v.SpeechRecognitionMenuId] = v.Level
+		if v.ParentId == parentId {
+			topMenus = append(topMenus, v)
+		}
+
+		if menuChildren[v.ParentId] == nil {
+			menuChildren[v.ParentId] = make([]*speech_recognition.SpeechRecognitionMenuNodeItem, 0)
+		}
+		menuChildren[v.ParentId] = append(menuChildren[v.ParentId], &speech_recognition.SpeechRecognitionMenuNodeItem{
+			UniqueCode: v.UniqueCode,
+			NodeType:   speech_recognition.SpeechRecognitionMenuNodeTypeDefault,
+			MenuId:     v.SpeechRecognitionMenuId,
+			MenuName:   v.MenuName,
+			ParentId:   v.ParentId,
+			Level:      v.Level,
+			Sort:       v.Sort,
+			CreateTime: utils.TimeTransferString(utils.FormatDateTime, v.CreateTime),
+		})
+	}
+
 	resp := make([]*speech_recognition.SpeechRecognitionMenuNodeItem, 0)
-	if len(menus) == 0 {
+	if len(topMenus) == 0 {
 		br.Data = resp
 		br.Ret = 200
 		br.Success = true
 		br.Msg = "获取成功"
 		return
 	}
+
+	// 目录下的语音识别
 	menuIds := make([]int, 0)
-	for _, m := range menus {
+	for _, m := range topMenus {
 		menuIds = append(menuIds, m.SpeechRecognitionMenuId)
 	}
-
-	// 子目录
-	childMenus := make([]*speech_recognition.SpeechRecognitionMenu, 0)
-	{
-		menuOb := new(speech_recognition.SpeechRecognitionMenu)
-		cond := fmt.Sprintf(` AND %s IN (%s)`, speech_recognition.SpeechRecognitionMenuCols.ParentId, utils.GetOrmInReplace(len(menuIds)))
-		pars := make([]interface{}, 0)
-		pars = append(pars, menuIds)
-		list, e := menuOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC, %s ASC", speech_recognition.SpeechRecognitionMenuCols.Sort, speech_recognition.SpeechRecognitionMenuCols.ParentId))
-		if e != nil {
-			br.Msg = "获取失败"
-			br.ErrMsg = "获取子目录列表失败, Err: " + e.Error()
-			return
-		}
-		childMenus = list
-	}
-	menuChildren := make(map[int][]*speech_recognition.SpeechRecognitionMenuNodeItem)
-	for _, m := range childMenus {
-		if menuChildren[m.ParentId] == nil {
-			menuChildren[m.ParentId] = make([]*speech_recognition.SpeechRecognitionMenuNodeItem, 0)
-		}
-		menuChildren[m.ParentId] = append(menuChildren[m.ParentId], &speech_recognition.SpeechRecognitionMenuNodeItem{
-			NodeType:   speech_recognition.SpeechRecognitionMenuNodeTypeDefault,
-			MenuId:     m.SpeechRecognitionMenuId,
-			MenuName:   m.MenuName,
-			ParentId:   m.ParentId,
-			Sort:       m.Sort,
-			CreateTime: utils.TimeTransferString(utils.FormatDateTime, m.CreateTime),
-		})
-	}
-
-	// 目录下的语音识别
 	speeches := make([]*speech_recognition.SpeechRecognition, 0)
 	{
 		speechOb := new(speech_recognition.SpeechRecognition)
@@ -375,30 +374,38 @@ func (this *SpeechRecognitionMenuController) Tree() {
 			menuSpeeches[s.MenuId] = make([]*speech_recognition.SpeechRecognitionMenuNodeItem, 0)
 		}
 		menuSpeeches[s.MenuId] = append(menuSpeeches[s.MenuId], &speech_recognition.SpeechRecognitionMenuNodeItem{
+			UniqueCode:            s.UniqueCode,
 			NodeType:              speech_recognition.SpeechRecognitionMenuNodeTypeSpeech,
 			SpeechRecognitionId:   s.SpeechRecognitionId,
 			SpeechRecognitionName: s.FileName,
 			ParentId:              s.MenuId,
+			Level:                 menuIdLevel[s.MenuId] + 1,
 			Sort:                  s.Sort,
 			CreateTime:            utils.TimeTransferString(utils.FormatDateTime, s.CreateTime),
 		})
 	}
 
-	for _, m := range menus {
+	for _, m := range topMenus {
+		child := make([]*speech_recognition.SpeechRecognitionMenuNodeItem, 0)
+		if menuSpeeches[m.SpeechRecognitionMenuId] != nil {
+			child = append(child, menuSpeeches[m.SpeechRecognitionMenuId]...)
+		}
+		if menuChildren[m.SpeechRecognitionMenuId] != nil {
+			child = append(child, menuChildren[m.SpeechRecognitionMenuId]...)
+		}
+		sort.Slice(child, func(i, j int) bool {
+			return child[i].Sort < child[j].Sort
+		})
 		t := &speech_recognition.SpeechRecognitionMenuNodeItem{
+			UniqueCode: m.UniqueCode,
 			NodeType:   speech_recognition.SpeechRecognitionMenuNodeTypeDefault,
 			MenuId:     m.SpeechRecognitionMenuId,
 			MenuName:   m.MenuName,
 			ParentId:   m.ParentId,
+			Level:      m.Level,
 			Sort:       m.Sort,
 			CreateTime: utils.TimeTransferString(utils.FormatDateTime, m.CreateTime),
-			Children:   make([]*speech_recognition.SpeechRecognitionMenuNodeItem, 0),
-		}
-		if menuSpeeches[m.SpeechRecognitionMenuId] != nil {
-			t.Children = append(t.Children, menuSpeeches[m.SpeechRecognitionMenuId]...)
-		}
-		if menuChildren[m.SpeechRecognitionMenuId] != nil {
-			t.Children = append(t.Children, menuChildren[m.SpeechRecognitionMenuId]...)
+			Children:   child,
 		}
 		resp = append(resp, t)
 	}
@@ -408,3 +415,43 @@ func (this *SpeechRecognitionMenuController) Tree() {
 	br.Success = true
 	br.Msg = "获取成功"
 }
+
+// Move
+// @Title 移动目录/语音识别
+// @Description 移动目录/语音识别
+// @Param	request	body speech_recognition.SpeechRecognitionMenuMoveReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /move [post]
+func (this *SpeechRecognitionMenuController) Move() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	var req speech_recognition.SpeechRecognitionMenuMoveReq
+	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
+		br.Msg = "参数有误"
+		br.ErrMsg = "参数解析失败, Err: " + e.Error()
+		return
+	}
+	if req.MenuId <= 0 && req.SpeechId <= 0 {
+		br.Msg = "请选择目录或语音识别"
+		return
+	}
+
+	// TODO:移动
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 3 - 31
controllers/speech_recognition/speech_recognition_tag.go

@@ -7,6 +7,7 @@ import (
 	"eta/eta_api/models/speech_recognition"
 	"eta/eta_api/utils"
 	"fmt"
+	"strconv"
 	"strings"
 	"time"
 )
@@ -71,9 +72,10 @@ func (this *SpeechRecognitionTagController) Add() {
 		}
 	}
 
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	tagOb.UniqueCode = utils.MD5(fmt.Sprintf("%s_%s", tagOb.TableName(), timestamp))
 	tagOb.TagName = req.TagName
 	tagOb.MenuId = req.MenuId
-	// TODO:所属目录位置
 	tagOb.CreateTime = time.Now().Local()
 	tagOb.ModifyTime = time.Now().Local()
 	if e := tagOb.Create(); e != nil {
@@ -297,33 +299,3 @@ func (this *SpeechRecognitionTagController) List() {
 	br.Success = true
 	br.Msg = "获取成功"
 }
-
-// Move
-// @Title 移动标签/目录
-// @Description 移动标签/目录
-// @Param	request	body speech_recognition.SpeechRecognitionTagRemoveReq true "type json string"
-// @Success 200 string "操作成功"
-// @router /tag/move [post]
-func (this *SpeechRecognitionTagController) Move() {
-	br := new(models.BaseResponse).Init()
-	defer func() {
-		if br.ErrMsg == "" {
-			br.IsSendEmail = false
-		}
-		this.Data["json"] = br
-		this.ServeJSON()
-	}()
-	sysUser := this.SysUser
-	if sysUser == nil {
-		br.Msg = "请登录"
-		br.ErrMsg = "请登录,SysUser Is Empty"
-		br.Ret = 408
-		return
-	}
-
-	// TODO:移动标签/目录
-
-	br.Ret = 200
-	br.Success = true
-	br.Msg = "操作成功"
-}

+ 82 - 45
controllers/speech_recognition/speech_recognition_tag_menu.go

@@ -8,6 +8,8 @@ import (
 	"eta/eta_api/services"
 	"eta/eta_api/utils"
 	"fmt"
+	"sort"
+	"strconv"
 	"strings"
 	"time"
 )
@@ -71,6 +73,7 @@ func (this *SpeechRecognitionTagMenuController) Add() {
 
 	// 获取目录层级
 	level := 1
+	rootId := 0
 	{
 		if req.ParentId > 0 {
 			parentMenu, e := menuOb.GetItemById(req.ParentId)
@@ -80,12 +83,16 @@ func (this *SpeechRecognitionTagMenuController) Add() {
 				return
 			}
 			level += parentMenu.Level
+			rootId = parentMenu.RootId
 		}
 	}
 
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	menuOb.UniqueCode = utils.MD5(fmt.Sprintf("%s_%s", menuOb.TableName(), timestamp))
 	menuOb.MenuName = req.MenuName
 	menuOb.ParentId = req.ParentId
 	menuOb.Level = level
+	menuOb.RootId = rootId
 	menuOb.CreateTime = time.Now().Local()
 	menuOb.ModifyTime = time.Now().Local()
 	e := menuOb.Create()
@@ -298,13 +305,11 @@ func (this *SpeechRecognitionTagMenuController) Tree() {
 	// 前端采用懒加载, 所以只查询目录及当前目录下的语音识别
 	parentId, _ := this.GetInt("ParentId")
 
+	// 获取所有目录
 	menus := make([]*speech_recognition.SpeechRecognitionTagMenu, 0)
 	{
 		menuOb := new(speech_recognition.SpeechRecognitionTagMenu)
-		cond := fmt.Sprintf(` AND %s = ?`, speech_recognition.SpeechRecognitionTagMenuCols.ParentId)
-		pars := make([]interface{}, 0)
-		pars = append(pars, parentId)
-		list, e := menuOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", speech_recognition.SpeechRecognitionTagMenuCols.Sort))
+		list, e := menuOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, fmt.Sprintf("%s ASC", speech_recognition.SpeechRecognitionMenuCols.Sort))
 		if e != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取目录列表失败, Err: " + e.Error()
@@ -313,50 +318,44 @@ func (this *SpeechRecognitionTagMenuController) Tree() {
 		menus = list
 	}
 
+	topMenus := make([]*speech_recognition.SpeechRecognitionTagMenu, 0)                  // 顶部节点
+	menuChildren := make(map[int][]*speech_recognition.SpeechRecognitionTagMenuNodeItem) // 子目录节点
+	menuIdLevel := make(map[int]int)                                                     // 目录对应层级
+	for _, v := range menus {
+		menuIdLevel[v.SpeechRecognitionTagMenuId] = v.Level
+		if v.ParentId == parentId {
+			topMenus = append(topMenus, v)
+		}
+
+		if menuChildren[v.ParentId] == nil {
+			menuChildren[v.ParentId] = make([]*speech_recognition.SpeechRecognitionTagMenuNodeItem, 0)
+		}
+		menuChildren[v.ParentId] = append(menuChildren[v.ParentId], &speech_recognition.SpeechRecognitionTagMenuNodeItem{
+			UniqueCode: v.UniqueCode,
+			NodeType:   speech_recognition.SpeechRecognitionMenuNodeTypeDefault,
+			MenuId:     v.SpeechRecognitionTagMenuId,
+			MenuName:   v.MenuName,
+			ParentId:   v.ParentId,
+			Level:      v.Level,
+			Sort:       v.Sort,
+			CreateTime: utils.TimeTransferString(utils.FormatDateTime, v.CreateTime),
+		})
+	}
+
 	resp := make([]*speech_recognition.SpeechRecognitionTagMenuNodeItem, 0)
-	if len(menus) == 0 {
+	if len(topMenus) == 0 {
 		br.Data = resp
 		br.Ret = 200
 		br.Success = true
 		br.Msg = "获取成功"
 		return
 	}
+
+	// 目录下的标签
 	menuIds := make([]int, 0)
 	for _, m := range menus {
 		menuIds = append(menuIds, m.SpeechRecognitionTagMenuId)
 	}
-
-	// 子目录
-	childMenus := make([]*speech_recognition.SpeechRecognitionTagMenu, 0)
-	{
-		menuOb := new(speech_recognition.SpeechRecognitionTagMenu)
-		cond := fmt.Sprintf(` AND %s IN (%s)`, speech_recognition.SpeechRecognitionTagMenuCols.ParentId, utils.GetOrmInReplace(len(menuIds)))
-		pars := make([]interface{}, 0)
-		pars = append(pars, menuIds)
-		list, e := menuOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC, %s ASC", speech_recognition.SpeechRecognitionTagMenuCols.Sort, speech_recognition.SpeechRecognitionTagMenuCols.ParentId))
-		if e != nil {
-			br.Msg = "获取失败"
-			br.ErrMsg = "获取子目录列表失败, Err: " + e.Error()
-			return
-		}
-		childMenus = list
-	}
-	menuChildren := make(map[int][]*speech_recognition.SpeechRecognitionTagMenuNodeItem)
-	for _, m := range childMenus {
-		if menuChildren[m.ParentId] == nil {
-			menuChildren[m.ParentId] = make([]*speech_recognition.SpeechRecognitionTagMenuNodeItem, 0)
-		}
-		menuChildren[m.ParentId] = append(menuChildren[m.ParentId], &speech_recognition.SpeechRecognitionTagMenuNodeItem{
-			NodeType:   speech_recognition.SpeechRecognitionTagMenuNodeTypeDefault,
-			MenuId:     m.SpeechRecognitionTagMenuId,
-			MenuName:   m.MenuName,
-			ParentId:   m.ParentId,
-			Sort:       m.Sort,
-			CreateTime: utils.TimeTransferString(utils.FormatDateTime, m.CreateTime),
-		})
-	}
-
-	// 目录下的标签
 	tags := make([]*speech_recognition.SpeechRecognitionTag, 0)
 	{
 		tagOb := new(speech_recognition.SpeechRecognitionTag)
@@ -377,30 +376,38 @@ func (this *SpeechRecognitionTagMenuController) Tree() {
 			menuTags[s.MenuId] = make([]*speech_recognition.SpeechRecognitionTagMenuNodeItem, 0)
 		}
 		menuTags[s.MenuId] = append(menuTags[s.MenuId], &speech_recognition.SpeechRecognitionTagMenuNodeItem{
+			UniqueCode: s.UniqueCode,
 			NodeType:   speech_recognition.SpeechRecognitionTagMenuNodeTypeTag,
 			TagId:      s.SpeechRecognitionTagId,
 			TagName:    s.TagName,
 			ParentId:   s.MenuId,
+			Level:      menuIdLevel[s.MenuId] + 1,
 			Sort:       s.Sort,
 			CreateTime: utils.TimeTransferString(utils.FormatDateTime, s.CreateTime),
 		})
 	}
 
-	for _, m := range menus {
+	for _, m := range topMenus {
+		child := make([]*speech_recognition.SpeechRecognitionTagMenuNodeItem, 0)
+		if menuTags[m.SpeechRecognitionTagMenuId] != nil {
+			child = append(child, menuTags[m.SpeechRecognitionTagMenuId]...)
+		}
+		if menuChildren[m.SpeechRecognitionTagMenuId] != nil {
+			child = append(child, menuChildren[m.SpeechRecognitionTagMenuId]...)
+		}
+		sort.Slice(child, func(i, j int) bool {
+			return child[i].Sort < child[j].Sort
+		})
 		t := &speech_recognition.SpeechRecognitionTagMenuNodeItem{
+			UniqueCode: m.UniqueCode,
 			NodeType:   speech_recognition.SpeechRecognitionTagMenuNodeTypeDefault,
 			MenuId:     m.SpeechRecognitionTagMenuId,
 			MenuName:   m.MenuName,
 			ParentId:   m.ParentId,
+			Level:      m.Level,
 			Sort:       m.Sort,
 			CreateTime: utils.TimeTransferString(utils.FormatDateTime, m.CreateTime),
-			Children:   make([]*speech_recognition.SpeechRecognitionTagMenuNodeItem, 0),
-		}
-		if menuTags[m.SpeechRecognitionTagMenuId] != nil {
-			t.Children = append(t.Children, menuTags[m.SpeechRecognitionTagMenuId]...)
-		}
-		if menuChildren[m.SpeechRecognitionTagMenuId] != nil {
-			t.Children = append(t.Children, menuChildren[m.SpeechRecognitionTagMenuId]...)
+			Children:   child,
 		}
 		resp = append(resp, t)
 	}
@@ -453,3 +460,33 @@ func (this *SpeechRecognitionTagMenuController) List() {
 	br.Success = true
 	br.Msg = "获取成功"
 }
+
+// TODO:Move
+// @Title 移动标签/目录
+// @Description 移动标签/目录
+// @Param	request	body speech_recognition.SpeechRecognitionTagRemoveReq true "type json string"
+// @Success 200 string "操作成功"
+// @router /tag/move [post]
+func (this *SpeechRecognitionTagMenuController) Move() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	// TODO:移动标签/目录
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}

+ 8 - 5
go.mod

@@ -3,6 +3,7 @@ module eta/eta_api
 go 1.21.7
 
 require (
+	baliance.com/gooxml v1.0.1
 	github.com/PuerkitoBio/goquery v1.9.1
 	github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.2
 	github.com/alibabacloud-go/alimt-20181012/v2 v2.2.0
@@ -19,11 +20,12 @@ require (
 	github.com/beevik/etree v1.3.0
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
 	github.com/go-ldap/ldap v3.0.3+incompatible
-	github.com/go-redis/redis/v8 v8.11.5
+	github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc
 	github.com/go-sql-driver/mysql v1.7.0
 	github.com/go-xorm/xorm v0.7.9
 	github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b
 	github.com/gorilla/websocket v1.5.1
+	github.com/jung-kurt/gofpdf v1.16.2
 	github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53
 	github.com/minio/minio-go/v7 v7.0.69
 	github.com/mojocn/base64Captcha v1.3.6
@@ -62,6 +64,7 @@ require (
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 	github.com/dustin/go-humanize v1.0.1 // indirect
 	github.com/fatih/structs v1.1.0 // indirect
+	github.com/fsnotify/fsnotify v1.6.0 // indirect
 	github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
 	github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac // indirect
@@ -95,8 +98,8 @@ require (
 	github.com/richardlehane/msoleps v1.0.3 // indirect
 	github.com/rs/xid v1.5.0 // indirect
 	github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
-	github.com/sirupsen/logrus v1.9.0 // indirect
-	github.com/spf13/cast v1.4.1 // indirect
+	github.com/sirupsen/logrus v1.9.3 // indirect
+	github.com/spf13/cast v1.5.0 // indirect
 	github.com/tidwall/gjson v1.14.1 // indirect
 	github.com/tidwall/match v1.1.1 // indirect
 	github.com/tidwall/pretty v1.2.0 // indirect
@@ -104,11 +107,11 @@ require (
 	github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 // indirect
 	github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 // indirect
 	golang.org/x/crypto v0.19.0 // indirect
-	golang.org/x/image v0.14.0 // indirect
+	golang.org/x/image v0.15.0 // indirect
 	golang.org/x/net v0.21.0 // indirect
 	golang.org/x/sys v0.17.0 // indirect
 	golang.org/x/text v0.14.0 // indirect
-	golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
+	golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
 	google.golang.org/protobuf v1.30.0 // indirect
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 	gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d // indirect

+ 26 - 8
go.sum

@@ -1,3 +1,5 @@
+baliance.com/gooxml v1.0.1 h1:fG5lmxmjEVFfbKQ2NuyCuU3hMuuOb5avh5a38SZNO1o=
+baliance.com/gooxml v1.0.1/go.mod h1:+gpUgmkAF4zCtwOFPNRLDAvpVRWoKs5EeQTSv/HYFnw=
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
@@ -101,6 +103,7 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
 github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
 github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw=
 github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
@@ -146,9 +149,12 @@ github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga
 github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
 github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
+github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
+github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
 github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
+github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 github.com/garyburd/redigo v1.6.3/go.mod h1:rTb6epsqigu3kYKBnaF028A7Tf/Aw5s0cqA47doKKqw=
 github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
 github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
@@ -159,8 +165,9 @@ github.com/go-ldap/ldap v3.0.3+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
-github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
 github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
+github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc h1:jZY+lpZB92nvBo2f31oPC/ivGll6NcsnEOORm8Fkr4M=
+github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc/go.mod h1:25mL1NKxbJhB63ihiK8MnNeTRd+xAizd6bOdydrTLUQ=
 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
@@ -258,7 +265,10 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
 github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
+github.com/jung-kurt/gofpdf v1.16.2 h1:jgbatWHfRlPYiK85qgevsZTHviWXKwB1TTiKdz5PtRc=
+github.com/jung-kurt/gofpdf v1.16.2/go.mod h1:1hl7y57EsiPAkLbOwzpzqgx1A30nQCk/YmFV8S2vmK0=
 github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53 h1:+8X3HMX8A2QhvNg3dImiQTCiVUt6BQXz1mW+/DrWI+k=
 github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53/go.mod h1:E61jD6q4yJ6Cu9uDGRAfiENM1G5TVZhOog0Y3+GgTpQ=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
@@ -331,14 +341,16 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
-github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
 github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
+github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
+github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
 github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
 github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
 github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
 github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
+github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -382,6 +394,7 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR
 github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
 github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
 github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
+github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
 github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
 github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
 github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 h1:DAYUYH5869yV94zvCES9F51oYtN5oGlwjxJJz7ZCnik=
@@ -396,13 +409,15 @@ github.com/silenceper/wechat/v2 v2.1.6 h1:2br2DxNzhksmvIBJ+PfMqjqsvoZmd/5BnMIfjK
 github.com/silenceper/wechat/v2 v2.1.6/go.mod h1:7Iu3EhQYVtDUJAj+ZVRy8yom75ga7aDWv8RurLkVm0s=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
 github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
 github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
+github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
 github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -484,9 +499,10 @@ golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE
 golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/image v0.13.0/go.mod h1:6mmbMOeV28HuMTgA6OSRkdXKYw/t5W9Uwn2Yv1r3Yxk=
-golang.org/x/image v0.14.0 h1:tNgSxAFe3jC4uYqvZdTr84SZoM1KfwdC9SKIFrLjFn4=
-golang.org/x/image v0.14.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
+golang.org/x/image v0.15.0 h1:kOELfmgrmJlw4Cdb7g/QGuB3CvDrXbqEIww/pNtNBm8=
+golang.org/x/image v0.15.0/go.mod h1:HUYqC05R2ZcZ3ejNQsIHQDQiwWM4JBqmm6MKANTp4LE=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -563,6 +579,7 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -592,8 +609,9 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
 golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 h1:NusfzzA6yGQ+ua51ck7E3omNUX/JuqbFSaRGqU8CcLI=
 golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=
+golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

+ 15 - 3
models/speech_recognition/speech_recognition.go

@@ -20,10 +20,10 @@ const (
 // SpeechRecognition 语音识别主表
 type SpeechRecognition struct {
 	SpeechRecognitionId int       `orm:"column(speech_recognition_id);pk"`
+	UniqueCode          string    `description:"唯一编码"`
 	FileName            string    `description:"文件名称"`
 	ResourceUrl         string    `description:"文件路径"`
 	MenuId              int       `description:"目录ID"`
-	MenuPath            string    `description:"所属目录位置,例:/一级目录ID/二级目录ID"`
 	SysUserId           int       `description:"创建人ID"`
 	SysUserName         string    `description:"创建人姓名"`
 	State               int       `description:"状态:1-待转换;2-转换完成;3-转换失败"`
@@ -39,6 +39,7 @@ type SpeechRecognition struct {
 
 var SpeechRecognitionCols = struct {
 	SpeechRecognitionId string
+	UniqueCode          string
 	FileName            string
 	ResourceUrl         string
 	MenuId              string
@@ -56,6 +57,7 @@ var SpeechRecognitionCols = struct {
 	ModifyTime          string
 }{
 	SpeechRecognitionId: "speech_recognition_id",
+	UniqueCode:          "unique_code",
 	FileName:            "file_name",
 	ResourceUrl:         "resource_url",
 	MenuId:              "menu_id",
@@ -181,10 +183,10 @@ func (m *SpeechRecognition) GetPageItemsByCondition(condition string, pars []int
 // SpeechRecognitionItem 语音识别信息
 type SpeechRecognitionItem struct {
 	SpeechRecognitionId int
+	UniqueCode          string `description:"唯一编码"`
 	FileName            string `description:"文件名称"`
 	ResourceUrl         string `description:"文件路径"`
 	MenuId              int    `description:"目录ID"`
-	MenuPath            string `description:"所属目录位置,例:/一级目录ID/二级目录ID"`
 	SysUserId           int    `description:"创建人ID"`
 	SysUserName         string `description:"创建人姓名"`
 	State               int    `description:"状态:1-待转换;2-转换完成;3-转换失败"`
@@ -201,13 +203,13 @@ func FormatSpeechRecognition2Item(origin *SpeechRecognition) (item *SpeechRecogn
 	}
 	item = new(SpeechRecognitionItem)
 	item.SpeechRecognitionId = origin.SpeechRecognitionId
+	item.UniqueCode = origin.UniqueCode
 	item.FileName = origin.FileName
 	item.ResourceUrl = origin.ResourceUrl
 	if origin.FileState == SpeechRecognitionFileRemoveFlag {
 		item.ResourceUrl = ""
 	}
 	item.MenuId = origin.MenuId
-	item.MenuPath = origin.MenuPath
 	item.SysUserId = origin.SysUserId
 	item.SysUserName = origin.SysUserName
 	item.State = origin.State
@@ -339,6 +341,7 @@ func CreateContentAndUpdateSpeechAndApiLog(contents []*SpeechRecognitionContent,
 // SpeechRecognitionDetailItem 语音识别详情信息
 type SpeechRecognitionDetailItem struct {
 	SpeechRecognitionId int
+	UniqueCode          string                          `description:"唯一编码"`
 	FileName            string                          `description:"文件名称"`
 	ResourceUrl         string                          `description:"文件路径"`
 	MenuId              int                             `description:"目录ID"`
@@ -360,6 +363,7 @@ func FormatSpeechRecognition2DetailItem(origin *SpeechRecognition, contents []*S
 	}
 	item = new(SpeechRecognitionDetailItem)
 	item.SpeechRecognitionId = origin.SpeechRecognitionId
+	item.UniqueCode = origin.UniqueCode
 	item.FileName = origin.FileName
 	item.ResourceUrl = origin.ResourceUrl
 	if origin.FileState == SpeechRecognitionFileRemoveFlag {
@@ -424,6 +428,7 @@ type SpeechRecognitionListReq struct {
 	EndTime      string `form:"EndTime" description:"结束时间"`
 	CreateUserId int    `form:"CreateUserId" description:"创建人ID"`
 	TagId        int    `form:"TagId" description:"标签ID"`
+	MenuId       int    `form:"FormId" description:"目录ID"`
 }
 
 // SpeechRecognitionListResp 语音识别列表响应体
@@ -431,3 +436,10 @@ type SpeechRecognitionListResp struct {
 	List   []*SpeechRecognitionDetailItem
 	Paging *paging.PagingItem `description:"分页数据"`
 }
+
+// SpeechRecognitionContentExportReq 导出内容
+type SpeechRecognitionContentExportReq struct {
+	SpeechRecognitionId int  `description:"语音识别ID"`
+	ExportType          int  `description:"导出类型:1-txt;2-doc;3-pdf"`
+	Timestamp           bool `description:"是否显示时间戳"`
+}

+ 8 - 0
models/speech_recognition/speech_recognition_content.go

@@ -197,3 +197,11 @@ func (m *SpeechRecognitionContent) BatchUpdateContents(contents []SpeechRecognit
 	}
 	return
 }
+
+// ClearContentBySpeechId 清除转写文件内容
+func (m *SpeechRecognitionContent) ClearContentBySpeechId(speechId int) (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ?`, m.TableName(), SpeechRecognitionContentCols.SpeechRecognitionId)
+	_, err = o.Raw(sql, speechId).Exec()
+	return
+}

+ 19 - 0
models/speech_recognition/speech_recognition_menu.go

@@ -16,28 +16,34 @@ const (
 // SpeechRecognitionMenu 语音识别-目录表
 type SpeechRecognitionMenu struct {
 	SpeechRecognitionMenuId int       `orm:"column(speech_recognition_menu_id);pk"`
+	UniqueCode              string    `description:"唯一编码"`
 	MenuName                string    `description:"目录名称"`
 	ParentId                int       `description:"父级ID"`
 	Level                   int       `description:"目录层级"`
 	Sort                    int       `description:"排序"`
+	RootId                  int       `description:"顶级ID"`
 	CreateTime              time.Time `description:"创建时间"`
 	ModifyTime              time.Time `description:"修改时间"`
 }
 
 var SpeechRecognitionMenuCols = struct {
 	SpeechRecognitionMenuId string
+	UniqueCode              string
 	MenuName                string
 	ParentId                string
 	Level                   string
 	Sort                    string
+	RootId                  string
 	CreateTime              string
 	ModifyTime              string
 }{
 	SpeechRecognitionMenuId: "speech_recognition_menu_id",
+	UniqueCode:              "unique_code",
 	MenuName:                "menu_name",
 	ParentId:                "parent_id",
 	Level:                   "level",
 	Sort:                    "sort",
+	RootId:                  "root_id",
 	CreateTime:              "create_time",
 	ModifyTime:              "modify_time",
 }
@@ -149,12 +155,14 @@ func (m *SpeechRecognitionMenu) GetPageItemsByCondition(condition string, pars [
 
 // SpeechRecognitionMenuNodeItem 语音识别目录节点
 type SpeechRecognitionMenuNodeItem struct {
+	UniqueCode            string                           `description:"唯一编码"`
 	NodeType              int                              `description:"节点类型:0-目录;1-语音识别"`
 	MenuId                int                              `description:"目录ID"`
 	MenuName              string                           `description:"目录名称"`
 	SpeechRecognitionId   int                              `description:"语音识别ID"`
 	SpeechRecognitionName string                           `description:"语音识别名称"`
 	ParentId              int                              `description:"父级ID"`
+	Level                 int                              `description:"目录层级"`
 	Sort                  int                              `description:"排序"`
 	CreateTime            string                           `description:"创建时间"`
 	Children              []*SpeechRecognitionMenuNodeItem `description:"子节点"`
@@ -176,3 +184,14 @@ type SpeechRecognitionMenuEditReq struct {
 type SpeechRecognitionMenuRemoveReq struct {
 	MenuId int `description:"目录ID"`
 }
+
+// SpeechRecognitionMenuMoveReq 移动目录请求体
+type SpeechRecognitionMenuMoveReq struct {
+	MenuId       int `description:"目录ID"`
+	ParentMenuId int `description:"父级目录ID"`
+	PrevMenuId   int `description:"上一个兄弟节点目录ID"`
+	NextMenuId   int `description:"下一个兄弟节点目录ID"`
+	SpeechId     int `description:"语音识别ID, 大于0则表示移动语音识别"`
+	PrevSpeechId int `description:"上一个语音识别ID"`
+	NextSpeechId int `description:"下一个语音识别ID"`
+}

+ 5 - 3
models/speech_recognition/speech_recognition_tag.go

@@ -11,9 +11,9 @@ import (
 // SpeechRecognitionTag 语音识别-标签表
 type SpeechRecognitionTag struct {
 	SpeechRecognitionTagId int       `orm:"column(speech_recognition_tag_id);pk"`
+	UniqueCode             string    `description:"唯一编码"`
 	TagName                string    `description:"标签名称"`
 	MenuId                 int       `description:"目录ID"`
-	MenuPath               string    `description:"所属目录位置,例:/一级目录ID/二级目录ID"`
 	Sort                   int       `description:"排序"`
 	CreateTime             time.Time `description:"创建时间"`
 	ModifyTime             time.Time `description:"修改时间"`
@@ -21,6 +21,7 @@ type SpeechRecognitionTag struct {
 
 var SpeechRecognitionTagCols = struct {
 	SpeechRecognitionTagId string
+	UniqueCode             string
 	TagName                string
 	MenuId                 string
 	MenuPath               string
@@ -29,6 +30,7 @@ var SpeechRecognitionTagCols = struct {
 	ModifyTime             string
 }{
 	SpeechRecognitionTagId: "speech_recognition_tag_id",
+	UniqueCode:             "unique_code",
 	TagName:                "tag_name",
 	MenuId:                 "menu_id",
 	MenuPath:               "menu_path",
@@ -144,10 +146,10 @@ func (m *SpeechRecognitionTag) GetPageItemsByCondition(condition string, pars []
 
 // SpeechRecognitionTagItem 语音识别标签
 type SpeechRecognitionTagItem struct {
+	UniqueCode string `description:"唯一编码"`
 	TagId      int    `description:"标签ID"`
 	TagName    string `description:"标签名称"`
 	MenuId     int    `description:"目录ID"`
-	MenuPath   string `description:"所属目录位置,例:/一级目录ID/二级目录ID"`
 	Sort       int    `description:"排序"`
 	CreateTime string `description:"创建时间"`
 }
@@ -157,10 +159,10 @@ func FormatSpeechRecognitionTag2Item(origin *SpeechRecognitionTag) (item *Speech
 		return
 	}
 	item = new(SpeechRecognitionTagItem)
+	item.UniqueCode = origin.UniqueCode
 	item.TagId = origin.SpeechRecognitionTagId
 	item.TagName = origin.TagName
 	item.MenuId = origin.MenuId
-	item.MenuPath = origin.MenuPath
 	item.Sort = origin.Sort
 	item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
 	return

+ 19 - 0
models/speech_recognition/speech_recognition_tag_menu.go

@@ -16,28 +16,34 @@ const (
 // SpeechRecognitionTagMenu 标签目录表
 type SpeechRecognitionTagMenu struct {
 	SpeechRecognitionTagMenuId int       `orm:"column(speech_recognition_tag_menu_id);pk"`
+	UniqueCode                 string    `description:"唯一编码"`
 	MenuName                   string    `description:"目录名称"`
 	ParentId                   int       `description:"父级ID"`
 	Level                      int       `description:"目录层级"`
 	Sort                       int       `description:"排序"`
+	RootId                     int       `description:"顶级ID"`
 	CreateTime                 time.Time `description:"创建时间"`
 	ModifyTime                 time.Time `description:"修改时间"`
 }
 
 var SpeechRecognitionTagMenuCols = struct {
 	SpeechRecognitionTagMenuId string
+	UniqueCode                 string
 	MenuName                   string
 	ParentId                   string
 	Level                      string
 	Sort                       string
+	RootId                     string
 	CreateTime                 string
 	ModifyTime                 string
 }{
 	SpeechRecognitionTagMenuId: "speech_recognition_tag_menu_id",
+	UniqueCode:                 "unique_code",
 	MenuName:                   "menu_name",
 	ParentId:                   "parent_id",
 	Level:                      "level",
 	Sort:                       "sort",
+	RootId:                     "root_id",
 	CreateTime:                 "create_time",
 	ModifyTime:                 "modify_time",
 }
@@ -160,12 +166,14 @@ type SpeechRecognitionTagMenuItem struct {
 
 // SpeechRecognitionTagMenuNodeItem 标签目录树节点
 type SpeechRecognitionTagMenuNodeItem struct {
+	UniqueCode string                              `description:"唯一编码"`
 	NodeType   int                                 `description:"节点类型:0-目录;1-标签"`
 	MenuId     int                                 `description:"目录ID"`
 	MenuName   string                              `description:"目录名称"`
 	TagId      int                                 `description:"标签ID"`
 	TagName    string                              `description:"标签名称"`
 	ParentId   int                                 `description:"父级ID"`
+	Level      int                                 `description:"目录层级"`
 	Sort       int                                 `description:"排序"`
 	CreateTime string                              `description:"创建时间"`
 	Children   []*SpeechRecognitionTagMenuNodeItem `description:"子节点"`
@@ -187,3 +195,14 @@ type SpeechRecognitionTagMenuEditReq struct {
 type SpeechRecognitionTagMenuRemoveReq struct {
 	MenuId int `description:"目录ID"`
 }
+
+// SpeechRecognitionTagMenuMoveReq 移动目录请求体
+type SpeechRecognitionTagMenuMoveReq struct {
+	MenuId       int `description:"目录ID"`
+	ParentMenuId int `description:"父级目录ID"`
+	PrevMenuId   int `description:"上一个兄弟节点目录ID"`
+	NextMenuId   int `description:"下一个兄弟节点目录ID"`
+	TagId        int `description:"标签ID, 大于0则表示移动标签"`
+	PrevTagId    int `description:"上一个标签ID"`
+	NextTagId    int `description:"下一个标签ID"`
+}

+ 24 - 15
routers/commentsRouter.go

@@ -6093,18 +6093,18 @@ func init() {
 
     beego.GlobalControllerRouter["eta/eta_api/controllers/speech_recognition:SpeechRecognitionController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/speech_recognition:SpeechRecognitionController"],
         beego.ControllerComments{
-            Method: "List",
-            Router: `/list`,
-            AllowHTTPMethods: []string{"get"},
+            Method: "Export",
+            Router: `/export`,
+            AllowHTTPMethods: []string{"post"},
             MethodParams: param.Make(),
             Filters: nil,
             Params: nil})
 
     beego.GlobalControllerRouter["eta/eta_api/controllers/speech_recognition:SpeechRecognitionController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/speech_recognition:SpeechRecognitionController"],
         beego.ControllerComments{
-            Method: "Move",
-            Router: `/move`,
-            AllowHTTPMethods: []string{"post"},
+            Method: "List",
+            Router: `/list`,
+            AllowHTTPMethods: []string{"get"},
             MethodParams: param.Make(),
             Filters: nil,
             Params: nil})
@@ -6190,6 +6190,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/speech_recognition:SpeechRecognitionMenuController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/speech_recognition:SpeechRecognitionMenuController"],
+        beego.ControllerComments{
+            Method: "Move",
+            Router: `/move`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/speech_recognition:SpeechRecognitionTagController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/speech_recognition:SpeechRecognitionTagController"],
         beego.ControllerComments{
             Method: "Add",
@@ -6217,15 +6226,6 @@ func init() {
             Filters: nil,
             Params: nil})
 
-    beego.GlobalControllerRouter["eta/eta_api/controllers/speech_recognition:SpeechRecognitionTagController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/speech_recognition:SpeechRecognitionTagController"],
-        beego.ControllerComments{
-            Method: "Move",
-            Router: `/tag/move`,
-            AllowHTTPMethods: []string{"post"},
-            MethodParams: param.Make(),
-            Filters: nil,
-            Params: nil})
-
     beego.GlobalControllerRouter["eta/eta_api/controllers/speech_recognition:SpeechRecognitionTagController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/speech_recognition:SpeechRecognitionTagController"],
         beego.ControllerComments{
             Method: "Remove",
@@ -6280,6 +6280,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/speech_recognition:SpeechRecognitionTagMenuController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/speech_recognition:SpeechRecognitionTagMenuController"],
+        beego.ControllerComments{
+            Method: "Move",
+            Router: `/tag/move`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/trade_analysis:TradeAnalysisController"],
         beego.ControllerComments{
             Method: "GetClassifyName",

+ 119 - 0
services/speech_recognition.go

@@ -1,16 +1,28 @@
 package services
 
 import (
+	"baliance.com/gooxml/document"
+	"baliance.com/gooxml/measurement"
+	"baliance.com/gooxml/schema/soo/wml"
+	"bufio"
 	"eta/eta_api/models"
 	"eta/eta_api/models/speech_recognition"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/utils"
 	"fmt"
+	"github.com/jung-kurt/gofpdf"
+	"os"
 	"strconv"
 	"sync"
 	"time"
 )
 
+const (
+	SpeechRecognitionExportTypeTxt  = 1
+	SpeechRecognitionExportTypeDocx = 2
+	SpeechRecognitionExportTypePdf  = 3
+)
+
 // GetSpeechRecognitionTagMenuTreeRecursive 递归获取标签目录树
 func GetSpeechRecognitionTagMenuTreeRecursive(list []*speech_recognition.SpeechRecognitionTagMenu, parentId int) []*speech_recognition.SpeechRecognitionTagMenuItem {
 	res := make([]*speech_recognition.SpeechRecognitionTagMenuItem, 0)
@@ -111,3 +123,110 @@ func BatchConvertSpeech(speeches []*speech_recognition.SpeechRecognition) {
 
 	return
 }
+
+// SpeechRecognitionContentExport 导出语音识别内容
+func SpeechRecognitionContentExport(exportType int, exportTimestamp bool, fileName string, contents []*speech_recognition.SpeechRecognitionContent) (result string, err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println(err)
+		}
+	}()
+	if len(contents) == 0 {
+		return
+	}
+	// 整理内容
+	exportText := ""
+	exportArr := make([]string, 0)
+	exportPdfArr := make([]string, 0)
+	for _, v := range contents {
+		if v.Content == "" {
+			continue
+		}
+		sec := ""
+		secPdf := ""
+		if exportTimestamp {
+			// 毫秒转时间格式
+			sec = fmt.Sprintf("%s\n%s\n\n", utils.MillisecondsToHHMMSS(v.StartMs), v.Content)
+			secPdf = fmt.Sprintf("%s %s", utils.MillisecondsToHHMMSS(v.StartMs), v.Content)
+		} else {
+			sec = fmt.Sprintf("%s\n\n", v.Content)
+			secPdf = v.Content
+		}
+		exportText += sec
+		exportArr = append(exportArr, sec)
+		exportPdfArr = append(exportPdfArr, secPdf)
+	}
+
+	// 导出doc
+	if exportType == SpeechRecognitionExportTypeDocx {
+		doc := document.New()
+		for _, v := range exportArr {
+			p := doc.AddParagraph()
+			prop := p.Properties()
+			prop.Spacing().SetLineSpacing(measurement.Distance(1.5*15*measurement.Point), wml.ST_LineSpacingRuleAuto)
+			prop.SetAlignment(wml.ST_JcLeft)
+			run := p.AddRun()
+			runProp := run.Properties()
+			runProp.SetSize(measurement.Distance(15 * measurement.Point))
+			runProp.SetFontFamily("宋体")
+			run.AddText(v)
+			run.AddBreak()
+		}
+
+		filePath := fmt.Sprintf("%s.docx", fileName)
+		if e := doc.SaveToFile(filePath); e != nil {
+			err = fmt.Errorf("生成docx失败, Err: %s", e.Error())
+			return
+		}
+		result = filePath
+		return
+	}
+
+	// 导出pdf
+	if exportType == SpeechRecognitionExportTypePdf {
+		pdf := gofpdf.New("P", "mm", "A4", "")
+		pdf.AddPage()
+		pdf.AddUTF8Font("SimHei", "", "static/SimHei.ttf") // 此处字体文件只能用本地的
+		pdf.SetFont("SimHei", "", 14)
+
+		// 计算可用内容区域宽度
+		w, _ := pdf.GetPageSize()
+		marginLeft := 10.0
+		marginRight := 10.0
+		availableWidth := w - marginLeft - marginRight
+		for _, v := range exportPdfArr {
+			pdf.MultiCell(availableWidth, 10, v, "", "L", false)
+			pdf.MultiCell(availableWidth, 5, "", "", "L", false) // 单纯的换行
+		}
+		filePath := fmt.Sprintf("%s.pdf", fileName)
+		if e := pdf.OutputFileAndClose(filePath); e != nil {
+			err = fmt.Errorf("生成pdf失败, Err: %s", e.Error())
+			return
+		}
+		result = filePath
+		return
+	}
+
+	// 默认导出txt
+	filePath := fmt.Sprintf("%s.txt", fileName)
+	file, e := os.Create(filePath)
+	if e != nil {
+		err = fmt.Errorf("生成txt文件失败, err: %s", e.Error())
+		return
+	}
+	defer file.Close()
+
+	// 写入txt
+	writer := bufio.NewWriter(file)
+	_, e = writer.WriteString(exportText)
+	if e != nil {
+		err = fmt.Errorf("写入txt文件失败, err: %s", e.Error())
+		return
+	}
+	if e = writer.Flush(); e != nil {
+		err = fmt.Errorf("刷新txt缓存失败, err: %s", e.Error())
+		return
+	}
+	result = filePath
+	return
+}

BIN
static/SimHei.ttf


+ 26 - 0
utils/common.go

@@ -2297,3 +2297,29 @@ func GetColorMap() map[int]string {
 
 	return colorMap
 }
+
+// MillisecondsToHHMMSS 毫秒转HH:MM:SS
+func MillisecondsToHHMMSS(ms int) string {
+	duration := time.Duration(ms) * time.Millisecond
+	hours := int(duration / (time.Hour))
+	minutes := int((duration % (time.Hour)) / time.Minute)
+	seconds := int((duration % time.Hour % time.Minute) / time.Second)
+
+	// 将整数小时、分钟和秒数转换为字符串,并添加冒号分隔符
+	hourStr := strconv.Itoa(hours)
+	minuteStr := strconv.Itoa(minutes)
+	secondStr := strconv.Itoa(seconds)
+
+	// 根据不足两位数的情况补零
+	if len(hourStr) == 1 {
+		hourStr = "0" + hourStr
+	}
+	if len(minuteStr) == 1 {
+		minuteStr = "0" + minuteStr
+	}
+	if len(secondStr) == 1 {
+		secondStr = "0" + secondStr
+	}
+
+	return hourStr + ":" + minuteStr + ":" + secondStr
+}