package ai_predict_model

import (
	"encoding/json"
	"eta/eta_api/controllers"
	"eta/eta_api/models"
	aiPredictModel "eta/eta_api/models/ai_predict_model"
	"eta/eta_api/models/system"
	"eta/eta_api/services"
	"eta/eta_api/utils"
	"fmt"
	"github.com/rdlucklib/rdluck_tools/paging"
	"github.com/tealeg/xlsx"
	"os"
	"strconv"
	"strings"
	"time"
)

// AiPredictModelIndexController AI预测模型标的
type AiPredictModelIndexController struct {
	controllers.BaseAuthController
}

// List
// @Title 标的列表
// @Description 标的列表
// @Param   PageSize   query   int   true   "每页数据条数"
// @Param   CurrentIndex   query   int   true   "当前页页码,从1开始"
// @Param   ClassifyId   query   int   false   "分类id"
// @Param   Keyword   query   string   false   "搜索关键词"
// @Success 200 {object} data_manage.ChartListResp
// @router /index/list [get]
func (this *AiPredictModelIndexController) List() {
	br := new(models.BaseResponse).Init()
	defer func() {
		this.Data["json"] = br
		this.ServeJSON()
	}()
	sysUser := this.SysUser
	if sysUser == nil {
		br.Msg = "请登录"
		br.ErrMsg = "请登录,SysUser Is Empty"
		br.Ret = 408
		return
	}
	pageSize, _ := this.GetInt("PageSize")
	currentIndex, _ := this.GetInt("CurrentIndex")
	classifyId, _ := this.GetInt("ClassifyId")
	keyword := this.GetString("KeyWord")
	keyword = strings.TrimSpace(keyword)
	resp := new(aiPredictModel.AiPredictModelIndexPageListResp)

	// 分页
	var startSize int
	if pageSize <= 0 {
		pageSize = utils.PageSize20
	}
	if currentIndex <= 0 {
		currentIndex = 1
	}
	startSize = paging.StartIndex(currentIndex, pageSize)

	// 分类
	classifyIdName := make(map[int]string)
	{
		classifyOb := new(aiPredictModel.AiPredictModelClassify)
		list, e := classifyOb.GetItemsByCondition("", make([]interface{}, 0), []string{}, "")
		if e != nil {
			br.Msg = "获取失败"
			br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
			return
		}
		for _, v := range list {
			classifyIdName[v.AiPredictModelClassifyId] = v.ClassifyName
		}
	}

	// 筛选条件
	indexOb := new(aiPredictModel.AiPredictModelIndex)
	var cond string
	var pars []interface{}
	{
		if classifyId > 0 {
			cond += fmt.Sprintf(" AND %s = ?", indexOb.Cols().ClassifyId)
			pars = append(pars, classifyId)
		}
		if keyword != "" {
			cond += fmt.Sprintf(" AND %s LIKE ?", indexOb.Cols().IndexName)
			pars = append(pars, fmt.Sprint("%", keyword, "%"))
		}
	}

	// 获取列表
	total, e := indexOb.GetCountByCondition(cond, pars)
	if e != nil {
		br.Msg = "获取失败"
		br.ErrMsg = fmt.Sprintf("获取标的总数失败, %v", e)
		return
	}
	list, e := indexOb.GetPageItemsByCondition(cond, pars, []string{}, "", startSize, pageSize)
	if e != nil {
		br.Msg = "获取失败"
		br.ErrMsg = fmt.Sprintf("获取分页列表失败, %v", e)
		return
	}
	pageList := make([]*aiPredictModel.AiPredictModelIndexItem, 0)
	for _, v := range list {
		t := v.Format2Item()
		t.ClassifyName = classifyIdName[v.ClassifyId]
		pageList = append(pageList, t)
	}

	page := paging.GetPaging(currentIndex, pageSize, total)
	resp.Paging = page
	resp.List = pageList
	br.Data = resp
	br.Ret = 200
	br.Success = true
	br.Msg = "获取成功"
}

// Import
// @Title 导入标的和数据
// @Description 导入标的和数据
// @Param   IndexFile   query   file   true   "标的文件"
// @Success 200 Ret=200 录入成功
// @router /index/import [post]
func (this *AiPredictModelIndexController) Import() {
	br := new(models.BaseResponse).Init()
	defer func() {
		if br.ErrMsg == "" {
			br.IsSendEmail = false
		}
		this.Data["json"] = br
		this.ServeJSON()
	}()
	sysUser := this.SysUser
	if sysUser == nil {
		br.Msg = "请登录"
		br.ErrMsg = "请登录,SysUser Is Empty"
		br.Ret = 408
		return
	}

	file, _, e := this.GetFile("IndexFile")
	if e != nil {
		br.Msg = "导入失败"
		br.ErrMsg = fmt.Sprintf("获取文件失败, %v", e)
		return
	}
	path := "./static/ai_predict_model_temp_" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
	defer func() {
		_ = file.Close()
		_ = os.Remove(path)
	}()
	if e = this.SaveToFile("IndexFile", path); e != nil {
		br.Msg = "导入失败"
		br.ErrMsg = fmt.Sprintf("保存文件失败, %v", e)
		return
	}
	xlFile, e := xlsx.OpenFile(path)
	if e != nil {
		br.Msg = "导入失败"
		br.ErrMsg = fmt.Sprintf("打开excel文件失败, %v", e)
		return
	}

	// 获取分类和用户,遍历时校验
	classifyNameId := make(map[string]int)
	adminNameId := make(map[string]int)
	{
		classifyOb := new(aiPredictModel.AiPredictModelClassify)
		classifyCond := fmt.Sprintf(` AND %s = ?`, classifyOb.Cols().ParentId)
		classifyPars := make([]interface{}, 0)
		classifyPars = append(classifyPars, 0) // 只取一级分类(临时过渡方案,业务端只会加一级)
		classifies, e := classifyOb.GetItemsByCondition(classifyCond, classifyPars, []string{}, "")
		if e != nil {
			br.Msg = "导入失败"
			br.ErrMsg = fmt.Sprintf("获取分类失败, %v", e)
			return
		}
		for _, v := range classifies {
			classifyNameId[v.ClassifyName] = v.AiPredictModelClassifyId
		}

		admins, e := system.GetSysAdminList(``, make([]interface{}, 0), []string{}, "")
		if e != nil {
			br.Msg = "导入失败"
			br.ErrMsg = fmt.Sprintf("获取用户失败, %v", e)
			return
		}
		for _, v := range admins {
			adminNameId[v.RealName] = v.AdminId
		}
	}

	// 遍历sheet页
	// 列表页:预测标的|分类|模型框架|创建人|预测日期|预测值|预测频度|方向准确率|绝对偏差
	type ImportDataColKey struct {
		IndexName string
		ColKey    int
		DataDate  time.Time
	}
	imports := make(map[string]*aiPredictModel.AiPredictModelImportData)
	importsData := make(map[string]map[time.Time]*aiPredictModel.AiPredictModelData)
	importsDailyData := make(map[string]map[time.Time]*aiPredictModel.AiPredictModelData)
	for sheetKey, sheet := range xlFile.Sheets {
		maxRow := sheet.MaxRow

		// 列表页
		if sheetKey == 0 {
			for i := 0; i < maxRow; i++ {
				// 忽略首行标题
				if i < 1 {
					continue
				}
				row := sheet.Row(i)
				cells := row.Cells
				if len(cells) < 9 {
					continue
				}

				// 标的名称
				indexName := strings.TrimSpace(cells[0].String())
				if indexName == "" {
					continue
				}
				if imports[indexName] == nil {
					imports[indexName] = new(aiPredictModel.AiPredictModelImportData)
					imports[indexName].Index = new(aiPredictModel.AiPredictModelIndex)
					imports[indexName].Data = make([]*aiPredictModel.AiPredictModelData, 0)
				}
				imports[indexName].Index.IndexName = indexName
				imports[indexName].Index.CreateTime = time.Now()
				imports[indexName].Index.ModifyTime = time.Now()

				// 分类
				classifyName := strings.TrimSpace(cells[1].String())
				if classifyNameId[classifyName] <= 0 {
					br.Msg = fmt.Sprintf("分类:%s不存在", classifyName)
					return
				}
				imports[indexName].Index.ClassifyId = classifyNameId[classifyName]

				// 创建人
				adminName := strings.TrimSpace(cells[3].String())
				if adminNameId[adminName] <= 0 {
					br.Msg = fmt.Sprintf("创建人:%s不存在", adminName)
					return
				}
				imports[indexName].Index.SysUserId = adminNameId[adminName]
				imports[indexName].Index.SysUserRealName = adminName

				// 其余信息
				imports[indexName].Index.ModelFramework = strings.TrimSpace(cells[2].String())
				strDate := strings.TrimSpace(cells[4].String())
				predictDate, _ := time.Parse("2006/01/02", strDate)
				if predictDate.IsZero() {
					predictDate, _ = time.Parse("01-02-06", strDate)
					if predictDate.IsZero() {
						predictDate, _ = time.Parse("2006/1/2", strDate)
					}
				}
				imports[indexName].Index.PredictDate = predictDate

				strVal := strings.TrimSpace(cells[5].String())
				if strVal == "" {
					continue
				}
				predictVal, _ := strconv.ParseFloat(strVal, 64)
				imports[indexName].Index.PredictValue = predictVal
				imports[indexName].Index.PredictFrequency = strings.TrimSpace(cells[6].String())
				imports[indexName].Index.DirectionAccuracy = strings.TrimSpace(cells[7].String())
				imports[indexName].Index.AbsoluteDeviation = strings.TrimSpace(cells[8].String())
			}
		}

		// 月度数据页
		if sheetKey == 1 {
			// 每五列为一个指标的数据
			colKeys := make(map[int]*ImportDataColKey) // 每一列对应的指标名称以及对应的字段序号
			for i := 0; i < maxRow; i++ {
				// 首行为指标名称
				if i == 0 {
					nameCol := 0
					row := sheet.Row(i)
					for ck, cell := range row.Cells {
						nameCol += 1
						if nameCol > 5 {
							nameCol = 1
						}
						if nameCol == 1 {
							// nameCol=1时为指标/数据行则为日期
							indexName := strings.TrimSpace(cell.String())
							if indexName == "" {
								continue
							}
							importsData[indexName] = make(map[time.Time]*aiPredictModel.AiPredictModelData)

							colKeys[ck] = &ImportDataColKey{
								ColKey:    1,
								IndexName: indexName,
							}

							// 后面四列分别对应: 实际值|预测值|方向|偏差率, 这里直接加无须考虑是否会越界
							colKeys[ck+1] = &ImportDataColKey{
								ColKey:    2,
								IndexName: indexName,
							}
							colKeys[ck+2] = &ImportDataColKey{
								ColKey:    3,
								IndexName: indexName,
							}
							colKeys[ck+3] = &ImportDataColKey{
								ColKey:    4,
								IndexName: indexName,
							}
							colKeys[ck+4] = &ImportDataColKey{
								ColKey:    5,
								IndexName: indexName,
							}
							continue
						}
					}
					continue
				}

				// 第二行为标题,跳过
				if i == 1 {
					continue
				}

				// 剩余为数据行
				row := sheet.Row(i)
				for ck, cell := range row.Cells {
					if colKeys[ck] == nil {
						continue
					}
					if colKeys[ck].IndexName == "" {
						continue
					}
					switch colKeys[ck].ColKey {
					case 1:
						// 日期列
						strDate := strings.TrimSpace(cell.String())
						dataDate, _ := time.Parse("2006/01/02", strDate)
						if dataDate.IsZero() {
							dataDate, _ = time.Parse("01-02-06", strDate)
							if dataDate.IsZero() {
								dataDate, _ = time.Parse("2006/1/2", strDate)
								if dataDate.IsZero() {
									continue
								}
							}
						}
						colKeys[ck].DataDate = dataDate
						colKeys[ck+1].DataDate = dataDate
						colKeys[ck+2].DataDate = dataDate
						colKeys[ck+3].DataDate = dataDate
						colKeys[ck+4].DataDate = dataDate
						importRow := imports[colKeys[ck].IndexName]
						if importRow == nil {
							continue
						}
						// 新增当前日期数据
						importsData[colKeys[ck].IndexName][dataDate] = new(aiPredictModel.AiPredictModelData)
						importsData[colKeys[ck].IndexName][dataDate].DataTime = dataDate
						importsData[colKeys[ck].IndexName][dataDate].CreateTime = time.Now()
						importsData[colKeys[ck].IndexName][dataDate].ModifyTime = time.Now()
						importsData[colKeys[ck].IndexName][dataDate].Source = aiPredictModel.ModelDataSourceMonthly
					case 2, 3:
						// 实际值和预测值, 可能为空
						dataDate := colKeys[ck].DataDate
						if importsData[colKeys[ck].IndexName][dataDate] == nil {
							continue
						}
						strVal := strings.TrimSpace(cell.String())
						if strVal == "" {
							continue
						}
						val, _ := strconv.ParseFloat(strVal, 64)
						if colKeys[ck].ColKey == 2 {
							importsData[colKeys[ck].IndexName][dataDate].Value.Valid = true
							importsData[colKeys[ck].IndexName][dataDate].Value.Float64 = val
						} else {
							importsData[colKeys[ck].IndexName][dataDate].PredictValue.Valid = true
							importsData[colKeys[ck].IndexName][dataDate].PredictValue.Float64 = val
						}
					case 4, 5:
						// 方向/偏差率
						dataDate := colKeys[ck].DataDate
						if importsData[colKeys[ck].IndexName][dataDate] == nil {
							continue
						}
						str := strings.TrimSpace(cell.String())
						if str == "" {
							continue
						}
						if colKeys[ck].ColKey == 4 {
							importsData[colKeys[ck].IndexName][dataDate].Direction = str
						} else {
							importsData[colKeys[ck].IndexName][dataDate].DeviationRate = str
						}
					default:
						continue
					}
				}
			}
		}

		// 日度数据页
		if sheetKey == 2 {
			// 每3列为一个指标的数据
			colKeys := make(map[int]*ImportDataColKey) // 每一列对应的指标名称以及对应的字段序号
			for i := 0; i < maxRow; i++ {
				// 首行为指标名称
				if i == 0 {
					nameCol := 0
					row := sheet.Row(i)
					for ck, cell := range row.Cells {
						nameCol += 1
						if nameCol > 3 {
							nameCol = 1
						}
						if nameCol == 1 {
							// nameCol=1时为指标/数据行则为日期
							indexName := strings.TrimSpace(cell.String())
							if indexName == "" {
								continue
							}
							importsDailyData[indexName] = make(map[time.Time]*aiPredictModel.AiPredictModelData)

							colKeys[ck] = &ImportDataColKey{
								ColKey:    1,
								IndexName: indexName,
							}

							// 后面两列分别对应: 实际值|预测值, 这里直接加无须考虑是否会越界
							colKeys[ck+1] = &ImportDataColKey{
								ColKey:    2,
								IndexName: indexName,
							}
							colKeys[ck+2] = &ImportDataColKey{
								ColKey:    3,
								IndexName: indexName,
							}
							continue
						}
					}
					continue
				}

				// 第二行为标题,遇到"预测值"单元格,需要取出其中的值作为预测图例名称
				if i == 1 {
					row := sheet.Row(i)
					for ck, cell := range row.Cells {
						if colKeys[ck] == nil {
							continue
						}
						if colKeys[ck].IndexName == "" {
							continue
						}
						if colKeys[ck].ColKey != 3 {
							continue
						}
						if imports[colKeys[ck].IndexName] != nil && imports[colKeys[ck].IndexName].Index != nil {
							var extraConfig aiPredictModel.AiPredictModelIndexExtraConfig
							extraConfig.DailyChart.PredictLegendName = strings.TrimSpace(cell.String())
							b, _ := json.Marshal(extraConfig)
							imports[colKeys[ck].IndexName].Index.ExtraConfig = string(b)
						}
					}
					continue
				}

				// 剩余为数据行
				row := sheet.Row(i)
				for ck, cell := range row.Cells {
					if colKeys[ck] == nil {
						continue
					}
					if colKeys[ck].IndexName == "" {
						continue
					}
					switch colKeys[ck].ColKey {
					case 1:
						// 日期列
						strDate := strings.TrimSpace(cell.String())
						dataDate, _ := time.Parse("2006/01/02", strDate)
						if dataDate.IsZero() {
							dataDate, _ = time.Parse("01-02-06", strDate)
							if dataDate.IsZero() {
								dataDate, _ = time.Parse("2006/1/2", strDate)
								if dataDate.IsZero() {
									continue
								}
							}
						}
						colKeys[ck].DataDate = dataDate
						colKeys[ck+1].DataDate = dataDate
						colKeys[ck+2].DataDate = dataDate
						importRow := imports[colKeys[ck].IndexName]
						if importRow == nil {
							continue
						}
						// 新增当前日期数据
						importsDailyData[colKeys[ck].IndexName][dataDate] = new(aiPredictModel.AiPredictModelData)
						importsDailyData[colKeys[ck].IndexName][dataDate].DataTime = dataDate
						importsDailyData[colKeys[ck].IndexName][dataDate].CreateTime = time.Now()
						importsDailyData[colKeys[ck].IndexName][dataDate].ModifyTime = time.Now()
						importsDailyData[colKeys[ck].IndexName][dataDate].Source = aiPredictModel.ModelDataSourceDaily
					case 2, 3:
						// 实际值和预测值, 可能为空
						dataDate := colKeys[ck].DataDate
						if importsDailyData[colKeys[ck].IndexName][dataDate] == nil {
							continue
						}
						strVal := strings.TrimSpace(cell.String())
						if strVal == "" {
							continue
						}
						val, _ := strconv.ParseFloat(strVal, 64)
						if colKeys[ck].ColKey == 2 {
							importsDailyData[colKeys[ck].IndexName][dataDate].Value.Valid = true
							importsDailyData[colKeys[ck].IndexName][dataDate].Value.Float64 = val
						} else {
							importsDailyData[colKeys[ck].IndexName][dataDate].PredictValue.Valid = true
							importsDailyData[colKeys[ck].IndexName][dataDate].PredictValue.Float64 = val
						}
					default:
						continue
					}
				}
			}
		}
	}

	for indexName, v := range importsData {
		if imports[indexName] == nil {
			continue
		}
		for _, dateData := range v {
			imports[indexName].Data = append(imports[indexName].Data, dateData)
		}
	}
	for indexName, v := range importsDailyData {
		if imports[indexName] == nil {
			continue
		}
		for _, dateData := range v {
			imports[indexName].Data = append(imports[indexName].Data, dateData)
		}
	}
	importIndexes := make([]*aiPredictModel.AiPredictModelImportData, 0)
	for _, v := range imports {
		importIndexes = append(importIndexes, v)
	}

	// 导入指标
	if e = services.ImportAiPredictModelIndexAndData(importIndexes); e != nil {
		br.Msg = "操作失败"
		br.ErrMsg = fmt.Sprintf("导入指标数据失败, %v", e)
		return
	}

	br.Ret = 200
	br.Success = true
	br.Msg = "操作成功"
}

// Detail
// @Title 标的详情
// @Description 标的详情
// @Param   IndexId   query   int   true   "标的ID"
// @Success 200 {object} data_manage.ChartListResp
// @router /index/detail [get]
func (this *AiPredictModelIndexController) Detail() {
	br := new(models.BaseResponse).Init()
	defer func() {
		if br.ErrMsg == "" {
			br.IsSendEmail = false
		}
		this.Data["json"] = br
		this.ServeJSON()
	}()
	sysUser := this.SysUser
	if sysUser == nil {
		br.Msg = "请登录"
		br.ErrMsg = "请登录,SysUser Is Empty"
		br.Ret = 408
		return
	}
	indexId, _ := this.GetInt("IndexId")
	if indexId <= 0 {
		br.Msg = "参数有误"
		br.ErrMsg = fmt.Sprintf("参数有误, IndexId: %d", indexId)
		return
	}
	resp := new(aiPredictModel.AiPredictModelDetailResp)

	indexOb := new(aiPredictModel.AiPredictModelIndex)
	indexItem, e := indexOb.GetItemById(indexId)
	if e != nil {
		if e.Error() == utils.ErrNoRow() {
			br.Msg = "标的已被删除,请刷新页面"
			return
		}
		br.Msg = "获取失败"
		br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
		return
	}

	// 获取标的数据
	monthData, dailyData := make([]*aiPredictModel.AiPredictModelData, 0), make([]*aiPredictModel.AiPredictModelData, 0)
	{
		tableData := make([]*aiPredictModel.AiPredictModelDataItem, 0)
		dataOb := new(aiPredictModel.AiPredictModelData)
		dataCond := fmt.Sprintf(` AND %s = ?`, dataOb.Cols().IndexCode)
		dataPars := make([]interface{}, 0)
		dataPars = append(dataPars, indexItem.IndexCode)
		list, e := dataOb.GetItemsByCondition(dataCond, dataPars, []string{}, fmt.Sprintf("%s DESC", dataOb.Cols().DataTime))
		if e != nil {
			br.Msg = "获取失败"
			br.ErrMsg = fmt.Sprintf("获取标的数据失败, %v", e)
			return
		}

		// tableData取月度数据,最多显示10条
		count, limit := 0, 10
		for _, v := range list {
			// 日度数据
			if v.Source == aiPredictModel.ModelDataSourceDaily {
				dailyData = append(dailyData, v)
				continue
			}

			// 月度数据
			if count < limit {
				tableData = append(tableData, v.Format2Item())
				count += 1
			}
			monthData = append(monthData, v)
		}
		resp.TableData = tableData
	}

	// 月度图表
	if len(monthData) > 0 {
		chartDetail, e := services.GetAiPredictChartDetailByData(indexItem, monthData, aiPredictModel.ModelDataSourceMonthly)
		if e != nil {
			br.Msg = "获取失败"
			br.ErrMsg = fmt.Sprintf("获取月度图表失败, %v", e)
			return
		}
		resp.ChartView = chartDetail
	}

	// 日度图表
	if len(dailyData) > 0 {
		dailyChartDetail, e := services.GetAiPredictChartDetailByData(indexItem, dailyData, aiPredictModel.ModelDataSourceDaily)
		if e != nil {
			br.Msg = "获取失败"
			br.ErrMsg = fmt.Sprintf("获取日度图表失败, %v", e)
			return
		}
		resp.DailyChartView = dailyChartDetail
	}

	br.Data = resp
	br.Ret = 200
	br.Success = true
	br.Msg = "获取成功"
}

// Save
// @Title 保存标的
// @Description 保存标的
// @Param	request	body aiPredictModel.AiPredictModelIndexSaveReq true "type json string"
// @Success 200 Ret=200 保存成功
// @router /index/save [post]
func (this *AiPredictModelIndexController) Save() {
	br := new(models.BaseResponse).Init()
	defer func() {
		if br.ErrMsg == "" {
			br.IsSendEmail = false
		}
		this.Data["json"] = br
		this.ServeJSON()
	}()
	sysUser := this.SysUser
	if sysUser == nil {
		br.Msg = "请登录"
		br.ErrMsg = "请登录,SysUser Is Empty"
		br.Ret = 408
		return
	}
	var req aiPredictModel.AiPredictModelIndexSaveReq
	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
		br.Msg = "参数解析异常"
		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
		return
	}
	if req.IndexId < 0 {
		br.Msg = "参数有误"
		br.ErrMsg = fmt.Sprintf("标的ID有误, IndexId: %d", req.IndexId)
		return
	}

	indexOb := new(aiPredictModel.AiPredictModelIndex)
	indexItem, e := indexOb.GetItemById(req.IndexId)
	if e != nil {
		if e.Error() == utils.ErrNoRow() {
			br.Msg = "标的已被删除,请刷新页面"
			return
		}
		br.Msg = "操作失败"
		br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
		return
	}

	var extraConfig aiPredictModel.AiPredictModelIndexExtraConfig
	if indexItem.ExtraConfig != "" {
		if e = json.Unmarshal([]byte(indexItem.ExtraConfig), &extraConfig); e != nil {
			br.Msg = "操作失败"
			br.ErrMsg = fmt.Sprintf("标的配置解析失败, %v", e)
			return
		}
	}
	if req.MonthlyChart != nil {
		extraConfig.MonthlyChart.LeftMin = req.MonthlyChart.LeftMin
		extraConfig.MonthlyChart.LeftMax = req.MonthlyChart.LeftMax
		extraConfig.MonthlyChart.Unit = req.MonthlyChart.Unit
	}
	if req.DailyChart != nil {
		extraConfig.DailyChart.LeftMin = req.DailyChart.LeftMin
		extraConfig.DailyChart.LeftMax = req.DailyChart.LeftMax
		extraConfig.DailyChart.Unit = req.DailyChart.Unit
	}

	configByte, _ := json.Marshal(extraConfig)
	indexItem.ExtraConfig = string(configByte)
	indexItem.ModifyTime = time.Now()
	updateCols := []string{indexOb.Cols().ExtraConfig, indexOb.Cols().ModifyTime}
	if e = indexItem.Update(updateCols); e != nil {
		br.Msg = "操作失败"
		br.ErrMsg = fmt.Sprintf("保存标的失败, %v", e)
		return
	}

	br.Ret = 200
	br.Msg = "操作成功"
	br.Success = true
}

// DashboardSave
// @Title 保存看板
// @Description 保存看板
// @Param	request	body aiPredictModel.AiPredictModelDashboardSaveReq true "type json string"
// @Success 200 Ret=200 新增成功
// @router /index/dashboard/save [post]
func (this *AiPredictModelIndexController) DashboardSave() {
	br := new(models.BaseResponse).Init()
	defer func() {
		if br.ErrMsg == "" {
			br.IsSendEmail = false
		}
		this.Data["json"] = br
		this.ServeJSON()
	}()
	sysUser := this.SysUser
	if sysUser == nil {
		br.Msg = "请登录"
		br.ErrMsg = "请登录,SysUser Is Empty"
		br.Ret = 408
		return
	}
	var req aiPredictModel.AiPredictModelDashboardSaveReq
	if e := json.Unmarshal(this.Ctx.Input.RequestBody, &req); e != nil {
		br.Msg = "参数解析异常"
		br.ErrMsg = fmt.Sprintf("参数解析异常, %v", e)
		return
	}
	if req.IndexId <= 0 {
		br.Msg = "参数有误"
		br.ErrMsg = fmt.Sprintf("参数有误, %d", req.IndexId)
		return
	}
	req.DashboardName = strings.TrimSpace(req.DashboardName)

	indexOb := new(aiPredictModel.AiPredictModelIndex)
	_, e := indexOb.GetItemById(req.IndexId)
	if e != nil {
		if e.Error() == utils.ErrNoRow() {
			br.Msg = "标的已被删除,请刷新页面"
			return
		}
		br.Msg = "操作失败"
		br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
		return
	}

	// 获取看板
	var isUpdate bool
	var updateCols []string
	dashboardItem := new(aiPredictModel.AiPredictModelDashboard)
	if req.IndexId > 0 {
		cond := fmt.Sprintf(" AND %s = ?", dashboardItem.Cols().AiPredictModelIndexId)
		pars := make([]interface{}, 0)
		pars = append(pars, req.IndexId)
		item, e := dashboardItem.GetItemByCondition(cond, pars, "")
		if e != nil && e.Error() != utils.ErrNoRow() {
			br.Msg = "操作失败"
			br.ErrMsg = fmt.Sprintf("获取标的看板失败, %v", e)
			return
		}
		if item != nil {
			isUpdate = true
			dashboardItem = item
			dashboardItem.DashboardName = req.DashboardName
			dashboardItem.ModifyTime = time.Now()
			updateCols = append(updateCols, dashboardItem.Cols().DashboardName, dashboardItem.Cols().ModifyTime)
		}
	}
	if !isUpdate {
		dashboardItem.AiPredictModelIndexId = req.IndexId
		dashboardItem.DashboardName = req.DashboardName
		dashboardItem.SysUserId = sysUser.AdminId
		dashboardItem.SysUserRealName = sysUser.RealName
		dashboardItem.CreateTime = time.Now()
		dashboardItem.ModifyTime = time.Now()
	}

	// 详情
	dashboardDetails := make([]*aiPredictModel.AiPredictModelDashboardDetail, 0)
	for i, v := range req.List {
		t := &aiPredictModel.AiPredictModelDashboardDetail{
			Type:       v.Type,
			UniqueCode: v.UniqueCode,
			Sort:       i + 1,
			CreateTime: time.Now(),
			ModifyTime: time.Now(),
		}
		dashboardDetails = append(dashboardDetails, t)
	}

	// 保存
	if e := dashboardItem.SaveIndexDashboard(dashboardItem, dashboardDetails, isUpdate, updateCols); e != nil {
		br.Msg = "操作失败"
		br.ErrMsg = fmt.Sprintf("保存标的看板失败, %v", e)
		return
	}

	br.Ret = 200
	br.Success = true
	br.Msg = "操作成功"
}

// DashboardDetail
// @Title 看板详情
// @Description 看板详情
// @Param   IndexId   query   int   true   "标的ID"
// @Success 200 {object} aiPredictModel.AiPredictModelDashboardDetailResp
// @router /index/dashboard/detail [get]
func (this *AiPredictModelIndexController) DashboardDetail() {
	br := new(models.BaseResponse).Init()
	defer func() {
		if br.ErrMsg == "" {
			br.IsSendEmail = false
		}
		this.Data["json"] = br
		this.ServeJSON()
	}()
	sysUser := this.SysUser
	if sysUser == nil {
		br.Msg = "请登录"
		br.ErrMsg = "请登录,SysUser Is Empty"
		br.Ret = 408
		return
	}
	indexId, _ := this.GetInt("IndexId")
	resp := new(aiPredictModel.AiPredictModelDashboardDetailResp)

	indexOb := new(aiPredictModel.AiPredictModelIndex)
	indexItem, e := indexOb.GetItemById(indexId)
	if e != nil {
		if e.Error() == utils.ErrNoRow() {
			br.Msg = "标的已被删除,请刷新页面"
			return
		}
		br.Msg = "获取失败"
		br.ErrMsg = fmt.Sprintf("获取标的失败, %v", e)
		return
	}
	resp.CreateUserId = indexItem.SysUserId
	resp.CreateUserRealName = indexItem.SysUserRealName

	// 获取标的看板
	dashboardOb := new(aiPredictModel.AiPredictModelDashboard)
	dashboardItem := new(aiPredictModel.AiPredictModelDashboardItem)
	{
		cond := fmt.Sprintf(" AND %s = ?", dashboardOb.Cols().AiPredictModelIndexId)
		pars := make([]interface{}, 0)
		pars = append(pars, indexId)
		item, e := dashboardOb.GetItemByCondition(cond, pars, "")
		if e != nil && e.Error() != utils.ErrNoRow() {
			br.Msg = "操作失败"
			br.ErrMsg = fmt.Sprintf("获取标的看板失败, %v", e)
			return
		}
		if item != nil {
			dashboardItem = item.Format2Item()
		}
	}

	// 获取看板详情
	dashboardDetails := make([]*aiPredictModel.AiPredictModelDashboardDetailItem, 0)
	if dashboardItem.DashboardId > 0 {
		detailOb := new(aiPredictModel.AiPredictModelDashboardDetail)
		cond := fmt.Sprintf(" AND %s = ?", detailOb.Cols().AiPredictModelDashboardId)
		pars := make([]interface{}, 0)
		pars = append(pars, dashboardItem.DashboardId)
		list, e := detailOb.GetItemsByCondition(cond, pars, []string{}, fmt.Sprintf("%s ASC", detailOb.Cols().Sort))
		if e != nil {
			br.Msg = "获取失败"
			br.ErrMsg = fmt.Sprintf("获取看板详情失败, %v", e)
			return
		}
		for _, v := range list {
			dashboardDetails = append(dashboardDetails, v.Format2Item())
		}
	}

	resp.AiPredictModelDashboardItem = dashboardItem
	resp.List = dashboardDetails
	br.Data = resp
	br.Ret = 200
	br.Success = true
	br.Msg = "获取成功"
}