speech_recognition.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. package speech_recognition
  2. import (
  3. "eta_gn/eta_api/global"
  4. "eta_gn/eta_api/utils"
  5. "fmt"
  6. "github.com/rdlucklib/rdluck_tools/paging"
  7. "strings"
  8. "time"
  9. )
  10. const (
  11. SpeechRecognitionFileRemoveFlag = 1 // 文件删除标记
  12. SpeechRecognitionStateWait = 1
  13. SpeechRecognitionStateSuccess = 2
  14. SpeechRecognitionStateFail = 3
  15. )
  16. type SpeechRecognition struct {
  17. SpeechRecognitionId int `gorm:"primaryKey;column:speech_recognition_id;type:int(10) unsigned;not null"` // 语音识别Id
  18. UniqueCode string `gorm:"column:unique_code;type:varchar(64);not null;default:''"` // 唯一编码
  19. FileName string `gorm:"column:file_name;type:varchar(128);not null;default:''"` // 文件名称
  20. ResourceUrl string `gorm:"column:resource_url;type:varchar(255);not null;default:''"` // 文件路径
  21. MenuId int `gorm:"index:idx_menu_id;column:menu_id;type:int(10) unsigned;not null;default:0"` // 目录Id
  22. SysUserId int `gorm:"column:sys_user_id;type:int(10) unsigned;not null;default:0"` // 创建人Id
  23. SysUserName string `gorm:"column:sys_user_name;type:varchar(128);not null;default:''"` // 创建人姓名
  24. State int `gorm:"column:state;type:tinyint(4) unsigned;not null;default:0"` // 状态:1-待转换;2-转换完成;3-转换失败
  25. Abstract string `gorm:"column:abstract;type:text"` // 摘要,取前几段内容
  26. Sort int `gorm:"column:sort;type:int(10) unsigned;not null;default:0"` // 目录下的排序
  27. FileState int `gorm:"column:file_state;type:tinyint(4) unsigned;not null;default:0"` // 文件删除状态:0-正常;1-删除(该字段作为软删标识)
  28. FileSecond int `gorm:"column:file_second;type:int(10) unsigned;not null;default:0"` // 文件时长(秒)
  29. FileSize int `gorm:"column:file_size;type:int(10) unsigned;not null;default:0"` // 文件大小(byte)
  30. ConvertRemark string `gorm:"column:convert_remark;type:varchar(255);not null;default:''"` // 转写备注-失败原因
  31. CreateTime time.Time `gorm:"column:create_time;type:datetime"` // 创建时间
  32. ModifyTime time.Time `gorm:"column:modify_time;type:datetime"` // 更新时间
  33. }
  34. var SpeechRecognitionCols = struct {
  35. SpeechRecognitionId string
  36. UniqueCode string
  37. FileName string
  38. ResourceUrl string
  39. MenuId string
  40. MenuPath string
  41. SysUserId string
  42. SysUserName string
  43. State string
  44. Abstract string
  45. Sort string
  46. FileState string
  47. FileSecond string
  48. FileSize string
  49. ConvertRemark string
  50. CreateTime string
  51. ModifyTime string
  52. }{
  53. SpeechRecognitionId: "speech_recognition_id",
  54. UniqueCode: "unique_code",
  55. FileName: "file_name",
  56. ResourceUrl: "resource_url",
  57. MenuId: "menu_id",
  58. MenuPath: "menu_path",
  59. SysUserId: "sys_user_id",
  60. SysUserName: "sys_user_name",
  61. State: "state",
  62. Abstract: "abstract",
  63. Sort: "sort",
  64. FileState: "file_state",
  65. FileSecond: "file_second",
  66. FileSize: "file_size",
  67. ConvertRemark: "convert_remark",
  68. CreateTime: "create_time",
  69. ModifyTime: "modify_time",
  70. }
  71. func (m *SpeechRecognition) TableName() string {
  72. return "speech_recognition"
  73. }
  74. func (m *SpeechRecognition) PrimaryId() string {
  75. return SpeechRecognitionCols.SpeechRecognitionId
  76. }
  77. func (m *SpeechRecognition) Create() (err error) {
  78. err = global.DEFAULT_DmSQL.Create(m).Error
  79. return
  80. }
  81. func (m *SpeechRecognition) CreateMulti(items []*SpeechRecognition) (err error) {
  82. if len(items) == 0 {
  83. return
  84. }
  85. err = global.DEFAULT_DmSQL.CreateInBatches(items, utils.MultiAddNum).Error
  86. return
  87. }
  88. func (m *SpeechRecognition) Update(cols []string) (err error) {
  89. err = global.DEFAULT_DmSQL.Select(cols).Updates(m).Error
  90. return
  91. }
  92. func (m *SpeechRecognition) Del() (err error) {
  93. sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
  94. err = global.DEFAULT_DmSQL.Exec(sql, m.SpeechRecognitionId).Error
  95. return
  96. }
  97. func (m *SpeechRecognition) MultiDel(menuIds []int) (err error) {
  98. if len(menuIds) == 0 {
  99. return
  100. }
  101. sql := fmt.Sprintf(`DELETE FROM %s WHERE %s IN (%s)`, m.TableName(), m.PrimaryId(), utils.GetOrmInReplace(len(menuIds)))
  102. err = global.DEFAULT_DmSQL.Exec(sql, menuIds).Error
  103. return
  104. }
  105. func (m *SpeechRecognition) GetItemById(id int) (item *SpeechRecognition, err error) {
  106. sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
  107. err = global.DEFAULT_DmSQL.Raw(sql, id).First(&item).Error
  108. return
  109. }
  110. func (m *SpeechRecognition) GetItemByCondition(condition string, pars []interface{}, orderRule string) (item *SpeechRecognition, err error) {
  111. order := ``
  112. if orderRule != "" {
  113. order = ` ORDER BY ` + orderRule
  114. }
  115. sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s %s LIMIT 1`, m.TableName(), condition, order)
  116. err = global.DEFAULT_DmSQL.Raw(sql, pars...).First(&item).Error
  117. return
  118. }
  119. func (m *SpeechRecognition) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
  120. sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
  121. err = global.DEFAULT_DmSQL.Raw(sql, pars...).Scan(&count).Error
  122. return
  123. }
  124. func (m *SpeechRecognition) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*SpeechRecognition, err error) {
  125. fields := strings.Join(fieldArr, ",")
  126. if len(fieldArr) == 0 {
  127. fields = `*`
  128. }
  129. order := `ORDER BY create_time DESC`
  130. if orderRule != "" {
  131. order = ` ORDER BY ` + orderRule
  132. }
  133. sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
  134. err = global.DEFAULT_DmSQL.Raw(sql, pars...).Find(&items).Error
  135. return
  136. }
  137. func (m *SpeechRecognition) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*SpeechRecognition, err error) {
  138. fields := strings.Join(fieldArr, ",")
  139. if len(fieldArr) == 0 {
  140. fields = `*`
  141. }
  142. order := `ORDER BY create_time DESC`
  143. if orderRule != "" {
  144. order = ` ORDER BY ` + orderRule
  145. }
  146. sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
  147. pars = append(pars, startSize)
  148. pars = append(pars, pageSize)
  149. err = global.DEFAULT_DmSQL.Raw(sql, pars...).Find(&items).Error
  150. return
  151. }
  152. type SpeechRecognitionItem struct {
  153. SpeechRecognitionId int
  154. UniqueCode string `description:"唯一编码"`
  155. FileName string `description:"文件名称"`
  156. ResourceUrl string `description:"文件路径"`
  157. MenuId int `description:"目录ID"`
  158. SysUserId int `description:"创建人ID"`
  159. SysUserName string `description:"创建人姓名"`
  160. State int `description:"状态:1-待转换;2-转换完成;3-转换失败"`
  161. Abstract string `description:"摘要,取前几段内容"`
  162. Sort int `description:"目录下的排序"`
  163. ConvertRemark string `description:"转写备注-失败原因"`
  164. CreateTime string `description:"创建时间"`
  165. ModifyTime string `description:"修改时间"`
  166. }
  167. func FormatSpeechRecognition2Item(origin *SpeechRecognition) (item *SpeechRecognitionItem) {
  168. if origin == nil {
  169. return
  170. }
  171. item = new(SpeechRecognitionItem)
  172. item.SpeechRecognitionId = origin.SpeechRecognitionId
  173. item.UniqueCode = origin.UniqueCode
  174. item.FileName = origin.FileName
  175. item.ResourceUrl = origin.ResourceUrl
  176. if origin.FileState == SpeechRecognitionFileRemoveFlag {
  177. item.ResourceUrl = ""
  178. }
  179. item.MenuId = origin.MenuId
  180. item.SysUserId = origin.SysUserId
  181. item.SysUserName = origin.SysUserName
  182. item.State = origin.State
  183. item.Abstract = origin.Abstract
  184. item.Sort = origin.Sort
  185. item.ConvertRemark = origin.ConvertRemark
  186. item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
  187. item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
  188. return
  189. }
  190. type SpeechRecognitionSaveReq struct {
  191. SpeechRecognitionId int `description:"语音识别ID"`
  192. FileName string `description:"文件名称"`
  193. TagIds []int `description:"标签IDs"`
  194. Contents []SpeechRecognitionSaveContentReq `description:"保存内容"`
  195. }
  196. type SpeechRecognitionSaveContentReq struct {
  197. SpeechRecognitionContentId int `description:"语音识别内容ID"`
  198. Content string `description:"段落内容"`
  199. }
  200. type SpeechRecognitionRenameReq struct {
  201. SpeechRecognitionId int `description:"语音识别ID"`
  202. FileName string `description:"文件名称"`
  203. }
  204. type SpeechRecognitionRemoveReq struct {
  205. SpeechRecognitionId int `description:"语音识别ID"`
  206. }
  207. type SpeechRecognitionRemoveFileReq struct {
  208. SpeechRecognitionId int `description:"语音识别ID"`
  209. }
  210. type SpeechRecognitionSaveTagReq struct {
  211. SpeechRecognitionId int `description:"语音识别ID"`
  212. TagIds []int `description:"标签IDs"`
  213. }
  214. type SpeechRecognitionConvertReq struct {
  215. MenuId int `description:"目录ID"`
  216. Files []SpeechRecognitionConvertFiles `description:"转写文件"`
  217. }
  218. type SpeechRecognitionConvertFiles struct {
  219. FileName string `description:"文件名称"`
  220. ResourceUrl string `description:"文件地址"`
  221. FileSecond int `description:"文件时长(秒)"`
  222. FileSize int `description:"文件大小(byte)"`
  223. }
  224. func UpdateSpeechAndApiLog(speechItem *SpeechRecognition, speechCols []string, apiLogItem *SpeechRecognitionApiLog, logCols []string) (err error) {
  225. if speechItem == nil || apiLogItem == nil {
  226. err = fmt.Errorf("speechItem nil or apiLogItem nil")
  227. return
  228. }
  229. tx := global.DEFAULT_DmSQL.Begin()
  230. defer func() {
  231. if err != nil {
  232. _ = tx.Rollback()
  233. return
  234. }
  235. _ = tx.Commit()
  236. }()
  237. e := tx.Select(speechCols).Updates(speechItem).Error
  238. if e != nil {
  239. err = fmt.Errorf("update speech err: %v", e)
  240. return
  241. }
  242. e = tx.Select(logCols).Updates(apiLogItem).Error
  243. if e != nil {
  244. err = fmt.Errorf("update api err: %v", e)
  245. return
  246. }
  247. return
  248. }
  249. func CreateContentAndUpdateSpeechAndApiLog(contents []*SpeechRecognitionContent, speechItem *SpeechRecognition, speechCols []string, apiLogItem *SpeechRecognitionApiLog, logCols []string) (err error) {
  250. if speechItem == nil || apiLogItem == nil {
  251. err = fmt.Errorf("speechItem nil or apiLogItem nil")
  252. return
  253. }
  254. tx := global.DEFAULT_DmSQL.Begin()
  255. defer func() {
  256. if err != nil {
  257. _ = tx.Rollback()
  258. return
  259. }
  260. _ = tx.Commit()
  261. }()
  262. e := tx.Select(speechCols).Updates(speechItem).Error
  263. if e != nil {
  264. err = fmt.Errorf("update speech err: %v", e)
  265. return
  266. }
  267. e = tx.Select(logCols).Updates(apiLogItem).Error
  268. if e != nil {
  269. err = fmt.Errorf("update api err: %v", e)
  270. return
  271. }
  272. if len(contents) > 0 {
  273. e = tx.CreateInBatches(contents, utils.MultiAddNum).Error
  274. if e != nil {
  275. err = fmt.Errorf("insert multi contents err: %s", e.Error())
  276. return
  277. }
  278. }
  279. return
  280. }
  281. type SpeechRecognitionDetailItem struct {
  282. SpeechRecognitionId int
  283. UniqueCode string `description:"唯一编码"`
  284. FileName string `description:"文件名称"`
  285. FileExt string `description:"文件后缀名"`
  286. ResourceUrl string `description:"文件地址"`
  287. MenuId int `description:"目录ID"`
  288. MenuPath []*SpeechRecognitionMenuItem `description:"目录全路径, 多级并列为一个数组"`
  289. SysUserId int `description:"创建人ID"`
  290. SysUserName string `description:"创建人姓名"`
  291. Abstract string `description:"摘要,取前几段内容"`
  292. Sort int `description:"目录下的排序"`
  293. FileSecond string `description:"文件时长(HH:MM:SS/MM:SS)"`
  294. FileSize string `description:"文件大小(MB)"`
  295. CreateTime string `description:"创建时间"`
  296. ModifyTime string `description:"修改时间"`
  297. Contents []*SpeechRecognitionContentItem `description:"语音识别内容"`
  298. Tags []*SpeechRecognitionDetailTag `description:"标签"`
  299. }
  300. func FormatSpeechRecognition2DetailItem(origin *SpeechRecognition, contents []*SpeechRecognitionContentItem, tags []*SpeechRecognitionDetailTag, menuPath []*SpeechRecognitionMenuItem) (item *SpeechRecognitionDetailItem) {
  301. if origin == nil {
  302. return
  303. }
  304. item = new(SpeechRecognitionDetailItem)
  305. item.SpeechRecognitionId = origin.SpeechRecognitionId
  306. item.UniqueCode = origin.UniqueCode
  307. item.FileName = origin.FileName
  308. item.ResourceUrl = origin.ResourceUrl
  309. if item.ResourceUrl != "" {
  310. pointArr := strings.Split(item.ResourceUrl, ".")
  311. if len(pointArr) > 0 {
  312. item.FileExt = pointArr[len(pointArr)-1]
  313. }
  314. }
  315. item.ResourceUrl = origin.ResourceUrl
  316. if origin.FileState == SpeechRecognitionFileRemoveFlag {
  317. item.ResourceUrl = ""
  318. }
  319. item.MenuId = origin.MenuId
  320. item.MenuPath = menuPath
  321. item.SysUserId = origin.SysUserId
  322. item.SysUserName = origin.SysUserName
  323. item.Abstract = origin.Abstract
  324. item.Sort = origin.Sort
  325. item.FileSecond = utils.SecondsToHHMMSS(origin.FileSecond)
  326. item.FileSize = fmt.Sprintf("%.2fM", utils.ByteToMB(origin.FileSize))
  327. item.CreateTime = utils.TimeTransferString(utils.FormatDateTime, origin.CreateTime)
  328. item.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, origin.ModifyTime)
  329. item.Contents = contents
  330. item.Tags = tags
  331. return
  332. }
  333. type SpeechRecognitionDetailTag struct {
  334. TagId int `description:"标签ID" gorm:"tag_id"`
  335. TagName string `description:"标签名称" gorm:"tag_name"`
  336. }
  337. func GetSpeechRecognitionTagBySpeechId(speechId int) (items []*SpeechRecognitionDetailTag, err error) {
  338. sql := `SELECT a.speech_recognition_tag_id AS tag_id, a.tag_name FROM speech_recognition_tag AS a
  339. JOIN speech_recognition_tag_mapping AS b ON a.speech_recognition_tag_id = b.tag_id
  340. WHERE b.speech_recognition_id = ?`
  341. err = global.DEFAULT_DmSQL.Raw(sql, speechId).Find(&items).Error
  342. return
  343. }
  344. type SpeechRecognitionMappingTags struct {
  345. SpeechRecognitionId int `description:"语音识别ID" gorm:"speech_recognition_id"`
  346. TagId int `description:"标签ID" gorm:"tag_id"`
  347. TagName string `description:"标签名称" gorm:"tag_name"`
  348. }
  349. func GetSpeechRecognitionTagsBySpeechIds(speechIds []int) (items []*SpeechRecognitionMappingTags, err error) {
  350. if len(speechIds) == 0 {
  351. return
  352. }
  353. sql := fmt.Sprintf(`SELECT a.speech_recognition_id, a.tag_id, b.tag_name FROM speech_recognition_tag_mapping AS a
  354. JOIN speech_recognition_tag AS b ON a.tag_id = b.speech_recognition_tag_id
  355. WHERE a.speech_recognition_id IN (%s)`, utils.GetOrmInReplace(len(speechIds)))
  356. err = global.DEFAULT_DmSQL.Raw(sql, speechIds).Find(&items).Error
  357. return
  358. }
  359. type SpeechRecognitionListReq struct {
  360. PageSize int `form:"PageSize"`
  361. CurrentIndex int `form:"CurrentIndex"`
  362. FileName string `form:"FileName" description:"文件名称"`
  363. StartTime string `form:"StartTime" description:"开始时间"`
  364. EndTime string `form:"EndTime" description:"结束时间"`
  365. CreateUserId string `form:"CreateUserId" description:"创建人IDs"`
  366. TagId int `form:"TagId" description:"标签ID"`
  367. TagIds string `form:"TagIds" description:"标签IDs, 英文逗号分隔"`
  368. MenuId int `form:"MenuId" description:"目录ID"`
  369. IsTagMenu bool `form:"IsTagMenu" description:"是否为标签目录"`
  370. }
  371. type SpeechRecognitionListResp struct {
  372. List []*SpeechRecognitionDetailItem
  373. Paging *paging.PagingItem `description:"分页数据"`
  374. }
  375. type SpeechRecognitionContentExportReq struct {
  376. SpeechRecognitionId int `description:"语音识别ID"`
  377. ExportType int `description:"导出类型:1-txt;2-doc;3-pdf"`
  378. Timestamp bool `description:"是否显示时间戳"`
  379. }
  380. func (m *SpeechRecognition) UpdateSortByMenuId(menuId, nowSort int, prevSpeechId int, updateSort string) (err error) {
  381. sql := fmt.Sprintf(`UPDATE %s SET %s = %s WHERE %s = ? `, m.TableName(), SpeechRecognitionCols.Sort, updateSort, SpeechRecognitionCols.MenuId)
  382. if prevSpeechId > 0 {
  383. sql += fmt.Sprintf(` AND (%s > ? OR (%s > %d AND %s = %d))`, SpeechRecognitionCols.Sort, SpeechRecognitionCols.SpeechRecognitionId, prevSpeechId, SpeechRecognitionCols.Sort, nowSort)
  384. } else {
  385. sql += fmt.Sprintf(` AND %s > ?`, SpeechRecognitionCols.Sort)
  386. }
  387. err = global.DEFAULT_DmSQL.Exec(sql, menuId, nowSort).Error
  388. return
  389. }
  390. func (m *SpeechRecognition) GetMaxSortByMenuId(menuId int) (sort int, err error) {
  391. sql := fmt.Sprintf(`SELECT COALESCE(MAX(sort),0) AS sort FROM %s WHERE %s = ?`, m.TableName(), SpeechRecognitionCols.MenuId)
  392. err = global.DEFAULT_DmSQL.Raw(sql, menuId).Scan(&sort).Error
  393. return
  394. }
  395. func (m *SpeechRecognition) GetFirstByMenuId(menuId int) (item *SpeechRecognition, err error) {
  396. sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? ORDER BY %s ASC, %s ASC LIMIT 1`, m.TableName(), SpeechRecognitionCols.MenuId, SpeechRecognitionCols.Sort, SpeechRecognitionCols.SpeechRecognitionId)
  397. err = global.DEFAULT_DmSQL.Raw(sql, menuId).First(&item).Error
  398. return
  399. }
  400. type SpeechRecognitionConvertCheckNameReq struct {
  401. FileName string `description:"文件名称"`
  402. }
  403. func (m *SpeechRecognition) SpeechSave(speechItem *SpeechRecognition, speechCols []string, contents []SpeechRecognitionSaveContentReq, tagMappings []*SpeechRecognitionTagMapping) (err error) {
  404. if speechItem == nil {
  405. err = fmt.Errorf("speech nil")
  406. return
  407. }
  408. tx := global.DEFAULT_DmSQL.Begin()
  409. defer func() {
  410. if err != nil {
  411. _ = tx.Rollback()
  412. return
  413. }
  414. _ = tx.Commit()
  415. }()
  416. if len(speechCols) > 0 {
  417. e := tx.Select(speechCols).Updates(speechItem).Error
  418. if e != nil {
  419. err = fmt.Errorf("update speech err: %s", e.Error())
  420. return
  421. }
  422. }
  423. if len(contents) > 0 {
  424. sql := fmt.Sprintf(`UPDATE %s SET %s = ?, %s = 1, %s = NOW() WHERE %s = ?`, "speech_recognition_content", SpeechRecognitionContentCols.Content, SpeechRecognitionContentCols.IsUpdate, SpeechRecognitionContentCols.ModifyTime, SpeechRecognitionContentCols.SpeechRecognitionContentId)
  425. for _, v := range contents {
  426. e := tx.Exec(sql, v.Content, v.SpeechRecognitionContentId).Error
  427. if e != nil {
  428. err = fmt.Errorf("update exec err: %s", e.Error())
  429. return
  430. }
  431. }
  432. }
  433. sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ?`, "speech_recognition_tag_mapping", SpeechRecognitionTagMappingCols.SpeechRecognitionId)
  434. e := tx.Exec(sql, speechItem.SpeechRecognitionId).Error
  435. if e != nil {
  436. err = fmt.Errorf("remove tag mappings err: %s", e.Error())
  437. return
  438. }
  439. if len(tagMappings) > 0 {
  440. e = tx.CreateInBatches(tagMappings, utils.MultiAddNum).Error
  441. if e != nil {
  442. err = fmt.Errorf("insert tag mappings err: %s", e.Error())
  443. return
  444. }
  445. }
  446. return
  447. }