package services

import (
	"baliance.com/gooxml/document"
	"baliance.com/gooxml/measurement"
	"baliance.com/gooxml/schema/soo/wml"
	"bufio"
	"errors"
	"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

	SpeechMenuCheckRemoveTypePass    = 0 // 目录删除校验-可删除
	SpeechMenuCheckRemoveTypeRefused = 1 // 目录删除校验-不可删除
	SpeechMenuCheckRemoveTypeWarning = 2 // 目录删除校验-警告
)

// GetSpeechRecognitionMenuTreeRecursive 递归获取标签目录树
func GetSpeechRecognitionMenuTreeRecursive(list []*speech_recognition.SpeechRecognitionMenu, parentId int) []*speech_recognition.SpeechRecognitionMenuItem {
	res := make([]*speech_recognition.SpeechRecognitionMenuItem, 0)
	for _, v := range list {
		if v.ParentId == parentId {
			t := new(speech_recognition.SpeechRecognitionMenuItem)
			t.UniqueCode = v.UniqueCode
			t.MenuId = v.SpeechRecognitionMenuId
			t.MenuName = v.MenuName
			t.ParentId = v.ParentId
			t.Level = v.Level
			t.Sort = v.Sort
			t.CreateTime = utils.TimeTransferString(utils.FormatDateTime, v.CreateTime)
			t.Children = GetSpeechRecognitionMenuTreeRecursive(list, v.SpeechRecognitionMenuId)
			res = append(res, t)
		}
	}
	return res
}

// GetSpeechRecognitionTagMenuTreeRecursive 递归获取标签目录树
func GetSpeechRecognitionTagMenuTreeRecursive(list []*speech_recognition.SpeechRecognitionTagMenu, parentId int) []*speech_recognition.SpeechRecognitionTagMenuItem {
	res := make([]*speech_recognition.SpeechRecognitionTagMenuItem, 0)
	for _, v := range list {
		if v.ParentId == parentId {
			t := new(speech_recognition.SpeechRecognitionTagMenuItem)
			t.UniqueCode = v.UniqueCode
			t.MenuId = v.SpeechRecognitionTagMenuId
			t.MenuName = v.MenuName
			t.ParentId = v.ParentId
			t.Level = v.Level
			t.Sort = v.Sort
			t.CreateTime = utils.TimeTransferString(utils.FormatDateTime, v.CreateTime)
			t.Children = GetSpeechRecognitionTagMenuTreeRecursive(list, v.SpeechRecognitionTagMenuId)
			res = append(res, t)
		}
	}
	return res
}

// BatchConvertSpeech 批量转写语音
func BatchConvertSpeech(speeches []*speech_recognition.SpeechRecognition) {
	var err error
	defer func() {
		if err != nil {
			tips := fmt.Sprintf("批量转写语音失败, ErrMsg: %s", err.Error())
			utils.FileLog.Info(tips)
			go alarm_msg.SendAlarmMsg(tips, 1)
		}
	}()

	conf, e := models.GetBusinessConf()
	if e != nil {
		err = fmt.Errorf("获取配置失败, Err: %s", e.Error())
		return
	}
	if conf[models.BusinessConfTencentApiSecretId] == "" || conf[models.BusinessConfTencentApiSecretKey] == "" || conf[models.BusinessConfTencentApiRecTaskCallbackUrl] == "" {
		err = fmt.Errorf("API配置有误, SecretId: %s, SecretKey: %s, Callback: %s", conf[models.BusinessConfTencentApiSecretId], conf[models.BusinessConfTencentApiSecretKey], conf[models.BusinessConfTencentApiRecTaskCallbackUrl])
		return
	}

	// 限制接口请求频率
	apiLimit := make(chan struct{}, 20)
	var wg sync.WaitGroup

	for _, v := range speeches {
		wg.Add(1)

		go func(speech *speech_recognition.SpeechRecognition) {
			defer func() {
				wg.Done()
				<-apiLimit
			}()
			apiLimit <- struct{}{}

			// 发起请求
			var errMsg string
			var r TencentRecTaskReq
			r.FileUrl = speech.ResourceUrl
			r.SecretId = conf[models.BusinessConfTencentApiSecretId]
			r.SecretKey = conf[models.BusinessConfTencentApiSecretKey]
			r.CallbackUrl = conf[models.BusinessConfTencentApiRecTaskCallbackUrl]
			taskId, e := TencentCreateRecTask(r)
			if e != nil {
				errMsg = "创建语音识别任务失败"
				utils.FileLog.Info("TencentCreateRecTask创建语音识别任务失败, ErrMsg: %s", e.Error())
			}

			if errMsg == "" {
				apiLog := new(speech_recognition.SpeechRecognitionApiLog)
				apiLog.SpeechRecognitionId = speech.SpeechRecognitionId
				apiLog.RequestId = strconv.Itoa(taskId)
				apiLog.RequestCode = -1
				apiLog.CreateTime = time.Now().Local()
				apiLog.ModifyTime = time.Now().Local()
				if e = apiLog.Create(); e != nil {
					errMsg = "生成API请求失败"
					utils.FileLog.Info("CreateApiLog生成API请求记录失败, ErrMsg: %s", e.Error())
					return
				}
			}

			// 有报错则更新对应语音识别状态
			if errMsg == "" {
				return
			}
			speech.State = speech_recognition.SpeechRecognitionStateFail
			speech.ConvertRemark = errMsg
			speech.ModifyTime = time.Now().Local()
			updateCols := []string{speech_recognition.SpeechRecognitionCols.State, speech_recognition.SpeechRecognitionCols.ConvertRemark, speech_recognition.SpeechRecognitionCols.ModifyTime}
			if e = speech.Update(updateCols); e != nil {
				utils.FileLog.Info("UpdateSpeech更新语音识别状态失败, ErrMsg: %s", e.Error())
				return
			}
		}(v)
	}

	wg.Wait()

	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
}

// ------------------ 移动目录/语音识别(有空的话后面可以优化一下,以后这种菜单混合移动的情况不会少,CV起来怪麻烦的) ------------------ //

// MoveSpeechMenu 移动语音识别目录/识别文件
func MoveSpeechMenu(req speech_recognition.SpeechRecognitionMenuMoveReq) (err error, errMsg string) {
	menuId := req.MenuId
	parentMenuId := req.ParentMenuId
	prevMenuId := req.PrevMenuId
	nextMenuId := req.NextMenuId

	speechId := req.SpeechId
	prevSpeechId := req.PrevSpeechId
	nextSpeechId := req.NextSpeechId

	//首先确定移动的对象是目录还是语音识别
	//判断上一个节点是目录还是语音识别
	//判断下一个节点是目录还是语音识别
	//同时更新目录目录下的目录sort和语音识别sort
	//更新当前移动的目录或者语音识别sort

	menuOb := new(speech_recognition.SpeechRecognitionMenu)
	speechOb := new(speech_recognition.SpeechRecognition)

	var parentSpeechMenu *speech_recognition.SpeechRecognitionMenu
	if parentMenuId > 0 {
		t, e := menuOb.GetItemById(parentMenuId)
		if e != nil {
			errMsg = "移动失败"
			err = errors.New("获取上级目录信息失败,Err:" + err.Error())
			return
		}
		parentSpeechMenu = t
	}

	//如果有传入 上一个兄弟节点目录id
	var (
		speechMenu *speech_recognition.SpeechRecognitionMenu
		prevMenu   *speech_recognition.SpeechRecognitionMenu
		nextMenu   *speech_recognition.SpeechRecognitionMenu

		speechItem *speech_recognition.SpeechRecognition
		prevSpeech *speech_recognition.SpeechRecognition
		nextSpeech *speech_recognition.SpeechRecognition
		prevSort   int
		nextSort   int
	)

	// 移动对象为目录
	if speechId == 0 {
		speechMenu, err = menuOb.GetItemById(menuId)
		if err != nil {
			if err.Error() == utils.ErrNoRow() {
				errMsg = "当前目录不存在"
				err = errors.New("获取目录信息失败,Err:" + err.Error())
				return
			}
			errMsg = "移动失败"
			err = errors.New("获取目录信息失败,Err:" + err.Error())
			return
		}
		if parentMenuId > 0 && parentSpeechMenu.Level == 3 {
			errMsg = "最高只支持添加3级目录"
			err = errors.New(errMsg)
			return
		}

		// 如果是移动目录, 那么校验一下父级目录下是否有重名目录
		{
			cond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s <> ?`, speech_recognition.SpeechRecognitionMenuCols.MenuName, speech_recognition.SpeechRecognitionMenuCols.ParentId, speech_recognition.SpeechRecognitionMenuCols.SpeechRecognitionMenuId)
			pars := make([]interface{}, 0)
			pars = append(pars, speechMenu.MenuName, parentMenuId, menuId)
			exists, e := menuOb.GetItemByCondition(cond, pars, "")
			if e != nil && e.Error() != utils.ErrNoRow() {
				errMsg = "移动失败"
				err = fmt.Errorf("获取父级目录下的同名目录失败, Err: %s", e.Error())
				return
			}
			if exists != nil {
				errMsg = "移动失败,目录名称已存在"
				return
			}
		}
	} else {
		speechItem, err = speechOb.GetItemById(speechId)
		if err != nil {
			if err.Error() == utils.ErrNoRow() {
				errMsg = "当前语音识别不存在"
				err = errors.New("获取目录信息失败,Err:" + err.Error())
				return
			}
			errMsg = "移动失败"
			err = errors.New("获取目录信息失败,Err:" + err.Error())
			return
		}
		if parentMenuId == 0 {
			errMsg = "移动失败,语音识别必须挂在目录下"
			err = errors.New(errMsg)
			return
		}
	}

	if prevMenuId > 0 {
		prevMenu, err = menuOb.GetItemById(prevMenuId)
		if err != nil {
			errMsg = "移动失败"
			err = errors.New("获取上一个兄弟节点目录信息失败,Err:" + err.Error())
			return
		}
		prevSort = prevMenu.Sort
	} else if prevSpeechId > 0 {
		prevSpeech, err = speechOb.GetItemById(prevSpeechId)
		if err != nil {
			errMsg = "移动失败"
			err = errors.New("获取上一个兄弟节点信息失败,Err:" + err.Error())
			return
		}
		prevSort = prevSpeech.Sort
	}

	// 下一个兄弟节点
	if nextMenuId > 0 {
		nextMenu, err = menuOb.GetItemById(nextMenuId)
		if err != nil {
			errMsg = "移动失败"
			err = errors.New("获取下一个兄弟节点目录信息失败,Err:" + err.Error())
			return
		}
		nextSort = nextMenu.Sort
	} else if nextSpeechId > 0 {
		nextSpeech, err = speechOb.GetItemById(nextSpeechId)
		if err != nil {
			errMsg = "移动失败"
			err = errors.New("获取下一个兄弟节点目录信息失败,Err:" + err.Error())
			return
		}
		nextSort = nextSpeech.Sort
	}

	err, errMsg = moveSpeechMenu(parentSpeechMenu, speechMenu, prevMenu, nextMenu, speechItem, prevSpeech, nextSpeech, parentMenuId, prevSort, nextSort)
	return
}

func moveSpeechMenu(parentSpeechMenu, speechMenu, prevMenu, nextMenu *speech_recognition.SpeechRecognitionMenu, speechItem, prevSpeech, nextSpeech *speech_recognition.SpeechRecognition, parentMenuId int, prevSort, nextSort int) (err error, errMsg string) {
	updateCol := make([]string, 0)

	menuOb := new(speech_recognition.SpeechRecognitionMenu)
	speechOb := new(speech_recognition.SpeechRecognition)

	// 移动对象为目录, 判断目录是否存在
	if speechMenu != nil {
		oldParentId := speechMenu.ParentId
		oldLevel := speechMenu.Level
		var menuIds []int
		if oldParentId != parentMenuId {
			//更新子目录对应的level
			childList, e, m := GetChildSpeechMenuByMenuId(speechMenu.SpeechRecognitionMenuId)
			if e != nil {
				errMsg = "移动失败"
				err = errors.New("查询子目录失败,Err:" + e.Error() + m)
				return
			}

			if len(childList) > 0 {
				for _, v := range childList {
					if v.SpeechRecognitionMenuId == speechMenu.SpeechRecognitionMenuId {
						continue
					}
					menuIds = append(menuIds, v.SpeechRecognitionMenuId)
				}
			}
		}
		//判断上级id是否一致,如果不一致的话,那么需要移动该目录层级
		if speechMenu.ParentId != parentMenuId && parentMenuId != 0 {
			if speechMenu.Level != parentSpeechMenu.Level+1 { //禁止层级调整
				errMsg = "移动失败"
				err = errors.New("不支持目录层级变更")
				return
			}
			speechMenu.ParentId = parentSpeechMenu.SpeechRecognitionMenuId
			speechMenu.RootId = parentSpeechMenu.RootId
			speechMenu.Level = parentSpeechMenu.Level + 1
			speechMenu.ModifyTime = time.Now()
			updateCol = append(updateCol, "ParentId", "RootId", "Level", "ModifyTime")
		} else if speechMenu.ParentId != parentMenuId && parentMenuId == 0 {
			errMsg = "移动失败"
			err = errors.New("不支持目录层级变更")
			return
		}

		if prevSort > 0 {
			//如果是移动在两个兄弟节点之间
			if nextSort > 0 {
				//下一个兄弟节点
				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
				if prevSort == nextSort || prevSort == speechMenu.Sort {
					//变更兄弟节点的排序
					updateSortStr := `sort + 2`

					//变更目录
					if prevMenu != nil {
						_ = menuOb.UpdateSortByParentId(parentMenuId, prevMenu.SpeechRecognitionMenuId, prevMenu.Sort, updateSortStr)
					} else {
						_ = menuOb.UpdateSortByParentId(parentMenuId, 0, prevSort, updateSortStr)
					}

					//变更语音识别
					if prevSpeech != nil {
						//变更兄弟节点的排序
						_ = speechOb.UpdateSortByMenuId(parentMenuId, prevSort, prevSpeech.SpeechRecognitionId, updateSortStr)
					} else {
						_ = speechOb.UpdateSortByMenuId(parentMenuId, prevSort, 0, updateSortStr)
					}
				} else {
					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
					if nextSort-prevSort == 1 {
						//变更兄弟节点的排序
						updateSortStr := `sort + 1`

						//变更目录
						if prevMenu != nil {
							_ = menuOb.UpdateSortByParentId(parentMenuId, prevMenu.SpeechRecognitionMenuId, prevSort, updateSortStr)
						} else {
							_ = menuOb.UpdateSortByParentId(parentMenuId, 0, prevSort, updateSortStr)
						}

						//变更语音识别
						if prevSpeech != nil {
							//变更兄弟节点的排序
							_ = speechOb.UpdateSortByMenuId(parentMenuId, prevSort, prevSpeech.SpeechRecognitionId, updateSortStr)
						} else {
							_ = speechOb.UpdateSortByMenuId(parentMenuId, prevSort, 0, updateSortStr)
						}

					}
				}
			}

			speechMenu.Sort = prevSort + 1
			speechMenu.ModifyTime = time.Now()
			updateCol = append(updateCol, "Sort", "ModifyTime")
		} else if prevMenu == nil && nextMenu == nil && prevSpeech == nil && nextSpeech == nil && parentMenuId > 0 {
			//处理只拖动到目录里,默认放到目录底部的情况
			var maxSort int
			maxSort, err = GetSpeechMenuMaxSort(parentMenuId)
			if err != nil {
				errMsg = "移动失败"
				err = errors.New("查询组内排序信息失败,Err:" + err.Error())
				return
			}
			speechMenu.Sort = maxSort + 1 //那就是排在组内最后一位
			speechMenu.ModifyTime = time.Now()
			updateCol = append(updateCol, "Sort", "ModifyTime")
		} else {
			// 拖动到父级目录的第一位
			firstMenu, tmpErr := menuOb.GetFirstByParentId(parentMenuId)
			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
				errMsg = "移动失败"
				err = errors.New("获取获取当前父级目录下的排序第一条的目录信息失败,Err:" + tmpErr.Error())
				return
			}

			//如果该目录下存在其他目录,且第一个其他目录的排序等于0,那么需要调整排序
			if firstMenu != nil && firstMenu.Sort == 0 {
				updateSortStr := ` sort + 1 `
				_ = menuOb.UpdateSortByParentId(parentMenuId, firstMenu.SpeechRecognitionMenuId-1, 0, updateSortStr)
				//该目录下的所有语音识别也需要+1
				_ = speechOb.UpdateSortByMenuId(parentMenuId, 0, 0, updateSortStr)
			} else {
				//如果该目录下存在语音识别,且第一个语音识别的排序等于0,那么需要调整排序
				firstSpeech, tErr := speechOb.GetFirstByMenuId(parentMenuId)
				if tErr != nil && tErr.Error() != utils.ErrNoRow() {
					errMsg = "移动失败"
					err = errors.New("获取获取当前父级目录下的排序第一条的目录信息失败,Err:" + tErr.Error())
					return
				}

				//如果该目录下存在其他目录,且第一个其他目录的排序等于0,那么需要调整排序
				if firstSpeech != nil && firstSpeech.Sort == 0 {
					updateSortStr := ` sort + 1 `
					_ = speechOb.UpdateSortByMenuId(parentMenuId, 0, firstSpeech.SpeechRecognitionId-1, updateSortStr)
					_ = menuOb.UpdateSortByParentId(parentMenuId, 0, 0, updateSortStr)
				}
			}

			speechMenu.Sort = 0 //那就是排在第一位
			speechMenu.ModifyTime = time.Now()
			updateCol = append(updateCol, "Sort", "ModifyTime")
		}

		//更新
		if len(updateCol) > 0 {
			err = speechMenu.Update(updateCol)
			if err != nil {
				errMsg = "移动失败"
				err = errors.New("修改失败,Err:" + err.Error())
				return
			}
			//更新对应目录的root_id和层级
			if oldParentId != parentMenuId {
				if len(menuIds) > 0 {
					levelStep := speechMenu.Level - oldLevel
					err = menuOb.UpdateChildByParentMenuId(menuIds, speechMenu.RootId, levelStep)
					if err != nil {
						errMsg = "移动失败"
						err = errors.New("更新子目录失败,Err:" + err.Error())
						return
					}
				}
			}
		}
	} else {
		if speechItem == nil {
			errMsg = "当前语音识别不存在"
			err = errors.New(errMsg)
			return
		}
		//如果改变了目录,那么移动该语音识别数据
		if speechItem.MenuId != parentMenuId {
			speechItem.MenuId = parentMenuId
			speechItem.ModifyTime = time.Now()
			updateCol = append(updateCol, speech_recognition.SpeechRecognitionCols.MenuId, speech_recognition.SpeechRecognitionCols.ModifyTime)
		}
		if prevSort > 0 {
			//如果是移动在两个兄弟节点之间
			if nextSort > 0 {
				//下一个兄弟节点
				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
				if prevSort == nextSort || prevSort == speechItem.Sort {
					//变更兄弟节点的排序
					updateSortStr := `sort + 2`

					//变更目录
					if prevMenu != nil {
						_ = menuOb.UpdateSortByParentId(parentMenuId, prevMenu.SpeechRecognitionMenuId, prevMenu.Sort, updateSortStr)
					} else {
						_ = menuOb.UpdateSortByParentId(parentMenuId, 0, prevSort, updateSortStr)
					}

					//变更语音识别
					if prevSpeech != nil {
						//变更兄弟节点的排序
						_ = speechOb.UpdateSortByMenuId(parentMenuId, prevSort, prevSpeech.SpeechRecognitionId, updateSortStr)
					} else {
						_ = speechOb.UpdateSortByMenuId(parentMenuId, prevSort, 0, updateSortStr)
					}
				} else {
					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
					if nextSort-prevSort == 1 {
						//变更兄弟节点的排序
						updateSortStr := `sort + 1`
						//变更目录
						if prevMenu != nil {
							_ = menuOb.UpdateSortByParentId(parentMenuId, prevMenu.SpeechRecognitionMenuId, prevSort, updateSortStr)
						} else {
							_ = menuOb.UpdateSortByParentId(parentMenuId, 0, prevSort, updateSortStr)
						}

						//变更语音识别
						if prevSpeech != nil {
							//变更兄弟节点的排序
							_ = speechOb.UpdateSortByMenuId(parentMenuId, prevSort, prevSpeech.SpeechRecognitionId, updateSortStr)
						} else {
							_ = speechOb.UpdateSortByMenuId(parentMenuId, prevSort, 0, updateSortStr)
						}
					}
				}
			}

			speechItem.Sort = prevSort + 1
			speechItem.ModifyTime = time.Now()
			updateCol = append(updateCol, "Sort", "ModifyTime")
		} else if prevMenu == nil && nextMenu == nil && prevSpeech == nil && nextSpeech == nil && parentMenuId > 0 {
			//处理只拖动到目录里,默认放到目录底部的情况
			var maxSort int
			maxSort, err = GetSpeechMenuMaxSort(parentMenuId)
			if err != nil {
				errMsg = "移动失败"
				err = errors.New("查询组内排序信息失败,Err:" + err.Error())
				return
			}
			speechItem.Sort = maxSort + 1 //那就是排在组内最后一位
			speechItem.ModifyTime = time.Now()
			updateCol = append(updateCol, "Sort", "ModifyTime")
		} else {
			// 拖动到父级目录的第一位
			firstMenu, tmpErr := menuOb.GetFirstByParentId(parentMenuId)
			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
				errMsg = "移动失败"
				err = errors.New("获取获取当前父级目录下的排序第一条的目录信息失败,Err:" + tmpErr.Error())
				return
			}

			//如果该目录下存在其他目录,且第一个其他目录的排序等于0,那么需要调整排序
			if firstMenu != nil && firstMenu.Sort == 0 {
				updateSortStr := ` sort + 1 `
				_ = menuOb.UpdateSortByParentId(parentMenuId, firstMenu.SpeechRecognitionMenuId-1, 0, updateSortStr)
				//该目录下的所有语音识别也需要+1
				_ = speechOb.UpdateSortByMenuId(parentMenuId, 0, 0, updateSortStr)
			} else {
				//如果该目录下存在语音识别,且第一个语音识别的排序等于0,那么需要调整排序
				firstSpeech, tErr := speechOb.GetFirstByMenuId(parentMenuId)
				if tErr != nil && tErr.Error() != utils.ErrNoRow() {
					errMsg = "移动失败"
					err = errors.New("获取获取当前父级目录下的排序第一条的目录信息失败,Err:" + tErr.Error())
					return
				}

				//如果该目录下存在其他目录,且第一个其他目录的排序等于0,那么需要调整排序
				if firstSpeech != nil && firstSpeech.Sort == 0 {
					updateSortStr := ` sort + 1 `
					_ = speechOb.UpdateSortByMenuId(parentMenuId, 0, firstSpeech.SpeechRecognitionId-1, updateSortStr)
					_ = menuOb.UpdateSortByParentId(parentMenuId, 0, 0, updateSortStr)
				}
			}

			speechItem.Sort = 0 //那就是排在第一位
			speechItem.ModifyTime = time.Now()
			updateCol = append(updateCol, "Sort", "ModifyTime")
		}

		//更新
		if len(updateCol) > 0 {
			err = speechItem.Update(updateCol)
			if err != nil {
				errMsg = "移动失败"
				err = errors.New("修改失败,Err:" + err.Error())
				return
			}
		}
	}
	return
}

func GetChildSpeechMenuByMenuId(menuId int) (targetList []*speech_recognition.SpeechRecognitionMenu, err error, errMsg string) {
	menuOb := new(speech_recognition.SpeechRecognitionMenu)
	//判断是否是挂在顶级目录下
	speechMenu, err := menuOb.GetItemById(menuId)
	if err != nil {
		if err.Error() == utils.ErrNoRow() {
			errMsg = "当前目录不存在"
			err = errors.New(errMsg)
			return
		}
		errMsg = "获取失败"
		err = errors.New("获取目录信息失败,Err:" + err.Error())
		return
	}

	cond := fmt.Sprintf(" AND %s = ?", speech_recognition.SpeechRecognitionMenuCols.RootId)
	pars := make([]interface{}, 0)
	pars = append(pars, speechMenu.RootId)
	order := fmt.Sprintf("%s ASC, %s ASC, %s ASC", speech_recognition.SpeechRecognitionMenuCols.Level, speech_recognition.SpeechRecognitionMenuCols.Sort, speech_recognition.SpeechRecognitionMenuCols.SpeechRecognitionMenuId)
	tmpList, err := speechMenu.GetItemsByCondition(cond, pars, []string{}, order)
	if err != nil && err.Error() != utils.ErrNoRow() {
		errMsg = "获取失败"
		err = errors.New("获取数据失败,Err:" + err.Error())
		return
	}
	idMap := make(map[int]struct{})
	if len(tmpList) > 0 {
		for _, v := range tmpList {
			if v.SpeechRecognitionMenuId == speechMenu.SpeechRecognitionMenuId {
				idMap[v.SpeechRecognitionMenuId] = struct{}{}
			}
		}
		for _, v := range tmpList {
			if _, ok := idMap[v.ParentId]; ok {
				idMap[v.SpeechRecognitionMenuId] = struct{}{}
			}
		}
		for _, v := range tmpList {
			if _, ok := idMap[v.SpeechRecognitionMenuId]; ok {
				targetList = append(targetList, v)
			}
		}
	}
	return
}

// GetSpeechMenuMaxSort 获取同级下最大的排序
func GetSpeechMenuMaxSort(parentId int) (maxSort int, err error) {
	menuOb := new(speech_recognition.SpeechRecognitionMenu)
	speechOb := new(speech_recognition.SpeechRecognition)

	//获取该层级下最大的排序数
	menuMax, err := menuOb.GetMaxSortByParentId(parentId)
	if err != nil {
		return
	}
	maxSort = menuMax
	speechMax, err := speechOb.GetMaxSortByMenuId(parentId)
	if err != nil {
		return
	}
	if maxSort < speechMax {
		maxSort = speechMax
	}
	return
}

// ------------------ 移动目录/语音识别 ------------------ //

// ------------------ 移动标签目录/标签 ------------------ //

// MoveSpeechTagMenu 移动标签目录/标签
func MoveSpeechTagMenu(req speech_recognition.SpeechRecognitionTagMenuMoveReq) (err error, errMsg string) {
	menuId := req.MenuId
	parentMenuId := req.ParentMenuId
	prevMenuId := req.PrevMenuId
	nextMenuId := req.NextMenuId

	tagId := req.TagId
	prevTagId := req.PrevTagId
	nextTagId := req.NextTagId

	//首先确定移动的对象是目录还是标签
	//判断上一个节点是目录还是标签
	//判断下一个节点是目录还是标签
	//同时更新目录目录下的目录sort和标签sort
	//更新当前移动的目录或者标签sort

	menuOb := new(speech_recognition.SpeechRecognitionTagMenu)
	tagOb := new(speech_recognition.SpeechRecognitionTag)

	var parentTagMenu *speech_recognition.SpeechRecognitionTagMenu
	if parentMenuId > 0 {
		t, e := menuOb.GetItemById(parentMenuId)
		if e != nil {
			errMsg = "移动失败"
			err = errors.New("获取上级目录信息失败,Err:" + err.Error())
			return
		}
		parentTagMenu = t
	}

	//如果有传入 上一个兄弟节点目录id
	var (
		tagMenu  *speech_recognition.SpeechRecognitionTagMenu
		prevMenu *speech_recognition.SpeechRecognitionTagMenu
		nextMenu *speech_recognition.SpeechRecognitionTagMenu

		tagItem  *speech_recognition.SpeechRecognitionTag
		prevTag  *speech_recognition.SpeechRecognitionTag
		nextTag  *speech_recognition.SpeechRecognitionTag
		prevSort int
		nextSort int
	)

	// 移动对象为目录
	if tagId == 0 {
		tagMenu, err = menuOb.GetItemById(menuId)
		if err != nil {
			if err.Error() == utils.ErrNoRow() {
				errMsg = "当前目录不存在"
				err = errors.New("获取目录信息失败,Err:" + err.Error())
				return
			}
			errMsg = "移动失败"
			err = errors.New("获取目录信息失败,Err:" + err.Error())
			return
		}
		if parentMenuId > 0 && parentTagMenu.Level == 3 {
			errMsg = "最高只支持添加3级目录"
			err = errors.New(errMsg)
			return
		}

		// 如果是移动目录, 那么校验一下父级目录下是否有重名目录
		{
			cond := fmt.Sprintf(` AND %s = ? AND %s = ? AND %s <> ?`, speech_recognition.SpeechRecognitionTagMenuCols.MenuName, speech_recognition.SpeechRecognitionTagMenuCols.ParentId, speech_recognition.SpeechRecognitionTagMenuCols.SpeechRecognitionTagMenuId)
			pars := make([]interface{}, 0)
			pars = append(pars, tagMenu.MenuName, parentMenuId, menuId)
			exists, e := menuOb.GetItemByCondition(cond, pars, "")
			if e != nil && e.Error() != utils.ErrNoRow() {
				errMsg = "移动失败"
				err = fmt.Errorf("获取父级目录下的同名目录失败, Err: %s", e.Error())
				return
			}
			if exists != nil {
				errMsg = "移动失败,目录名称已存在"
				return
			}
		}
	} else {
		tagItem, err = tagOb.GetItemById(tagId)
		if err != nil {
			if err.Error() == utils.ErrNoRow() {
				errMsg = "当前标签不存在"
				err = errors.New("获取目录信息失败,Err:" + err.Error())
				return
			}
			errMsg = "移动失败"
			err = errors.New("获取目录信息失败,Err:" + err.Error())
			return
		}
		if parentMenuId == 0 {
			errMsg = "移动失败,标签必须挂在目录下"
			err = errors.New(errMsg)
			return
		}
	}

	if prevMenuId > 0 {
		prevMenu, err = menuOb.GetItemById(prevMenuId)
		if err != nil {
			errMsg = "移动失败"
			err = errors.New("获取上一个兄弟节点目录信息失败,Err:" + err.Error())
			return
		}
		prevSort = prevMenu.Sort
	} else if prevTagId > 0 {
		prevTag, err = tagOb.GetItemById(prevTagId)
		if err != nil {
			errMsg = "移动失败"
			err = errors.New("获取上一个兄弟节点信息失败,Err:" + err.Error())
			return
		}
		prevSort = prevTag.Sort
	}

	// 下一个兄弟节点
	if nextMenuId > 0 {
		nextMenu, err = menuOb.GetItemById(nextMenuId)
		if err != nil {
			errMsg = "移动失败"
			err = errors.New("获取下一个兄弟节点目录信息失败,Err:" + err.Error())
			return
		}
		nextSort = nextMenu.Sort
	} else if nextTagId > 0 {
		nextTag, err = tagOb.GetItemById(nextTagId)
		if err != nil {
			errMsg = "移动失败"
			err = errors.New("获取下一个兄弟节点目录信息失败,Err:" + err.Error())
			return
		}
		nextSort = nextTag.Sort
	}

	err, errMsg = moveSpeechTagMenu(parentTagMenu, tagMenu, prevMenu, nextMenu, tagItem, prevTag, nextTag, parentMenuId, prevSort, nextSort)
	return
}

func moveSpeechTagMenu(parentTagMenu, tagMenu, prevMenu, nextMenu *speech_recognition.SpeechRecognitionTagMenu, tagItem, prevTag, nextTag *speech_recognition.SpeechRecognitionTag, parentMenuId int, prevSort, nextSort int) (err error, errMsg string) {
	updateCol := make([]string, 0)

	menuOb := new(speech_recognition.SpeechRecognitionTagMenu)
	tagOb := new(speech_recognition.SpeechRecognitionTag)

	// 移动对象为目录, 判断目录是否存在
	if tagMenu != nil {
		oldParentId := tagMenu.ParentId
		oldLevel := tagMenu.Level
		var menuIds []int
		if oldParentId != parentMenuId {
			//更新子目录对应的level
			childList, e, m := GetChildSpeechTagMenuByMenuId(tagMenu.SpeechRecognitionTagMenuId)
			if e != nil {
				errMsg = "移动失败"
				err = errors.New("查询子目录失败,Err:" + e.Error() + m)
				return
			}

			if len(childList) > 0 {
				for _, v := range childList {
					if v.SpeechRecognitionTagMenuId == tagMenu.SpeechRecognitionTagMenuId {
						continue
					}
					menuIds = append(menuIds, v.SpeechRecognitionTagMenuId)
				}
			}
		}
		//判断上级id是否一致,如果不一致的话,那么需要移动该目录层级
		if tagMenu.ParentId != parentMenuId && parentMenuId != 0 {
			if tagMenu.Level != parentTagMenu.Level+1 { //禁止层级调整
				errMsg = "移动失败"
				err = errors.New("不支持目录层级变更")
				return
			}
			tagMenu.ParentId = parentTagMenu.SpeechRecognitionTagMenuId
			tagMenu.RootId = parentTagMenu.RootId
			tagMenu.Level = parentTagMenu.Level + 1
			tagMenu.ModifyTime = time.Now()
			updateCol = append(updateCol, "ParentId", "RootId", "Level", "ModifyTime")
		} else if tagMenu.ParentId != parentMenuId && parentMenuId == 0 {
			errMsg = "移动失败"
			err = errors.New("不支持目录层级变更")
			return
		}

		if prevSort > 0 {
			//如果是移动在两个兄弟节点之间
			if nextSort > 0 {
				//下一个兄弟节点
				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
				if prevSort == nextSort || prevSort == tagMenu.Sort {
					//变更兄弟节点的排序
					updateSortStr := `sort + 2`

					//变更目录
					if prevMenu != nil {
						_ = menuOb.UpdateSortByParentId(parentMenuId, prevMenu.SpeechRecognitionTagMenuId, prevMenu.Sort, updateSortStr)
					} else {
						_ = menuOb.UpdateSortByParentId(parentMenuId, 0, prevSort, updateSortStr)
					}

					//变更标签
					if prevTag != nil {
						//变更兄弟节点的排序
						_ = tagOb.UpdateSortByMenuId(parentMenuId, prevSort, prevTag.SpeechRecognitionTagId, updateSortStr)
					} else {
						_ = tagOb.UpdateSortByMenuId(parentMenuId, prevSort, 0, updateSortStr)
					}
				} else {
					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
					if nextSort-prevSort == 1 {
						//变更兄弟节点的排序
						updateSortStr := `sort + 1`

						//变更目录
						if prevMenu != nil {
							_ = menuOb.UpdateSortByParentId(parentMenuId, prevMenu.SpeechRecognitionTagMenuId, prevSort, updateSortStr)
						} else {
							_ = menuOb.UpdateSortByParentId(parentMenuId, 0, prevSort, updateSortStr)
						}

						//变更标签
						if prevTag != nil {
							//变更兄弟节点的排序
							_ = tagOb.UpdateSortByMenuId(parentMenuId, prevSort, prevTag.SpeechRecognitionTagId, updateSortStr)
						} else {
							_ = tagOb.UpdateSortByMenuId(parentMenuId, prevSort, 0, updateSortStr)
						}

					}
				}
			}

			tagMenu.Sort = prevSort + 1
			tagMenu.ModifyTime = time.Now()
			updateCol = append(updateCol, "Sort", "ModifyTime")
		} else if prevMenu == nil && nextMenu == nil && prevTag == nil && nextTag == nil && parentMenuId > 0 {
			//处理只拖动到目录里,默认放到目录底部的情况
			var maxSort int
			maxSort, err = GetSpeechTagMenuMaxSort(parentMenuId)
			if err != nil {
				errMsg = "移动失败"
				err = errors.New("查询组内排序信息失败,Err:" + err.Error())
				return
			}
			tagMenu.Sort = maxSort + 1 //那就是排在组内最后一位
			tagMenu.ModifyTime = time.Now()
			updateCol = append(updateCol, "Sort", "ModifyTime")
		} else {
			// 拖动到父级目录的第一位
			firstTagMenu, tmpErr := menuOb.GetFirstByParentId(parentMenuId)
			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
				errMsg = "移动失败"
				err = errors.New("获取获取当前父级目录下的排序第一条的目录信息失败,Err:" + tmpErr.Error())
				return
			}

			//如果该目录下存在其他目录,且第一个其他目录的排序等于0,那么需要调整排序
			if firstTagMenu != nil && firstTagMenu.Sort == 0 {
				updateSortStr := ` sort + 1 `
				_ = menuOb.UpdateSortByParentId(parentMenuId, firstTagMenu.SpeechRecognitionTagMenuId-1, 0, updateSortStr)
				//该目录下的所有标签也需要+1
				_ = tagOb.UpdateSortByMenuId(parentMenuId, 0, 0, updateSortStr)
			} else {
				//如果该目录下存在标签,且第一个标签的排序等于0,那么需要调整排序
				firstTag, tErr := tagOb.GetFirstByMenuId(parentMenuId)
				if tErr != nil && tErr.Error() != utils.ErrNoRow() {
					errMsg = "移动失败"
					err = errors.New("获取获取当前父级目录下的排序第一条的目录信息失败,Err:" + tErr.Error())
					return
				}

				//如果该目录下存在其他目录,且第一个其他目录的排序等于0,那么需要调整排序
				if firstTag != nil && firstTag.Sort == 0 {
					updateSortStr := ` sort + 1 `
					_ = tagOb.UpdateSortByMenuId(parentMenuId, 0, firstTag.SpeechRecognitionTagId-1, updateSortStr)
					_ = menuOb.UpdateSortByParentId(parentMenuId, 0, 0, updateSortStr)
				}
			}

			tagMenu.Sort = 0 //那就是排在第一位
			tagMenu.ModifyTime = time.Now()
			updateCol = append(updateCol, "Sort", "ModifyTime")
		}

		//更新
		if len(updateCol) > 0 {
			err = tagMenu.Update(updateCol)
			if err != nil {
				errMsg = "移动失败"
				err = errors.New("修改失败,Err:" + err.Error())
				return
			}
			//更新对应目录的root_id和层级
			if oldParentId != parentMenuId {
				if len(menuIds) > 0 {
					levelStep := tagMenu.Level - oldLevel
					err = menuOb.UpdateChildByParentMenuId(menuIds, tagMenu.RootId, levelStep)
					if err != nil {
						errMsg = "移动失败"
						err = errors.New("更新子目录失败,Err:" + err.Error())
						return
					}
				}
			}
		}
	} else {
		if tagItem == nil {
			errMsg = "当前标签不存在"
			err = errors.New(errMsg)
			return
		}
		//如果改变了目录,那么移动该标签数据
		if tagItem.MenuId != parentMenuId {
			tagItem.MenuId = parentMenuId
			tagItem.ModifyTime = time.Now()
			updateCol = append(updateCol, speech_recognition.SpeechRecognitionTagCols.MenuId, speech_recognition.SpeechRecognitionTagCols.ModifyTime)
		}
		if prevSort > 0 {
			//如果是移动在两个兄弟节点之间
			if nextSort > 0 {
				//下一个兄弟节点
				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
				if prevSort == nextSort || prevSort == tagItem.Sort {
					//变更兄弟节点的排序
					updateSortStr := `sort + 2`

					//变更目录
					if prevMenu != nil {
						_ = menuOb.UpdateSortByParentId(parentMenuId, prevMenu.SpeechRecognitionTagMenuId, prevMenu.Sort, updateSortStr)
					} else {
						_ = menuOb.UpdateSortByParentId(parentMenuId, 0, prevSort, updateSortStr)
					}

					//变更标签
					if prevTag != nil {
						//变更兄弟节点的排序
						_ = tagOb.UpdateSortByMenuId(parentMenuId, prevSort, prevTag.SpeechRecognitionTagId, updateSortStr)
					} else {
						_ = tagOb.UpdateSortByMenuId(parentMenuId, prevSort, 0, updateSortStr)
					}
				} else {
					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
					if nextSort-prevSort == 1 {
						//变更兄弟节点的排序
						updateSortStr := `sort + 1`
						//变更目录
						if prevMenu != nil {
							_ = menuOb.UpdateSortByParentId(parentMenuId, prevMenu.SpeechRecognitionTagMenuId, prevSort, updateSortStr)
						} else {
							_ = menuOb.UpdateSortByParentId(parentMenuId, 0, prevSort, updateSortStr)
						}

						//变更标签
						if prevTag != nil {
							//变更兄弟节点的排序
							_ = tagOb.UpdateSortByMenuId(parentMenuId, prevSort, prevTag.SpeechRecognitionTagId, updateSortStr)
						} else {
							_ = tagOb.UpdateSortByMenuId(parentMenuId, prevSort, 0, updateSortStr)
						}
					}
				}
			}

			tagItem.Sort = prevSort + 1
			tagItem.ModifyTime = time.Now()
			updateCol = append(updateCol, "Sort", "ModifyTime")
		} else if prevMenu == nil && nextMenu == nil && prevTag == nil && nextTag == nil && parentMenuId > 0 {
			//处理只拖动到目录里,默认放到目录底部的情况
			var maxSort int
			maxSort, err = GetSpeechTagMenuMaxSort(parentMenuId)
			if err != nil {
				errMsg = "移动失败"
				err = errors.New("查询组内排序信息失败,Err:" + err.Error())
				return
			}
			tagItem.Sort = maxSort + 1 //那就是排在组内最后一位
			tagItem.ModifyTime = time.Now()
			updateCol = append(updateCol, "Sort", "ModifyTime")
		} else {
			// 拖动到父级目录的第一位
			firstTagMenu, tmpErr := menuOb.GetFirstByParentId(parentMenuId)
			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
				errMsg = "移动失败"
				err = errors.New("获取获取当前父级目录下的排序第一条的目录信息失败,Err:" + tmpErr.Error())
				return
			}

			//如果该目录下存在其他目录,且第一个其他目录的排序等于0,那么需要调整排序
			if firstTagMenu != nil && firstTagMenu.Sort == 0 {
				updateSortStr := ` sort + 1 `
				_ = menuOb.UpdateSortByParentId(parentMenuId, firstTagMenu.SpeechRecognitionTagMenuId-1, 0, updateSortStr)
				//该目录下的所有标签也需要+1
				_ = tagOb.UpdateSortByMenuId(parentMenuId, 0, 0, updateSortStr)
			} else {
				//如果该目录下存在标签,且第一个标签的排序等于0,那么需要调整排序
				firstTag, tErr := tagOb.GetFirstByMenuId(parentMenuId)
				if tErr != nil && tErr.Error() != utils.ErrNoRow() {
					errMsg = "移动失败"
					err = errors.New("获取获取当前父级目录下的排序第一条的目录信息失败,Err:" + tErr.Error())
					return
				}

				//如果该目录下存在其他目录,且第一个其他目录的排序等于0,那么需要调整排序
				if firstTag != nil && firstTag.Sort == 0 {
					updateSortStr := ` sort + 1 `
					_ = tagOb.UpdateSortByMenuId(parentMenuId, 0, firstTag.SpeechRecognitionTagId-1, updateSortStr)
					_ = menuOb.UpdateSortByParentId(parentMenuId, 0, 0, updateSortStr)
				}
			}

			tagItem.Sort = 0 //那就是排在第一位
			tagItem.ModifyTime = time.Now()
			updateCol = append(updateCol, "Sort", "ModifyTime")
		}

		//更新
		if len(updateCol) > 0 {
			err = tagItem.Update(updateCol)
			if err != nil {
				errMsg = "移动失败"
				err = errors.New("修改失败,Err:" + err.Error())
				return
			}
		}
	}
	return
}

func GetChildSpeechTagMenuByMenuId(menuId int) (targetList []*speech_recognition.SpeechRecognitionTagMenu, err error, errMsg string) {
	menuOb := new(speech_recognition.SpeechRecognitionTagMenu)
	//判断是否是挂在顶级目录下
	tagMenu, err := menuOb.GetItemById(menuId)
	if err != nil {
		if err.Error() == utils.ErrNoRow() {
			errMsg = "当前目录不存在"
			err = errors.New(errMsg)
			return
		}
		errMsg = "获取失败"
		err = errors.New("获取目录信息失败,Err:" + err.Error())
		return
	}

	cond := fmt.Sprintf(" AND %s = ?", speech_recognition.SpeechRecognitionTagMenuCols.RootId)
	pars := make([]interface{}, 0)
	pars = append(pars, tagMenu.RootId)
	order := fmt.Sprintf("%s ASC, %s ASC, %s ASC", speech_recognition.SpeechRecognitionTagMenuCols.Level, speech_recognition.SpeechRecognitionTagMenuCols.Sort, speech_recognition.SpeechRecognitionTagMenuCols.SpeechRecognitionTagMenuId)
	tmpList, err := tagMenu.GetItemsByCondition(cond, pars, []string{}, order)
	if err != nil && err.Error() != utils.ErrNoRow() {
		errMsg = "获取失败"
		err = errors.New("获取数据失败,Err:" + err.Error())
		return
	}
	idMap := make(map[int]struct{})
	if len(tmpList) > 0 {
		for _, v := range tmpList {
			if v.SpeechRecognitionTagMenuId == tagMenu.SpeechRecognitionTagMenuId {
				idMap[v.SpeechRecognitionTagMenuId] = struct{}{}
			}
		}
		for _, v := range tmpList {
			if _, ok := idMap[v.ParentId]; ok {
				idMap[v.SpeechRecognitionTagMenuId] = struct{}{}
			}
		}
		for _, v := range tmpList {
			if _, ok := idMap[v.SpeechRecognitionTagMenuId]; ok {
				targetList = append(targetList, v)
			}
		}
	}
	return
}

// GetSpeechTagMenuMaxSort 获取同级下最大的排序
func GetSpeechTagMenuMaxSort(parentId int) (maxSort int, err error) {
	menuOb := new(speech_recognition.SpeechRecognitionTagMenu)
	tagOb := new(speech_recognition.SpeechRecognitionTag)

	//获取该层级下最大的排序数
	menuMax, err := menuOb.GetMaxSortByParentId(parentId)
	if err != nil {
		return
	}
	maxSort = menuMax
	speechMax, err := tagOb.GetMaxSortByMenuId(parentId)
	if err != nil {
		return
	}
	if maxSort < speechMax {
		maxSort = speechMax
	}
	return
}

// ------------------ 移动标签目录/标签 ------------------ //

// GetSpeechRecognitionMenuChildrenRecursive 递归获取目录的子目录集
func GetSpeechRecognitionMenuChildrenRecursive(list []*speech_recognition.SpeechRecognitionMenu, parentId int) []int {
	childIds := make([]int, 0)
	for _, v := range list {
		if v.ParentId == parentId {
			childIds = append(childIds, v.SpeechRecognitionMenuId)
			ids := GetSpeechRecognitionMenuChildrenRecursive(list, v.SpeechRecognitionMenuId)
			if len(ids) > 0 {
				childIds = append(childIds, ids...)
			}
		}
	}
	return childIds
}

// GetSpeechRecognitionTagMenuChildrenRecursive 递归获取标签目录的子目录集
func GetSpeechRecognitionTagMenuChildrenRecursive(list []*speech_recognition.SpeechRecognitionTagMenu, parentId int) []int {
	childIds := make([]int, 0)
	for _, v := range list {
		if v.ParentId == parentId {
			childIds = append(childIds, v.SpeechRecognitionTagMenuId)
			ids := GetSpeechRecognitionTagMenuChildrenRecursive(list, v.SpeechRecognitionTagMenuId)
			if len(ids) > 0 {
				childIds = append(childIds, ids...)
			}
		}
	}
	return childIds
}

// CheckSpeechRecognitionMenuRemove 校验是否可删除目录
func CheckSpeechRecognitionMenuRemove(menuId int) (result *speech_recognition.SpeechRecognitionMenuRemoveCheckResp, menuIds []int, err error) {
	menuOb := new(speech_recognition.SpeechRecognitionMenu)
	result = new(speech_recognition.SpeechRecognitionMenuRemoveCheckResp)

	// 递归获取目录的子目录集合
	menus, e := menuOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
	if e != nil {
		err = fmt.Errorf("获取语音识别目录列表失败, err: %s", e.Error())
		return
	}
	childIds := GetSpeechRecognitionMenuChildrenRecursive(menus, menuId)
	menuIds = make([]int, 0)
	menuIds = append(menuIds, menuId)
	if len(childIds) > 0 {
		menuIds = append(menuIds, childIds...)
	}

	// 校验目录下(包含子目录)是否有内容
	speechOb := new(speech_recognition.SpeechRecognition)
	cond := fmt.Sprintf(` AND %s IN (%s)`, speech_recognition.SpeechRecognitionCols.MenuId, utils.GetOrmInReplace(len(menuIds)))
	pars := make([]interface{}, 0)
	pars = append(pars, menuIds)
	count, e := speechOb.GetCountByCondition(cond, pars)
	if e != nil {
		err = fmt.Errorf("获取目录下的语音识别数失败, err: %s", e.Error())
		return
	}
	if count > 0 {
		result.CheckResult = SpeechMenuCheckRemoveTypeRefused
		result.Tips = "该分类关联转写文件,删除失败!"
		return
	}

	// 如果无语音识别, 但存在子目录, 则返回第二种提示
	if len(childIds) > 0 {
		result.CheckResult = SpeechMenuCheckRemoveTypeWarning
		result.Tips = "该分类包含内容为空的子分类"
		return
	}

	// 通过校验
	result.Tips = "校验通过,可以删除"
	return
}

// CheckSpeechRecognitionTagMenuRemove 校验是否可删除标签目录
func CheckSpeechRecognitionTagMenuRemove(menuId int) (result *speech_recognition.SpeechRecognitionMenuRemoveCheckResp, menuIds []int, err error) {
	menuOb := new(speech_recognition.SpeechRecognitionTagMenu)
	result = new(speech_recognition.SpeechRecognitionMenuRemoveCheckResp)

	// 递归获取目录的子目录集合
	menus, e := menuOb.GetItemsByCondition(``, make([]interface{}, 0), []string{}, "")
	if e != nil {
		err = fmt.Errorf("获取标签目录列表失败, err: %s", e.Error())
		return
	}
	childIds := GetSpeechRecognitionTagMenuChildrenRecursive(menus, menuId)
	menuIds = make([]int, 0)
	menuIds = append(menuIds, menuId)
	if len(childIds) > 0 {
		menuIds = append(menuIds, childIds...)
	}

	// 校验目录下(包含子目录)是否有内容
	tagOb := new(speech_recognition.SpeechRecognitionTag)
	cond := fmt.Sprintf(` AND %s IN (%s)`, speech_recognition.SpeechRecognitionTagCols.MenuId, utils.GetOrmInReplace(len(menuIds)))
	pars := make([]interface{}, 0)
	pars = append(pars, menuIds)
	count, e := tagOb.GetCountByCondition(cond, pars)
	if e != nil {
		err = fmt.Errorf("获取目录下的标签数失败, err: %s", e.Error())
		return
	}
	if count > 0 {
		result.CheckResult = SpeechMenuCheckRemoveTypeRefused
		result.Tips = "该分类关联标签,删除失败!"
		return
	}

	// 如果无内容, 但存在子目录, 则返回第二种提示
	if len(childIds) > 0 {
		result.CheckResult = SpeechMenuCheckRemoveTypeWarning
		result.Tips = "该分类包含内容为空的子分类"
		return
	}

	// 通过校验
	result.Tips = "校验通过,可以删除"
	return
}

// GetSpeechRecognitionMenuPathRecursive 根据子目录递归获取目录树
func GetSpeechRecognitionMenuPathRecursive(list []*speech_recognition.SpeechRecognitionMenu, menuId int) []*speech_recognition.SpeechRecognitionMenuItem {
	res := make([]*speech_recognition.SpeechRecognitionMenuItem, 0)
	for _, v := range list {
		if v.SpeechRecognitionMenuId == menuId {
			t := new(speech_recognition.SpeechRecognitionMenuItem)
			t.UniqueCode = v.UniqueCode
			t.MenuId = v.SpeechRecognitionMenuId
			t.MenuName = v.MenuName
			t.ParentId = v.ParentId
			t.Level = v.Level
			t.Sort = v.Sort
			t.CreateTime = utils.TimeTransferString(utils.FormatDateTime, v.CreateTime)
			if v.ParentId > 0 {
				res = GetSpeechRecognitionMenuPathRecursive(list, v.ParentId)
			}
			res = append(res, t)
		}
	}
	return res
}