index.go 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746
  1. package ai_predict_model
  2. import (
  3. "encoding/json"
  4. "eta/eta_api/controllers"
  5. "eta/eta_api/models"
  6. aiPredictModel "eta/eta_api/models/ai_predict_model"
  7. "eta/eta_api/models/ai_predict_model/request"
  8. "eta/eta_api/models/ai_predict_model/response"
  9. "eta/eta_api/models/data_manage"
  10. dataSourceModel "eta/eta_api/models/data_source"
  11. "eta/eta_api/models/system"
  12. "eta/eta_api/services"
  13. "eta/eta_api/services/data"
  14. "eta/eta_api/services/elastic"
  15. "eta/eta_api/utils"
  16. "fmt"
  17. "os"
  18. "strconv"
  19. "strings"
  20. "time"
  21. "github.com/rdlucklib/rdluck_tools/paging"
  22. "github.com/tealeg/xlsx"
  23. )
  24. // AiPredictModelIndexController AI预测模型标的
  25. type AiPredictModelIndexController struct {
  26. controllers.BaseAuthController
  27. }
  28. // List
  29. // @Title 标的列表
  30. // @Description 标的列表
  31. // @Param PageSize query int true "每页数据条数"
  32. // @Param CurrentIndex query int true "当前页页码,从1开始"
  33. // @Param ClassifyId query int false "分类id"
  34. // @Param IndexId query int false "模型标的ID"
  35. // @Param Keyword query string false "搜索关键词"
  36. // @Success 200 {object} data_manage.ChartListResp
  37. // @router /index/list [get]
  38. func (this *AiPredictModelIndexController) List() {
  39. br := new(models.BaseResponse).Init()
  40. defer func() {
  41. this.Data["json"] = br
  42. this.ServeJSON()
  43. }()
  44. sysUser := this.SysUser
  45. if sysUser == nil {
  46. br.Msg = "请登录"
  47. br.ErrMsg = "请登录,SysUser Is Empty"
  48. br.Ret = 408
  49. return
  50. }
  51. pageSize, _ := this.GetInt("PageSize")
  52. currentIndex, _ := this.GetInt("CurrentIndex")
  53. classifyId, _ := this.GetInt("ClassifyId")
  54. indexId, _ := this.GetInt("IndexId")
  55. keyword := this.GetString("KeyWord")
  56. if keyword == "" {
  57. keyword = this.GetString("Keyword")
  58. }
  59. keyword = strings.TrimSpace(keyword)
  60. resp := new(aiPredictModel.AiPredictModelIndexPageListResp)
  61. // 分页
  62. var startSize int
  63. if pageSize <= 0 {
  64. pageSize = utils.PageSize20
  65. }
  66. if currentIndex <= 0 {
  67. currentIndex = 1
  68. }
  69. startSize = paging.StartIndex(currentIndex, pageSize)
  70. // 分类
  71. classifyIdName := make(map[int]string)
  72. {
  73. classifyOb := new(aiPredictModel.AiPredictModelClassify)
  74. list, e := classifyOb.GetItemsByCondition("", make([]interface{}, 0), []string{}, "")
  75. if e != nil {
  76. br.Msg = "获取失败"
  77. br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
  78. return
  79. }
  80. for _, v := range list {
  81. classifyIdName[v.AiPredictModelClassifyId] = v.ClassifyName
  82. }
  83. }
  84. classifyIds := make([]int, 0)
  85. // 筛选条件
  86. highlightMap := make(map[int]string)
  87. indexOb := new(aiPredictModel.AiPredictModelIndex)
  88. var cond string
  89. var pars []interface{}
  90. {
  91. if indexId > 0 {
  92. cond += fmt.Sprintf(" AND %s = ?", indexOb.Cols().PrimaryId)
  93. pars = append(pars, indexId)
  94. }
  95. if classifyId > 0 {
  96. // 查询所有子分类
  97. classifyInfo, err := aiPredictModel.GetAiPredictModelClassifyById(classifyId)
  98. if err != nil && !utils.IsErrNoRow(err) {
  99. br.Msg = "查询失败"
  100. br.ErrMsg = "查询失败,Err:" + err.Error()
  101. return
  102. }
  103. classifyIds, err = aiPredictModel.GetAiPredictModelClassifyChildIdsByLevelPath(classifyInfo.LevelPath)
  104. if err != nil && !utils.IsErrNoRow(err) {
  105. br.Msg = "查询失败"
  106. br.ErrMsg = "查询子分类失败,Err:" + err.Error()
  107. return
  108. }
  109. cond += fmt.Sprintf(" AND %s IN (%s)", indexOb.Cols().ClassifyId, utils.GetOrmInReplace(len(classifyIds)))
  110. pars = append(pars, classifyIds)
  111. }
  112. //if keyword != "" {
  113. // cond += fmt.Sprintf(" AND %s LIKE ?", indexOb.Cols().IndexName)
  114. // pars = append(pars, fmt.Sprint("%", keyword, "%"))
  115. //}
  116. // 有关键词从es中搜索
  117. if keyword != "" {
  118. _, list, e := elastic.SearchDataSourceIndex(utils.EsDataSourceIndexName, keyword, utils.DATA_SOURCE_AI_PREDICT_MODEL, 0, classifyIds, []int{}, []string{}, startSize, pageSize)
  119. if e != nil {
  120. br.Msg = "获取失败"
  121. br.ErrMsg = fmt.Sprintf("ES-搜索AI预测模型列表失败, %v", e)
  122. return
  123. }
  124. if len(list) == 0 {
  125. resp.List = make([]*aiPredictModel.AiPredictModelIndexItem, 0)
  126. br.Ret = 200
  127. br.Success = true
  128. br.Msg = "获取成功"
  129. br.Data = resp
  130. return
  131. }
  132. var ids []int
  133. for _, v := range list {
  134. ids = append(ids, v.PrimaryId)
  135. highlightMap[v.PrimaryId] = v.SearchText
  136. }
  137. cond += fmt.Sprintf(` AND %s IN (%s)`, indexOb.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
  138. pars = append(pars, ids)
  139. }
  140. }
  141. // 获取列表
  142. total, e := indexOb.GetCountByCondition(cond, pars)
  143. if e != nil {
  144. br.Msg = "获取失败"
  145. br.ErrMsg = fmt.Sprintf("获取标的总数失败, %v", e)
  146. return
  147. }
  148. list, e := indexOb.GetPageItemsByCondition(cond, pars, []string{}, "", startSize, pageSize)
  149. if e != nil {
  150. br.Msg = "获取失败"
  151. br.ErrMsg = fmt.Sprintf("获取分页列表失败, %v", e)
  152. return
  153. }
  154. pageList := make([]*aiPredictModel.AiPredictModelIndexItem, 0)
  155. for _, v := range list {
  156. t := v.Format2Item()
  157. t.ClassifyName = classifyIdName[v.ClassifyId]
  158. // 搜索高亮
  159. t.SearchText = v.IndexName
  160. s := highlightMap[v.AiPredictModelIndexId]
  161. if s != "" {
  162. t.SearchText = s
  163. }
  164. pageList = append(pageList, t)
  165. }
  166. page := paging.GetPaging(currentIndex, pageSize, total)
  167. resp.Paging = page
  168. resp.List = pageList
  169. br.Data = resp
  170. br.Ret = 200
  171. br.Success = true
  172. br.Msg = "获取成功"
  173. }
  174. // Import
  175. // @Title 导入标的和数据
  176. // @Description 导入标的和数据
  177. // @Param IndexFile query file true "标的文件"
  178. // @Success 200 Ret=200 录入成功
  179. // @router /index/import [post]
  180. func (this *AiPredictModelIndexController) Import() {
  181. br := new(models.BaseResponse).Init()
  182. defer func() {
  183. if br.ErrMsg == "" {
  184. br.IsSendEmail = false
  185. }
  186. this.Data["json"] = br
  187. this.ServeJSON()
  188. }()
  189. sysUser := this.SysUser
  190. if sysUser == nil {
  191. br.Msg = "请登录"
  192. br.ErrMsg = "请登录,SysUser Is Empty"
  193. br.Ret = 408
  194. return
  195. }
  196. file, _, e := this.GetFile("IndexFile")
  197. if e != nil {
  198. br.Msg = "导入失败"
  199. br.ErrMsg = fmt.Sprintf("获取文件失败, %v", e)
  200. return
  201. }
  202. path := "./static/ai_predict_model_temp_" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
  203. defer func() {
  204. _ = file.Close()
  205. _ = os.Remove(path)
  206. }()
  207. if e = this.SaveToFile("IndexFile", path); e != nil {
  208. br.Msg = "导入失败"
  209. br.ErrMsg = fmt.Sprintf("保存文件失败, %v", e)
  210. return
  211. }
  212. xlFile, e := xlsx.OpenFile(path)
  213. if e != nil {
  214. br.Msg = "导入失败"
  215. br.ErrMsg = fmt.Sprintf("打开excel文件失败, %v", e)
  216. return
  217. }
  218. // 获取分类和用户,遍历时校验
  219. classifyNameId := make(map[string]int)
  220. classifyMap := make(map[int]string)
  221. adminNameId := make(map[string]int)
  222. {
  223. classifyOb := new(aiPredictModel.AiPredictModelClassify)
  224. classifyCond := ``
  225. classifyPars := make([]interface{}, 0)
  226. classifies, e := classifyOb.GetItemsByCondition(classifyCond, classifyPars, []string{}, "")
  227. if e != nil {
  228. br.Msg = "导入失败"
  229. br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
  230. return
  231. }
  232. // 构建完整的分类路径映射
  233. for _, v := range classifies {
  234. if this.Lang == utils.EnLangVersion {
  235. classifyMap[v.AiPredictModelClassifyId] = v.ClassifyNameEn
  236. } else {
  237. classifyMap[v.AiPredictModelClassifyId] = v.ClassifyName
  238. }
  239. }
  240. for _, v := range classifies {
  241. levels := strings.Split(v.LevelPath, ",")
  242. fullName := ""
  243. for _, level := range levels {
  244. if level == "" {
  245. continue
  246. }
  247. cid, _ := strconv.Atoi(level)
  248. fullName = fullName + "/" + classifyMap[cid]
  249. }
  250. fullName = strings.Trim(fullName, "/")
  251. classifyNameId[fullName] = v.AiPredictModelClassifyId
  252. }
  253. admins, e := system.GetSysAdminList(``, make([]interface{}, 0), []string{}, "")
  254. if e != nil {
  255. br.Msg = "导入失败"
  256. br.ErrMsg = fmt.Sprintf("获取用户失败, %v", e)
  257. return
  258. }
  259. for _, v := range admins {
  260. adminNameId[v.RealName] = v.AdminId
  261. }
  262. }
  263. // 遍历sheet页
  264. // 列表页:预测标的|分类|模型框架|创建人|预测日期|预测值|预测频度|方向准确率|绝对偏差
  265. type ImportDataColKey struct {
  266. IndexName string
  267. ColKey int
  268. DataDate time.Time
  269. }
  270. imports := make(map[string]*aiPredictModel.AiPredictModelImportData)
  271. importsData := make(map[string]map[time.Time]*aiPredictModel.AiPredictModelData)
  272. importsDailyData := make(map[string]map[time.Time]*aiPredictModel.AiPredictModelData)
  273. for sheetKey, sheet := range xlFile.Sheets {
  274. maxRow := sheet.MaxRow
  275. // 列表页
  276. if sheetKey == 0 {
  277. for i := 0; i < maxRow; i++ {
  278. // 忽略首行标题
  279. if i < 1 {
  280. continue
  281. }
  282. row := sheet.Row(i)
  283. cells := row.Cells
  284. if len(cells) < 9 {
  285. continue
  286. }
  287. // 标的名称
  288. indexName := strings.TrimSpace(cells[0].String())
  289. if indexName == "" {
  290. continue
  291. }
  292. if imports[indexName] == nil {
  293. imports[indexName] = new(aiPredictModel.AiPredictModelImportData)
  294. imports[indexName].Index = new(aiPredictModel.AiPredictModelIndex)
  295. imports[indexName].Data = make([]*aiPredictModel.AiPredictModelData, 0)
  296. }
  297. imports[indexName].Index.IndexName = indexName
  298. imports[indexName].Index.CreateTime = time.Now()
  299. imports[indexName].Index.ModifyTime = time.Now()
  300. imports[indexName].Index.TrainStatus = `训练成功`
  301. imports[indexName].Index.RunStatus = `运行成功`
  302. // 分类改成多级用斜杠/区分例如:一级/二级/三级/四级/五级/六级
  303. classifyName := strings.TrimSpace(cells[1].String())
  304. if classifyNameId[classifyName] <= 0 {
  305. br.Msg = fmt.Sprintf("分类:%s不存在", classifyName)
  306. br.ErrMsg = fmt.Sprintf("分类:%s不存在,请先在系统中创建对应的分类", classifyName)
  307. return
  308. }
  309. imports[indexName].Index.ClassifyId = classifyNameId[classifyName]
  310. // 创建人
  311. adminName := strings.TrimSpace(cells[3].String())
  312. if adminNameId[adminName] <= 0 {
  313. br.Msg = fmt.Sprintf("创建人:%s不存在", adminName)
  314. return
  315. }
  316. imports[indexName].Index.SysUserId = adminNameId[adminName]
  317. imports[indexName].Index.SysUserRealName = adminName
  318. // 其余信息
  319. imports[indexName].Index.ModelFramework = strings.TrimSpace(cells[2].String())
  320. strDate := strings.TrimSpace(cells[4].String())
  321. predictDate, _ := utils.GetExcelDate(strDate)
  322. imports[indexName].Index.PredictDate = predictDate
  323. strVal := strings.TrimSpace(cells[5].String())
  324. if strVal == "" {
  325. continue
  326. }
  327. predictVal, _ := strconv.ParseFloat(strVal, 64)
  328. imports[indexName].Index.PredictValue = predictVal
  329. imports[indexName].Index.PredictFrequency = strings.TrimSpace(cells[6].String())
  330. imports[indexName].Index.DirectionAccuracy = strings.TrimSpace(cells[7].String())
  331. imports[indexName].Index.AbsoluteDeviation = strings.TrimSpace(cells[8].String())
  332. }
  333. }
  334. // 月度数据页
  335. if sheetKey == 1 {
  336. // 每五列为一个指标的数据
  337. colKeys := make(map[int]*ImportDataColKey) // 每一列对应的指标名称以及对应的字段序号
  338. for i := 0; i < maxRow; i++ {
  339. // 首行为指标名称
  340. if i == 0 {
  341. nameCol := 0
  342. row := sheet.Row(i)
  343. for ck, cell := range row.Cells {
  344. nameCol += 1
  345. if nameCol > 5 {
  346. nameCol = 1
  347. }
  348. if nameCol == 1 {
  349. // nameCol=1时为指标/数据行则为日期
  350. indexName := strings.TrimSpace(cell.String())
  351. if indexName == "" {
  352. continue
  353. }
  354. importsData[indexName] = make(map[time.Time]*aiPredictModel.AiPredictModelData)
  355. colKeys[ck] = &ImportDataColKey{
  356. ColKey: 1,
  357. IndexName: indexName,
  358. }
  359. // 后面四列分别对应: 实际值|预测值|方向|偏差率, 这里直接加无须考虑是否会越界
  360. colKeys[ck+1] = &ImportDataColKey{
  361. ColKey: 2,
  362. IndexName: indexName,
  363. }
  364. colKeys[ck+2] = &ImportDataColKey{
  365. ColKey: 3,
  366. IndexName: indexName,
  367. }
  368. colKeys[ck+3] = &ImportDataColKey{
  369. ColKey: 4,
  370. IndexName: indexName,
  371. }
  372. colKeys[ck+4] = &ImportDataColKey{
  373. ColKey: 5,
  374. IndexName: indexName,
  375. }
  376. continue
  377. }
  378. }
  379. continue
  380. }
  381. // 第二行为标题,跳过
  382. if i == 1 {
  383. continue
  384. }
  385. // 剩余为数据行
  386. row := sheet.Row(i)
  387. for ck, cell := range row.Cells {
  388. if colKeys[ck] == nil {
  389. continue
  390. }
  391. if colKeys[ck].IndexName == "" {
  392. continue
  393. }
  394. switch colKeys[ck].ColKey {
  395. case 1:
  396. // 日期列
  397. strDate := strings.TrimSpace(cell.String())
  398. dataDate, _ := utils.GetExcelDate(strDate)
  399. if dataDate.IsZero() {
  400. continue
  401. }
  402. colKeys[ck].DataDate = dataDate
  403. colKeys[ck+1].DataDate = dataDate
  404. colKeys[ck+2].DataDate = dataDate
  405. colKeys[ck+3].DataDate = dataDate
  406. colKeys[ck+4].DataDate = dataDate
  407. importRow := imports[colKeys[ck].IndexName]
  408. if importRow == nil {
  409. continue
  410. }
  411. // 新增当前日期数据
  412. importsData[colKeys[ck].IndexName][dataDate] = new(aiPredictModel.AiPredictModelData)
  413. importsData[colKeys[ck].IndexName][dataDate].DataTime = dataDate
  414. importsData[colKeys[ck].IndexName][dataDate].CreateTime = time.Now()
  415. importsData[colKeys[ck].IndexName][dataDate].ModifyTime = time.Now()
  416. importsData[colKeys[ck].IndexName][dataDate].Source = aiPredictModel.ModelDataSourceMonthly
  417. case 2, 3:
  418. // 实际值和预测值, 可能为空
  419. dataDate := colKeys[ck].DataDate
  420. if importsData[colKeys[ck].IndexName][dataDate] == nil {
  421. continue
  422. }
  423. strVal := strings.TrimSpace(cell.String())
  424. if strVal == "" {
  425. continue
  426. }
  427. val, _ := strconv.ParseFloat(strVal, 64)
  428. if colKeys[ck].ColKey == 2 {
  429. importsData[colKeys[ck].IndexName][dataDate].Value.Valid = true
  430. importsData[colKeys[ck].IndexName][dataDate].Value.Float64 = val
  431. } else {
  432. importsData[colKeys[ck].IndexName][dataDate].PredictValue.Valid = true
  433. importsData[colKeys[ck].IndexName][dataDate].PredictValue.Float64 = val
  434. }
  435. case 4, 5:
  436. // 方向/偏差率
  437. dataDate := colKeys[ck].DataDate
  438. if importsData[colKeys[ck].IndexName][dataDate] == nil {
  439. continue
  440. }
  441. str := strings.TrimSpace(cell.String())
  442. if str == "" {
  443. continue
  444. }
  445. if colKeys[ck].ColKey == 4 {
  446. importsData[colKeys[ck].IndexName][dataDate].Direction = str
  447. } else {
  448. importsData[colKeys[ck].IndexName][dataDate].DeviationRate = str
  449. }
  450. default:
  451. continue
  452. }
  453. }
  454. }
  455. }
  456. // 日度数据页
  457. if sheetKey == 2 {
  458. // 每3列为一个指标的数据
  459. colKeys := make(map[int]*ImportDataColKey) // 每一列对应的指标名称以及对应的字段序号
  460. for i := 0; i < maxRow; i++ {
  461. // 首行为指标名称
  462. if i == 0 {
  463. nameCol := 0
  464. row := sheet.Row(i)
  465. for ck, cell := range row.Cells {
  466. nameCol += 1
  467. if nameCol > 3 {
  468. nameCol = 1
  469. }
  470. if nameCol == 1 {
  471. // nameCol=1时为指标/数据行则为日期
  472. indexName := strings.TrimSpace(cell.String())
  473. if indexName == "" {
  474. continue
  475. }
  476. importsDailyData[indexName] = make(map[time.Time]*aiPredictModel.AiPredictModelData)
  477. colKeys[ck] = &ImportDataColKey{
  478. ColKey: 1,
  479. IndexName: indexName,
  480. }
  481. // 后面两列分别对应: 实际值|预测值, 这里直接加无须考虑是否会越界
  482. colKeys[ck+1] = &ImportDataColKey{
  483. ColKey: 2,
  484. IndexName: indexName,
  485. }
  486. colKeys[ck+2] = &ImportDataColKey{
  487. ColKey: 3,
  488. IndexName: indexName,
  489. }
  490. continue
  491. }
  492. }
  493. continue
  494. }
  495. // 第二行为标题,遇到"预测值"单元格,需要取出其中的值作为预测图例名称
  496. if i == 1 {
  497. row := sheet.Row(i)
  498. for ck, cell := range row.Cells {
  499. if colKeys[ck] == nil {
  500. continue
  501. }
  502. if colKeys[ck].IndexName == "" {
  503. continue
  504. }
  505. if colKeys[ck].ColKey != 3 {
  506. continue
  507. }
  508. if imports[colKeys[ck].IndexName] != nil && imports[colKeys[ck].IndexName].Index != nil {
  509. var extraConfig aiPredictModel.AiPredictModelIndexExtraConfig
  510. extraConfig.DailyChart.PredictLegendName = strings.TrimSpace(cell.String())
  511. b, _ := json.Marshal(extraConfig)
  512. imports[colKeys[ck].IndexName].Index.ExtraConfig = string(b)
  513. }
  514. }
  515. continue
  516. }
  517. // 剩余为数据行
  518. row := sheet.Row(i)
  519. for ck, cell := range row.Cells {
  520. if colKeys[ck] == nil {
  521. continue
  522. }
  523. if colKeys[ck].IndexName == "" {
  524. continue
  525. }
  526. switch colKeys[ck].ColKey {
  527. case 1:
  528. // 日期列
  529. strDate := strings.TrimSpace(cell.String())
  530. dataDate, _ := utils.GetExcelDate(strDate)
  531. if dataDate.IsZero() {
  532. continue
  533. }
  534. colKeys[ck].DataDate = dataDate
  535. colKeys[ck+1].DataDate = dataDate
  536. colKeys[ck+2].DataDate = dataDate
  537. importRow := imports[colKeys[ck].IndexName]
  538. if importRow == nil {
  539. continue
  540. }
  541. // 新增当前日期数据
  542. importsDailyData[colKeys[ck].IndexName][dataDate] = new(aiPredictModel.AiPredictModelData)
  543. importsDailyData[colKeys[ck].IndexName][dataDate].DataTime = dataDate
  544. importsDailyData[colKeys[ck].IndexName][dataDate].CreateTime = time.Now()
  545. importsDailyData[colKeys[ck].IndexName][dataDate].ModifyTime = time.Now()
  546. importsDailyData[colKeys[ck].IndexName][dataDate].Source = aiPredictModel.ModelDataSourceDaily
  547. case 2, 3:
  548. // 实际值和预测值, 可能为空
  549. dataDate := colKeys[ck].DataDate
  550. if importsDailyData[colKeys[ck].IndexName][dataDate] == nil {
  551. continue
  552. }
  553. strVal := strings.TrimSpace(cell.String())
  554. if strVal == "" {
  555. continue
  556. }
  557. val, _ := strconv.ParseFloat(strVal, 64)
  558. if colKeys[ck].ColKey == 2 {
  559. importsDailyData[colKeys[ck].IndexName][dataDate].Value.Valid = true
  560. importsDailyData[colKeys[ck].IndexName][dataDate].Value.Float64 = val
  561. } else {
  562. importsDailyData[colKeys[ck].IndexName][dataDate].PredictValue.Valid = true
  563. importsDailyData[colKeys[ck].IndexName][dataDate].PredictValue.Float64 = val
  564. }
  565. default:
  566. continue
  567. }
  568. }
  569. }
  570. }
  571. }
  572. for indexName, v := range importsData {
  573. if imports[indexName] == nil {
  574. continue
  575. }
  576. for _, dateData := range v {
  577. imports[indexName].Data = append(imports[indexName].Data, dateData)
  578. }
  579. }
  580. for indexName, v := range importsDailyData {
  581. if imports[indexName] == nil {
  582. continue
  583. }
  584. for _, dateData := range v {
  585. imports[indexName].Data = append(imports[indexName].Data, dateData)
  586. }
  587. }
  588. importIndexes := make([]*aiPredictModel.AiPredictModelImportData, 0)
  589. for _, v := range imports {
  590. importIndexes = append(importIndexes, v)
  591. }
  592. // 导入指标
  593. if e = services.ImportAiPredictModelIndexAndData(importIndexes, sysUser.AdminId, sysUser.RealName); e != nil {
  594. br.Msg = "操作失败"
  595. br.ErrMsg = fmt.Sprintf("导入指标数据失败, %v", e)
  596. return
  597. }
  598. // 写入es
  599. go func() {
  600. for _, v := range importIndexes {
  601. indexItem := new(dataSourceModel.SearchDataSource)
  602. indexItem.PrimaryId = v.Index.AiPredictModelIndexId
  603. indexItem.IndexName = v.Index.IndexName
  604. indexItem.IndexCode = v.Index.IndexCode
  605. indexItem.ClassifyId = v.Index.ClassifyId
  606. indexItem.Source = utils.DATA_SOURCE_AI_PREDICT_MODEL
  607. indexItem.SourceName = "AI预测模型"
  608. indexItem.CreateTime = utils.TimeTransferString(utils.FormatDateTime, v.Index.CreateTime)
  609. indexItem.ModifyTime = utils.TimeTransferString(utils.FormatDateTime, v.Index.ModifyTime)
  610. docId := fmt.Sprintf("%d-%d", indexItem.Source, indexItem.PrimaryId)
  611. if e := elastic.EsAddOrEditDataSourceIndex(utils.EsDataSourceIndexName, docId, indexItem); e != nil {
  612. utils.FileLog.Info("AI预测模型-写入es失败, %v", e)
  613. continue
  614. }
  615. }
  616. }()
  617. br.Ret = 200
  618. br.Success = true
  619. br.Msg = "操作成功"
  620. }
  621. // Detail
  622. // @Title 标的详情
  623. // @Description 标的详情
  624. // @Param IndexId query int true "标的ID"
  625. // @Success 200 {object} data_manage.ChartListResp
  626. // @router /index/detail [get]
  627. func (this *AiPredictModelIndexController) Detail() {
  628. br := new(models.BaseResponse).Init()
  629. defer func() {
  630. if br.ErrMsg == "" {
  631. br.IsSendEmail = false
  632. }
  633. this.Data["json"] = br
  634. this.ServeJSON()
  635. }()
  636. sysUser := this.SysUser
  637. if sysUser == nil {
  638. br.Msg = "请登录"
  639. br.ErrMsg = "请登录,SysUser Is Empty"
  640. br.Ret = 408
  641. return
  642. }
  643. indexId, _ := this.GetInt("IndexId")
  644. if indexId <= 0 {
  645. br.Msg = "参数有误"
  646. br.ErrMsg = fmt.Sprintf("参数有误, IndexId: %d", indexId)
  647. return
  648. }
  649. resp := new(aiPredictModel.AiPredictModelDetailResp)
  650. indexOb := new(aiPredictModel.AiPredictModelIndex)
  651. indexItem, e := indexOb.GetItemById(indexId)
  652. if e != nil {
  653. if utils.IsErrNoRow(e) {
  654. br.Msg = "标的已被删除,请刷新页面"
  655. return
  656. }
  657. br.Msg = "获取失败"
  658. br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
  659. return
  660. }
  661. // 获取标的数据
  662. monthData, dailyData := make([]*aiPredictModel.AiPredictModelData, 0), make([]*aiPredictModel.AiPredictModelData, 0)
  663. {
  664. tableData := make([]*aiPredictModel.AiPredictModelDataItem, 0)
  665. dataOb := new(aiPredictModel.AiPredictModelData)
  666. dataCond := fmt.Sprintf(` AND %s = ?`, dataOb.Cols().IndexCode)
  667. dataPars := make([]interface{}, 0)
  668. dataPars = append(dataPars, indexItem.IndexCode)
  669. list, e := dataOb.GetItemsByCondition(dataCond, dataPars, []string{}, fmt.Sprintf("%s DESC", dataOb.Cols().DataTime))
  670. if e != nil {
  671. br.Msg = "获取失败"
  672. br.ErrMsg = fmt.Sprintf("获取标的数据失败, %v", e)
  673. return
  674. }
  675. // tableData取月度数据,最多显示10条
  676. count, limit := 0, 10
  677. for _, v := range list {
  678. // 日度数据
  679. if v.Source == aiPredictModel.ModelDataSourceDaily {
  680. dailyData = append(dailyData, v)
  681. continue
  682. }
  683. // 月度数据
  684. if count < limit {
  685. tableData = append(tableData, v.Format2Item())
  686. count += 1
  687. }
  688. monthData = append(monthData, v)
  689. }
  690. resp.TableData = tableData
  691. }
  692. // 月度图表
  693. if len(monthData) > 0 {
  694. chartDetail, e := services.GetAiPredictChartDetailByData(indexItem, monthData, aiPredictModel.ModelDataSourceMonthly)
  695. if e != nil {
  696. br.Msg = "获取失败"
  697. br.ErrMsg = fmt.Sprintf("获取月度图表失败, %v", e)
  698. return
  699. }
  700. resp.ChartView = chartDetail
  701. }
  702. // 日度图表
  703. if len(dailyData) > 0 {
  704. dailyChartDetail, e := services.GetAiPredictChartDetailByData(indexItem, dailyData, aiPredictModel.ModelDataSourceDaily)
  705. if e != nil {
  706. br.Msg = "获取失败"
  707. br.ErrMsg = fmt.Sprintf("获取日度图表失败, %v", e)
  708. return
  709. }
  710. resp.DailyChartView = dailyChartDetail
  711. }
  712. br.Data = resp
  713. br.Ret = 200
  714. br.Success = true
  715. br.Msg = "获取成功"
  716. }
  717. // Save
  718. // @Title 保存标的
  719. // @Description 保存标的
  720. // @Param request body aiPredictModel.AiPredictModelIndexSaveReq true "type json string"
  721. // @Success 200 Ret=200 保存成功
  722. // @router /index/save [post]
  723. func (this *AiPredictModelIndexController) Save() {
  724. br := new(models.BaseResponse).Init()
  725. defer func() {
  726. if br.ErrMsg == "" {
  727. br.IsSendEmail = false
  728. }
  729. this.Data["json"] = br
  730. this.ServeJSON()
  731. }()
  732. sysUser := this.SysUser
  733. if sysUser == nil {
  734. br.Msg = "请登录"
  735. br.ErrMsg = "请登录,SysUser Is Empty"
  736. br.Ret = 408
  737. return
  738. }
  739. var req aiPredictModel.AiPredictModelIndexSaveReq
  740. if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
  741. br.Msg = "参数解析异常"
  742. br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
  743. return
  744. }
  745. if req.IndexId < 0 {
  746. br.Msg = "参数有误"
  747. br.ErrMsg = fmt.Sprintf("标的ID有误, IndexId: %d", req.IndexId)
  748. return
  749. }
  750. indexOb := new(aiPredictModel.AiPredictModelIndex)
  751. indexItem, e := indexOb.GetItemById(req.IndexId)
  752. if e != nil {
  753. if utils.IsErrNoRow(e) {
  754. br.Msg = "标的已被删除,请刷新页面"
  755. return
  756. }
  757. br.Msg = "操作失败"
  758. br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
  759. return
  760. }
  761. var extraConfig aiPredictModel.AiPredictModelIndexExtraConfig
  762. if indexItem.ExtraConfig != "" {
  763. if e = json.Unmarshal([]byte(indexItem.ExtraConfig), &extraConfig); e != nil {
  764. br.Msg = "操作失败"
  765. br.ErrMsg = fmt.Sprintf("标的配置解析失败, %v", e)
  766. return
  767. }
  768. }
  769. if req.MonthlyChart != nil {
  770. extraConfig.MonthlyChart.LeftMin = req.MonthlyChart.LeftMin
  771. extraConfig.MonthlyChart.LeftMax = req.MonthlyChart.LeftMax
  772. extraConfig.MonthlyChart.Unit = req.MonthlyChart.Unit
  773. }
  774. if req.DailyChart != nil {
  775. extraConfig.DailyChart.LeftMin = req.DailyChart.LeftMin
  776. extraConfig.DailyChart.LeftMax = req.DailyChart.LeftMax
  777. extraConfig.DailyChart.Unit = req.DailyChart.Unit
  778. }
  779. configByte, _ := json.Marshal(extraConfig)
  780. indexItem.ExtraConfig = string(configByte)
  781. indexItem.ModifyTime = time.Now()
  782. updateCols := []string{indexOb.Cols().ExtraConfig, indexOb.Cols().ModifyTime}
  783. if e = indexItem.Update(updateCols); e != nil {
  784. br.Msg = "操作失败"
  785. br.ErrMsg = fmt.Sprintf("保存标的失败, %v", e)
  786. return
  787. }
  788. br.Ret = 200
  789. br.Msg = "操作成功"
  790. br.Success = true
  791. }
  792. // DashboardSave
  793. // @Title 保存看板
  794. // @Description 保存看板
  795. // @Param request body aiPredictModel.AiPredictModelDashboardSaveReq true "type json string"
  796. // @Success 200 Ret=200 新增成功
  797. // @router /index/dashboard/save [post]
  798. func (this *AiPredictModelIndexController) DashboardSave() {
  799. br := new(models.BaseResponse).Init()
  800. defer func() {
  801. if br.ErrMsg == "" {
  802. br.IsSendEmail = false
  803. }
  804. this.Data["json"] = br
  805. this.ServeJSON()
  806. }()
  807. sysUser := this.SysUser
  808. if sysUser == nil {
  809. br.Msg = "请登录"
  810. br.ErrMsg = "请登录,SysUser Is Empty"
  811. br.Ret = 408
  812. return
  813. }
  814. var req aiPredictModel.AiPredictModelDashboardSaveReq
  815. if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
  816. br.Msg = "参数解析异常"
  817. br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
  818. return
  819. }
  820. if req.IndexId <= 0 {
  821. br.Msg = "参数有误"
  822. br.ErrMsg = fmt.Sprintf("参数有误, %d", req.IndexId)
  823. return
  824. }
  825. req.DashboardName = strings.TrimSpace(req.DashboardName)
  826. indexOb := new(aiPredictModel.AiPredictModelIndex)
  827. _, e := indexOb.GetItemById(req.IndexId)
  828. if e != nil {
  829. if utils.IsErrNoRow(e) {
  830. br.Msg = "标的已被删除,请刷新页面"
  831. return
  832. }
  833. br.Msg = "操作失败"
  834. br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
  835. return
  836. }
  837. // 获取看板
  838. var isUpdate bool
  839. var updateCols []string
  840. dashboardItem := new(aiPredictModel.AiPredictModelDashboard)
  841. if req.IndexId > 0 {
  842. cond := fmt.Sprintf(" AND %s = ?", dashboardItem.Cols().AiPredictModelIndexId)
  843. pars := make([]interface{}, 0)
  844. pars = append(pars, req.IndexId)
  845. item, e := dashboardItem.GetItemByCondition(cond, pars, "")
  846. if e != nil && !utils.IsErrNoRow(e) {
  847. br.Msg = "操作失败"
  848. br.ErrMsg = fmt.Sprintf("获取标的看板失败, %v", e)
  849. return
  850. }
  851. if item != nil && item.AiPredictModelIndexId > 0 {
  852. isUpdate = true
  853. dashboardItem = item
  854. dashboardItem.DashboardName = req.DashboardName
  855. dashboardItem.ModifyTime = time.Now()
  856. updateCols = append(updateCols, dashboardItem.Cols().DashboardName, dashboardItem.Cols().ModifyTime)
  857. }
  858. }
  859. if !isUpdate {
  860. dashboardItem.AiPredictModelIndexId = req.IndexId
  861. dashboardItem.DashboardName = req.DashboardName
  862. dashboardItem.SysUserId = sysUser.AdminId
  863. dashboardItem.SysUserRealName = sysUser.RealName
  864. dashboardItem.CreateTime = time.Now()
  865. dashboardItem.ModifyTime = time.Now()
  866. }
  867. // 详情
  868. dashboardDetails := make([]*aiPredictModel.AiPredictModelDashboardDetail, 0)
  869. for i, v := range req.List {
  870. t := &aiPredictModel.AiPredictModelDashboardDetail{
  871. Type: v.Type,
  872. UniqueCode: v.UniqueCode,
  873. Sort: i + 1,
  874. CreateTime: time.Now(),
  875. ModifyTime: time.Now(),
  876. }
  877. dashboardDetails = append(dashboardDetails, t)
  878. }
  879. // 保存
  880. if e := dashboardItem.SaveIndexDashboard(dashboardItem, dashboardDetails, isUpdate, updateCols); e != nil {
  881. br.Msg = "操作失败"
  882. br.ErrMsg = fmt.Sprintf("保存标的看板失败, %v", e)
  883. return
  884. }
  885. br.Ret = 200
  886. br.Success = true
  887. br.Msg = "操作成功"
  888. }
  889. // DashboardDetail
  890. // @Title 看板详情
  891. // @Description 看板详情
  892. // @Param IndexId query int true "标的ID"
  893. // @Success 200 {object} aiPredictModel.AiPredictModelDashboardDetailResp
  894. // @router /index/dashboard/detail [get]
  895. func (this *AiPredictModelIndexController) DashboardDetail() {
  896. br := new(models.BaseResponse).Init()
  897. defer func() {
  898. if br.ErrMsg == "" {
  899. br.IsSendEmail = false
  900. }
  901. this.Data["json"] = br
  902. this.ServeJSON()
  903. }()
  904. sysUser := this.SysUser
  905. if sysUser == nil {
  906. br.Msg = "请登录"
  907. br.ErrMsg = "请登录,SysUser Is Empty"
  908. br.Ret = 408
  909. return
  910. }
  911. indexId, _ := this.GetInt("IndexId")
  912. resp := new(aiPredictModel.AiPredictModelDashboardDetailResp)
  913. indexOb := new(aiPredictModel.AiPredictModelIndex)
  914. indexItem, e := indexOb.GetItemById(indexId)
  915. if e != nil {
  916. if utils.IsErrNoRow(e) {
  917. br.Msg = "标的已被删除,请刷新页面"
  918. return
  919. }
  920. br.Msg = "获取失败"
  921. br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
  922. return
  923. }
  924. resp.CreateUserId = indexItem.SysUserId
  925. resp.CreateUserRealName = indexItem.SysUserRealName
  926. // 获取标的看板
  927. dashboardOb := new(aiPredictModel.AiPredictModelDashboard)
  928. dashboardItem := new(aiPredictModel.AiPredictModelDashboardItem)
  929. {
  930. cond := fmt.Sprintf(" AND %s = ?", dashboardOb.Cols().AiPredictModelIndexId)
  931. pars := make([]interface{}, 0)
  932. pars = append(pars, indexId)
  933. item, e := dashboardOb.GetItemByCondition(cond, pars, "")
  934. if e != nil && !utils.IsErrNoRow(e) {
  935. br.Msg = "操作失败"
  936. br.ErrMsg = fmt.Sprintf("获取标的看板失败, %v", e)
  937. return
  938. }
  939. if item != nil {
  940. dashboardItem = item.Format2Item()
  941. }
  942. }
  943. // 获取看板详情
  944. dashboardDetails := make([]*aiPredictModel.AiPredictModelDashboardDetailItem, 0)
  945. if dashboardItem.DashboardId > 0 {
  946. detailOb := new(aiPredictModel.AiPredictModelDashboardDetail)
  947. cond := fmt.Sprintf(" AND %s = ?", detailOb.Cols().AiPredictModelDashboardId)
  948. pars := make([]interface{}, 0)
  949. pars = append(pars, dashboardItem.DashboardId)
  950. list, e := detailOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", detailOb.Cols().Sort))
  951. if e != nil {
  952. br.Msg = "获取失败"
  953. br.ErrMsg = fmt.Sprintf("获取看板详情失败, %v", e)
  954. return
  955. }
  956. for _, v := range list {
  957. dashboardDetails = append(dashboardDetails, v.Format2Item())
  958. }
  959. }
  960. resp.AiPredictModelDashboardItem = dashboardItem
  961. resp.List = dashboardDetails
  962. br.Data = resp
  963. br.Ret = 200
  964. br.Success = true
  965. br.Msg = "获取成功"
  966. }
  967. // SearchByEs
  968. // @Title 图表模糊搜索(从es获取)
  969. // @Description 图表模糊搜索(从es获取)
  970. // @Param Keyword query string true "图表名称"
  971. // @Param IsShowMe query bool true "是否只看我的,true、false"
  972. // @Param Source query int true "来源,14:日度预测,15:月度预测,默认0:全部14+15"
  973. // @Success 200 {object} data_manage.ChartInfo
  974. // @router /chart/search_by_es [get]
  975. func (this *AiPredictModelIndexController) SearchByEs() {
  976. br := new(models.BaseResponse).Init()
  977. defer func() {
  978. if br.ErrMsg == "" {
  979. br.IsSendEmail = false
  980. }
  981. this.Data["json"] = br
  982. this.ServeJSON()
  983. }()
  984. sysUser := this.SysUser
  985. if sysUser == nil {
  986. br.Msg = "请登录"
  987. br.ErrMsg = "请登录,SysUser Is Empty"
  988. br.Ret = 408
  989. return
  990. }
  991. pageSize, _ := this.GetInt("PageSize")
  992. currentIndex, _ := this.GetInt("CurrentIndex")
  993. var startSize int
  994. if pageSize <= 0 {
  995. pageSize = utils.PageSize20
  996. }
  997. if currentIndex <= 0 {
  998. currentIndex = 1
  999. }
  1000. startSize = paging.StartIndex(currentIndex, pageSize)
  1001. keyword := this.GetString("Keyword")
  1002. keyword = strings.TrimSpace(keyword)
  1003. if keyword == "" {
  1004. keyword = this.GetString("KeyWord")
  1005. keyword = strings.TrimSpace(keyword)
  1006. }
  1007. //只看我的
  1008. isShowMe, _ := this.GetBool("IsShowMe")
  1009. showSysId := 0
  1010. if isShowMe {
  1011. showSysId = sysUser.AdminId
  1012. }
  1013. source, _ := this.GetInt("Source")
  1014. sourceList := make([]int, 0)
  1015. if source <= 0 {
  1016. sourceList = append(sourceList, utils.CHART_SOURCE_AI_PREDICT_MODEL_MONTHLY, utils.CHART_SOURCE_AI_PREDICT_MODEL_DAILY)
  1017. } else {
  1018. sourceList = append(sourceList, source)
  1019. }
  1020. var searchList []*data_manage.ChartInfoMore
  1021. var total int64
  1022. var err error
  1023. // 获取当前账号的不可见指标(AI预测的指标为标的均可见)
  1024. noPermissionChartIdList := make([]int, 0)
  1025. //{
  1026. // obj := data_manage.EdbInfoNoPermissionAdmin{}
  1027. // confList, err := obj.GetAllChartListByAdminId(this.SysUser.AdminId)
  1028. // if err != nil && !utils.IsErrNoRow(err) {
  1029. // br.Msg = "获取失败"
  1030. // br.ErrMsg = "获取不可见指标配置数据失败,Err:" + err.Error()
  1031. // return
  1032. // }
  1033. // for _, v := range confList {
  1034. // noPermissionChartIdList = append(noPermissionChartIdList, v.ChartInfoId)
  1035. // }
  1036. //}
  1037. if keyword != "" {
  1038. searchList, total, err = data.EsSearchChartInfo(keyword, showSysId, sourceList, noPermissionChartIdList, startSize, pageSize)
  1039. } else {
  1040. total, searchList, err = data_manage.ChartInfoSearchByEmptyKeyWord(showSysId, sourceList, noPermissionChartIdList, startSize, pageSize)
  1041. if err != nil && !utils.IsErrNoRow(err) {
  1042. br.Msg = "获取失败"
  1043. br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
  1044. return
  1045. }
  1046. }
  1047. finalList := make([]*data_manage.ChartInfoMore, 0)
  1048. if len(searchList) > 0 {
  1049. chartInfoIds := ""
  1050. chartEdbMap := make(map[int][]*data_manage.ChartEdbInfoMapping)
  1051. for _, v := range searchList {
  1052. chartInfoIds += strconv.Itoa(v.ChartInfoId) + ","
  1053. }
  1054. if chartInfoIds != "" {
  1055. chartInfoIds = strings.Trim(chartInfoIds, ",")
  1056. //判断是否需要展示英文标识
  1057. edbList, e := data_manage.GetChartEdbMappingListByChartInfoIds(chartInfoIds)
  1058. if e != nil {
  1059. br.Msg = "获取失败"
  1060. br.ErrMsg = "获取图表,指标信息失败,Err:" + e.Error()
  1061. return
  1062. }
  1063. for _, v := range edbList {
  1064. chartEdbMap[v.ChartInfoId] = append(chartEdbMap[v.ChartInfoId], v)
  1065. }
  1066. }
  1067. for _, v := range searchList {
  1068. tmp := new(data_manage.ChartInfoMore)
  1069. tmp.ChartInfo = v.ChartInfo
  1070. // 图表数据权限
  1071. tmp.HaveOperaAuth = true
  1072. //判断是否需要展示英文标识
  1073. if edbTmpList, ok := chartEdbMap[v.ChartInfoId]; ok {
  1074. tmp.IsEnChart = data.CheckIsEnChart(v.ChartNameEn, edbTmpList, v.Source, v.ChartType)
  1075. }
  1076. tmp.SearchText = v.SearchText
  1077. if tmp.SearchText == "" {
  1078. tmp.SearchText = v.ChartName
  1079. }
  1080. finalList = append(finalList, tmp)
  1081. }
  1082. }
  1083. //新增搜索词记录
  1084. {
  1085. searchKeyword := new(data_manage.SearchKeyword)
  1086. searchKeyword.KeyWord = keyword
  1087. searchKeyword.CreateTime = time.Now()
  1088. go data_manage.AddSearchKeyword(searchKeyword)
  1089. }
  1090. page := paging.GetPaging(currentIndex, pageSize, int(total))
  1091. resp := data_manage.ChartInfoListByEsResp{
  1092. Paging: page,
  1093. List: finalList,
  1094. }
  1095. br.Ret = 200
  1096. br.Success = true
  1097. br.Msg = "获取成功"
  1098. br.Data = resp
  1099. }
  1100. // ScriptPathSave
  1101. // @Title 保存标的关联脚本路径
  1102. // @Description 保存标的关联脚本路径
  1103. // @Param request body request.AiPredictModelIndexSaveScriptPathReq true "type json string"
  1104. // @Success 200 Ret=200 保存成功
  1105. // @router /index/script_path/save [post]
  1106. func (this *AiPredictModelIndexController) ScriptPathSave() {
  1107. br := new(models.BaseResponse).Init()
  1108. defer func() {
  1109. if br.ErrMsg == "" {
  1110. br.IsSendEmail = false
  1111. }
  1112. this.Data["json"] = br
  1113. this.ServeJSON()
  1114. }()
  1115. sysUser := this.SysUser
  1116. if sysUser == nil {
  1117. br.Msg = "请登录"
  1118. br.ErrMsg = "请登录,SysUser Is Empty"
  1119. br.Ret = 408
  1120. return
  1121. }
  1122. var req request.AiPredictModelIndexSaveScriptPathReq
  1123. if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
  1124. br.Msg = "参数解析异常"
  1125. br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
  1126. return
  1127. }
  1128. if req.IndexId < 0 {
  1129. br.Msg = "参数有误"
  1130. br.ErrMsg = fmt.Sprintf("标的ID有误, IndexId: %d", req.IndexId)
  1131. return
  1132. }
  1133. req.ScriptPath = strings.TrimSpace(req.ScriptPath)
  1134. if req.ScriptPath == "" {
  1135. br.Msg = "标的路径不能为空"
  1136. br.ErrMsg = fmt.Sprintf("标的路径有误, ScriptPath: %s", req.ScriptPath)
  1137. return
  1138. }
  1139. indexOb := new(aiPredictModel.AiPredictModelIndex)
  1140. indexItem, e := indexOb.GetItemById(req.IndexId)
  1141. if e != nil {
  1142. if utils.IsErrNoRow(e) {
  1143. br.Msg = "标的已被删除,请刷新页面"
  1144. return
  1145. }
  1146. br.Msg = "操作失败"
  1147. br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
  1148. return
  1149. }
  1150. indexItem.ScriptPath = req.ScriptPath
  1151. indexItem.ModifyTime = time.Now()
  1152. updateCols := []string{indexOb.Cols().ScriptPath, indexOb.Cols().ModifyTime}
  1153. if e = indexItem.Update(updateCols); e != nil {
  1154. br.Msg = "操作失败"
  1155. br.ErrMsg = fmt.Sprintf("保存标的失败, %v", e)
  1156. return
  1157. }
  1158. br.Ret = 200
  1159. br.Msg = "操作成功"
  1160. br.Success = true
  1161. }
  1162. // GetCurrentRunningAiPredictModelIndexCount
  1163. // @Title 获取当前正在运行中的模型数量
  1164. // @Description 获取当前正在运行中的模型数量
  1165. // @Success 200 Ret=200 保存成功
  1166. // @Success 200 {object} response.CurrentRunningCountResp
  1167. // @router /index/running/count [get]
  1168. func (this *AiPredictModelIndexController) GetCurrentRunningAiPredictModelIndexCount() {
  1169. br := new(models.BaseResponse).Init()
  1170. defer func() {
  1171. if br.ErrMsg == "" {
  1172. br.IsSendEmail = false
  1173. }
  1174. this.Data["json"] = br
  1175. this.ServeJSON()
  1176. }()
  1177. sysUser := this.SysUser
  1178. if sysUser == nil {
  1179. br.Msg = "请登录"
  1180. br.ErrMsg = "请登录,SysUser Is Empty"
  1181. br.Ret = 408
  1182. return
  1183. }
  1184. // 查找当前标的是否存在待训练/训练中的模型
  1185. count, err := services.GetCurrentRunningAiPredictModelIndexCount()
  1186. if err != nil {
  1187. br.Msg = "训练失败"
  1188. br.ErrMsg = "训练失败,查找待训练的模型失败,Err:" + err.Error()
  1189. return
  1190. }
  1191. resp := response.CurrentRunningCountResp{
  1192. Total: count,
  1193. }
  1194. br.Data = resp
  1195. br.Ret = 200
  1196. br.Success = true
  1197. br.Msg = "获取成功"
  1198. }
  1199. // Run
  1200. // @Title 获取当前正在运行中的模型数量
  1201. // @Description 获取当前正在运行中的模型数量
  1202. // @Success 200 Ret=200 保存成功
  1203. // @Success 200 {object} response.CurrentRunningCountResp
  1204. // @router /index/run [post]
  1205. func (this *AiPredictModelIndexController) Run() {
  1206. br := new(models.BaseResponse).Init()
  1207. defer func() {
  1208. this.Data["json"] = br
  1209. this.ServeJSON()
  1210. }()
  1211. sysUser := this.SysUser
  1212. if sysUser == nil {
  1213. br.Msg = "请登录"
  1214. br.ErrMsg = "请登录,SysUser Is Empty"
  1215. br.Ret = 408
  1216. return
  1217. }
  1218. var req request.AiPredictModelIndexRunReq
  1219. if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
  1220. br.Msg = "参数解析异常"
  1221. br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
  1222. return
  1223. }
  1224. classifyId := req.ClassifyId
  1225. //indexId, _ := this.GetInt("IndexId")
  1226. keyword := strings.TrimSpace(req.Keyword)
  1227. // 查找当前标的是否存在待训练/训练中的模型
  1228. count, err := services.GetCurrentRunningAiPredictModelIndexCount()
  1229. if err != nil {
  1230. br.Msg = "训练失败"
  1231. br.ErrMsg = "训练失败,查找待训练的模型失败,Err:" + err.Error()
  1232. return
  1233. }
  1234. if count > 0 {
  1235. br.Msg = "当前有模型正在训练/运行中,请勿重复训练"
  1236. br.ErrMsg = "当前有模型正在训练/运行中,请勿重复训练"
  1237. br.IsSendEmail = false
  1238. return
  1239. }
  1240. // 分类
  1241. classifyIdName := make(map[int]string)
  1242. {
  1243. classifyOb := new(aiPredictModel.AiPredictModelClassify)
  1244. list, e := classifyOb.GetItemsByCondition("", make([]interface{}, 0), []string{}, "")
  1245. if e != nil {
  1246. br.Msg = "获取失败"
  1247. br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
  1248. return
  1249. }
  1250. for _, v := range list {
  1251. classifyIdName[v.AiPredictModelClassifyId] = v.ClassifyName
  1252. }
  1253. }
  1254. // 筛选条件
  1255. highlightMap := make(map[int]string)
  1256. indexOb := new(aiPredictModel.AiPredictModelIndex)
  1257. var cond string
  1258. var pars []interface{}
  1259. cond += fmt.Sprintf(` AND %s NOT IN (?) AND %s NOT IN (?) `, indexOb.Cols().TrainStatus, indexOb.Cols().RunStatus)
  1260. pars = append(pars, []string{aiPredictModel.TrainStatusWaiting, aiPredictModel.TrainStatusTraining}, []string{aiPredictModel.RunStatusWaiting, aiPredictModel.RunStatusRunning})
  1261. if req.SelectAll {
  1262. // 如果列表全选
  1263. if classifyId > 0 {
  1264. cond += fmt.Sprintf(" AND %s = ?", indexOb.Cols().ClassifyId)
  1265. pars = append(pars, classifyId)
  1266. }
  1267. // 有关键词从es中搜索
  1268. if keyword != "" {
  1269. // 使用scroll API获取所有匹配的数据
  1270. list, e := getAllSearchDataSource(keyword, utils.DATA_SOURCE_AI_PREDICT_MODEL, 0)
  1271. if e != nil {
  1272. br.Msg = "获取失败"
  1273. br.ErrMsg = fmt.Sprintf("获取失败,Err:%v", e)
  1274. return
  1275. }
  1276. if len(list) == 0 {
  1277. br.Msg = "没有找到可以运行的标的"
  1278. br.IsSendEmail = false
  1279. return
  1280. }
  1281. var ids []int
  1282. for _, v := range list {
  1283. ids = append(ids, v.PrimaryId)
  1284. highlightMap[v.PrimaryId] = v.SearchText
  1285. }
  1286. cond += fmt.Sprintf(` AND %s IN (%s)`, indexOb.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
  1287. pars = append(pars, ids)
  1288. }
  1289. // 不勾选的标的
  1290. if len(req.NotIndexIdList) > 0 {
  1291. var ids []int
  1292. for _, v := range req.NotIndexIdList {
  1293. ids = append(ids, v)
  1294. }
  1295. cond += fmt.Sprintf(` AND %s NOT IN (%s)`, indexOb.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
  1296. pars = append(pars, ids)
  1297. }
  1298. } else {
  1299. // 如果不是列表全选
  1300. if len(req.IndexIdList) <= 0 {
  1301. br.Msg = `请选择标的`
  1302. br.IsSendEmail = false
  1303. return
  1304. }
  1305. var ids []int
  1306. for _, v := range req.IndexIdList {
  1307. ids = append(ids, v)
  1308. }
  1309. cond += fmt.Sprintf(` AND %s IN (%s)`, indexOb.Cols().PrimaryId, utils.GetOrmInReplace(len(ids)))
  1310. pars = append(pars, ids)
  1311. }
  1312. // 获取列表
  1313. list, e := indexOb.GetItemsByCondition(cond, pars, []string{`ai_predict_model_index_id,ai_predict_model_index_config_id,script_path`}, "")
  1314. if e != nil {
  1315. br.Msg = "获取失败"
  1316. br.ErrMsg = fmt.Sprintf("获取列表失败, %v", e)
  1317. return
  1318. }
  1319. indexIdList := make([]int, 0)
  1320. for _, v := range list {
  1321. // 没有配置python脚本路径
  1322. if v.ScriptPath == `` {
  1323. continue
  1324. }
  1325. // 没有配置默认参数
  1326. if v.AiPredictModelIndexConfigId <= 0 {
  1327. continue
  1328. }
  1329. indexIdList = append(indexIdList, v.AiPredictModelIndexId)
  1330. }
  1331. if len(indexIdList) <= 0 {
  1332. br.Msg = "没有找到可以运行的标的"
  1333. br.IsSendEmail = false
  1334. return
  1335. }
  1336. err = indexOb.UpdateRunStatusByIdList(indexIdList)
  1337. if err != nil {
  1338. br.Msg = "运行失败"
  1339. br.ErrMsg = fmt.Sprintf("运行失败, %v", e)
  1340. return
  1341. }
  1342. // 加入模型运行任务中
  1343. go services.AddAiModelRunTask(indexIdList, this.SysUser)
  1344. br.Data = indexIdList
  1345. br.Ret = 200
  1346. br.Success = true
  1347. br.Msg = "运行成功"
  1348. }
  1349. // getAllSearchDataSource
  1350. // @Description: 根据条件获取ES中的所有数据
  1351. // @author: Roc
  1352. // @datetime 2025-05-08 11:15:27
  1353. // @param keyword string
  1354. // @param source int
  1355. // @param subSource int
  1356. // @return list []*dataSourceModel.SearchDataSourceItem
  1357. // @return err error
  1358. func getAllSearchDataSource(keyword string, source, subSource int) (list []*dataSourceModel.SearchDataSourceItem, err error) {
  1359. dataLimit := 1000 // 每页获取的数据量
  1360. var scrollId string
  1361. // 使用scroll API获取所有匹配的数据
  1362. scrollId, list, e := elastic.SearchDataSourceIndexWithScroll(utils.EsDataSourceIndexName, keyword, source, subSource, []int{}, []int{}, []string{}, dataLimit)
  1363. if e != nil {
  1364. err = fmt.Errorf("ES-搜索列表失败, %v", e)
  1365. return
  1366. }
  1367. // 最终清理用的 scrollId 放到一个指针里,保证 defer 拿到最新值
  1368. finalScrollId := &scrollId
  1369. defer func() {
  1370. // 清除滚动查询的缓存
  1371. if *finalScrollId != "" {
  1372. elastic.ClearScrollDataSourceIndex(*finalScrollId)
  1373. }
  1374. }()
  1375. // 如果scrollId不为空,说明还有更多数据,继续获取
  1376. for scrollId != "" {
  1377. nextScrollId, nextList, e := elastic.ScrollDataSourceIndex(scrollId)
  1378. if e != nil {
  1379. err = fmt.Errorf("ES-获取更多数据失败, %v", e)
  1380. return
  1381. }
  1382. // 如果没有更多数据,则退出循环
  1383. if len(nextList) <= 0 {
  1384. break
  1385. }
  1386. list = append(list, nextList...)
  1387. if nextScrollId != `` {
  1388. scrollId = nextScrollId
  1389. *finalScrollId = scrollId // 更新 finalScrollId 的指向内容
  1390. }
  1391. }
  1392. return
  1393. }
  1394. // DownloadTemplate
  1395. // @Title 下载导入模版
  1396. // @Description 下载导入模版
  1397. // @Success 200 {object} models.EdbdataClassifyResp
  1398. // @Param Source query int false "来源:1:同花顺;2:wind;34:钢联"
  1399. // @Param IsApi query int false "是否api:1:是;0:否"
  1400. // @router /index/download_template [get]
  1401. func (c *AiPredictModelIndexController) DownloadTemplate() {
  1402. br := new(models.BaseResponse).Init()
  1403. defer func() {
  1404. c.Data["json"] = br
  1405. c.ServeJSON()
  1406. }()
  1407. c.Ctx.Output.Download("./static/template/AI预测模型导入模版.xlsx", "AI预测模型导入模版.xlsx")
  1408. br.Ret = 200
  1409. br.Success = true
  1410. br.Msg = "下载成功"
  1411. }
  1412. // Export
  1413. // @Title 导出AI预测模型
  1414. // @Description 导出AI预测模型
  1415. // @Success 200 {object} models.EdbdataClassifyResp
  1416. // @Param IndexId query int false "预测标的ID"
  1417. // @router /index/export [get]
  1418. func (c *AiPredictModelIndexController) Export() {
  1419. br := new(models.BaseResponse).Init()
  1420. defer func() {
  1421. c.Data["json"] = br
  1422. c.ServeJSON()
  1423. }()
  1424. sysUser := c.SysUser
  1425. if sysUser == nil {
  1426. br.Msg = "请登录"
  1427. br.ErrMsg = "请登录,SysUser Is Empty"
  1428. br.Ret = 408
  1429. return
  1430. }
  1431. indexId, _ := c.GetInt("IndexId")
  1432. if indexId <= 0 {
  1433. br.Msg = "参数有误"
  1434. br.ErrMsg = fmt.Sprintf("参数有误, IndexId: %d", indexId)
  1435. return
  1436. }
  1437. // 获取标的信息
  1438. indexOb := new(aiPredictModel.AiPredictModelIndex)
  1439. indexItem, e := indexOb.GetItemById(indexId)
  1440. if e != nil {
  1441. if utils.IsErrNoRow(e) {
  1442. br.Msg = "标的已被删除,请刷新页面"
  1443. return
  1444. }
  1445. br.Msg = "获取失败"
  1446. br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
  1447. return
  1448. }
  1449. // 获取分类名称
  1450. classifyOb := new(aiPredictModel.AiPredictModelClassify)
  1451. classifies, e := classifyOb.GetItemsByCondition("", make([]interface{}, 0), []string{}, "")
  1452. if e != nil {
  1453. br.Msg = "导出失败"
  1454. br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
  1455. return
  1456. }
  1457. // 构建分类ID到完整路径的映射
  1458. classifyIdToFullPath := make(map[int]string)
  1459. classifyMap := make(map[int]string)
  1460. for _, v := range classifies {
  1461. if c.Lang == utils.EnLangVersion {
  1462. classifyMap[v.AiPredictModelClassifyId] = v.ClassifyNameEn
  1463. } else {
  1464. classifyMap[v.AiPredictModelClassifyId] = v.ClassifyName
  1465. }
  1466. }
  1467. for _, v := range classifies {
  1468. levels := strings.Split(v.LevelPath, ",")
  1469. fullPath := ""
  1470. for _, level := range levels {
  1471. if level == "" {
  1472. continue
  1473. }
  1474. cid, _ := strconv.Atoi(level)
  1475. if fullPath == "" {
  1476. fullPath = classifyMap[cid]
  1477. } else {
  1478. fullPath = fullPath + "/" + classifyMap[cid]
  1479. }
  1480. }
  1481. classifyIdToFullPath[v.AiPredictModelClassifyId] = fullPath
  1482. }
  1483. // 获取标的数据
  1484. dataOb := new(aiPredictModel.AiPredictModelData)
  1485. dataCond := fmt.Sprintf(` AND %s = ?`, dataOb.Cols().IndexCode)
  1486. dataPars := make([]interface{}, 0)
  1487. dataPars = append(dataPars, indexItem.IndexCode)
  1488. dataList, e := dataOb.GetItemsByCondition(dataCond, dataPars, []string{}, fmt.Sprintf("%s DESC", dataOb.Cols().DataTime))
  1489. if e != nil {
  1490. br.Msg = "获取失败"
  1491. br.ErrMsg = fmt.Sprintf("获取标的数据失败, %v", e)
  1492. return
  1493. }
  1494. // 创建Excel文件
  1495. xlFile := xlsx.NewFile()
  1496. // 创建预测标的列表页
  1497. sheet1, err := xlFile.AddSheet("列表页")
  1498. if err != nil {
  1499. br.Msg = "导出失败"
  1500. br.ErrMsg = fmt.Sprintf("创建Excel sheet失败, %v", err)
  1501. return
  1502. }
  1503. // 添加标题行
  1504. titleRow := sheet1.AddRow()
  1505. titles := []string{"预测标的", "分类", "模型类别", "创建人", "预测日期", "预测值", "预测频度", "方向准确率", "绝对偏差"}
  1506. for _, title := range titles {
  1507. cell := titleRow.AddCell()
  1508. cell.Value = title
  1509. }
  1510. // 添加数据行
  1511. dataRow := sheet1.AddRow()
  1512. dataRow.AddCell().Value = indexItem.IndexName
  1513. dataRow.AddCell().Value = classifyIdToFullPath[indexItem.ClassifyId]
  1514. dataRow.AddCell().Value = indexItem.ModelFramework
  1515. dataRow.AddCell().Value = indexItem.SysUserRealName
  1516. dataRow.AddCell().Value = indexItem.PredictDate.Format("2006-01-02")
  1517. dataRow.AddCell().Value = fmt.Sprintf("%.4f", indexItem.PredictValue)
  1518. dataRow.AddCell().Value = indexItem.PredictFrequency
  1519. dataRow.AddCell().Value = indexItem.DirectionAccuracy
  1520. dataRow.AddCell().Value = indexItem.AbsoluteDeviation
  1521. // 创建月度数据页
  1522. sheet2, err := xlFile.AddSheet("详情页")
  1523. if err != nil {
  1524. br.Msg = "导出失败"
  1525. br.ErrMsg = fmt.Sprintf("创建Excel sheet失败, %v", err)
  1526. return
  1527. }
  1528. // 添加标的名称行
  1529. nameRow := sheet2.AddRow()
  1530. nameCell := nameRow.AddCell()
  1531. nameCell.Value = indexItem.IndexName
  1532. // 添加标题行
  1533. titleRow2 := sheet2.AddRow()
  1534. titles2 := []string{"指标日期", "实际值", "预测值", "方向", "偏差率"}
  1535. for _, title := range titles2 {
  1536. cell := titleRow2.AddCell()
  1537. cell.Value = title
  1538. }
  1539. // 添加月度数据
  1540. monthlyData := make([]*aiPredictModel.AiPredictModelData, 0)
  1541. for _, data := range dataList {
  1542. if data.Source == aiPredictModel.ModelDataSourceMonthly {
  1543. monthlyData = append(monthlyData, data)
  1544. }
  1545. }
  1546. for _, data := range monthlyData {
  1547. dataRow := sheet2.AddRow()
  1548. dataRow.AddCell().Value = data.DataTime.Format("2006-01-02")
  1549. if data.Value.Valid {
  1550. dataRow.AddCell().Value = fmt.Sprintf("%.4f", data.Value.Float64)
  1551. } else {
  1552. dataRow.AddCell().Value = ""
  1553. }
  1554. if data.PredictValue.Valid {
  1555. dataRow.AddCell().Value = fmt.Sprintf("%.4f", data.PredictValue.Float64)
  1556. } else {
  1557. dataRow.AddCell().Value = ""
  1558. }
  1559. dataRow.AddCell().Value = data.Direction
  1560. dataRow.AddCell().Value = data.DeviationRate
  1561. }
  1562. // 创建日度数据页
  1563. sheet3, err := xlFile.AddSheet("日度数据表")
  1564. if err != nil {
  1565. br.Msg = "导出失败"
  1566. br.ErrMsg = fmt.Sprintf("创建Excel sheet失败, %v", err)
  1567. return
  1568. }
  1569. // 添加标的名称行
  1570. nameRow3 := sheet3.AddRow()
  1571. nameCell3 := nameRow3.AddCell()
  1572. nameCell3.Value = indexItem.IndexName
  1573. // 添加标题行
  1574. titleRow3 := sheet3.AddRow()
  1575. titles3 := []string{"日期", "实际值", "预测值"}
  1576. for _, title := range titles3 {
  1577. cell := titleRow3.AddCell()
  1578. cell.Value = title
  1579. }
  1580. // 添加日度数据
  1581. dailyData := make([]*aiPredictModel.AiPredictModelData, 0)
  1582. for _, data := range dataList {
  1583. if data.Source == aiPredictModel.ModelDataSourceDaily {
  1584. dailyData = append(dailyData, data)
  1585. }
  1586. }
  1587. for _, data := range dailyData {
  1588. dataRow := sheet3.AddRow()
  1589. dataRow.AddCell().Value = data.DataTime.Format("2006-01-02")
  1590. if data.Value.Valid {
  1591. dataRow.AddCell().Value = fmt.Sprintf("%.4f", data.Value.Float64)
  1592. } else {
  1593. dataRow.AddCell().Value = ""
  1594. }
  1595. if data.PredictValue.Valid {
  1596. dataRow.AddCell().Value = fmt.Sprintf("%.4f", data.PredictValue.Float64)
  1597. } else {
  1598. dataRow.AddCell().Value = ""
  1599. }
  1600. }
  1601. // 文件名称:预测标的名称+年月日
  1602. fileName := fmt.Sprintf("%s_%s.xlsx", indexItem.IndexName, time.Now().Format(utils.FormatDateShortUnSpace))
  1603. filePath := "./static/" + fileName
  1604. // 保存Excel文件
  1605. if err := xlFile.Save(filePath); err != nil {
  1606. br.Msg = "导出失败"
  1607. br.ErrMsg = fmt.Sprintf("保存Excel文件失败, %v", err)
  1608. return
  1609. }
  1610. // 下载文件
  1611. defer os.Remove(filePath)
  1612. c.Ctx.Output.Download(filePath, fileName)
  1613. br.Ret = 200
  1614. br.Success = true
  1615. br.Msg = "导出成功"
  1616. }