Browse Source

Merge remote-tracking branch 'origin/master' into eta/1.0.2

# Conflicts:
#	controllers/data_manage/excel/excel_info.go
#	go.mod
#	go.sum
#	services/data/edb_info_calculate.go
#	utils/logs.go
Roc 1 year ago
parent
commit
19e6d3556d
85 changed files with 5242 additions and 1765 deletions
  1. 2 1
      .gitignore
  2. 17 7
      controllers/banner.go
  3. 98 51
      controllers/base_auth.go
  4. 88 10
      controllers/base_common.go
  5. 18 7
      controllers/cloud_disk.go
  6. 172 246
      controllers/data_manage/chart_info.go
  7. 171 45
      controllers/data_manage/edb_classify.go
  8. 71 40
      controllers/data_manage/edb_info.go
  9. 61 17
      controllers/data_manage/edb_info_calculate.go
  10. 244 108
      controllers/data_manage/future_good/future_good_chart_info.go
  11. 44 16
      controllers/data_manage/line_feature/chart_info.go
  12. 13 11
      controllers/data_manage/multiple_graph_config.go
  13. 178 3
      controllers/data_manage/predict_edb_classify.go
  14. 138 125
      controllers/data_manage/predict_edb_info.go
  15. 16 18
      controllers/data_manage/supply_analysis/variety_edb.go
  16. 13 3
      controllers/english_report/email.go
  17. 252 105
      controllers/english_report/english_classify.go
  18. 104 11
      controllers/english_report/report.go
  19. 114 0
      controllers/meeting_probabilities.go
  20. 25 9
      controllers/ppt_english.go
  21. 20 9
      controllers/ppt_v2.go
  22. 31 12
      controllers/report.go
  23. 117 47
      controllers/resource.go
  24. 20 15
      controllers/sys_admin.go
  25. 9 0
      controllers/sys_role.go
  26. 11 0
      controllers/target.go
  27. 22 10
      controllers/voice.go
  28. 0 0
      etalogs/binlog/20231023.log
  29. 14 5
      go.mod
  30. 29 6
      go.sum
  31. 7 0
      models/data_manage/chart_classify.go
  32. 131 58
      models/data_manage/chart_info.go
  33. 89 1
      models/data_manage/edb_classify.go
  34. 27 7
      models/data_manage/edb_info.go
  35. 1 1
      models/data_manage/edb_info_calculate.go
  36. 6 1
      models/data_manage/excel_style.go
  37. 4 4
      models/data_manage/line_feature/line_feature.go
  38. 2 1
      models/data_manage/line_feature/request/line_feature.go
  39. 1 0
      models/data_manage/multiple_graph_config.go
  40. 54 0
      models/data_manage/request/meeting_probabilities.go
  41. 14 13
      models/data_manage/request/predict_edb_info.go
  42. 10 7
      models/data_manage/response/predit_edb_info.go
  43. 141 52
      models/english_report.go
  44. 1 0
      models/english_report_email.go
  45. 24 8
      models/english_video.go
  46. 33 0
      models/meeting_probabilities.go
  47. 1 1
      models/ppt_english/ppt_english_grant.go
  48. 4 0
      models/ppt_english/ppt_english_group.go
  49. 1 0
      models/ppt_v2_grant.go
  50. 4 0
      models/ppt_v2_group.go
  51. 0 17
      models/response/company.go
  52. 0 124
      models/response/statistic_report.go
  53. 93 0
      models/sandbox/sandbox_classify.go
  54. 54 0
      routers/commentsRouter.go
  55. 5 0
      routers/router.go
  56. 1 1
      services/data/base_edb_lib.go
  57. 547 86
      services/data/chart_info.go
  58. 2 2
      services/data/correlation/chart_info.go
  59. 570 78
      services/data/edb_classify.go
  60. 0 1
      services/data/edb_data.go
  61. 188 127
      services/data/edb_info.go
  62. 23 0
      services/data/edb_info_calculate.go
  63. 39 28
      services/data/future_good/chart_info.go
  64. 1 1
      services/data/line_equation/chart_info.go
  65. 5 5
      services/data/line_feature/chart_info.go
  66. 6 8
      services/data/predict_edb_info.go
  67. 2 3
      services/data/supply_analysis/variety_edb.go
  68. 13 5
      services/data/trade_analysis/trade_analysis.go
  69. 144 102
      services/english_report.go
  70. 2 7
      services/english_video.go
  71. 1 1
      services/excel/lucky_sheet.go
  72. 15 6
      services/file.go
  73. 347 0
      services/minio.go
  74. 25 3
      services/ppt.go
  75. 15 0
      services/ppt/ppt_english_group.go
  76. 14 0
      services/ppt/ppt_group.go
  77. 29 9
      services/report.go
  78. 61 19
      services/sms.go
  79. 11 0
      services/task.go
  80. 61 9
      services/user_login.go
  81. 30 8
      services/video.go
  82. 70 1
      utils/common.go
  83. 59 0
      utils/config.go
  84. 16 0
      utils/constants.go
  85. 126 23
      utils/logs.go

+ 2 - 1
.gitignore

@@ -4,11 +4,12 @@
 /routers/.DS_Store
 /rdlucklog
 /conf/*.conf
-/binlog/*.log
+/binlog/*
 /*.pdf
 /eta_api.tar.gz
 /eta_api
 /static/searchKeywordCount.xlsx
+/static/images/*
 .DS_Store
 /doc/
 *.DS_Store

+ 17 - 7
controllers/banner.go

@@ -34,7 +34,7 @@ func (this *BannerController) Upload() {
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
-	err = os.MkdirAll(uploadDir, 777)
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		br.Msg = "存储目录创建失败"
 		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
@@ -50,12 +50,22 @@ func (this *BannerController) Upload() {
 		br.ErrMsg = "文件上传失败,Err:" + err.Error()
 		return
 	}
-	//上传到阿里云
-	resourceUrl, err := services.UploadAliyunV2(fileName, fpath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
+	resourceUrl := ``
+	//上传到阿里云 和 minio
+	if utils.ObjectStorageClient == "minio" {
+		resourceUrl, err = services.UploadImgToMinIo(fileName, fpath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+	} else {
+		resourceUrl, err = services.UploadAliyunV2(fileName, fpath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
 	}
 
 	defer func() {

+ 98 - 51
controllers/base_auth.go

@@ -7,6 +7,7 @@ import (
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/services/data"
 	"fmt"
+	"github.com/sirupsen/logrus"
 	"net/http"
 	"net/url"
 	"strconv"
@@ -17,12 +18,8 @@ import (
 
 	"eta/eta_api/models"
 	"eta/eta_api/utils"
-
-	"github.com/rdlucklib/rdluck_tools/log"
 )
 
-var apiLog *log.Log
-
 type AfterHandle func(bodyByte []byte) error
 
 // AfterHandlerUrlMap 结束后待处理的url
@@ -51,37 +48,28 @@ var AdminOperateRecordMap = map[string]string{
 	"/adminapi/system/menu/list":                                     "/adminapi/system/menu/list",
 }
 
-func init() {
-	if utils.RunMode == "release" {
-		logDir := `/data/rdlucklog/eta_api`
-		apiLog = log.Init("20060102.api", logDir)
-	} else {
-		apiLog = log.Init("20060102.api")
-	}
-}
-
 type BaseAuthController struct {
 	web.Controller
 	SysUser *system.Admin
 }
 
-func (this *BaseAuthController) Prepare() {
+func (c *BaseAuthController) Prepare() {
 	fmt.Println("enter prepare")
-	method := this.Ctx.Input.Method()
-	uri := this.Ctx.Input.URI()
+	method := c.Ctx.Input.Method()
+	uri := c.Ctx.Input.URI()
 	fmt.Println("Url:", uri)
 	if method != "HEAD" {
 		if method == "POST" || method == "GET" {
-			authorization := this.Ctx.Input.Header("authorization")
+			authorization := c.Ctx.Input.Header("authorization")
 			if authorization == "" {
-				authorization = this.Ctx.Input.Header("Authorization")
+				authorization = c.Ctx.Input.Header("Authorization")
 			}
 			if authorization == "" {
-				newAuthorization := this.GetString("authorization")
+				newAuthorization := c.GetString("authorization")
 				if newAuthorization != `` {
 					authorization = "authorization=" + newAuthorization
 				} else {
-					newAuthorization = this.GetString("Authorization")
+					newAuthorization = c.GetString("Authorization")
 					authorization = "authorization=" + newAuthorization
 				}
 			} else {
@@ -101,14 +89,14 @@ func (this *BaseAuthController) Prepare() {
 				}
 			}
 			if authorization == "" {
-				this.JSON(models.BaseResponse{Ret: 408, Msg: "请重新授权!", ErrMsg: "请重新授权:Token is empty or account is empty"}, false, false)
-				this.StopRun()
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "请重新授权!", ErrMsg: "请重新授权:Token is empty or account is empty"}, false, false)
+				c.StopRun()
 				return
 			}
 			authorizationArr := strings.Split(authorization, "$")
 			if len(authorizationArr) <= 1 {
-				this.JSON(models.BaseResponse{Ret: 408, Msg: "请重新授权!", ErrMsg: "请重新授权:Token is empty or account is empty"}, false, false)
-				this.StopRun()
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "请重新授权!", ErrMsg: "请重新授权:Token is empty or account is empty"}, false, false)
+				c.StopRun()
 				return
 			}
 			tokenStr := authorizationArr[0]
@@ -121,51 +109,51 @@ func (this *BaseAuthController) Prepare() {
 			//校验token是否合法
 			// JWT校验Token和Account
 			if !utils.CheckToken(account, token) {
-				this.JSON(models.BaseResponse{Ret: 408, Msg: "鉴权失败,请重新登录!", ErrMsg: "登录失效,请重新登陆!,CheckToken Fail"}, false, false)
-				this.StopRun()
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "鉴权失败,请重新登录!", ErrMsg: "登录失效,请重新登陆!,CheckToken Fail"}, false, false)
+				c.StopRun()
 				return
 			}
 			session, err := system.GetSysSessionByToken(token)
 			if err != nil {
 				if err.Error() == utils.ErrNoRow() {
-					this.JSON(models.BaseResponse{Ret: 408, Msg: "信息已变更,请重新登陆!", ErrMsg: "Token 信息已变更:Token: " + token}, false, false)
-					this.StopRun()
+					c.JSON(models.BaseResponse{Ret: 408, Msg: "信息已变更,请重新登陆!", ErrMsg: "Token 信息已变更:Token: " + token}, false, false)
+					c.StopRun()
 					return
 				}
-				this.JSON(models.BaseResponse{Ret: 408, Msg: "网络异常,请稍后重试!", ErrMsg: "获取用户信息异常,Eerr:" + err.Error()}, false, false)
-				this.StopRun()
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "网络异常,请稍后重试!", ErrMsg: "获取用户信息异常,Eerr:" + err.Error()}, false, false)
+				c.StopRun()
 				return
 			}
 			if session == nil {
-				this.JSON(models.BaseResponse{Ret: 408, Msg: "网络异常,请稍后重试!", ErrMsg: "sesson is empty "}, false, false)
-				this.StopRun()
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "网络异常,请稍后重试!", ErrMsg: "sesson is empty "}, false, false)
+				c.StopRun()
 				return
 			}
 			if time.Now().After(session.ExpiredTime) {
-				this.JSON(models.BaseResponse{Ret: 408, Msg: "请重新登录!", ErrMsg: "获取用户信息异常,Eerr:" + err.Error()}, false, false)
-				this.StopRun()
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "请重新登录!", ErrMsg: "获取用户信息异常,Eerr:" + err.Error()}, false, false)
+				c.StopRun()
 				return
 			}
 			admin, err := system.GetSysUserById(session.SysUserId)
 			if err != nil {
 				if err.Error() == utils.ErrNoRow() {
-					this.JSON(models.BaseResponse{Ret: 408, Msg: "信息已变更,请重新登陆!", ErrMsg: "获取admin 信息失败 " + strconv.Itoa(session.SysUserId)}, false, false)
-					this.StopRun()
+					c.JSON(models.BaseResponse{Ret: 408, Msg: "信息已变更,请重新登陆!", ErrMsg: "获取admin 信息失败 " + strconv.Itoa(session.SysUserId)}, false, false)
+					c.StopRun()
 					return
 				}
-				this.JSON(models.BaseResponse{Ret: 408, Msg: "网络异常,请稍后重试!", ErrMsg: "获取admin信息异常,Eerr:" + err.Error()}, false, false)
-				this.StopRun()
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "网络异常,请稍后重试!", ErrMsg: "获取admin信息异常,Eerr:" + err.Error()}, false, false)
+				c.StopRun()
 				return
 			}
 			if admin == nil {
-				this.JSON(models.BaseResponse{Ret: 408, Msg: "网络异常,请稍后重试!", ErrMsg: "admin is empty "}, false, false)
-				this.StopRun()
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "网络异常,请稍后重试!", ErrMsg: "admin is empty "}, false, false)
+				c.StopRun()
 				return
 			}
 			//如果不是启用状态
 			if admin.Enabled != 1 {
-				this.JSON(models.BaseResponse{Ret: 408, Msg: "账户信息异常!", ErrMsg: "账户被禁用,不允许登陆!,CheckToken Fail"}, false, false)
-				this.StopRun()
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "账户信息异常!", ErrMsg: "账户被禁用,不允许登陆!,CheckToken Fail"}, false, false)
+				c.StopRun()
 				return
 			}
 
@@ -174,15 +162,15 @@ func (this *BaseAuthController) Prepare() {
 				loginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN, session.Id)
 				loginInfo, _ := utils.Rc.RedisString(loginKey)
 				if loginInfo == `` {
-					this.JSON(models.BaseResponse{Ret: 408, Msg: "超时未操作,系统自动退出!", ErrMsg: "超时未操作,系统自动退出!"}, false, false)
-					this.StopRun()
+					c.JSON(models.BaseResponse{Ret: 408, Msg: "超时未操作,系统自动退出!", ErrMsg: "超时未操作,系统自动退出!"}, false, false)
+					c.StopRun()
 					return
 				}
 
 				if loginInfo != "1" {
 					msg := `该账号于` + admin.LastLoginTime + "在其他网络登录。此客户端已退出登录。"
-					this.JSON(models.BaseResponse{Ret: 408, Msg: msg, ErrMsg: msg}, false, false)
-					this.StopRun()
+					c.JSON(models.BaseResponse{Ret: 408, Msg: msg, ErrMsg: msg}, false, false)
+					c.StopRun()
 					return
 				}
 
@@ -196,10 +184,10 @@ func (this *BaseAuthController) Prepare() {
 			}
 
 			admin.RoleTypeCode = GetSysUserRoleTypeCode(admin.RoleTypeCode)
-			this.SysUser = admin
+			c.SysUser = admin
 		} else {
-			this.JSON(models.BaseResponse{Ret: 408, Msg: "请求异常,请联系客服!", ErrMsg: "POST之外的请求,暂不支持"}, false, false)
-			this.StopRun()
+			c.JSON(models.BaseResponse{Ret: 408, Msg: "请求异常,请联系客服!", ErrMsg: "POST之外的请求,暂不支持"}, false, false)
+			c.StopRun()
 			return
 		}
 	}
@@ -305,8 +293,7 @@ func (c *BaseAuthController) JSON(data interface{}, hasIndent bool, coding bool)
 	if requestBody == "" {
 		requestBody = c.Ctx.Input.URI()
 	}
-	apiLog.Println("请求地址:", c.Ctx.Input.URI(), "Authorization:", c.Ctx.Input.Header("Authorization"), "RequestBody:", requestBody, "ResponseBody", string(content), "IP:", ip)
-
+	c.logUri(data, requestBody, ip)
 	// 如果不是debug分支的话,那么需要加密返回
 	if utils.RunMode != "debug" {
 		content = utils.DesBase64Encrypt(content)
@@ -368,3 +355,63 @@ func GetSysUserRoleTypeCode(roleTypeCode string) string {
 	}
 	return ""
 }
+
+func (c *BaseAuthController) logUri(data interface{}, requestBody, ip string) {
+	authorization := ""
+	method := c.Ctx.Input.Method()
+	uri := c.Ctx.Input.URI()
+	fmt.Println("Url:", uri)
+	if method != "HEAD" {
+		if method == "POST" || method == "GET" {
+			authorization = c.Ctx.Input.Header("authorization")
+			if authorization == "" {
+				authorization = c.Ctx.Input.Header("Authorization")
+			}
+			if authorization == "" {
+				newAuthorization := c.GetString("authorization")
+				if newAuthorization != `` {
+					authorization = "authorization=" + newAuthorization
+				} else {
+					newAuthorization = c.GetString("Authorization")
+					authorization = "authorization=" + newAuthorization
+				}
+			} else {
+				if strings.Contains(authorization, ";") {
+					authorization = strings.Replace(authorization, ";", "$", 1)
+				}
+			}
+			if authorization == "" {
+				strArr := strings.Split(uri, "?")
+				for k, v := range strArr {
+					fmt.Println(k, v)
+				}
+				if len(strArr) > 1 {
+					authorization = strArr[1]
+					authorization = strings.Replace(authorization, "Authorization", "authorization", -1)
+					fmt.Println(authorization)
+				}
+			}
+		}
+	}
+
+	var reqData interface{}
+	err := json.Unmarshal([]byte(requestBody), &reqData)
+	if err != nil {
+		utils.ApiLog.WithFields(logrus.Fields{
+			"uri":           c.Ctx.Input.URI(),
+			"authorization": authorization,
+			"requestBody":   requestBody,
+			"responseBody":  data,
+			"ip":            ip,
+		}).Info("请求详情")
+	} else {
+		utils.ApiLog.WithFields(logrus.Fields{
+			"uri":           c.Ctx.Input.URI(),
+			"authorization": authorization,
+			"requestBody":   reqData,
+			"responseBody":  data,
+			"ip":            ip,
+		}).Info("请求详情")
+	}
+	return
+}

+ 88 - 10
controllers/base_common.go

@@ -4,27 +4,44 @@ import (
 	"encoding/json"
 	"eta/eta_api/cache"
 	"eta/eta_api/models"
+	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/beego/beego/v2/server/web"
+	"github.com/sirupsen/logrus"
 	"net/http"
 	"net/url"
+	"strings"
 )
 
 type BaseCommonController struct {
 	web.Controller
 }
 
-func (this *BaseCommonController) Prepare() {
+func (c *BaseCommonController) Prepare() {
 	var requestBody string
-	method := this.Ctx.Input.Method()
+	method := c.Ctx.Input.Method()
 	if method == "GET" {
-		requestBody = this.Ctx.Request.RequestURI
+		requestBody = c.Ctx.Request.RequestURI
 	} else {
-		requestBody, _ = url.QueryUnescape(string(this.Ctx.Input.RequestBody))
+		requestBody, _ = url.QueryUnescape(string(c.Ctx.Input.RequestBody))
+	}
+	ip := c.Ctx.Input.IP()
+	var reqData interface{}
+	err := json.Unmarshal([]byte(requestBody), &reqData)
+	if err != nil {
+		utils.ApiLog.WithFields(logrus.Fields{
+			"uri":         c.Ctx.Input.URI(),
+			"requestBody": requestBody,
+			"ip":          ip,
+		}).Info("Prepare 请求详情")
+	} else {
+		utils.ApiLog.WithFields(logrus.Fields{
+			"uri":         c.Ctx.Input.URI(),
+			"requestBody": reqData,
+			"ip":          ip,
+		}).Info("Prepare 请求详情")
 	}
-	ip := this.Ctx.Input.IP()
-	apiLog.Println("请求地址:", this.Ctx.Input.URI(), "RequestBody:", requestBody, "IP:", ip)
 }
 
 func (c *BaseCommonController) ServeJSON(encoding ...bool) {
@@ -39,12 +56,14 @@ func (c *BaseCommonController) ServeJSON(encoding ...bool) {
 		hasEncoding = true
 	}
 	if c.Data["json"] == nil {
-		go utils.SendEmail("异常提醒:"+utils.RunMode, "接口:"+"URI:"+c.Ctx.Input.URI()+";无返回值", utils.EmailSendToUsers)
+		//go utils.SendEmail("异常提醒:"+utils.RunMode, "接口:"+"URI:"+c.Ctx.Input.URI()+";无返回值", utils.EmailSendToUsers)
+		go alarm_msg.SendAlarmMsg("接口:URI:"+c.Ctx.Input.URI()+";无返回值", 3)
 		return
 	}
 	baseRes := c.Data["json"].(*models.BaseResponse)
 	if baseRes != nil && !baseRes.Success && baseRes.IsSendEmail {
-		go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "URI:"+c.Ctx.Input.URI()+" ErrMsg:"+baseRes.ErrMsg+";Msg"+baseRes.Msg, utils.EmailSendToUsers)
+		//go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "URI:"+c.Ctx.Input.URI()+" ErrMsg:"+baseRes.ErrMsg+";Msg"+baseRes.Msg, utils.EmailSendToUsers)
+		go alarm_msg.SendAlarmMsg("失败提醒:URI:"+c.Ctx.Input.URI()+" ErrMsg:"+baseRes.ErrMsg+";Msg"+baseRes.Msg, 3)
 	}
 
 	//新增uuid记录
@@ -97,8 +116,7 @@ func (c *BaseCommonController) JSON(data interface{}, hasIndent bool, coding boo
 	fmt.Println("params")
 	fmt.Println(params)
 	requestBody, _ := url.QueryUnescape(string(c.Ctx.Input.RequestBody))
-	apiLog.Println("请求地址:", c.Ctx.Input.URI(), "Authorization:", c.Ctx.Input.Header("Authorization"), "RequestBody:", requestBody, "ResponseBody", string(content), "IP:", ip)
-
+	c.logUri(data, requestBody, ip)
 	// 如果不是debug分支的话,那么需要加密返回
 	if utils.RunMode != "debug" {
 		content = utils.DesBase64Encrypt(content)
@@ -110,3 +128,63 @@ func (c *BaseCommonController) JSON(data interface{}, hasIndent bool, coding boo
 	}
 	return c.Ctx.Output.Body(content)
 }
+
+func (c *BaseCommonController) logUri(data interface{}, requestBody, ip string) {
+	authorization := ""
+	method := c.Ctx.Input.Method()
+	uri := c.Ctx.Input.URI()
+	fmt.Println("Url:", uri)
+	if method != "HEAD" {
+		if method == "POST" || method == "GET" {
+			authorization = c.Ctx.Input.Header("authorization")
+			if authorization == "" {
+				authorization = c.Ctx.Input.Header("Authorization")
+			}
+			if authorization == "" {
+				newAuthorization := c.GetString("authorization")
+				if newAuthorization != `` {
+					authorization = "authorization=" + newAuthorization
+				} else {
+					newAuthorization = c.GetString("Authorization")
+					authorization = "authorization=" + newAuthorization
+				}
+			} else {
+				if strings.Contains(authorization, ";") {
+					authorization = strings.Replace(authorization, ";", "$", 1)
+				}
+			}
+			if authorization == "" {
+				strArr := strings.Split(uri, "?")
+				for k, v := range strArr {
+					fmt.Println(k, v)
+				}
+				if len(strArr) > 1 {
+					authorization = strArr[1]
+					authorization = strings.Replace(authorization, "Authorization", "authorization", -1)
+					fmt.Println(authorization)
+				}
+			}
+		}
+	}
+
+	var reqData interface{}
+	err := json.Unmarshal([]byte(requestBody), &reqData)
+	if err != nil {
+		utils.ApiLog.WithFields(logrus.Fields{
+			"uri":           c.Ctx.Input.URI(),
+			"authorization": authorization,
+			"requestBody":   requestBody,
+			"responseBody":  data,
+			"ip":            ip,
+		}).Info("请求详情")
+	} else {
+		utils.ApiLog.WithFields(logrus.Fields{
+			"uri":           c.Ctx.Input.URI(),
+			"authorization": authorization,
+			"requestBody":   reqData,
+			"responseBody":  data,
+			"ip":            ip,
+		}).Info("请求详情")
+	}
+	return
+}

+ 18 - 7
controllers/cloud_disk.go

@@ -564,7 +564,7 @@ func (this *CloudDiskController) ResourceUpload() {
 	}
 
 	uploadDir := utils.STATIC_DIR + "hongze/" + time.Now().Format("20060102")
-	if e = os.MkdirAll(uploadDir, 766); e != nil {
+	if e = os.MkdirAll(uploadDir, utils.DIR_MOD); e != nil {
 		br.Msg = "存储目录创建失败"
 		br.ErrMsg = "存储目录创建失败, Err:" + e.Error()
 		return
@@ -581,13 +581,24 @@ func (this *CloudDiskController) ResourceUpload() {
 	}()
 	// 上传到阿里云
 	ossDir := "static/cloud_disk/"
-	resourceUrl, e := services.UploadAliyunToDir(ossFileName, filePath, ossDir, "")
-	if e != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败, Err:" + e.Error()
-		return
-	}
 
+	resourceUrl := ``
+	//上传到阿里云 和 minio
+	if utils.ObjectStorageClient == "minio" {
+		resourceUrl, e = services.UploadMinIoToDir(ossFileName, filePath, ossDir, "")
+		if e != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败, Err:" + e.Error()
+			return
+		}
+	} else {
+		resourceUrl, e = services.UploadAliyunToDir(ossFileName, filePath, ossDir, "")
+		if e != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败, Err:" + e.Error()
+			return
+		}
+	}
 	// 新增云盘资源
 	extMap := services.GetCloudDiskResourceFileTypeExtMap()
 	resourceIcon := extMap[ext]

+ 172 - 246
controllers/data_manage/chart_info.go

@@ -121,7 +121,6 @@ func (this *ChartInfoController) ChartInfoSave() {
 		edbInfoIdArrStr = append(edbInfoIdArrStr, strconv.Itoa(v))
 	}
 	edbInfoIdStr := strings.Join(edbInfoIdArrStr, ",")
-
 	err = data_manage.ModifyChartInfoAndMapping(edbInfoIdStr, &req, chartItem.ChartType)
 	if err != nil {
 		br.Msg = "保存失败"
@@ -760,43 +759,15 @@ func (this *ChartInfoController) ChartInfoDetail() {
 
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
-	seasonStartDate := this.GetString("SeasonStartDate")
-	seasonEndDate := this.GetString("SeasonEndDate")
+	startYear, _ := this.GetInt("StartYear")
 
 	edbInfoId := this.GetString("EdbInfoId")
 	chartType, _ := this.GetInt("ChartType")
 
-	fmt.Println("seasonStartDate:", seasonStartDate)
-	fmt.Println("seasonEndDate:", seasonEndDate)
-
 	calendar := this.GetString("Calendar")
 	if calendar == "" {
 		calendar = "公历"
 	}
-	switch dateType {
-	case 1:
-		startDate = "2000-01-01"
-	case 2:
-		startDate = "2010-01-01"
-	case 3:
-		startDate = "2015-01-01"
-	case 4:
-		//startDate = strconv.Itoa(time.Now().Year()) + "-01-01"
-		startDate = "2021-01-01"
-	case 5:
-		startDate = startDate + "-01"
-		endDate = endDate + "-01"
-	case 6:
-		startDate = startDate + "-01"
-	case 7:
-		startDate = "2018-01-01"
-	case 8:
-		startDate = "2019-01-01"
-	case 9:
-		startDate = "2020-01-01"
-	case 11:
-		startDate = "2022-01-01"
-	}
 
 	var err error
 	chartInfo := new(data_manage.ChartInfoView)
@@ -816,27 +787,6 @@ func (this *ChartInfoController) ChartInfoDetail() {
 		chartType = chartInfo.ChartType
 	}
 
-	if chartType == 2 {
-		if seasonStartDate != "" {
-			startDate = seasonStartDate + "-01"
-		} else {
-			fivePre := time.Now().AddDate(-4, 0, 0).Year()
-			startDate = strconv.Itoa(fivePre) + "-01-01"
-		}
-		if seasonEndDate != "" {
-			seasonEndDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, seasonEndDate+"-01", time.Local)
-			if tmpErr != nil {
-				br.Msg = "获取失败"
-				br.ErrMsg = "获取图表,指标信息失败,Err:" + tmpErr.Error()
-				return
-			}
-			endDate = seasonEndDateTime.AddDate(0, 1, -1).Format(utils.FormatDate)
-			//endDate = seasonEndDate + "-12-31"
-		} else {
-			endDate = "" //time.Now().AddDate(2, 0, 0).Format(utils.FormatDate)
-		}
-	}
-
 	mappingList := make([]*data_manage.ChartEdbInfoMapping, 0)
 	if chartInfoId > 0 {
 		mappingList, err = data_manage.GetChartEdbMappingList(chartInfoId)
@@ -874,9 +824,27 @@ func (this *ChartInfoController) ChartInfoDetail() {
 		}
 		extraConfigStr = chartInfo.BarConfig
 	}
+	yearMax := 0
+	if dateType == utils.DateTypeNYears {
+		for _, v := range mappingList {
+			if v.LatestDate != "" {
+				lastDateT, tErr := time.Parse(utils.FormatDate, v.LatestDate)
+				if tErr != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取图表日期信息失败,Err:" + tErr.Error()
+					return
+				}
+				if lastDateT.Year() > yearMax {
+					yearMax = lastDateT.Year()
+				}
+			}
+		}
+	}
+	// 开始/结束日期
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, yearMax)
 
 	// 获取图表中的指标数据
-	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(chartInfoId, chartType, calendar, startDate, endDate, mappingList, extraConfigStr)
+	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(chartInfoId, chartType, calendar, startDate, endDate, mappingList, extraConfigStr, chartInfo.SeasonExtraConfig)
 	if err != nil {
 		br.Msg = "获取失败"
 		if errMsg != `` {
@@ -921,6 +889,15 @@ func (this *ChartInfoController) ChartInfoDetail() {
 				chartInfo.MyChartClassifyId = myChartList[0].MyChartClassifyId
 			}
 		}
+		if chartInfo.ChartType == 2 {
+			if chartInfo.SeasonStartDate != "" {
+				chartInfo.StartDate = chartInfo.SeasonStartDate
+				chartInfo.EndDate = chartInfo.SeasonEndDate
+				if chartInfo.DateType == 3 {
+					chartInfo.DateType = 5
+				}
+			}
+		}
 	}
 
 	//图表操作权限
@@ -988,43 +965,14 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 		return
 	}
 
-	seasonStartDate := req.SeasonStartDate
-	seasonEndDate := req.SeasonEndDate
-	fmt.Println("seasonStartDate:", seasonStartDate)
-	fmt.Println("seasonEndDate:", seasonEndDate)
-
 	calendar := req.Calendar
 	if calendar == "" {
 		calendar = "公历"
 	}
 
-	// 开始/结束日期
-	startDate, endDate := utils.GetDateByDateType(req.DateType, req.StartDate, req.EndDate)
-
 	chartInfo := new(data_manage.ChartInfoView)
 	chartInfo.ChartType = req.ChartType
 
-	if req.ChartType == 2 {
-		if seasonStartDate != "" {
-			startDate = seasonStartDate + "-01"
-		} else {
-			fivePre := time.Now().AddDate(-4, 0, 0).Year()
-			startDate = strconv.Itoa(fivePre) + "-01-01"
-		}
-		if seasonEndDate != "" {
-			seasonEndDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, seasonEndDate+"-01", time.Local)
-			if tmpErr != nil {
-				br.Msg = "获取失败"
-				br.ErrMsg = "获取图表,指标信息失败,Err:" + tmpErr.Error()
-				return
-			}
-			endDate = seasonEndDateTime.AddDate(0, 1, -1).Format(utils.FormatDate)
-			//endDate = seasonEndDate + "-12-31"
-		} else {
-			endDate = "" //time.Now().AddDate(2, 0, 0).Format(utils.FormatDate)
-		}
-	}
-
 	edbInfoIdList := make([]int, 0)
 	edbInfoIdMapping := make(map[int]*data_manage.ChartSaveItem)
 	for _, v := range req.ChartEdbInfoList {
@@ -1037,7 +985,7 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
 		return
 	}
-
+	yearMax := 0
 	for k, v := range mappingList {
 		if tmpV, ok := edbInfoIdMapping[v.EdbInfoId]; ok {
 			v.EdbInfoType = tmpV.EdbInfoType
@@ -1053,10 +1001,41 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 			v.PredictChartColor = tmpV.PredictChartColor
 			v.ChartWidth = tmpV.ChartWidth
 			//v.Source = tmpV.Source
+			// 得到指标更新的最新的年份
+			if req.DateType == utils.DateTypeNYears && v.LatestDate != "" {
+				lastDateT, tErr := time.Parse(utils.FormatDate, v.LatestDate)
+				if tErr != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取图表日期信息失败,Err:" + tErr.Error()
+					return
+				}
+				if lastDateT.Year() > yearMax {
+					yearMax = lastDateT.Year()
+				}
+			}
 		}
 		mappingList[k] = v
 	}
 
+	// 开始/结束日期
+	startDate, endDate := utils.GetDateByDateTypeV2(req.DateType, req.StartDate, req.EndDate, req.StartYear, yearMax)
+	if startDate == "" {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败 时间格式错误"
+		return
+	}
+
+	var seasonExtraConfig string
+	if req.ChartType == 2 {
+		seasonExtra, tErr := json.Marshal(req.SeasonExtraConfig)
+		if tErr != nil {
+			br.Msg = "季节性图表配置信息异常"
+			br.ErrMsg = "季节性图表配置信息异常,Err:" + tErr.Error()
+			return
+		}
+
+		seasonExtraConfig = string(seasonExtra)
+	}
 	// 图表额外数据参数
 	extraConfigStr := chartInfo.ExtraConfig
 	// 柱方图的一些配置
@@ -1077,7 +1056,7 @@ func (this *ChartInfoController) PreviewChartInfoDetail() {
 	}
 
 	// 获取图表中的指标数据
-	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(0, req.ChartType, calendar, startDate, endDate, mappingList, extraConfigStr)
+	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(0, req.ChartType, calendar, startDate, endDate, mappingList, extraConfigStr, seasonExtraConfig)
 	if err != nil {
 		br.Msg = "获取失败"
 		if errMsg != `` {
@@ -1167,42 +1146,19 @@ func (this *ChartInfoController) ChartInfoDetailV2() {
 
 	dateType := chartInfo.DateType
 	fmt.Println("dateType:", dateType)
-	if dateType <= 0 {
-		dateType = 3
-	}
 
 	chartType := chartInfo.ChartType
 	startDate := chartInfo.StartDate
 	endDate := chartInfo.EndDate
 	seasonStartDate := chartInfo.SeasonStartDate
 	seasonEndDate := chartInfo.SeasonEndDate
+	startYear := chartInfo.StartYear
+
 	calendar := chartInfo.Calendar
 
 	if calendar == "" {
 		calendar = "公历"
 	}
-	startDate, endDate = utils.GetDateByDateType(dateType, startDate, endDate)
-
-	if chartType == 2 {
-		if seasonStartDate != "" {
-			startDate = seasonStartDate + "-01"
-		} else {
-			fivePre := time.Now().AddDate(-4, 0, 0).Year()
-			startDate = strconv.Itoa(fivePre) + "-01-01"
-		}
-		if seasonEndDate != "" {
-			seasonEndDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, seasonEndDate+"-01", time.Local)
-			if tmpErr != nil {
-				br.Msg = "获取失败"
-				br.ErrMsg = "获取图表,指标信息失败,Err:" + tmpErr.Error()
-				return
-			}
-			endDate = seasonEndDateTime.AddDate(0, 1, -1).Format(utils.FormatDate)
-			//endDate = seasonEndDate + "-12-31"
-		} else {
-			endDate = "" //time.Now().AddDate(2, 0, 0).Format(utils.FormatDate)
-		}
-	}
 
 	// 相关联指标
 	mappingList, err := data_manage.GetChartEdbMappingList(chartInfoId)
@@ -1212,6 +1168,43 @@ func (this *ChartInfoController) ChartInfoDetailV2() {
 		return
 	}
 
+	if chartType == 2 {
+		startDate = seasonStartDate
+		endDate = seasonEndDate
+		if dateType <= 0 {
+			if startDate != "" {
+				dateType = 5
+			} else {
+				dateType = utils.DateTypeNYears
+			}
+		}
+	} else {
+		if dateType <= 0 {
+			dateType = 3
+		}
+	}
+	yearMax := 0
+	if dateType == utils.DateTypeNYears {
+		for _, v := range mappingList {
+			if v.LatestDate != "" {
+				lastDateT, tErr := time.Parse(utils.FormatDate, v.LatestDate)
+				if tErr != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取图表日期信息失败,Err:" + tErr.Error()
+					return
+				}
+				if lastDateT.Year() > yearMax {
+					yearMax = lastDateT.Year()
+				}
+			}
+		}
+	}
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, yearMax)
+
+	if chartInfo.ChartType == 2 {
+		chartInfo.StartDate = startDate
+		chartInfo.EndDate = endDate
+	}
 	// 图表额外数据参数
 	extraConfigStr := chartInfo.ExtraConfig
 	// 柱方图的一些配置
@@ -1232,7 +1225,7 @@ func (this *ChartInfoController) ChartInfoDetailV2() {
 	}
 
 	// 获取图表中的指标数据
-	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(chartInfoId, chartType, calendar, startDate, endDate, mappingList, extraConfigStr)
+	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(chartInfoId, chartType, calendar, startDate, endDate, mappingList, extraConfigStr, chartInfo.SeasonExtraConfig)
 	if err != nil {
 		br.Msg = "获取失败"
 		if errMsg != `` {
@@ -1408,56 +1401,26 @@ func (this *ChartInfoController) ChartInfoEdbInfoDetail() {
 
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
+	startYear, _ := this.GetInt("StartYear")
 
 	isTimeBetween := false //是否是时间区间
-	switch dateType {
-	case 1:
-		startDate = "2000-01-01"
-		endDate = ""
-	case 2:
-		startDate = "2010-01-01"
-		endDate = ""
-	case 3:
-		startDate = "2015-01-01"
-		endDate = ""
-	case 4:
-		//startDate = strconv.Itoa(time.Now().Year()) + "-01-01"
-		startDate = "2021-01-01"
-		endDate = ""
-	case 5:
-		startDate = startDate + "-01"
-		endDate = endDate + "-01"
+	if dateType == 5 {
 		isTimeBetween = true
-	case 6:
-		//startDate = startDate + "-01"
-		endDate = ""
-	case 7:
-		startDate = "2018-01-01"
-		endDate = ""
-	case 8:
-		startDate = "2019-01-01"
-		endDate = ""
-	case 9:
-		startDate = "2020-01-01"
-		endDate = ""
-	case 11:
-		startDate = "2022-01-01"
-		endDate = ""
-	default:
-		startDate = startDate + "-01"
-		endDate = endDate + "-01"
-	}
-
-	// 兼容日期错误
-	{
-		if strings.Count(startDate, "-") == 1 {
-			startDate = startDate + "-01"
-		}
-		if strings.Count(endDate, "-") == 1 {
-			endDate = endDate + "-01"
-		}
 	}
 
+	edbInfo, err := data_manage.GetEdbInfoById(edbInfoId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.Msg = "获取失败,Err:" + err.Error()
+		return
+	}
+	maxYear := 0
+	if edbInfo.LatestDate != "" {
+		latestDateT, _ := time.Parse(utils.FormatDate, edbInfo.LatestDate)
+		maxYear = latestDateT.Year()
+	}
+
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
 	if startDate == "" {
 		br.Msg = "参数错误"
 		br.Msg = "参数错误,无效的查询日期"
@@ -1498,13 +1461,6 @@ func (this *ChartInfoController) ChartInfoEdbInfoDetail() {
 		startDateReal = startDate
 	}
 
-	edbInfo, err := data_manage.GetEdbInfoById(edbInfoId)
-	if err != nil {
-		br.Msg = "获取失败"
-		br.Msg = "获取失败,Err:" + err.Error()
-		return
-	}
-
 	var dataList []*data_manage.EdbDataList
 	var minData, maxData float64
 
@@ -2055,13 +2011,10 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 	chartInfoId := chartInfo.ChartInfoId
 
 	dateType := chartInfo.DateType
-	if dateType <= 0 {
-		dateType = 3
-	}
+
 	startDate := chartInfo.StartDate
 	endDate := chartInfo.EndDate
-	seasonStartDate := chartInfo.SeasonStartDate
-	seasonEndDate := chartInfo.SeasonEndDate
+	startYear := chartInfo.StartYear
 	calendar := chartInfo.Calendar
 	chartType := chartInfo.ChartType
 
@@ -2070,69 +2023,6 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 	}
 
 	fmt.Println("a dateType:", dateType)
-	switch dateType {
-	case 1:
-		startDate = "2000-01-01"
-		endDate = ""
-	case 2:
-		startDate = "2010-01-01"
-		endDate = ""
-	case 3:
-		startDate = "2015-01-01"
-		endDate = ""
-	case 4:
-		//startDate = strconv.Itoa(time.Now().Year()) + "-01-01"
-		startDate = "2021-01-01"
-		endDate = ""
-	case 5:
-		startDate = startDate + "-01"
-		endDate = endDate + "-01"
-	case 6:
-		//startDate = startDate + "-01"
-		endDate = ""
-	case 7:
-		startDate = "2018-01-01"
-		endDate = ""
-	case 8:
-		startDate = "2019-01-01"
-		endDate = ""
-	case 9:
-		startDate = "2020-01-01"
-		endDate = ""
-		endDate = ""
-	case 11:
-		startDate = "2022-01-01"
-		endDate = ""
-	}
-
-	// 兼容日期错误
-	{
-		if strings.Count(startDate, "-") == 1 {
-			startDate = startDate + "-01"
-		}
-		if strings.Count(endDate, "-") == 1 {
-			endDate = endDate + "-01"
-		}
-	}
-	if chartType == 2 {
-		if seasonStartDate != "" {
-			startDate = seasonStartDate + "-01"
-		} else {
-			fivePre := time.Now().AddDate(-4, 0, 0).Year()
-			startDate = strconv.Itoa(fivePre) + "-01-01"
-		}
-		if seasonEndDate != "" {
-			seasonEndDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, seasonEndDate+"-01", time.Local)
-			if tmpErr != nil {
-				msg = "获取失败"
-				errMsg = "获取图表,指标信息失败,Err:" + tmpErr.Error()
-				return
-			}
-			endDate = seasonEndDateTime.AddDate(0, 1, -1).Format(utils.FormatDate)
-		} else {
-			//endDate = time.Now().Format(utils.FormatDate)
-		}
-	}
 
 	mappingList, err := data_manage.GetChartEdbMappingList(chartInfoId)
 	if err != nil {
@@ -2141,6 +2031,24 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 		return
 	}
 
+	yearMax := 0
+	if dateType == utils.DateTypeNYears {
+		for _, v := range mappingList {
+			if v.LatestDate != "" {
+				lastDateT, tErr := time.Parse(utils.FormatDate, v.LatestDate)
+				if tErr != nil {
+					msg = "获取失败"
+					errMsg = "获取图表日期信息失败,Err:" + tErr.Error()
+					return
+				}
+				if lastDateT.Year() > yearMax {
+					yearMax = lastDateT.Year()
+				}
+			}
+		}
+	}
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, yearMax)
+
 	extraConfigStr := chartInfo.ExtraConfig //图表额外数据参数
 	var barConfig data_manage.BarChartInfoReq
 	// 柱方图的一些配置
@@ -2162,7 +2070,7 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 	}
 
 	// 获取图表中的指标数据
-	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(chartInfoId, chartType, calendar, startDate, endDate, mappingList, extraConfigStr)
+	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(chartInfoId, chartType, calendar, startDate, endDate, mappingList, extraConfigStr, chartInfo.SeasonExtraConfig)
 	if err != nil {
 		msg = "获取失败"
 		if errMsg != `` {
@@ -2266,7 +2174,7 @@ func (this *ChartInfoController) ChartInfoBase64Upload() {
 
 	uploadDir := "static/images/"
 	if !utils.FileIsExist(uploadDir) {
-		err := os.MkdirAll(uploadDir, 777)
+		err := os.MkdirAll(uploadDir, utils.DIR_MOD)
 		if err != nil {
 			br.Msg = "图表保存失败"
 			br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
@@ -2310,6 +2218,8 @@ func (this *ChartInfoController) ChartInfoBase64Upload() {
 	case <-doneChannel:
 		fmt.Println("done")
 	case err := <-errorChannel:
+		br.Msg = "文件上传失败"
+		br.ErrMsg = fmt.Sprintf("execute command failure err: %s", err.Error())
 		fmt.Println("execute command failure err:" + err.Error())
 		return
 	}
@@ -2320,13 +2230,27 @@ func (this *ChartInfoController) ChartInfoBase64Upload() {
 
 	saveToOssPath = uploadDir + time.Now().Format("200601/20060102/")
 	saveToOssPath += outFileName
-	err = services.UploadFileToAliyun("", outFileName, saveToOssPath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
+
+	//上传到阿里云 和 minio
+	resourceUrl := ``
+	if utils.ObjectStorageClient == "minio" {
+		err = services.UploadFileToMinIo("", outFileName, saveToOssPath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+		resourceUrl = utils.MinIoImghost + saveToOssPath
+	} else {
+		err = services.UploadFileToAliyun("", outFileName, saveToOssPath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+		resourceUrl = utils.Imghost + saveToOssPath
 	}
-	resourceUrl := utils.Imghost + saveToOssPath
+
 	resp.ResourceUrl = resourceUrl
 	resp.Source = "convert"
 	//resp.CacheKey = imgDataKey
@@ -2530,13 +2454,15 @@ func (this *ChartInfoController) CopyChartInfo() {
 		ChartImage:      oldChartInfo.ChartImage,
 		BarConfig:       oldChartInfo.BarConfig,
 		//Sort:     sort,
-		LeftMin:     oldChartInfo.LeftMin,
-		LeftMax:     oldChartInfo.LeftMax,
-		RightMin:    oldChartInfo.RightMin,
-		RightMax:    oldChartInfo.RightMax,
-		Disabled:    oldChartInfo.Disabled,
-		Source:      oldChartInfo.Source,
-		ExtraConfig: oldChartInfo.ExtraConfig,
+		LeftMin:           oldChartInfo.LeftMin,
+		LeftMax:           oldChartInfo.LeftMax,
+		RightMin:          oldChartInfo.RightMin,
+		RightMax:          oldChartInfo.RightMax,
+		Disabled:          oldChartInfo.Disabled,
+		Source:            oldChartInfo.Source,
+		ExtraConfig:       oldChartInfo.ExtraConfig,
+		SeasonExtraConfig: oldChartInfo.SeasonExtraConfig,
+		StartYear:         oldChartInfo.StartYear,
 	}
 	newId, err := data_manage.AddChartInfo(chartInfo)
 	if err != nil {
@@ -2680,7 +2606,7 @@ func (this *ChartInfoController) PreviewBarChartInfo() {
 	}
 
 	// 获取图表中的指标数据
-	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(0, chartType, "", "", "", mappingList, string(this.Ctx.Input.RequestBody))
+	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(0, chartType, "", "", "", mappingList, string(this.Ctx.Input.RequestBody), "")
 	if err != nil {
 		br.Msg = "获取失败"
 		if errMsg != `` {
@@ -2775,7 +2701,7 @@ func (this *ChartInfoController) PreviewSectionScatterChartInfo() {
 	mappingList, err := data_manage.GetChartEdbMappingListByEdbInfoIdList(edbInfoIdArr)
 
 	// 获取图表中的指标数据
-	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(0, chartType, "", "", "", mappingList, req.ExtraConfig)
+	edbList, xEdbIdValue, yDataList, dataResp, err, errMsg := data.GetChartEdbData(0, chartType, "", "", "", mappingList, req.ExtraConfig, "")
 	if err != nil {
 		br.Msg = "获取失败"
 		if errMsg != `` {

+ 171 - 45
controllers/data_manage/edb_classify.go

@@ -8,6 +8,7 @@ import (
 	"eta/eta_api/models/system"
 	"eta/eta_api/services/data"
 	"eta/eta_api/utils"
+	"sort"
 )
 
 // EdbClassifyController 数据管理-分类模块
@@ -15,47 +16,6 @@ type EdbClassifyController struct {
 	controllers.BaseAuthController
 }
 
-//
-//// @Title 分类列表
-//// @Description 分类列表接口
-//// @Success 200 {object} data_manage.EdbClassifyListResp
-//// @router /classify/list [get]
-//func (this *EdbClassifyController) List() {
-//	br := new(models.BaseResponse).Init()
-//	defer func() {
-//		this.Data["json"] = br
-//		this.ServeJSON()
-//	}()
-//	rootList, err := data_manage.GetEdbClassifyByParentId(0)
-//	if err != nil {
-//		br.Msg = "获取失败"
-//		br.ErrMsg = "获取数据失败,Err:" + err.Error()
-//		return
-//	}
-//
-//	classifyAll, err := data_manage.GetEdbClassifyAll()
-//	if err != nil {
-//		br.Msg = "获取失败"
-//		br.ErrMsg = "获取数据失败,Err:" + err.Error()
-//		return
-//	}
-//
-//	fmt.Println("start:", time.Now())
-//	nodeAll := make([]*data_manage.EdbClassifyItems, 0)
-//	for k := range rootList {
-//		rootNode := rootList[k]
-//		data.EdbClassifyListMakeTree(classifyAll, rootNode)
-//		nodeAll = append(nodeAll, rootNode)
-//	}
-//	fmt.Println("end:", time.Now())
-//	resp := new(data_manage.EdbClassifyListResp)
-//	resp.AllNodes = nodeAll
-//	br.Ret = 200
-//	br.Success = true
-//	br.Msg = "获取成功"
-//	br.Data = resp
-//}
-
 // ListV2
 // @Title 分类列表
 // @Description 分类列表接口
@@ -527,13 +487,13 @@ func (this *EdbClassifyController) EdbClassifyMove() {
 		return
 	}
 
-	if req.ClassifyId <= 0 {
+	if req.ClassifyId <= 0 && req.EdbInfoId <= 0 {
 		br.Msg = "参数错误"
-		br.ErrMsg = "分类id小于等于0"
+		br.ErrMsg = "请选择拖动目标,分类目录或者指标"
 		return
 	}
 
-	err, errMsg := data.MoveEdbClassify(req.ClassifyId, req.ParentClassifyId, req.PrevClassifyId, req.NextClassifyId, sysUser)
+	err, errMsg := data.MoveEdbClassify(req, sysUser, 0)
 	if errMsg != `` {
 		br.Msg = errMsg
 		br.ErrMsg = errMsg
@@ -763,7 +723,7 @@ func (this *EdbClassifyController) ClassifyEdbInfoList() {
 		noPermissionEdbInfoIdMap[v.EdbInfoId] = true
 	}
 
-	allEdbInfo, err := data_manage.GetEdbInfoByClassifyId(classifyId, 0)
+	allEdbInfo, err := data_manage.GetEdbInfoByClassifyId(classifyId, 0, 0)
 	if err != nil && err.Error() != utils.ErrNoRow() {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取数据失败,Err:" + err.Error()
@@ -897,6 +857,172 @@ func (this *EdbClassifyController) ItemsV3() {
 	br.Data = resp
 }
 
+// SimpleList
+// @Title 单层分类列表
+// @Description 单层分类列表
+// @Success 200 {object} data_manage.EdbClassifyListResp
+// @router /classify/simple [get]
+func (this *EdbClassifyController) SimpleList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	// 默认查一级分类和一级分类下的指标信息,
+	// 如果是 子级分类,查询该子级分类的下一级分类和指标信息
+	// 增加标识判断是文件夹还是指标列表
+	parentId, _ := this.GetInt("ParentId")
+
+	rootList, err := data_manage.GetEdbClassifyByParentId(parentId, 0)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	nodeAll := make([]*data_manage.EdbClassifyItems, 0)
+
+	var sortList data_manage.EdbClassifyItemList
+	if parentId > 0 {
+		// 查询挂在当前分类上的指标列表
+		// 获取当前账号的不可见指标
+		obj := data_manage.EdbInfoNoPermissionAdmin{}
+		confList, err := obj.GetAllListByAdminId(this.SysUser.AdminId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取不可见指标配置数据失败,Err:" + err.Error()
+			return
+		}
+		noPermissionEdbInfoIdMap := make(map[int]bool)
+		for _, v := range confList {
+			noPermissionEdbInfoIdMap[v.EdbInfoId] = true
+		}
+		allEdbInfo, err := data_manage.GetEdbInfoByClassifyId(parentId, 0, 0)
+		if err != nil {
+			br.Msg = "获取指标数据失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}
+
+		for _, v := range allEdbInfo {
+			// 如果指标不可见,那么就不返回该指标
+			if _, ok := noPermissionEdbInfoIdMap[v.EdbInfoId]; ok {
+				continue
+			}
+			button := data.GetEdbOpButton(this.SysUser, v.SysUserId, v.EdbType, utils.EDB_INFO_TYPE)
+			button.AddButton = false //不管有没有权限,指标都是没有添加按钮的
+			v.Button = button
+			v.Children = make([]*data_manage.EdbClassifyItems, 0)
+			v.ParentId = parentId
+			nodeAll = append(nodeAll, v)
+		}
+	}
+	if len(rootList) > 0 {
+		for _, v := range rootList {
+			button := data.GetEdbClassifyOpButton(this.SysUser, v.SysUserId)
+			v.Button = button
+			v.Children = make([]*data_manage.EdbClassifyItems, 0)
+			nodeAll = append(nodeAll, v)
+		}
+	}
+	if len(nodeAll) > 0 {
+		//根据sort值排序
+		sortList = nodeAll
+		sort.Sort(sortList)
+	}
+
+	language := `CN`
+	// 指标显示的语言
+	{
+		configDetail, _ := system.GetConfigDetailByCode(this.SysUser.AdminId, system.EdbLanguageVar)
+		if configDetail != nil {
+			language = configDetail.ConfigValue
+		} else {
+			configDetail, _ = system.GetDefaultConfigDetailByCode(system.EdbLanguageVar)
+			if configDetail != nil {
+				language = configDetail.ConfigValue
+			}
+		}
+	}
+
+	// 是否允许添加一级分类
+	canOpClassify := true
+	button := data.GetEdbClassifyOpButton(this.SysUser, 0)
+	if !button.AddButton {
+		canOpClassify = false
+	}
+
+	resp := new(data_manage.EdbClassifyListResp)
+	resp.AllNodes = sortList
+	resp.Language = language
+	resp.CanOpClassify = canOpClassify
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// ClassifyTree
+// @Title 多层分类列表树
+// @Description 多层分类列表树
+// @Success 200 {object} data_manage.EdbClassifyListResp
+// @router /classify/tree [get]
+func (this *EdbClassifyController) ClassifyTree() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	allList, err := data_manage.GetNormalEdbClassifyAll()
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	nodeAll := make([]*data_manage.EdbClassifyItems, 0)
+	var sortList data_manage.EdbClassifyItemList
+
+	if len(allList) > 0 {
+		for k, v := range allList {
+			button := data.GetEdbClassifyOpButton(this.SysUser, v.SysUserId)
+			allList[k].Button = button
+		}
+		nodeAll = data.GetClassifyTreeRecursive(allList, 0)
+		//根据sort值排序
+		sortList = nodeAll
+		sort.Sort(sortList)
+	}
+	language := `CN`
+	// 指标显示的语言
+	{
+		configDetail, _ := system.GetConfigDetailByCode(this.SysUser.AdminId, system.EdbLanguageVar)
+		if configDetail != nil {
+			language = configDetail.ConfigValue
+		} else {
+			configDetail, _ = system.GetDefaultConfigDetailByCode(system.EdbLanguageVar)
+			if configDetail != nil {
+				language = configDetail.ConfigValue
+			}
+		}
+	}
+
+	// 是否允许添加一级分类
+	canOpClassify := true
+	button := data.GetEdbClassifyOpButton(this.SysUser, 0)
+	if !button.AddButton {
+		canOpClassify = false
+	}
+
+	resp := new(data_manage.EdbClassifyListResp)
+	resp.AllNodes = sortList
+	resp.Language = language
+	resp.CanOpClassify = canOpClassify
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
 // EdbClassifyMoveV2
 // @Title 分类移动接口v2
 // @Description 分类移动接口v2

+ 71 - 40
controllers/data_manage/edb_info.go

@@ -1748,11 +1748,16 @@ func (this *EdbInfoController) EdbInfoList() {
 	//是否展示英文标识
 	edbInfoItem.IsEnEdb = data.CheckIsEnEdb(edbInfoItem.EdbNameEn, edbInfoItem.Unit, edbInfoItem.UnitEn)
 	//查询目录
-	resultList, _ := data_manage.GetClassifyAllByClassifyId(edbInfoItem.ClassifyId)
+	classifyList, err, errMsg := data.GetFullClassifyByClassifyId(edbInfoItem.ClassifyId)
+	if err != nil {
+		br.Msg = err.Error()
+		br.ErrMsg = errMsg
+		return
+	}
 
 	resp.Paging = page
 	resp.Item = edbInfoItem
-	resp.ClassifyList = resultList
+	resp.ClassifyList = classifyList
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"
@@ -2444,11 +2449,20 @@ func (this *EdbInfoController) EdbInfoDetail() {
 		br.ErrMsg = "获取指标信息失败,Err:" + err.Error()
 		return
 	}
+	var resp data_manage.EdbInfoFullClassify
+	classifyList, err, errMsg := data.GetFullClassifyByClassifyId(edbInfo.ClassifyId)
+	if err != nil {
+		br.Msg = err.Error()
+		br.ErrMsg = errMsg
+		return
+	}
+	resp.EdbInfo = edbInfo
+	resp.ClassifyList = classifyList
 
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"
-	br.Data = edbInfo
+	br.Data = resp
 }
 
 // EdbInfoFilter
@@ -2962,7 +2976,7 @@ func (this *ChartInfoController) EdbInfoData() {
 
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
-	startDate, endDate = utils.GetDateByDateType(dateType, startDate, endDate)
+	startYear, _ := this.GetInt("StartYear")
 
 	edbInfo, err := data_manage.GetEdbInfoById(edbInfoId)
 	if err != nil {
@@ -2974,14 +2988,30 @@ func (this *ChartInfoController) EdbInfoData() {
 		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
+	maxYear := 0
+	if edbInfo.LatestDate != "" {
+		latestDateT, _ := time.Parse(utils.FormatDate, edbInfo.LatestDate)
+		maxYear = latestDateT.Year()
+	}
+
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
 	dataList, err := data_manage.GetEdbDataList(edbInfo.Source, edbInfoId, startDate, endDate)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
-	resp := new(data_manage.EdbInfoDataResp)
-	resp.EdbInfo = edbInfo
+	fullEdb := new(data_manage.EdbInfoFullClassify)
+	resp := new(data_manage.EdbInfoDataFullClassifyResp)
+	classifyList, err, errMsg := data.GetFullClassifyByClassifyId(edbInfo.ClassifyId)
+	if err != nil {
+		br.Msg = err.Error()
+		br.ErrMsg = errMsg
+		return
+	}
+	fullEdb.EdbInfo = edbInfo
+	fullEdb.ClassifyList = classifyList
+	resp.EdbInfo = fullEdb
 	resp.DataList = dataList
 	br.Ret = 200
 	br.Success = true
@@ -3672,7 +3702,7 @@ func (this *ChartInfoController) EdbInfoDataTb() {
 
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
-	startDate, endDate = utils.GetDateByDateType(dateType, startDate, endDate)
+	startYear, _ := this.GetInt("StartYear")
 
 	edbInfo, err := data_manage.GetEdbInfoById(edbInfoId)
 	if err != nil {
@@ -3680,6 +3710,13 @@ func (this *ChartInfoController) EdbInfoDataTb() {
 		br.Msg = "获取失败,Err:" + err.Error()
 		return
 	}
+	maxYear := 0
+	if edbInfo.LatestDate != "" {
+		latestDateT, _ := time.Parse(utils.FormatDate, edbInfo.LatestDate)
+		maxYear = latestDateT.Year()
+	}
+
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
 
 	var startDateTime time.Time
 	if startDate != `` {
@@ -3742,8 +3779,10 @@ func (this *ChartInfoController) EdbInfoDataSeasonal() {
 		br.Ret = 408
 		return
 	}
+	dateType, _ := this.GetInt("DateType")
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
+	startYear, _ := this.GetInt("StartYear")
 
 	edbInfoIdStr := this.GetString("EdbInfoId")
 	edbInfoId, err := strconv.Atoi(edbInfoIdStr)
@@ -3760,20 +3799,12 @@ func (this *ChartInfoController) EdbInfoDataSeasonal() {
 	if calendar == "" {
 		calendar = "公历"
 	}
-	if startDate != `` {
-		startDate = startDate + "-01"
-	} else {
-		fivePre := time.Now().AddDate(-4, 0, 0).Year()
-		startDate = strconv.Itoa(fivePre) + "-01-01"
-	}
-	if endDate != `` {
-		seasonEndDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, endDate+"-01", time.Local)
-		if tmpErr != nil {
-			br.Msg = "获取失败"
-			br.ErrMsg = "获取图表,指标信息失败,Err:" + tmpErr.Error()
-			return
+	if dateType == 0 {
+		if startDate == "" {
+			dateType = utils.DateTypeNYears
+		} else {
+			dateType = 5
 		}
-		endDate = seasonEndDateTime.AddDate(0, 1, -1).Format(utils.FormatDate)
 	}
 
 	edbInfo, err := data_manage.GetEdbInfoById(edbInfoId)
@@ -3782,6 +3813,13 @@ func (this *ChartInfoController) EdbInfoDataSeasonal() {
 		br.Msg = "获取失败,Err:" + err.Error()
 		return
 	}
+	maxYear := 0
+	if edbInfo.LatestDate != "" {
+		latestDateT, _ := time.Parse(utils.FormatDate, edbInfo.LatestDate)
+		maxYear = latestDateT.Year()
+	}
+
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
 
 	dataList, err := data.GetChartEdbSeasonalData(calendar, startDate, endDate, edbInfo)
 	if err != nil {
@@ -4266,7 +4304,7 @@ func (this *EdbInfoController) EdbChartAdminList() {
 		systemAdminMap[v.AdminId] = v
 		departmentMap[v.DepartmentName] = v.DepartmentName
 		departmentAdminMap[v.AdminId] = v.DepartmentName
-		groupMap[v.GroupName] = v.DepartmentName
+		groupMap[v.GroupName+v.DepartmentName] = v.DepartmentName
 	}
 
 	depList := make([]company.DepartmentGroupAdmins, 0)
@@ -4286,7 +4324,7 @@ func (this *EdbInfoController) EdbChartAdminList() {
 			if v1 == depList[k].RealName && v1 != "" {
 				item := &company.DepartmentGroupAdmins{
 					AdminId:  "group_" + strconv.Itoa(k),
-					RealName: k1,
+					RealName: strings.Replace(k1,v1,"",-1),
 				}
 				depList[k].ChildrenList = append(depList[k].ChildrenList, item)
 			}
@@ -4295,8 +4333,8 @@ func (this *EdbInfoController) EdbChartAdminList() {
 
 	for _, admin := range systemAdminList {
 		for i, v := range depList {
-			for _, group := range v.ChildrenList {
-				if admin.GroupName == group.RealName {
+			for k, group := range v.ChildrenList {
+				if admin.GroupName == group.RealName && admin.DepartmentName == v.RealName {
 					item := &company.DepartmentGroupAdmins{
 						AdminId:      strconv.Itoa(admin.AdminId),
 						RealName:     admin.RealName,
@@ -4304,7 +4342,7 @@ func (this *EdbInfoController) EdbChartAdminList() {
 						Authority:    admin.Authority,
 					}
 					if group.RealName != "" {
-						group.ChildrenList = append(group.ChildrenList, item)
+						depList[i].ChildrenList[k].ChildrenList = append(depList[i].ChildrenList[k].ChildrenList, item)
 					} else {
 						depList[i].ChildrenList = append(depList[i].ChildrenList, item)
 					}
@@ -4313,10 +4351,11 @@ func (this *EdbInfoController) EdbChartAdminList() {
 		}
 	}
 
-	for _, groupList := range depList {
+	for i, groupList := range depList {
 		for k, v := range groupList.ChildrenList {
 			if v.RealName == "" {
-				groupList.ChildrenList = append(groupList.ChildrenList[:k], groupList.ChildrenList[k+1:]...)
+				newChildrenList := append(groupList.ChildrenList[:k], groupList.ChildrenList[k+1:]...)
+				depList[i].ChildrenList = newChildrenList
 			}
 		}
 	}
@@ -4484,26 +4523,18 @@ func (this *EdbInfoController) EdbChartList() {
 	// 分类筛选
 	classifyId, _ := this.GetInt("ClassifyId")
 	if classifyId > 0 {
-		strClassifyIds, e := data_manage.GetEdbClassify(classifyId)
+		childClassify, e, _ := data.GetChildClassifyByClassifyId(classifyId)
 		if e != nil && e.Error() != utils.ErrNoRow() {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取分类信息失败, GetEdbClassify,Err:" + e.Error()
 			return
 		}
 		var classifyIds []int
-		classifyIdArr := strings.Split(strClassifyIds, ",")
-		for _, v := range classifyIdArr {
-			c, _ := strconv.Atoi(v)
-			if c > 0 {
-				classifyIds = append(classifyIds, c)
-			}
-		}
-		if len(classifyIds) > 0 {
-			condition += fmt.Sprintf(` AND classify_id IN (%s) `, utils.GetOrmInReplace(len(classifyIds)))
-			pars = append(pars, classifyIds)
-		} else {
-			condition += ` AND 1=2 `
+		for _, v := range childClassify {
+			classifyIds = append(classifyIds, v.ClassifyId)
 		}
+		condition += fmt.Sprintf(` AND classify_id IN (%s) `, utils.GetOrmInReplace(len(classifyIds)))
+		pars = append(pars, classifyIds)
 	}
 
 	// 创建人

+ 61 - 17
controllers/data_manage/edb_info_calculate.go

@@ -216,8 +216,61 @@ func (this *ChartInfoController) CalculateDetail() {
 		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
+
+	fullEdb := new(data_manage.EdbInfoFullClassify)
+	classifyList, err, errMsg := data.GetFullClassifyByClassifyId(edbInfo.ClassifyId)
+	if err != nil {
+		br.Msg = err.Error()
+		br.ErrMsg = errMsg
+		return
+	}
+	fullEdb.EdbInfo = edbInfo
+	fullEdb.ClassifyList = classifyList
+
+	// 拟合残差计算相关系数
+	if edbInfo.Source == utils.DATA_SOURCE_CALCULATE_NHCC {
+		var aEdbInfo, bEdbInfo *data_manage.EdbInfoCalculateDetail
+		for _, v := range calculateList {
+			if v.FromTag == "A" {
+				aEdbInfo = v
+				continue
+			}
+			if v.FromTag == "B" {
+				bEdbInfo = v
+				continue
+			}
+		}
+		if aEdbInfo != nil && bEdbInfo != nil {
+			edbInfoFromTagList := []data_manage.EdbInfoFromTag{
+				{
+					EdbInfoId: aEdbInfo.FromEdbInfoId,
+					FromTag:   aEdbInfo.FromTag,
+					MoveValue: aEdbInfo.MoveValue,
+				}, {
+					EdbInfoId: bEdbInfo.FromEdbInfoId,
+					FromTag:   bEdbInfo.FromTag,
+					MoveValue: bEdbInfo.MoveValue,
+				},
+			}
+
+			req2 := &data_manage.EdbInfoCalculateBatchSaveReqByEdbLib{
+				AdminId:      sysUser.AdminId,
+				AdminName:    sysUser.RealName,
+				Formula:      edbInfo.CalculateFormula,
+				EdbInfoIdArr: edbInfoFromTagList,
+			}
+
+			// 计算
+			val, err, _ := data.CallCalculateComputeCorrelation(req2)
+			if err == nil {
+				fullEdb.CorrelationStr = val
+			}
+		}
+
+	}
+
 	resp := new(data_manage.CalculateDetailResp)
-	resp.EdbInfoDetail = edbInfo
+	resp.EdbInfoDetail = fullEdb
 	resp.CalculateList = calculateList
 	br.Ret = 200
 	br.Success = true
@@ -1872,25 +1925,16 @@ func (this *ChartInfoController) CalculateComputeCorrelation() {
 		Calendar:     req.Calendar,
 	}
 
-	// 调用指标库去更新
-	reqJson, err := json.Marshal(req2)
-	if err != nil {
-		br.Msg = "参数解析异常!"
-		br.ErrMsg = "参数解析失败,Err:" + err.Error()
-		return
-	}
-	respItem, err := data.CalculateComputeCorrelation(string(reqJson))
+	val, err, errMsg := data.CallCalculateComputeCorrelation(req2)
 	if err != nil {
-		br.Msg = "新增失败"
-		br.ErrMsg = "新增失败,Err:" + err.Error()
+		br.Msg = "计算失败"
+		if errMsg != `` {
+			br.Msg = errMsg
+		}
+		br.ErrMsg = err.Error()
 		return
 	}
-
-	if respItem.Ret == 200 {
-		br.Data = respItem.Data
-	} else {
-		br.Data = ``
-	}
+	br.Data = val
 
 	br.Ret = 200
 	br.Success = true

+ 244 - 108
controllers/data_manage/future_good/future_good_chart_info.go

@@ -493,8 +493,8 @@ func (this *FutureGoodChartInfoController) ChartInfoAdd() {
 	}
 	chartInfo.StartDate = req.StartDate
 	chartInfo.EndDate = req.EndDate
-	chartInfo.SeasonStartDate = req.SeasonStartDate
-	chartInfo.SeasonEndDate = req.SeasonEndDate
+	chartInfo.SeasonStartDate = req.StartDate
+	chartInfo.SeasonEndDate = req.EndDate
 	chartInfo.LeftMin = req.LeftMin
 	chartInfo.LeftMax = req.LeftMax
 	chartInfo.RightMin = req.RightMin
@@ -1236,8 +1236,10 @@ func (this *FutureGoodChartInfoController) ChartInfoDetail() {
 
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
-	seasonStartDate := this.GetString("SeasonStartDate")
-	seasonEndDate := this.GetString("SeasonEndDate")
+	startYear, _ := this.GetInt("StartYear")
+
+	//seasonStartDate := this.GetString("SeasonStartDate")
+	//seasonEndDate := this.GetString("SeasonEndDate")
 
 	chartType, _ := this.GetInt("ChartType")
 
@@ -1245,30 +1247,6 @@ func (this *FutureGoodChartInfoController) ChartInfoDetail() {
 	if calendar == "" {
 		calendar = "公历"
 	}
-	switch dateType {
-	case 1:
-		startDate = "2000-01-01"
-	case 2:
-		startDate = "2010-01-01"
-	case 3:
-		startDate = "2015-01-01"
-	case 4:
-		//startDate = strconv.Itoa(time.Now().Year()) + "-01-01"
-		startDate = "2021-01-01"
-	case 5:
-		startDate = startDate + "-01"
-		endDate = endDate + "-01"
-	case 6:
-		startDate = startDate + "-01"
-	case 7:
-		startDate = "2018-01-01"
-	case 8:
-		startDate = "2019-01-01"
-	case 9:
-		startDate = "2020-01-01"
-	case 11:
-		startDate = "2022-01-01"
-	}
 
 	var err error
 	chartInfo := new(data_manage.ChartInfoView)
@@ -1287,23 +1265,9 @@ func (this *FutureGoodChartInfoController) ChartInfoDetail() {
 		chartType = chartInfo.ChartType
 	}
 
-	if chartType == 2 {
-		if seasonStartDate != "" {
-			startDate = seasonStartDate + "-01-01"
-		} else {
-			fivePre := time.Now().AddDate(-4, 0, 0).Year()
-			startDate = strconv.Itoa(fivePre) + "-01-01"
-		}
-		if seasonEndDate != "" {
-			endDate = seasonEndDate + "-12-31"
-		} else {
-			endDate = "" //time.Now().AddDate(2, 0, 0).Format(utils.FormatDate)
-		}
-	}
-
 	switch chartInfo.Source {
 	case utils.CHART_SOURCE_FUTURE_GOOD:
-		getFutureGoodChartInfo(chartInfo, startDate, endDate, sysUser, br)
+		getFutureGoodChartInfo(chartInfo, chartType, dateType, startYear, startDate, endDate, sysUser, br)
 		return
 	case utils.CHART_SOURCE_FUTURE_GOOD_PROFIT:
 		getFutureGoodProfitChartInfo(chartInfo, sysUser, br)
@@ -1312,7 +1276,7 @@ func (this *FutureGoodChartInfoController) ChartInfoDetail() {
 }
 
 // getFutureGoodChartInfo 获取商品价格曲线的数据
-func getFutureGoodChartInfo(chartInfo *data_manage.ChartInfoView, startDate, endDate string, sysUser *system.Admin, br *models.BaseResponse) {
+func getFutureGoodChartInfo(chartInfo *data_manage.ChartInfoView, chartType, dateType, startYear int, startDate, endDate string, sysUser *system.Admin, br *models.BaseResponse) {
 	chartInfoId := chartInfo.ChartInfoId
 
 	var edbInfoMapping, futureGoodEdbInfoMapping *data_manage.ChartEdbInfoMapping
@@ -1342,10 +1306,16 @@ func getFutureGoodChartInfo(chartInfo *data_manage.ChartInfoView, startDate, end
 		return
 	}
 
+	maxYear := 0
+	if edbInfoMapping.LatestDate != "" {
+		latestDateT, _ := time.Parse(utils.FormatDate, edbInfoMapping.LatestDate)
+		maxYear = latestDateT.Year()
+	}
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
+
 	// 商品价格曲线图的一些配置
 	var barConfig data_manage.BarChartInfoReq
 	barChartInfoDateList := make([]data_manage.BarChartInfoDateReq, 0)
-	barChartInfoSort := data_manage.BarChartInfoSortReq{}
 
 	if chartInfo.BarConfig == `` {
 		br.Msg = "商品价格曲线图未配置"
@@ -1360,10 +1330,9 @@ func getFutureGoodChartInfo(chartInfo *data_manage.ChartInfoView, startDate, end
 	}
 
 	barChartInfoDateList = barConfig.DateList
-	barChartInfoSort = barConfig.Sort
 
 	// 获取图表中的指标数据
-	barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(chartInfoId, startDate, endDate, edbInfoMapping, futureGoodEdbInfoMapping, barChartInfoDateList, barChartInfoSort)
+	barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(chartInfoId, startDate, endDate, edbInfoMapping, futureGoodEdbInfoMapping, barChartInfoDateList, true)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
@@ -1581,7 +1550,6 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 	// 商品价格曲线图的一些配置
 	var barConfig data_manage.BarChartInfoReq
 	barChartInfoDateList := make([]data_manage.BarChartInfoDateReq, 0)
-	barChartInfoSort := data_manage.BarChartInfoSortReq{}
 
 	if chartInfo.BarConfig == `` {
 		msg = "商品价格曲线图未配置"
@@ -1596,10 +1564,9 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 	}
 
 	barChartInfoDateList = barConfig.DateList
-	barChartInfoSort = barConfig.Sort
 
 	// 获取图表中的指标数据
-	barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(chartInfoId, startDate, endDate, edbInfoMapping, futureGoodEdbInfoMapping, barChartInfoDateList, barChartInfoSort)
+	barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(chartInfoId, startDate, endDate, edbInfoMapping, futureGoodEdbInfoMapping, barChartInfoDateList, true)
 	if err != nil {
 		msg = "获取失败"
 		errMsg = "获取图表,指标信息失败,Err:" + err.Error()
@@ -1786,56 +1753,26 @@ func (this *FutureGoodChartInfoController) ChartInfoEdbInfoDetail() {
 
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
+	startYear, _ := this.GetInt("StartYear")
 
 	isTimeBetween := false //是否是时间区间
-	switch dateType {
-	case 1:
-		startDate = "2000-01-01"
-		endDate = ""
-	case 2:
-		startDate = "2010-01-01"
-		endDate = ""
-	case 3:
-		startDate = "2015-01-01"
-		endDate = ""
-	case 4:
-		//startDate = strconv.Itoa(time.Now().Year()) + "-01-01"
-		startDate = "2021-01-01"
-		endDate = ""
-	case 5:
-		startDate = startDate + "-01"
-		endDate = endDate + "-01"
+	if dateType == 5 {
 		isTimeBetween = true
-	case 6:
-		//startDate = startDate + "-01"
-		endDate = ""
-	case 7:
-		startDate = "2018-01-01"
-		endDate = ""
-	case 8:
-		startDate = "2019-01-01"
-		endDate = ""
-	case 9:
-		startDate = "2020-01-01"
-		endDate = ""
-	case 11:
-		startDate = "2022-01-01"
-		endDate = ""
-	default:
-		startDate = startDate + "-01"
-		endDate = endDate + "-01"
 	}
 
-	// 兼容日期错误
-	{
-		if strings.Count(startDate, "-") == 1 {
-			startDate = startDate + "-01"
-		}
-		if strings.Count(endDate, "-") == 1 {
-			endDate = endDate + "-01"
-		}
+	edbInfo, err := data_manage.GetEdbInfoById(edbInfoId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.Msg = "获取失败,Err:" + err.Error()
+		return
+	}
+	maxYear := 0
+	if edbInfo.LatestDate != "" {
+		latestDateT, _ := time.Parse(utils.FormatDate, edbInfo.LatestDate)
+		maxYear = latestDateT.Year()
 	}
 
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
 	if startDate == "" {
 		br.Msg = "参数错误"
 		br.Msg = "参数错误,无效的查询日期"
@@ -1876,13 +1813,6 @@ func (this *FutureGoodChartInfoController) ChartInfoEdbInfoDetail() {
 		startDateReal = startDate
 	}
 
-	edbInfo, err := data_manage.GetEdbInfoById(edbInfoId)
-	if err != nil {
-		br.Msg = "获取失败"
-		br.Msg = "获取失败,Err:" + err.Error()
-		return
-	}
-
 	var dataList []*data_manage.EdbDataList
 	var minData, maxData float64
 
@@ -1975,6 +1905,198 @@ func (this *FutureGoodChartInfoController) ChartInfoEdbInfoDetail() {
 	br.Data = resp
 }
 
+// BaseChartInfoDetailFromUniqueCode
+// @Title 根据编码获取图表详情(基础信息,没有数据)
+// @Description 根据编码获取图表详情(基础信息,没有数据)
+// @Param   UniqueCode   query   int  true       "图表唯一编码,如果是管理后台访问,传固定字符串:7c69b590249049942070ae9dcd5bf6dc"
+// @Success 200 {object} data_manage.ChartInfoDetailFromUniqueCodeResp
+// @router /chart_info/base_detail/from_unique_code [get]
+func (this *FutureGoodChartInfoController) BaseChartInfoDetailFromUniqueCode() {
+	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
+	}
+	uniqueCode := this.GetString("UniqueCode")
+	if uniqueCode == "" {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,uniqueCode is empty"
+		return
+	}
+
+	status := true
+	chartInfo, err := data_manage.GetChartInfoViewByUniqueCode(uniqueCode)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			status = false
+		} else {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+			return
+		}
+	}
+
+	if chartInfo == nil {
+		status = false
+	}
+
+	if !status {
+		resp := new(data_manage.ChartInfoDetailFromUniqueCodeResp)
+		endInfoList := make([]*data_manage.ChartEdbInfoMapping, 0)
+		resp.EdbInfoList = endInfoList
+		resp.ChartInfo = chartInfo
+		resp.Status = false
+
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
+	switch chartInfo.Source {
+	case utils.CHART_SOURCE_FUTURE_GOOD, utils.CHART_SOURCE_FUTURE_GOOD_PROFIT:
+		// 现货指标
+		edbInfoMapping, err := data_manage.GetEtaEdbChartEdbMapping(chartInfo.ChartInfoId)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取图表,现货指标信息失败,Err:" + err.Error()
+			return
+		}
+		edbList := []*data_manage.ChartEdbInfoMapping{
+			edbInfoMapping,
+		}
+
+		// 期货指标
+		futureGoodEdbInfoMapping, err := data_manage.GetFutureGoodEdbChartEdbMapping(chartInfo.ChartInfoId)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取图表的期货商品指标信息失败,Err:" + err.Error()
+			return
+		}
+		edbList = append(edbList, futureGoodEdbInfoMapping)
+
+		// 商品价格曲线图的一些配置
+		var barConfig data_manage.BarChartInfoReq
+		barChartInfoDateList := make([]data_manage.BarChartInfoDateReq, 0)
+
+		if chartInfo.BarConfig == `` {
+			br.Msg = "商品价格曲线图未配置"
+			br.ErrMsg = "商品价格曲线图未配置"
+			return
+		}
+		err = json.Unmarshal([]byte(chartInfo.BarConfig), &barConfig)
+		if err != nil {
+			br.Msg = "商品价格曲线图配置异常"
+			br.ErrMsg = "商品价格曲线图配置异常"
+			return
+		}
+
+		barChartInfoDateList = barConfig.DateList
+
+		startDate := chartInfo.StartDate
+		endDate := chartInfo.EndDate
+
+		// 兼容日期错误
+		{
+			if strings.Count(startDate, "-") == 1 {
+				startDate = startDate + "-01"
+			}
+			if strings.Count(endDate, "-") == 1 {
+				endDate = endDate + "-01"
+			}
+		}
+
+		// 获取图表中的指标数据
+		barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(chartInfo.ChartInfoId, startDate, endDate, edbInfoMapping, futureGoodEdbInfoMapping, barChartInfoDateList, false)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
+			return
+		}
+		warnEdbList := make([]string, 0)
+		if len(edbList) <= 0 {
+			br.Msg = "商品价格曲线图表指标异常"
+			br.ErrMsg = "商品价格曲线图表异常"
+			return
+		}
+		baseEdbInfo := edbList[0] //现货指标
+
+		for _, v := range edbList {
+			if v.IsNullData {
+				warnEdbList = append(warnEdbList, v.EdbName+"("+v.EdbCode+")")
+			}
+			// 指标别名
+			if barConfigEdbInfoIdList != nil && len(barConfigEdbInfoIdList) > 0 {
+				for _, reqEdb := range barConfigEdbInfoIdList {
+					if v.EdbInfoId == reqEdb.EdbInfoId {
+						v.EdbAliasName = reqEdb.Name
+						v.EdbAliasNameEn = reqEdb.NameEn
+					}
+				}
+			}
+		}
+		if len(warnEdbList) > 0 {
+			chartInfo.WarnMsg = `图表引用指标异常,异常指标:` + strings.Join(warnEdbList, ",")
+		}
+		//判断是否加入我的图库
+		{
+			var myChartCondition string
+			var myChartPars []interface{}
+			myChartCondition += ` AND a.admin_id=? `
+			myChartPars = append(myChartPars, sysUser.AdminId)
+			myChartCondition += ` AND a.chart_info_id=? `
+			myChartPars = append(myChartPars, chartInfo.ChartInfoId)
+
+			myChartList, err := data_manage.GetMyChartByCondition(myChartCondition, myChartPars)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取我的图表信息失败,GetMyChartByCondition,Err:" + err.Error()
+				return
+			}
+			if myChartList != nil && len(myChartList) > 0 {
+				chartInfo.IsAdd = true
+				chartInfo.MyChartId = myChartList[0].MyChartId
+				chartInfo.MyChartClassifyId = myChartList[0].MyChartClassifyId
+			}
+		}
+
+		//图表操作权限
+		chartInfo.IsEdit = data.CheckOpChartPermission(sysUser, chartInfo.SysUserId)
+		chartInfo.Button.IsEdit = chartInfo.IsEdit
+		chartInfo.Button.IsCopy = true
+		//判断是否需要展示英文标识
+		chartInfo.IsEnChart = data.CheckIsEnChart(chartInfo.ChartNameEn, edbList[0:1], chartInfo.Source, chartInfo.ChartType)
+		chartInfo.UnitEn = baseEdbInfo.UnitEn
+
+		resp := data_manage.ChartInfoDetailFromUniqueCodeResp{}
+		resp.ChartInfo = chartInfo
+		resp.EdbInfoList = edbList
+		resp.XEdbIdValue = xEdbIdValue
+		resp.YDataList = yDataList
+		resp.XDataList = xDataList
+		resp.BarChartInfo = barConfig
+		resp.Status = true
+
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+	default:
+		br.Msg = "暂未实现"
+		br.ErrMsg = "暂未实现"
+		return
+	}
+}
+
 // ChartInfoSearchByEs
 // @Title 图表模糊搜索(从es获取)
 // @Description  图表模糊搜索(从es获取)
@@ -2229,7 +2351,7 @@ func (this *FutureGoodChartInfoController) ChartInfoBase64Upload() {
 
 	uploadDir := "static/images/"
 	if !utils.FileIsExist(uploadDir) {
-		err := os.MkdirAll(uploadDir, 777)
+		err := os.MkdirAll(uploadDir, utils.DIR_MOD)
 		if err != nil {
 			br.Msg = "图表保存失败"
 			br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
@@ -2273,6 +2395,8 @@ func (this *FutureGoodChartInfoController) ChartInfoBase64Upload() {
 	case <-doneChannel:
 		fmt.Println("done")
 	case err := <-errorChannel:
+		br.Msg = "文件上传失败"
+		br.ErrMsg = fmt.Sprintf("execute command failure err: %s", err.Error())
 		fmt.Println("execute command failure err:" + err.Error())
 		return
 	}
@@ -2283,13 +2407,25 @@ func (this *FutureGoodChartInfoController) ChartInfoBase64Upload() {
 
 	saveToOssPath = uploadDir + time.Now().Format("200601/20060102/")
 	saveToOssPath += outFileName
-	err = services.UploadFileToAliyun("", outFileName, saveToOssPath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
+	//上传到阿里云 和 minio
+	resourceUrl := ``
+	if utils.ObjectStorageClient == "minio" {
+		err = services.UploadFileToMinIo("", outFileName, saveToOssPath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+		resourceUrl = utils.MinIoImghost + saveToOssPath
+	} else {
+		err = services.UploadFileToAliyun("", outFileName, saveToOssPath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+		resourceUrl = utils.Imghost + saveToOssPath
 	}
-	resourceUrl := utils.Imghost + saveToOssPath
 	resp.ResourceUrl = resourceUrl
 	resp.Source = "convert"
 	//resp.CacheKey = imgDataKey
@@ -2696,7 +2832,7 @@ func (this *FutureGoodChartInfoController) PreviewBarChartInfo() {
 	}
 
 	// 获取图表中的指标数据
-	barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(0, "", "", edbInfoMapping, futureGoodEdbInfoMapping, req.DateList, req.Sort)
+	barConfigEdbInfoIdList, edbList, xEdbIdValue, xDataList, yDataList, err := future_goodServ.GetChartEdbData(0, "", "", edbInfoMapping, futureGoodEdbInfoMapping, req.DateList, true)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()

+ 44 - 16
controllers/data_manage/line_feature/chart_info.go

@@ -168,12 +168,16 @@ func (this *LineFeaturesChartInfoController) MultipleGraphPreview() {
 
 	// 曲线图表信息
 	curveConf := req.Curve
-	startDate, endDate := utils.GetDateByDateType(curveConf.DateType, curveConf.StartDate, curveConf.EndDate)
+	maxYear := 0
+	if edbInfoMapping.LatestDate != "" {
+		latestDateT, _ := time.Parse(utils.FormatDate, edbInfoMapping.LatestDate)
+		maxYear = latestDateT.Year()
+	}
+	startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxYear)
 	{
 		tmpChartInfo := *chartInfo
-
 		// 获取图表中的指标数据
-		edbList, _, _, _, err, errMsg := data.GetChartEdbData(tmpChartInfo.ChartInfoId, tmpChartInfo.ChartType, tmpChartInfo.Calendar, startDate, endDate, []*data_manage.ChartEdbInfoMapping{edbInfoMapping}, tmpChartInfo.ExtraConfig)
+		edbList, _, _, _, err, errMsg := data.GetChartEdbData(tmpChartInfo.ChartInfoId, tmpChartInfo.ChartType, tmpChartInfo.Calendar, startDate, endDate, []*data_manage.ChartEdbInfoMapping{edbInfoMapping}, tmpChartInfo.ExtraConfig, tmpChartInfo.SeasonExtraConfig)
 		if err != nil {
 			br.Msg = "获取失败"
 			if errMsg != `` {
@@ -428,10 +432,15 @@ func (this *LineFeaturesChartInfoController) MultipleGraphPreviewCurve() {
 		tmpChartInfo := *chartInfo
 
 		curveConf := req.Curve
-		startDate, endDate := utils.GetDateByDateType(curveConf.DateType, curveConf.StartDate, curveConf.EndDate)
+		maxYear := 0
+		if edbInfoMapping.LatestDate != "" {
+			latestDateT, _ := time.Parse(utils.FormatDate, edbInfoMapping.LatestDate)
+			maxYear = latestDateT.Year()
+		}
+		startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxYear)
 
 		// 获取图表中的指标数据
-		edbList, _, _, _, err, errMsg := data.GetChartEdbData(tmpChartInfo.ChartInfoId, tmpChartInfo.ChartType, tmpChartInfo.Calendar, startDate, endDate, []*data_manage.ChartEdbInfoMapping{edbInfoMapping}, tmpChartInfo.ExtraConfig)
+		edbList, _, _, _, err, errMsg := data.GetChartEdbData(tmpChartInfo.ChartInfoId, tmpChartInfo.ChartType, tmpChartInfo.Calendar, startDate, endDate, []*data_manage.ChartEdbInfoMapping{edbInfoMapping}, tmpChartInfo.ExtraConfig, tmpChartInfo.SeasonExtraConfig)
 		if err != nil {
 			br.Msg = "获取失败"
 			if errMsg != `` {
@@ -573,7 +582,12 @@ func (this *LineFeaturesChartInfoController) MultipleGraphConfigSaveChart() {
 	isSendEmail := true
 
 	curveConf := req.Curve
-	startDate, endDate := utils.GetDateByDateType(curveConf.DateType, curveConf.StartDate, curveConf.EndDate)
+	maxYear := 0
+	if edbInfoMapping.LatestDate != "" {
+		latestDateT, _ := time.Parse(utils.FormatDate, edbInfoMapping.LatestDate)
+		maxYear = latestDateT.Year()
+	}
+	startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxYear)
 	switch req.Source {
 	case utils.CHART_MULTIPLE_GRAPH_CURVE: // 曲线图
 		curveConf := req.Curve
@@ -605,8 +619,6 @@ func (this *LineFeaturesChartInfoController) MultipleGraphConfigSaveChart() {
 				StartDate:        curveConf.StartDate,
 				EndDate:          curveConf.EndDate,
 				Calendar:         "公历",
-				SeasonStartDate:  curveConf.StartDate,
-				SeasonEndDate:    curveConf.EndDate,
 				LeftMin:          fmt.Sprint(curveConf.LeftMin),
 				LeftMax:          fmt.Sprint(curveConf.LeftMax),
 			}
@@ -622,8 +634,6 @@ func (this *LineFeaturesChartInfoController) MultipleGraphConfigSaveChart() {
 				StartDate:        curveConf.StartDate,
 				EndDate:          curveConf.EndDate,
 				Calendar:         "公历",
-				SeasonStartDate:  curveConf.StartDate,
-				SeasonEndDate:    curveConf.EndDate,
 				LeftMin:          fmt.Sprint(curveConf.LeftMin),
 				LeftMax:          fmt.Sprint(curveConf.LeftMax),
 			}
@@ -906,8 +916,6 @@ func CopyMultipleGraphConfigSaveChart(req request.SaveMultipleGraphChartReq, thi
 			StartDate:        curveConf.StartDate,
 			EndDate:          curveConf.EndDate,
 			Calendar:         "公历",
-			SeasonStartDate:  curveConf.StartDate,
-			SeasonEndDate:    curveConf.EndDate,
 			LeftMin:          fmt.Sprint(curveConf.LeftMin),
 			LeftMax:          fmt.Sprint(curveConf.LeftMax),
 		}
@@ -1810,7 +1818,12 @@ func (this *LineFeaturesChartInfoController) Detail() {
 			br.ErrMsg = "格式化配置项失败,Err:" + err.Error()
 			return
 		}
-		startDate, endDate := utils.GetDateByDateType(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate)
+		maxYear := 0
+		if edbMapping.LatestDate != "" {
+			latestDateT, _ := time.Parse(utils.FormatDate, edbMapping.LatestDate)
+			maxYear = latestDateT.Year()
+		}
+		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxYear)
 		edbList, resultResp, err, errMsg = lineFeatureServ.GetStandardDeviationData(0, startDate, endDate, edbMapping, calculateValue)
 	case utils.CHART_SOURCE_LINE_FEATURE_PERCENTILE:
 		var percentileConfig request.Percentile
@@ -1820,7 +1833,12 @@ func (this *LineFeaturesChartInfoController) Detail() {
 			br.ErrMsg = "格式化配置项失败,Err:" + err.Error()
 			return
 		}
-		startDate, endDate := utils.GetDateByDateType(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate)
+		maxYear := 0
+		if edbMapping.LatestDate != "" {
+			latestDateT, _ := time.Parse(utils.FormatDate, edbMapping.LatestDate)
+			maxYear = latestDateT.Year()
+		}
+		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxYear)
 		edbList, resultResp, err, errMsg = lineFeatureServ.GetPercentileData(0, startDate, endDate, edbMapping, percentileConfig.CalculateValue, percentileConfig.CalculateUnit)
 	case utils.CHART_SOURCE_LINE_FEATURE_FREQUENCY:
 		var frequencyDistributionConfig request.FrequencyDistribution
@@ -2287,7 +2305,12 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 			errMsg = "格式化配置项失败,Err:" + tmpErr.Error()
 			return
 		}
-		startDate, endDate := utils.GetDateByDateType(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate)
+		maxYear := 0
+		if edbMapping.LatestDate != "" {
+			latestDateT, _ := time.Parse(utils.FormatDate, edbMapping.LatestDate)
+			maxYear = latestDateT.Year()
+		}
+		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxYear)
 		edbList, resultResp, err, msg = lineFeatureServ.GetStandardDeviationData(0, startDate, endDate, edbMapping, calculateValue)
 	case utils.CHART_SOURCE_LINE_FEATURE_PERCENTILE:
 		var percentileConfig request.Percentile
@@ -2297,7 +2320,12 @@ func GetChartInfoDetailFromUniqueCode(chartInfo *data_manage.ChartInfoView, isCa
 			errMsg = "格式化配置项失败,Err:" + err.Error()
 			return
 		}
-		startDate, endDate := utils.GetDateByDateType(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate)
+		maxYear := 0
+		if edbMapping.LatestDate != "" {
+			latestDateT, _ := time.Parse(utils.FormatDate, edbMapping.LatestDate)
+			maxYear = latestDateT.Year()
+		}
+		startDate, endDate := utils.GetDateByDateTypeV2(chartInfo.DateType, chartInfo.StartDate, chartInfo.EndDate, chartInfo.StartYear, maxYear)
 		edbList, resultResp, err, msg = lineFeatureServ.GetPercentileData(0, startDate, endDate, edbMapping, percentileConfig.CalculateValue, percentileConfig.CalculateUnit)
 	case utils.CHART_SOURCE_LINE_FEATURE_FREQUENCY:
 		var frequencyDistributionConfig request.FrequencyDistribution

+ 13 - 11
controllers/data_manage/multiple_graph_config.go

@@ -183,6 +183,7 @@ func (this *ChartInfoController) MultipleGraphPreview() {
 		br.ErrMsg = "获取图表,指标信息失败,Err:" + err.Error()
 		return
 	}
+	maxYear := 0
 	var edbInfoMappingA, edbInfoMappingB *data_manage.ChartEdbInfoMapping
 	for _, v := range mappingList {
 		if v.EdbInfoId == req.EdbInfoIdA {
@@ -191,6 +192,10 @@ func (this *ChartInfoController) MultipleGraphPreview() {
 		if v.EdbInfoId == req.EdbInfoIdB {
 			edbInfoMappingB = v
 		}
+		if v.LatestDate != "" {
+			latestDateT, _ := time.Parse(utils.FormatDate, v.LatestDate)
+			maxYear = latestDateT.Year()
+		}
 	}
 	if edbInfoMappingA == nil {
 		br.Msg = "指标A不存在"
@@ -212,13 +217,12 @@ func (this *ChartInfoController) MultipleGraphPreview() {
 	// 曲线图表信息
 	{
 		tmpChartInfo := *chartInfo
-
 		curveConf := req.Curve
-		startDate, endDate := utils.GetDateByDateType(curveConf.DateType, curveConf.StartDate, curveConf.EndDate)
 		edbInfoType := 0
 		if curveConf.EdbInfoType {
 			edbInfoType = 1
 		}
+
 		for _, v := range mappingList {
 			v.EdbInfoType = 1
 			v.IsOrder = false
@@ -230,8 +234,9 @@ func (this *ChartInfoController) MultipleGraphPreview() {
 			}
 		}
 
+		startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxYear)
 		// 获取图表中的指标数据
-		edbList, _, _, _, err, errMsg := data.GetChartEdbData(tmpChartInfo.ChartInfoId, tmpChartInfo.ChartType, tmpChartInfo.Calendar, startDate, endDate, mappingList, tmpChartInfo.ExtraConfig)
+		edbList, _, _, _, err, errMsg := data.GetChartEdbData(tmpChartInfo.ChartInfoId, tmpChartInfo.ChartType, tmpChartInfo.Calendar, startDate, endDate, mappingList, tmpChartInfo.ExtraConfig, tmpChartInfo.SeasonExtraConfig)
 		if err != nil {
 			br.Msg = "获取失败"
 			if errMsg != `` {
@@ -349,7 +354,7 @@ func (this *ChartInfoController) MultipleGraphPreview() {
 
 			// 数据的开始/结束日期
 			curveConf := req.Curve
-			startDate, endDate := utils.GetDateByDateType(curveConf.DateType, curveConf.StartDate, curveConf.EndDate)
+			startDate, endDate := utils.GetDateByDateTypeV2(curveConf.DateType, curveConf.StartDate, curveConf.EndDate, curveConf.StartYear, maxYear)
 
 			rollingCorrelationData := make([]interface{}, 0)
 			for _, rollingCorrelationConf := range rollingCorrelationConfList {
@@ -518,7 +523,7 @@ func (this *ChartInfoController) MultipleGraphPreviewCurve() {
 		}
 
 		// 获取图表中的指标数据
-		edbList, _, _, _, err, errMsg := data.GetChartEdbData(tmpChartInfo.ChartInfoId, tmpChartInfo.ChartType, tmpChartInfo.Calendar, startDate, endDate, mappingList, tmpChartInfo.ExtraConfig)
+		edbList, _, _, _, err, errMsg := data.GetChartEdbData(tmpChartInfo.ChartInfoId, tmpChartInfo.ChartType, tmpChartInfo.Calendar, startDate, endDate, mappingList, tmpChartInfo.ExtraConfig, tmpChartInfo.SeasonExtraConfig)
 		if err != nil {
 			br.Msg = "获取失败"
 			if errMsg != `` {
@@ -731,9 +736,8 @@ func (this *ChartInfoController) MultipleGraphConfigSaveChart() {
 				DateType:         curveConf.DateType,
 				StartDate:        curveConf.StartDate,
 				EndDate:          curveConf.EndDate,
+				StartYear:        curveConf.StartYear,
 				Calendar:         "公历",
-				SeasonStartDate:  curveConf.StartDate,
-				SeasonEndDate:    curveConf.EndDate,
 				LeftMin:          fmt.Sprint(curveConf.LeftMin),
 				LeftMax:          fmt.Sprint(curveConf.LeftMax),
 				RightMin:         fmt.Sprint(curveConf.RightMin),
@@ -750,9 +754,8 @@ func (this *ChartInfoController) MultipleGraphConfigSaveChart() {
 				DateType:         curveConf.DateType,
 				StartDate:        curveConf.StartDate,
 				EndDate:          curveConf.EndDate,
+				StartYear:        curveConf.StartYear,
 				Calendar:         "公历",
-				SeasonStartDate:  curveConf.StartDate,
-				SeasonEndDate:    curveConf.EndDate,
 				LeftMin:          fmt.Sprint(curveConf.LeftMin),
 				LeftMax:          fmt.Sprint(curveConf.LeftMax),
 				RightMin:         fmt.Sprint(curveConf.RightMin),
@@ -1037,9 +1040,8 @@ func CopyMultipleGraphConfigSaveChart(req request.SaveMultipleGraphChartReq, thi
 			DateType:         curveConf.DateType,
 			StartDate:        curveConf.StartDate,
 			EndDate:          curveConf.EndDate,
+			StartYear:        curveConf.StartYear,
 			Calendar:         "公历",
-			SeasonStartDate:  curveConf.StartDate,
-			SeasonEndDate:    curveConf.EndDate,
 			LeftMin:          fmt.Sprint(curveConf.LeftMin),
 			LeftMax:          fmt.Sprint(curveConf.LeftMax),
 			RightMin:         fmt.Sprint(curveConf.RightMin),

+ 178 - 3
controllers/data_manage/predict_edb_classify.go

@@ -8,6 +8,7 @@ import (
 	"eta/eta_api/models/system"
 	"eta/eta_api/services/data"
 	"eta/eta_api/utils"
+	"sort"
 )
 
 // PredictEdbClassifyController 数据管理-预测指标分类模块
@@ -387,13 +388,13 @@ func (this *PredictEdbClassifyController) Move() {
 		return
 	}
 
-	if req.ClassifyId <= 0 {
+	if req.ClassifyId <= 0 && req.EdbInfoId <= 0 {
 		br.Msg = "参数错误"
-		br.ErrMsg = "分类id小于等于0"
+		br.ErrMsg = "请选择拖动目标,分类目录或者指标"
 		return
 	}
 
-	err, errMsg := data.MoveEdbClassify(req.ClassifyId, req.ParentClassifyId, req.PrevClassifyId, req.NextClassifyId, sysUser)
+	err, errMsg := data.MoveEdbClassify(req, sysUser, 1)
 	if errMsg != `` {
 		br.Msg = errMsg
 		br.ErrMsg = errMsg
@@ -653,3 +654,177 @@ func (this *PredictEdbClassifyController) ListV2() {
 	br.Msg = "获取成功"
 	br.Data = resp
 }
+
+// SimpleList
+// @Title 单层分类列表
+// @Description 单层分类列表
+// @Success 200 {object} data_manage.EdbClassifyListResp
+// @router /predict_classify/simple [get]
+func (this *PredictEdbClassifyController) SimpleList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	// 默认查一级分类和一级分类下的指标信息,
+	// 如果是 子级分类,查询该子级分类的下一级分类和指标信息
+	// 增加标识判断是文件夹还是指标列表
+	parentId, _ := this.GetInt("ParentId")
+	isOnlyMe, _ := this.GetBool("IsOnlyMe")
+	// 如果选择了只看我的,那么只查询归属于我的账号
+	sysUserId := 0
+	if isOnlyMe {
+		sysUserId = this.SysUser.AdminId
+	}
+
+	rootList, err := data_manage.GetEdbClassifyByParentId(parentId, 1)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	nodeAll := make([]*data_manage.EdbClassifyItems, 0)
+
+	var sortList data_manage.EdbClassifyItemList
+	if parentId > 0 {
+		// 查询挂在当前分类上的指标列表
+		// 获取当前账号的不可见指标
+		obj := data_manage.EdbInfoNoPermissionAdmin{}
+		confList, err := obj.GetAllListByAdminId(this.SysUser.AdminId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取不可见指标配置数据失败,Err:" + err.Error()
+			return
+		}
+		noPermissionEdbInfoIdMap := make(map[int]bool)
+		for _, v := range confList {
+			noPermissionEdbInfoIdMap[v.EdbInfoId] = true
+		}
+		allEdbInfo, err := data_manage.GetEdbInfoByClassifyId(parentId, 1, sysUserId)
+		if err != nil {
+			br.Msg = "获取指标数据失败"
+			br.ErrMsg = "获取指标数据失败,Err:" + err.Error()
+			return
+		}
+
+		for _, v := range allEdbInfo {
+			// 如果指标不可见,那么就不返回该指标
+			if _, ok := noPermissionEdbInfoIdMap[v.EdbInfoId]; ok {
+				continue
+			}
+			button := data.GetPredictEdbOpButton(this.SysUser, v.SysUserId)
+			button.AddButton = false //不管有没有权限,指标都是没有添加按钮的
+			button.OpButton = false
+			button.DeleteButton = false
+			v.Button = button
+			v.Children = make([]*data_manage.EdbClassifyItems, 0)
+			v.ParentId = parentId
+			nodeAll = append(nodeAll, v)
+		}
+	}
+	if len(rootList) > 0 {
+		for _, v := range rootList {
+			button := data.GetPredictEdbClassifyOpButton(this.SysUser, v.SysUserId)
+			v.Button = button
+			v.Children = make([]*data_manage.EdbClassifyItems, 0)
+			nodeAll = append(nodeAll, v)
+		}
+	}
+	if len(nodeAll) > 0 {
+		//根据sort值排序
+		sortList = nodeAll
+		sort.Sort(sortList)
+	}
+
+	// 是否允许添加一级分类
+	canOpClassify := true
+	button := data.GetPredictEdbClassifyOpButton(this.SysUser, 0)
+	if !button.AddButton {
+		canOpClassify = false
+	}
+
+	language := `CN`
+	// 指标显示的语言
+	{
+		configDetail, _ := system.GetConfigDetailByCode(this.SysUser.AdminId, system.PredictEdbLanguageVar)
+		if configDetail != nil {
+			language = configDetail.ConfigValue
+		} else {
+			configDetail, _ = system.GetDefaultConfigDetailByCode(system.PredictEdbLanguageVar)
+			if configDetail != nil {
+				language = configDetail.ConfigValue
+			}
+		}
+	}
+
+	resp := new(data_manage.EdbClassifyListResp)
+	resp.AllNodes = sortList
+	resp.Language = language
+	resp.CanOpClassify = canOpClassify
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// ClassifyTree
+// @Title 多层分类列表树
+// @Description 多层分类列表树
+// @Success 200 {object} data_manage.EdbClassifyListResp
+// @router /predict_classify/tree [get]
+func (this *PredictEdbClassifyController) ClassifyTree() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	allList, err := data_manage.GetPredictEdbClassifyAll()
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	nodeAll := make([]*data_manage.EdbClassifyItems, 0)
+	var sortList data_manage.EdbClassifyItemList
+
+	if len(allList) > 0 {
+		for k, v := range allList {
+			button := data.GetPredictEdbClassifyOpButton(this.SysUser, v.SysUserId)
+			allList[k].Button = button
+		}
+		nodeAll = data.GetClassifyTreeRecursive(allList, 0)
+		//根据sort值排序
+		sortList = nodeAll
+		sort.Sort(sortList)
+	}
+	// 是否允许添加一级分类
+	canOpClassify := true
+	button := data.GetPredictEdbClassifyOpButton(this.SysUser, 0)
+	if !button.AddButton {
+		canOpClassify = false
+	}
+
+	language := `CN`
+	// 指标显示的语言
+	{
+		configDetail, _ := system.GetConfigDetailByCode(this.SysUser.AdminId, system.PredictEdbLanguageVar)
+		if configDetail != nil {
+			language = configDetail.ConfigValue
+		} else {
+			configDetail, _ = system.GetDefaultConfigDetailByCode(system.PredictEdbLanguageVar)
+			if configDetail != nil {
+				language = configDetail.ConfigValue
+			}
+		}
+	}
+
+	resp := new(data_manage.EdbClassifyListResp)
+	resp.AllNodes = sortList
+	resp.Language = language
+	resp.CanOpClassify = canOpClassify
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 138 - 125
controllers/data_manage/predict_edb_info.go

@@ -10,6 +10,7 @@ import (
 	"eta/eta_api/services/data"
 	"eta/eta_api/services/elastic"
 	"eta/eta_api/utils"
+	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"github.com/shopspring/decimal"
 	"sort"
@@ -75,14 +76,18 @@ func (this *PredictEdbInfoController) EdbChartList() {
 
 	// 筛选分类
 	if classifyId > 0 {
-		classifyIds, err := data_manage.GetEdbClassify(classifyId)
-		if err != nil && err.Error() != utils.ErrNoRow() {
-			br.Msg = "获取分类信息失败"
-			br.ErrMsg = "获取分类信息失败,GetEdbClassifyById,Err:" + err.Error()
+		childClassify, e, _ := data.GetChildClassifyByClassifyId(classifyId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取分类信息失败, GetEdbClassify,Err:" + e.Error()
 			return
 		}
-		condition += " AND classify_id IN(" + classifyIds + ") "
-
+		var classifyIds []int
+		for _, v := range childClassify {
+			classifyIds = append(classifyIds, v.ClassifyId)
+		}
+		condition += fmt.Sprintf(` AND classify_id IN (%s) `, utils.GetOrmInReplace(len(classifyIds)))
+		pars = append(pars, classifyIds)
 	}
 	if adminId > 0 {
 		condition += " AND sys_user_id = ? "
@@ -219,9 +224,6 @@ func (this *PredictEdbInfoController) List() {
 	//获取英文频度
 	edbInfoItem.FrequencyEn = data.GetFrequencyEn(edbInfoItem.Frequency)
 
-	//查询目录
-	resultList, _ := data_manage.GetClassifyAllByClassifyId(edbInfoItem.ClassifyId)
-
 	// 未来的指标预测数据
 	predictDataList := make([]*data_manage.EdbData, 0)
 
@@ -398,9 +400,17 @@ func (this *PredictEdbInfoController) List() {
 	if edbInfoItem.DataList == nil {
 		edbInfoItem.DataList = make([]*data_manage.EdbData, 0)
 	}
+	//查询目录
+	classifyList, err, errMsg := data.GetFullClassifyByClassifyId(edbInfoItem.ClassifyId)
+	if err != nil {
+		br.Msg = err.Error()
+		br.ErrMsg = errMsg
+		return
+	}
+
 	resp.Paging = page
 	resp.Item = edbInfoItem
-	resp.ClassifyList = resultList
+	resp.ClassifyList = classifyList
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"
@@ -965,13 +975,64 @@ func (this *PredictEdbInfoController) Detail() {
 			calculateList[k] = v
 		}
 	}
+	classifyList, err, errMsg := data.GetFullClassifyByClassifyId(edbInfo.ClassifyId)
+	if err != nil {
+		br.Msg = err.Error()
+		br.ErrMsg = errMsg
+		return
+	}
+
+	// 拟合残差计算相关系数
+	var correlationStr string
+	if edbInfo.Source == utils.DATA_SOURCE_PREDICT_CALCULATE_NHCC {
+		var aEdbInfo, bEdbInfo *data_manage.EdbInfoCalculateDetail
+		for _, v := range calculateList {
+			if v.FromTag == "A" {
+				aEdbInfo = v
+				continue
+			}
+			if v.FromTag == "B" {
+				bEdbInfo = v
+				continue
+			}
+		}
+		if aEdbInfo != nil && bEdbInfo != nil {
+			edbInfoFromTagList := []data_manage.EdbInfoFromTag{
+				{
+					EdbInfoId: aEdbInfo.FromEdbInfoId,
+					FromTag:   aEdbInfo.FromTag,
+					MoveValue: aEdbInfo.MoveValue,
+				}, {
+					EdbInfoId: bEdbInfo.FromEdbInfoId,
+					FromTag:   bEdbInfo.FromTag,
+					MoveValue: bEdbInfo.MoveValue,
+				},
+			}
+
+			req2 := &data_manage.EdbInfoCalculateBatchSaveReqByEdbLib{
+				AdminId:      sysUser.AdminId,
+				AdminName:    sysUser.RealName,
+				Formula:      edbInfo.CalculateFormula,
+				EdbInfoIdArr: edbInfoFromTagList,
+			}
+
+			// 计算
+			val, err, _ := data.CallCalculateComputeCorrelation(req2)
+			if err == nil {
+				correlationStr = val
+			}
+		}
+
+	}
 
 	resp := response.PredictEdbInfo{
-		EdbInfo:       *edbInfo,
-		RuleType:      ruleType,
-		FixedValue:    fixedValue,
-		CalculateList: calculateList,
-		RuleList:      predictEdbConfList,
+		EdbInfo:        *edbInfo,
+		RuleType:       ruleType,
+		FixedValue:     fixedValue,
+		CalculateList:  calculateList,
+		RuleList:       predictEdbConfList,
+		CorrelationStr: correlationStr,
+		ClassifyList:   classifyList,
 	}
 	br.Ret = 200
 	br.Success = true
@@ -1223,9 +1284,7 @@ func (this *PredictEdbInfoController) DataList() {
 
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
-
-	seasonStartDate := this.GetString("SeasonStartDate")
-	seasonEndDate := this.GetString("SeasonEndDate")
+	startYear, _ := this.GetInt("StartYear")
 
 	chartType, _ := this.GetInt("ChartType")
 	if chartType <= 0 {
@@ -1240,58 +1299,10 @@ func (this *PredictEdbInfoController) DataList() {
 
 	if chartType == 2 {
 		dateType = 0
-		if seasonStartDate != "" {
-			startDate = seasonStartDate + "-01-01"
-		} else {
-			fivePre := time.Now().AddDate(-4, 0, 0).Year()
-			startDate = strconv.Itoa(fivePre) + "-01-01"
-		}
-		if seasonEndDate != "" {
-			endDate = seasonEndDate + "-12-31"
-			isTimeBetween = true
-		} else {
-			endDate = "" //time.Now().AddDate(2, 0, 0).Format(utils.FormatDate)
-		}
+		isTimeBetween = true
 	}
-	switch dateType {
-	case 1:
-		startDate = "2000-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 2:
-		startDate = "2010-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 3:
-		startDate = "2015-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 4:
-		//startDate = strconv.Itoa(time.Now().Year()) + "-01-01"
-		startDate = "2021-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 5:
-		startDate = startDate + "-01"
-		endDate = endDate + "-01"
+	if dateType == 5 {
 		isTimeBetween = true
-	case 6:
-		startDate = startDate + "-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 7:
-		startDate = "2018-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 8:
-		startDate = "2019-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 9:
-		startDate = "2020-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 10:
-		startDate = ""
-		endDate = ""
-	case 11:
-		startDate = "2022-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-		//default:
-		//startDate = startDate + "-01"
-		//endDate = endDate + "-01"
 	}
 
 	// 获取预测指标信息
@@ -1301,7 +1312,21 @@ func (this *PredictEdbInfoController) DataList() {
 		br.ErrMsg = "获取预测指标信息失败,Err:" + err.Error()
 		return
 	}
+	maxYear := 0
+	if edbInfo.LatestDate != "" {
+		latestDateT, _ := time.Parse(utils.FormatDate, edbInfo.LatestDate)
+		maxYear = latestDateT.Year()
+	}
+
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
+	if endDate == "" {
+		endDate = time.Now().Format(utils.FormatDate)
+	}
 
+	if dateType == 10 {
+		startDate = ""
+		endDate = ""
+	}
 	// 按钮校验
 	button := data.GetPredictEdbOpButton(this.SysUser, edbInfo.SysUserId)
 	button.AddButton = false
@@ -1329,11 +1354,17 @@ func (this *PredictEdbInfoController) DataList() {
 		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
-
+	classifyList, err, errMsg := data.GetFullClassifyByClassifyId(edbInfo.ClassifyId)
+	if err != nil {
+		br.Msg = err.Error()
+		br.ErrMsg = errMsg
+		return
+	}
 	resp := response.PredictEdbInfoDataResp{
-		EdbInfo:  edbInfo,
-		DataList: resultDataList,
-		Button:   button,
+		EdbInfo:      edbInfo,
+		DataList:     resultDataList,
+		Button:       button,
+		ClassifyList: classifyList,
 	}
 	br.Ret = 200
 	br.Success = true
@@ -1370,8 +1401,10 @@ func (this *PredictEdbInfoController) ChartDataList() {
 	dateType := req.DateType
 	startDate := req.StartDate
 	endDate := req.EndDate
-	seasonStartDate := req.SeasonStartDate
-	seasonEndDate := req.SeasonEndDate
+	//seasonStartDate := req.SeasonStartDate
+	//seasonEndDate := req.SeasonEndDate
+	startYear := req.StartYear
+
 	calendar := req.Calendar
 	if calendar == "" {
 		calendar = "公历"
@@ -1381,58 +1414,10 @@ func (this *PredictEdbInfoController) ChartDataList() {
 
 	if req.ChartType == 2 {
 		dateType = 0
-		if seasonStartDate != "" {
-			startDate = seasonStartDate + "-01-01"
-		} else {
-			fivePre := time.Now().AddDate(-4, 0, 0).Year()
-			startDate = strconv.Itoa(fivePre) + "-01-01"
-		}
-		if seasonEndDate != "" {
-			endDate = seasonEndDate + "-12-31"
-			isTimeBetween = true
-		} else {
-			endDate = "" //time.Now().AddDate(2, 0, 0).Format(utils.FormatDate)
-		}
+		isTimeBetween = true
 	}
-	switch dateType {
-	case 1:
-		startDate = "2000-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 2:
-		startDate = "2010-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 3:
-		startDate = "2015-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 4:
-		//startDate = strconv.Itoa(time.Now().Year()) + "-01-01"
-		startDate = "2021-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 5:
-		startDate = startDate + "-01"
-		endDate = endDate + "-01"
+	if dateType == 5 {
 		isTimeBetween = true
-	case 6:
-		startDate = startDate + "-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 7:
-		startDate = "2018-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 8:
-		startDate = "2019-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 9:
-		startDate = "2020-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-	case 10:
-		startDate = ""
-		endDate = ""
-	case 11:
-		startDate = "2022-01-01"
-		endDate = time.Now().Format(utils.FormatDate)
-		//default:
-		//	startDate = startDate + "-01"
-		//	endDate = endDate + "-01"
 	}
 
 	// 规则列表
@@ -1545,6 +1530,22 @@ func (this *PredictEdbInfoController) ChartDataList() {
 		br.Success = true
 		return
 	}
+	// todo 确认预测指标是否用的是来源指标的最新日期
+	maxYear := 0
+	if sourceEdbInfoItem.LatestDate != "" {
+		latestDateT, _ := time.Parse(utils.FormatDate, sourceEdbInfoItem.LatestDate)
+		maxYear = latestDateT.Year()
+	}
+
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, maxYear)
+	if endDate == "" {
+		endDate = time.Now().Format(utils.FormatDate)
+	}
+
+	if dateType == 10 {
+		startDate = ""
+		endDate = ""
+	}
 
 	minValue := sourceEdbInfoItem.MinValue
 	maxValue := sourceEdbInfoItem.MaxValue
@@ -1833,9 +1834,11 @@ func (this *PredictEdbInfoController) Modify() {
 //	data.AddOrEditAllEdbInfoToEs()
 //}
 
+// ClassifyEdbInfoItems
 // @Title 获取分类下指标接口
 // @Description 获取分类下指标接口
 // @Param   ClassifyId   query   int  true       "分类id"
+// @Param   IsShowMe   query   bool  false       "是否只看我的"
 // @Success 200 {object} data_manage.EdbClassifyListResp
 // @router /predict_edb_info/items [get]
 func (this *PredictEdbInfoController) ClassifyEdbInfoItems() {
@@ -1851,6 +1854,9 @@ func (this *PredictEdbInfoController) ClassifyEdbInfoItems() {
 		return
 	}
 
+	// 是否只看我的
+	isShowMe, _ := this.GetBool("IsShowMe")
+
 	// 获取当前账号的不可见指标
 	obj := data_manage.EdbInfoNoPermissionAdmin{}
 	confList, err := obj.GetAllListByAdminId(this.SysUser.AdminId)
@@ -1864,7 +1870,13 @@ func (this *PredictEdbInfoController) ClassifyEdbInfoItems() {
 		noPermissionEdbInfoIdMap[v.EdbInfoId] = true
 	}
 
-	allEdbInfo, err := data_manage.GetEdbInfoByClassifyId(classifyId, 1)
+	// 如果选择了只看我的,那么只查询归属于我的账号
+	sysUserId := 0
+	if isShowMe {
+		sysUserId = this.SysUser.AdminId
+	}
+
+	allEdbInfo, err := data_manage.GetEdbInfoByClassifyId(classifyId, 1, sysUserId)
 	if err != nil && err.Error() != utils.ErrNoRow() {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取数据失败,Err:" + err.Error()
@@ -1877,6 +1889,7 @@ func (this *PredictEdbInfoController) ClassifyEdbInfoItems() {
 		if _, ok := noPermissionEdbInfoIdMap[v.EdbInfoId]; ok {
 			continue
 		}
+
 		button := data.GetEdbOpButton(this.SysUser, v.SysUserId, v.EdbType, utils.EDB_INFO_TYPE)
 		button.AddButton = false //不管有没有权限,指标都是没有添加按钮的
 		v.Button = button

+ 16 - 18
controllers/data_manage/supply_analysis/variety_edb.go

@@ -2,8 +2,6 @@ package supply_analysis
 
 import (
 	"encoding/json"
-	"fmt"
-	"github.com/tealeg/xlsx"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/models/data_manage/request"
@@ -12,6 +10,8 @@ import (
 	"eta/eta_api/services/data"
 	supply_analysisServ "eta/eta_api/services/data/supply_analysis"
 	"eta/eta_api/utils"
+	"fmt"
+	"github.com/tealeg/xlsx"
 	"os"
 	"path/filepath"
 	"strconv"
@@ -561,7 +561,7 @@ func (this *VarietyController) EdbInfoDataTb() {
 
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
-	startDate, endDate = utils.GetDateByDateType(dateType, startDate, endDate)
+	startYear, _ := this.GetInt("StartYear")
 
 	varietyEdbInfo, err := supply_analysis.GetVarietyEdbInfoByVarietyEdbId(varietyEdbId)
 	if err != nil {
@@ -604,6 +604,8 @@ func (this *VarietyController) EdbInfoDataTb() {
 		Calendar:         "",
 	}
 
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, varietyEdbInfo.EndDate.Year())
+
 	var startDateTime time.Time
 	if startDate != `` {
 		startDateTime, err = time.ParseInLocation(utils.FormatDate, startDate, time.Local)
@@ -679,22 +681,14 @@ func (this *VarietyController) EdbInfoDataSeasonal() {
 	if calendar == "" {
 		calendar = "公历"
 	}
+
+	dateType, _ := this.GetInt("DateType")
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
-	if startDate != `` {
-		startDate = startDate + "-01"
-	} else {
-		fivePre := time.Now().AddDate(-4, 0, 0).Year()
-		startDate = strconv.Itoa(fivePre) + "-01-01"
-	}
-	if endDate != `` {
-		seasonEndDateTime, tmpErr := time.ParseInLocation(utils.FormatDate, endDate+"-01", time.Local)
-		if tmpErr != nil {
-			br.Msg = "获取失败"
-			br.ErrMsg = "获取图表,指标信息失败,Err:" + tmpErr.Error()
-			return
-		}
-		endDate = seasonEndDateTime.AddDate(0, 1, -1).Format(utils.FormatDate)
+	startYear, _ := this.GetInt("StartYear")
+
+	if dateType == 0 {
+		dateType = utils.DateTypeNYears
 	}
 
 	// 指标详情
@@ -739,6 +733,8 @@ func (this *VarietyController) EdbInfoDataSeasonal() {
 		Calendar:         "",
 	}
 
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, varietyEdbInfo.EndDate.Year())
+
 	dataList, minVal, maxVal, err := supply_analysisServ.GetChartEdbSeasonalData(varietyEdbId, calendar, startDate, endDate, edbInfo.LatestDate)
 	if err != nil {
 		br.Msg = "获取失败"
@@ -793,7 +789,7 @@ func (this *VarietyController) EdbDataListV2() {
 
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
-	startDate, endDate = utils.GetDateByDateType(dateType, startDate, endDate)
+	startYear, _ := this.GetInt("StartYear")
 
 	varietyEdbInfo, err := supply_analysis.GetVarietyEdbInfoByVarietyEdbId(varietyEdbId)
 	if err != nil {
@@ -836,6 +832,8 @@ func (this *VarietyController) EdbDataListV2() {
 		Calendar:         "",
 	}
 
+	startDate, endDate = utils.GetDateByDateTypeV2(dateType, startDate, endDate, startYear, varietyEdbInfo.EndDate.Year())
+
 	var startDateTime time.Time
 	if startDate != `` {
 		startDateTime, err = time.ParseInLocation(utils.FormatDate, startDate, time.Local)

+ 13 - 3
controllers/english_report/email.go

@@ -588,7 +588,12 @@ func (this *EnglishReportEmailController) Send() {
 		br.Msg = "无权操作"
 		return
 	}
-
+	noCompanyIdsMap := make(map[int]struct{})
+	if len(req.NoCompanyIds) > 0 {
+		for _, v := range req.NoCompanyIds {
+			noCompanyIdsMap[v] = struct{}{}
+		}
+	}
 	// 指定品种的客户
 	sendCompanyIds := make([]int, 0)
 	if len(req.EnPermissions) > 0 {
@@ -598,8 +603,13 @@ func (this *EnglishReportEmailController) Send() {
 			br.ErrMsg = "获取指定品种的客户IDs失败, Err: " + e.Error()
 			return
 		}
-		sendCompanyIds = companyIds
+		for _, v := range companyIds {
+			if _, ok := noCompanyIdsMap[v]; !ok {
+				sendCompanyIds = append(sendCompanyIds, v)
+			}
+		}
 	}
+
 	// 指定收件人列表
 	sendEmailIds := make([]int, 0)
 	if req.EmailIds != "" {
@@ -875,7 +885,7 @@ func (this *EnglishReportEmailController) ImportListMatch() {
 	}
 
 	uploadDir := "static/xls"
-	err = os.MkdirAll(uploadDir, 766)
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		br.Msg = "存储目录创建失败"
 		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()

+ 252 - 105
controllers/english_report/english_classify.go

@@ -2,12 +2,12 @@ package english_report
 
 import (
 	"encoding/json"
-	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/services"
 	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
 	"sort"
 	"time"
 )
@@ -35,7 +35,6 @@ func (this *EnglishReportController) ListClassify() {
 	pageSize, _ := this.GetInt("PageSize")
 	currentIndex, _ := this.GetInt("CurrentIndex")
 	keyWord := this.GetString("KeyWord")
-	classifyType, _ := this.GetInt("ClassifyType", 0)
 
 	var startSize int
 	if pageSize <= 0 {
@@ -50,13 +49,21 @@ func (this *EnglishReportController) ListClassify() {
 	page := paging.GetPaging(currentIndex, pageSize, 0)
 	resp := new(models.EnglishClassifyListResp)
 
-	list, err := models.GetEnglishClassifyList(startSize, pageSize, keyWord, classifyType)
+	// 处理一级分类分页的情况
+	rootList, err := models.GetEnglishClassifyRootId(startSize, pageSize, keyWord)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
-	total, err := models.GetEnglishClassifyListCount(keyWord, classifyType)
+	var ids []int
+	var rootIds []int
+	rootMap := make(map[int]struct{}, 0)
+	for _, v := range rootList {
+		rootIds = append(rootIds, v.Id)
+		rootMap[v.Id] = struct{}{}
+	}
+	total, err := models.GetEnglishClassifyListCount(keyWord)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取失败,Err:" + err.Error()
@@ -72,65 +79,102 @@ func (this *EnglishReportController) ListClassify() {
 		br.Msg = "获取成功"
 		return
 	}
-	var parentIds []int
-	for _, v := range list {
-		parentIds = append(parentIds, v.Id)
-	}
+	page = paging.GetPaging(currentIndex, pageSize, total)
 
-	childMap := make(map[int][]*models.EnglishClassifyList)
-	tmpList, err := models.GetEnglishClassifyChildByParentIds(parentIds, keyWord, classifyType)
+	//获取相关的分类ID
+	idList, err := models.GetEnglishClassifyListByRootId(rootIds, keyWord)
 	if err != nil {
-		br.Msg = "获取二级分类失败"
-		br.ErrMsg = "获取二级分类失败,Err:" + err.Error()
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
-
-	// 二级分类-品种权限
+	secondListMap := make(map[int][]*models.EnglishClassifyList)
+	thirdListMap := make(map[int][]*models.EnglishClassifyList)
+	var thirdIds []int
+	var sortChildList models.RSChildClassifyList
+	// 三级分类-品种权限
 	permissionMap := make(map[int][]int)
-	{
-		classifyIds := make([]int, 0)
+
+	if len(idList) > 0 {
+		childIdMap := make(map[int]struct{}, 0)
+		for _, v := range idList {
+			if _, ok := childIdMap[v.ParentId]; !ok {
+				ids = append(ids, v.ParentId)
+				childIdMap[v.ParentId] = struct{}{}
+			}
+			if _, ok := childIdMap[v.Id]; !ok {
+				ids = append(ids, v.Id)
+				childIdMap[v.Id] = struct{}{}
+			}
+		}
+		tmpList, err := models.GetEnglishClassifyChildByIds(ids)
+		if err != nil {
+			br.Msg = "获取二级分类失败"
+			br.ErrMsg = "获取二级分类失败,Err:" + err.Error()
+			return
+		}
 		for _, v := range tmpList {
-			classifyIds = append(classifyIds, v.Id)
+			if _, ok := rootMap[v.ParentId]; !ok {
+				thirdIds = append(thirdIds, v.Id)
+			}
 		}
-		if len(classifyIds) > 0 {
-			cond := fmt.Sprintf(` AND %s IN (%s)`, models.EnClassifyPermissionColumns.EnClassifyId, utils.GetOrmInReplace(len(classifyIds)))
-			pars := make([]interface{}, 0)
-			pars = append(pars, classifyIds)
-			ob := new(models.EnClassifyPermission)
-			items, e := ob.GetItemsByCondition(cond, pars, []string{}, "")
-			if e != nil {
-				br.Msg = "获取失败"
-				br.ErrMsg = "获取客户权限列表失败, Err: " + e.Error()
-				return
+		{
+			classifyIds := thirdIds
+			if len(classifyIds) > 0 {
+				cond := fmt.Sprintf(` AND %s IN (%s)`, models.EnClassifyPermissionColumns.EnClassifyId, utils.GetOrmInReplace(len(classifyIds)))
+				pars := make([]interface{}, 0)
+				pars = append(pars, classifyIds)
+				ob := new(models.EnClassifyPermission)
+				items, e := ob.GetItemsByCondition(cond, pars, []string{}, "")
+				if e != nil {
+					br.Msg = "获取失败"
+					br.ErrMsg = "获取客户权限列表失败, Err: " + e.Error()
+					return
+				}
+				for _, v := range items {
+					if permissionMap[v.EnClassifyId] == nil {
+						permissionMap[v.EnClassifyId] = make([]int, 0)
+					}
+					permissionMap[v.EnClassifyId] = append(permissionMap[v.EnClassifyId], v.EnPermissionId)
+				}
 			}
-			for _, v := range items {
-				if permissionMap[v.EnClassifyId] == nil {
-					permissionMap[v.EnClassifyId] = make([]int, 0)
+		}
+		// 处理三级分类
+		for _, v := range tmpList {
+			if _, ok := rootMap[v.ParentId]; !ok {
+				if p, ok1 := permissionMap[v.Id]; ok1 {
+					v.EnPermissions = p
 				}
-				permissionMap[v.EnClassifyId] = append(permissionMap[v.EnClassifyId], v.EnPermissionId)
+				thirdListMap[v.ParentId] = append(thirdListMap[v.ParentId], v)
 			}
 		}
-	}
 
-	for _, v := range tmpList {
-		v.EnPermissions = permissionMap[v.Id]
-		childMap[v.ParentId] = append(childMap[v.ParentId], v)
-	}
-	for _, v := range list {
-		if child, ok := childMap[v.Id]; ok {
-			v.Child = child
+		//处理二级分类
+		for _, v := range tmpList {
+			if _, ok := rootMap[v.ParentId]; ok {
+				if child, ok1 := thirdListMap[v.Id]; ok1 {
+					sortChildList = child
+					sort.Sort(sortChildList)
+					v.Child = sortChildList
+				}
+				secondListMap[v.ParentId] = append(secondListMap[v.ParentId], v)
+			}
 		}
 	}
+
+	//处理一级分类
 	var sortList models.RSClassifyList
-	sortList = list
+	for _, v := range rootList {
+		if child, ok := secondListMap[v.Id]; ok {
+			sortChildList = child
+			sort.Sort(sortChildList)
+			v.Child = sortChildList
+		}
+		sortList = append(sortList, v)
+	}
+
 	sort.Sort(sortList)
 
-	for _, item := range sortList {
-		var sortChildList models.RSChildClassifyList
-		sortChildList = item.Child
-		sort.Sort(sortChildList)
-		item.Child = sortChildList
-	}
 	resp.List = sortList
 	resp.Paging = page
 
@@ -155,19 +199,38 @@ func (this *EnglishReportController) AddClassify() {
 	}()
 	classifyName := this.GetString("ClassifyName")
 	parentId, _ := this.GetInt("ParentId")
-	classifyType, _ := this.GetInt("ClassifyType", 0)
 	sort, _ := this.GetInt("Sort")
 
+	// 查新父级分类是否存在
+	rootId := 0
+	if parentId > 0 {
+		parentClassify, err := models.GetEnglishReportClassifyById(parentId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "上级分类不存在"
+				return
+			}
+			br.Msg = "查询上级分类信息失败"
+			br.ErrMsg = "查询上级分类信息失败,Err:" + err.Error()
+			return
+		}
+		if parentClassify.RootId == 0 {
+			rootId = parentClassify.Id
+		} else {
+			rootId = parentClassify.RootId
+		}
+	}
+
 	item := &models.EnglishClassify{
 		ClassifyName: classifyName,
 		Sort:         sort,
 		ParentId:     parentId,
+		RootId:       rootId,
 		CreateTime:   time.Now(),
 		ModifyTime:   time.Now(),
-		ClassifyType: classifyType,
 		IsShow:       1,
 	}
-	counts, err := models.GetEnglishClassifyCountsByName(classifyName, parentId, classifyType)
+	counts, err := models.GetEnglishClassifyCountsByName(classifyName, parentId)
 	if err != nil {
 		br.Msg = "查询失败"
 		br.ErrMsg = "查询失败,Err:" + err.Error()
@@ -178,12 +241,23 @@ func (this *EnglishReportController) AddClassify() {
 		br.ErrMsg = "该名称已存在!"
 		return
 	}
-	_, err = models.AddEnglishClassify(item)
+	var newId int64
+	newId, err = models.AddEnglishClassify(item)
 	if err != nil {
 		br.Msg = "保存失败"
 		br.ErrMsg = "保存失败,Err:" + err.Error()
 		return
 	}
+	if parentId == 0 { //一级目录的rootId等于自己本身
+		item.Id = int(newId)
+		item.RootId = int(newId)
+		err = item.UpdateEnglishClassify([]string{"RootId"})
+		if err != nil {
+			br.Msg = "保存失败"
+			br.ErrMsg = "保存失败,Err:" + err.Error()
+			return
+		}
+	}
 
 	br.Ret = 200
 	br.Success = true
@@ -223,26 +297,52 @@ func (this *EnglishReportController) EditClassify() {
 	}
 
 	oldItem, err := models.GetEnglishReportClassifyById(classifyId)
-	if err != nil && err.Error() != utils.ErrNoRow() {
-		br.Msg = "获取信息失败"
-		br.ErrMsg = "获取信息失败,Err:" + err.Error()
-		return
-	}
-	parent, err := models.GetEnglishReportClassifyById(parentId)
-	if err != nil && err.Error() != utils.ErrNoRow() {
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "当前分类不存在"
+			return
+		}
 		br.Msg = "获取信息失败"
 		br.ErrMsg = "获取信息失败,Err:" + err.Error()
 		return
 	}
-
 	if oldItem.ParentId == 0 && parentId > 0 {
 		// 一级分类变更为二级分类,禁止
-		br.Msg = "一级分类不允许更改为二级分类"
+		br.Msg = "一级分类不允许更改为二级分类或三级分类"
 		return
 	}
 
+	var parent *models.EnglishClassify
+	rootId := 0
+	if parentId > 0 {
+		parent, err = models.GetEnglishReportClassifyById(parentId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				br.Msg = "上级分类不存在"
+				return
+			}
+			br.Msg = "获取信息失败"
+			br.ErrMsg = "获取信息失败,Err:" + err.Error()
+			return
+		}
+		//二级分类不允许改成三级分类
+		if oldItem.ParentId > 0 && oldItem.ParentId == oldItem.RootId {
+			if parent.ParentId > 0 {
+				//即将改为为三级分类
+				br.Msg = "二级分类不允许更改为三级分类"
+				return
+			}
+		}
+
+		if parent.RootId == 0 {
+			rootId = parent.Id
+		} else {
+			rootId = parent.RootId
+		}
+	}
+
 	//判断是否已存在同个名称的分类
-	existItem, e := models.GetEnglishClassifyByClassifyNameParentId(classifyName, parentId, oldItem.ClassifyType)
+	existItem, e := models.GetEnglishClassifyByClassifyNameParentId(classifyName, parentId)
 	if e != nil {
 		if e.Error() != utils.ErrNoRow() {
 			br.Msg = "查询已存在的分类出错"
@@ -254,44 +354,52 @@ func (this *EnglishReportController) EditClassify() {
 		br.Msg = "分类名称:" + classifyName + "已存在"
 		return
 	}
-	if err != nil && err.Error() != utils.ErrNoRow() {
-		br.Msg = "获取信息失败"
-		br.ErrMsg = "获取信息失败,Err:" + err.Error()
-		return
-	}
 
-	//oldParentId := item.ParentId
+	if parentId == 0 { //一级分类的顶级分类为自己的ID
+		rootId = oldItem.Id
+	}
 	item := &models.EnglishClassify{
 		Id:           oldItem.Id,
 		ClassifyName: classifyName,
 		Sort:         sort,
 		ParentId:     parentId,
+		RootId:       rootId,
 		ModifyTime:   time.Now(),
-		ClassifyType: oldItem.ClassifyType,
 		IsShow:       1,
 	}
-	if oldItem.ClassifyType == 1 {
-		err = services.UpdateEnglishVideoClassifyId(oldItem, item, parent, classifyId)
+	{
+		// 更新研报里的分类名称
+		err = services.UpdateEnglishReportClassifyId(oldItem, item, classifyId)
 		if err != nil {
 			br.Msg = err.Error()
 			return
 		}
-	} else {
-		// 更新研报里的分类名称
-		err = services.UpdateEnglishReportClassifyId(oldItem, item, parent, classifyId)
+
+		//更新相关联的路演视频里的分类名称
+		err = services.UpdateEnglishVideoClassifyId(oldItem, item, classifyId)
 		if err != nil {
 			br.Msg = err.Error()
 			return
 		}
 	}
-
 	err = models.ModifyEnglishClassify(item)
 	if err != nil {
 		br.Msg = "保存失败"
 		br.ErrMsg = "保存失败,Err:" + err.Error()
 		return
 	}
-
+	err = models.UpdateEnglishReportClassifyByFirstSecondClassifyId(classifyId, parentId)
+	if err != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
+	err = models.UpdateEnglishVideoClassifyByFirstSecondClassifyId(classifyId, parentId)
+	if err != nil {
+		br.Msg = "保存失败"
+		br.ErrMsg = "保存失败,Err:" + err.Error()
+		return
+	}
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"
@@ -336,33 +444,29 @@ func (this *EnglishReportController) DelClassify() {
 		br.Ret = 403
 		return
 	}
+	reportCount, e := models.GetEnglishReportCounts(classifyId, classifyInfo.ParentId)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		br.Msg = "获取信息失败"
+		br.ErrMsg = "获取失败,Err:" + e.Error()
+		return
+	}
 
-	if classifyInfo.ClassifyType == 0 {
-		reportCount, e := models.GetEnglishReportCounts(classifyId, classifyInfo.ParentId)
-		if e != nil && e.Error() != utils.ErrNoRow() {
-			br.Msg = "获取信息失败"
-			br.ErrMsg = "获取失败,Err:" + e.Error()
-			return
-		}
-
-		if reportCount > 0 {
-			br.Msg = "该分类有关联报告,不允许删除"
-			br.Ret = 403
-			return
-		}
-	} else {
-		videoCount, e := models.GetEnglishVideoCounts(classifyId, classifyInfo.ParentId)
-		if e != nil && e.Error() != utils.ErrNoRow() {
-			br.Msg = "获取信息失败"
-			br.ErrMsg = "获取失败,Err:" + e.Error()
-			return
-		}
+	if reportCount > 0 {
+		br.Msg = "该分类有关联报告,不允许删除"
+		br.Ret = 403
+		return
+	}
+	videoCount, e := models.GetEnglishVideoCounts(classifyId, classifyInfo.ParentId)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		br.Msg = "获取信息失败"
+		br.ErrMsg = "获取失败,Err:" + e.Error()
+		return
+	}
 
-		if videoCount > 0 {
-			br.Msg = "该分类有关联的路演视频,不允许删除"
-			br.Ret = 403
-			return
-		}
+	if videoCount > 0 {
+		br.Msg = "该分类有关联的路演视频,不允许删除"
+		br.Ret = 403
+		return
 	}
 
 	if err = models.DeleteEnglishClassify(classifyId); err != nil {
@@ -391,7 +495,6 @@ func (this *EnglishReportController) FistListClassify() {
 	}()
 	pageSize, _ := this.GetInt("PageSize")
 	currentIndex, _ := this.GetInt("CurrentIndex")
-	classifyType, _ := this.GetInt("ClassifyType", 0)
 
 	var startSize int
 	if pageSize <= 0 {
@@ -402,22 +505,66 @@ func (this *EnglishReportController) FistListClassify() {
 	}
 
 	startSize = utils.StartIndex(currentIndex, pageSize)
-	list, err := models.GetEnglishFirstClassifyList(classifyType, startSize, pageSize)
+	rootList, err := models.GetEnglishFirstClassifyList(startSize, pageSize)
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
-	total, err := models.GetEnglishFirstClassifyListCount(classifyType)
+	total, err := models.GetEnglishFirstClassifyListCount()
 	if err != nil {
 		br.Msg = "获取失败"
 		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
-
 	page := paging.GetPaging(currentIndex, pageSize, total)
 	resp := new(models.EnglishClassifyListResp)
-	resp.List = list
+
+	if total == 0 {
+		resp.List = make([]*models.EnglishClassifyList, 0)
+		resp.Paging = page
+
+		br.Data = resp
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+	rootMap := make(map[int]struct{}, 0)
+	var rootIds []int
+	for _, v := range rootList {
+		rootMap[v.Id] = struct{}{}
+		rootIds = append(rootIds, v.Id)
+	}
+	tmpList, err := models.GetEnglishSecondClassifyList(rootIds)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	secondListMap := make(map[int][]*models.EnglishClassifyList)
+	//处理二级分类
+	for _, v := range tmpList {
+		if _, ok := rootMap[v.ParentId]; ok {
+			secondListMap[v.ParentId] = append(secondListMap[v.ParentId], v)
+		}
+	}
+
+	//处理一级分类
+	var sortList models.RSClassifyList
+	var sortChildList models.RSChildClassifyList
+	for _, v := range rootList {
+		if child, ok := secondListMap[v.Id]; ok {
+			sortChildList = child
+			sort.Sort(sortChildList)
+			v.Child = sortChildList
+		}
+		sortList = append(sortList, v)
+	}
+
+	sort.Sort(sortList)
+
+	resp.List = sortList
 	resp.Paging = page
 
 	br.Data = resp

+ 104 - 11
controllers/english_report/report.go

@@ -277,6 +277,24 @@ func (this *EnglishReportController) Detail() {
 	item.Content = html.UnescapeString(item.Content)
 	item.ContentSub = html.UnescapeString(item.ContentSub)
 
+	classifyNameMap := make(map[int]*models.EnglishClassifyFullName)
+	if item.ClassifyIdSecond > 0 {
+		nameList, tErr := models.GetEnglishClassifyFullNameByIds([]int{item.ClassifyIdSecond})
+		if tErr != nil {
+			br.Msg = "获取分类名称失败"
+			br.ErrMsg = "获取分类名称失败, ERR:" + tErr.Error()
+			return
+		}
+		for _, v := range nameList {
+			classifyNameMap[v.Id] = v
+		}
+		//处理分类名
+		if n, ok := classifyNameMap[item.ClassifyIdSecond]; ok {
+			item.ClassifyNameRoot = n.RootName
+			item.ClassifyIdRoot = n.RootId
+		}
+	}
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"
@@ -292,8 +310,8 @@ func (this *EnglishReportController) Detail() {
 // @Param   StartDate   query   string  true       "开始时间"
 // @Param   EndDate   query   string  true       "结束时间"
 // @Param   Frequency   query   string  true       "频度"
-// @Param   ClassifyNameFirst   query   string  true       "一级分类名称"
-// @Param   ClassifyNameSecond   query   string  true       "二级分类名称"
+// @Param   ClassifyIdFirst   query   string  true       "一级分类id"
+// @Param   ClassifyIdSecond   query   string  true       "二级分类id"
 // @Param   State   query   int  true       "状态"
 // @Param   KeyWord   query   string  true       "搜索关键词"
 // @Param   PublishSort   query   string  true       "desc:降序,asc 升序(预留)"
@@ -321,8 +339,9 @@ func (this *EnglishReportController) ListReport() {
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
 	frequency := this.GetString("Frequency")
-	classifyNameFirst := this.GetString("ClassifyNameFirst")
-	classifyNameSecond := this.GetString("ClassifyNameSecond")
+	classifyIdFirst, _ := this.GetInt("ClassifyIdFirst")
+	classifyIdSecond, _ := this.GetInt("ClassifyIdSecond")
+	classifyIdRoot, _ := this.GetInt("ClassifyIdRoot")
 	state, _ := this.GetInt("State")
 	keyWord := this.GetString("KeyWord")
 	companyType := this.GetString("CompanyType")
@@ -366,19 +385,47 @@ func (this *EnglishReportController) ListReport() {
 		condition += ` AND frequency = ? `
 		pars = append(pars, frequency)
 	}
-	if classifyNameFirst != "" {
-		condition += ` AND classify_name_first = ? `
-		pars = append(pars, classifyNameFirst)
+	if classifyIdFirst != 0 {
+		condition += ` AND classify_id_first = ? `
+		pars = append(pars, classifyIdFirst)
 	}
 
-	if classifyNameSecond != "" {
-		condition += ` AND classify_name_second = ? `
-		pars = append(pars, classifyNameSecond)
+	if classifyIdSecond != 0 {
+		condition += ` AND classify_id_second = ? `
+		pars = append(pars, classifyIdSecond)
 	}
 	if state > 0 {
 		condition += ` AND state = ? `
 		pars = append(pars, state)
 	}
+	if classifyIdRoot > 0 && classifyIdFirst == 0 && classifyIdSecond == 0 {
+		//查询顶级分类下的所有二级分类ID
+		childClassify, err := models.GetEnglishSecondClassifyList([]int{classifyIdRoot})
+		if err != nil {
+			br.Msg = "查询子分类出错"
+			br.ErrMsg = "查询子分类出错, Err:" + err.Error()
+			return
+		}
+		var childClassifyIds []int
+		if len(childClassify) > 0 {
+			for _, v := range childClassify {
+				childClassifyIds = append(childClassifyIds, v.Id)
+			}
+			condition += ` AND classify_id_first IN (` + utils.GetOrmInReplace(len(childClassifyIds)) + `)`
+			pars = append(pars, childClassifyIds)
+		} else {
+			//一级分类下没有子分类,直接返回空列表
+			page := paging.GetPaging(currentIndex, pageSize, 0)
+			resp := new(models.EnglishReportListResp)
+			resp.Paging = page
+			resp.List = make([]*models.EnglishReportList, 0)
+			br.Ret = 200
+			br.Success = true
+			br.Msg = "获取成功"
+			br.Data = resp
+			return
+		}
+	}
 	// 未群发邮件(包含推送邮件失败的)
 	if emailState == 1 {
 		failIds, e := models.GetHasFailEmailLogReportIds()
@@ -516,7 +563,23 @@ func (this *EnglishReportController) ListReport() {
 		br.ErrMsg = errOther.Error()
 		return
 	}
-
+	// 查询分类信息
+	var classifyIdSecondSlice []int
+	for _, item := range list {
+		classifyIdSecondSlice = append(classifyIdSecondSlice, item.ClassifyIdSecond)
+	}
+	classifyNameMap := make(map[int]*models.EnglishClassifyFullName)
+	if len(classifyIdSecondSlice) > 0 {
+		nameList, err := models.GetEnglishClassifyFullNameByIds(classifyIdSecondSlice)
+		if err != nil {
+			br.Msg = "获取分类名称失败"
+			br.ErrMsg = "获取分类名称失败, ERR:" + err.Error()
+			return
+		}
+		for _, v := range nameList {
+			classifyNameMap[v.Id] = v
+		}
+	}
 	for _, item := range list {
 		if item.State == 2 {
 			item.ShareUrl = "https://share.hzinsights.com/reportEn?code=" + item.ReportCode
@@ -557,6 +620,17 @@ func (this *EnglishReportController) ListReport() {
 		} else {
 			item.Editor = ret.Editor
 		}
+
+		//处理分类名
+		if n, ok := classifyNameMap[item.ClassifyIdSecond]; ok {
+			if n.RootId == 0 {
+				item.FullClassifyName = strings.Join([]string{n.ParentName, n.ClassifyName}, "/")
+			} else {
+				item.FullClassifyName = strings.Join([]string{n.RootName, n.ParentName, n.ClassifyName}, "/")
+			}
+			item.ClassifyIdRoot = n.RootId
+			item.ClassifyNameRoot = n.RootName
+		}
 	}
 
 	page := paging.GetPaging(currentIndex, pageSize, total)
@@ -932,7 +1006,26 @@ func (this *EnglishReportController) ClassifyIdDetail() {
 	if item != nil {
 		item.Content = html.UnescapeString(item.Content)
 		item.ContentSub = html.UnescapeString(item.ContentSub)
+
+		classifyNameMap := make(map[int]*models.EnglishClassifyFullName)
+		if item.ClassifyIdSecond > 0 {
+			nameList, tErr := models.GetEnglishClassifyFullNameByIds([]int{item.ClassifyIdSecond})
+			if tErr != nil {
+				br.Msg = "获取分类名称失败"
+				br.ErrMsg = "获取分类名称失败, ERR:" + tErr.Error()
+				return
+			}
+			for _, v := range nameList {
+				classifyNameMap[v.Id] = v
+			}
+			//处理分类名
+			if n, ok := classifyNameMap[item.ClassifyIdSecond]; ok {
+				item.ClassifyNameRoot = n.RootName
+				item.ClassifyIdRoot = n.RootId
+			}
+		}
 	}
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"

+ 114 - 0
controllers/meeting_probabilities.go

@@ -0,0 +1,114 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_api/models"
+	"eta/eta_api/models/data_manage/request"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/tealeg/xlsx"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+// MeetingProbabilitiesController 加息概率
+type MeetingProbabilitiesController struct {
+	BaseAuthController
+}
+
+// Detail
+// @Title 获取表格详情
+// @Description 获取表格详情接口
+// @Param   ExcelInfoId   query   int  true       "表格id"
+// @Success 200 {object} data_manage.ExcelInfo
+// @router /detail [get]
+func (c *MeetingProbabilitiesController) Detail() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		c.Data["json"] = br
+		c.ServeJSON()
+	}()
+	sysUser := c.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	date := c.GetString("DateTime")
+	isExport, _ := c.GetInt("IsExport")
+
+	// 获取数据详情bu
+	excelDetail, err := models.GetMeetingInfoById(date)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Ret = 200
+			br.Msg = "该日期无数据"
+			br.Msg = "该日期无数据,Err:" + err.Error()
+			return
+		}
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	if isExport == 1 {
+		sheet := new(request.ISheet)
+		err = json.Unmarshal([]byte(excelDetail.Content), &sheet)
+
+		MeetingProbabilitiesExport(c,br,sheet)
+		return
+	}
+
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = excelDetail
+}
+
+// MeetingProbabilitiesExport 导出Excel
+func MeetingProbabilitiesExport(this *MeetingProbabilitiesController, br *models.BaseResponse, sheetReq *request.ISheet) {
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请重新登录"
+		return
+	}
+
+	dir, _ := os.Executable()
+	exPath := filepath.Dir(dir)
+	downloadPath := exPath + "/" + time.Now().Format(utils.FormatDateTimeUnSpace) + ".xlsx"
+	xlsxFile := xlsx.NewFile()
+	sheet, e := xlsxFile.AddSheet("数据报表")
+	if e != nil {
+		br.Msg = "新增Sheet失败"
+		br.ErrMsg = "新增Sheet失败, Err: " + e.Error()
+		return
+	}
+
+
+	for _, v := range sheetReq.Data {
+		addRow := sheet.AddRow()
+		for _, s := range v {
+			if s != nil {
+				addRow.AddCell().SetString(s.M)
+			}
+		}
+	}
+
+	if e = xlsxFile.Save(downloadPath); e != nil {
+		br.Msg = "导出失败"
+		br.ErrMsg = "保存文件失败"
+		return
+	}
+	fileName := fmt.Sprint("MeetingProbabilities", time.Now().Format("2006.01.02"), ".xlsx")
+	this.Ctx.Output.Download(downloadPath, fileName)
+	defer func() {
+		_ = os.Remove(downloadPath)
+	}()
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "success"
+}
+

+ 25 - 9
controllers/ppt_english.go

@@ -492,7 +492,7 @@ func (this *PptEnglishController) PptUpload() {
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
-	err = os.MkdirAll(uploadDir, 766)
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		br.Msg = "存储目录创建失败"
 		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
@@ -516,14 +516,25 @@ func (this *PptEnglishController) PptUpload() {
 	pptName := utils.GetRandStringNoSpecialChar(28)
 	savePptxToOssPath += pptName + ".pptx"
 
-	//上传到阿里云
-	err = services.UploadFileToAliyun("", fpath, savePptxToOssPath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
+	//上传到阿里云 和 minio
+	pptxUrl := ``
+	if utils.ObjectStorageClient == "minio" {
+		err = services.UploadFileToMinIo("", fpath, savePptxToOssPath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+		pptxUrl = utils.MinIoImghost + savePptxToOssPath
+	} else {
+		err = services.UploadFileToAliyun("", fpath, savePptxToOssPath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+		pptxUrl = utils.Imghost + savePptxToOssPath
 	}
-	pptxUrl := utils.Imghost + savePptxToOssPath
 
 	defer func() {
 		os.Remove(fpath)
@@ -596,6 +607,11 @@ func (this *PptEnglishController) SaveLog() {
 	if err != nil {
 		br.Msg = "信息获取失败"
 		br.ErrMsg = "信息获取失败,Err:" + err.Error()
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "PPT已删除"
+			br.ErrMsg = "PPT已删除"
+			br.IsSendEmail = false
+		}
 		return
 	}
 	//pptInfo.TemplateType = req.FirstPage.TemplateType
@@ -918,4 +934,4 @@ func (this *PptEnglishController) TitleCheck() {
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "校验成功"
-}
+}

+ 20 - 9
controllers/ppt_v2.go

@@ -493,7 +493,7 @@ func (this *PptV2Controller) PptUpload() {
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
-	err = os.MkdirAll(uploadDir, 766)
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		br.Msg = "存储目录创建失败"
 		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
@@ -517,14 +517,25 @@ func (this *PptV2Controller) PptUpload() {
 	pptName := utils.GetRandStringNoSpecialChar(28)
 	savePptxToOssPath += pptName + ".pptx"
 
-	//上传到阿里云
-	err = services.UploadFileToAliyun("", fpath, savePptxToOssPath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
+	//上传到阿里云 和 minio
+	pptxUrl := ``
+	if utils.ObjectStorageClient == "minio" {
+		err = services.UploadFileToMinIo("", fpath, savePptxToOssPath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+		pptxUrl = utils.MinIoImghost + savePptxToOssPath
+	} else {
+		err = services.UploadFileToAliyun("", fpath, savePptxToOssPath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+		pptxUrl = utils.Imghost + savePptxToOssPath
 	}
-	pptxUrl := utils.Imghost + savePptxToOssPath
 
 	defer func() {
 		os.Remove(fpath)
@@ -1117,4 +1128,4 @@ func (this *PptV2Controller) TitleCheck() {
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "校验成功"
-}
+}

+ 31 - 12
controllers/report.go

@@ -794,7 +794,7 @@ func (this *ReportController) Upload() {
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
-	err = os.MkdirAll(uploadDir, 777)
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		br.Msg = "存储目录创建失败"
 		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
@@ -810,12 +810,23 @@ func (this *ReportController) Upload() {
 		br.ErrMsg = "文件上传失败,Err:" + err.Error()
 		return
 	}
-	//上传到阿里云
-	resourceUrl, err := services.UploadAliyunV2(fileName, fpath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
+
+	resourceUrl := ``
+	//上传到阿里云 和 minio
+	if utils.ObjectStorageClient == "minio" {
+		resourceUrl, err = services.UploadImgToMinIo(fileName, fpath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+	} else {
+		resourceUrl, err = services.UploadAliyunV2(fileName, fpath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
 	}
 
 	defer func() {
@@ -1102,7 +1113,7 @@ func (this *ReportUploadCommonController) UploadImg() {
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
-	err = os.MkdirAll(uploadDir, 777)
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		return
 	}
@@ -1114,10 +1125,18 @@ func (this *ReportUploadCommonController) UploadImg() {
 	if err != nil {
 		return
 	}
-	//上传到阿里云
-	resourceUrl, err := services.UploadAliyunV2(fileName, fpath)
-	if err != nil {
-		return
+	resourceUrl := ``
+	//上传到阿里云 和 minio
+	if utils.ObjectStorageClient == "minio" {
+		resourceUrl, err = services.UploadImgToMinIo(fileName, fpath)
+		if err != nil {
+			return
+		}
+	} else {
+		resourceUrl, err = services.UploadAliyunV2(fileName, fpath)
+		if err != nil {
+			return
+		}
 	}
 
 	defer func() {

+ 117 - 47
controllers/resource.go

@@ -42,7 +42,7 @@ func (this *ResourceController) Upload() {
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
-	err = os.MkdirAll(uploadDir, 766)
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		br.Msg = "存储目录创建失败"
 		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
@@ -58,12 +58,22 @@ func (this *ResourceController) Upload() {
 		br.ErrMsg = "文件上传失败,Err:" + err.Error()
 		return
 	}
-	//上传到阿里云
-	resourceUrl, err := services.UploadAliyunV2(fileName, fpath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
+	resourceUrl := ``
+	//上传到阿里云 和 minio
+	if utils.ObjectStorageClient == "minio" {
+		resourceUrl, err = services.UploadImgToMinIo(fileName, fpath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+	} else {
+		resourceUrl, err = services.UploadAliyunV2(fileName, fpath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
 	}
 
 	defer func() {
@@ -118,7 +128,7 @@ func (this *ResourceController) VideoUpload() {
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
 	//uploadDir := "./" + dateDir
 	fmt.Println(uploadDir)
-	err = os.MkdirAll(uploadDir, 777)
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		br.Msg = "存储目录创建失败"
 		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
@@ -229,16 +239,28 @@ func (this *ResourceController) VideoUpload() {
 
 	savePath := utils.Upload_Audio_Dir + time.Now().Format("200601/20060102/")
 	savePath += fileName
-	//上传到阿里云
-	err = services.UploadVideoAliyun(fileName, fpath, savePath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
+
+	//上传到阿里云 和 minio
+	resourceUrl := ``
+	if utils.ObjectStorageClient == "minio" {
+		err = services.UploadVideoToMinIo(fileName, fpath, savePath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+		resourceUrl = utils.MinIoImghost + savePath
+	} else {
+		err = services.UploadVideoAliyun(fileName, fpath, savePath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+		resourceUrl = utils.Imghost + savePath
 	}
 	utils.FileLog.Info("%s:", time.Now().Format(utils.FormatDateTime))
 	utils.FileLog.Info("end update oss ")
-	resourceUrl := utils.Imghost + savePath
 
 	item := new(models.Resource)
 	item.ResourceUrl = resourceUrl
@@ -301,7 +323,7 @@ func (this *ResourceController) VoiceUpload() {
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
 	//uploadDir := "./" + dateDir
 	fmt.Println(uploadDir)
-	err = os.MkdirAll(uploadDir, 766)
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		br.Msg = "存储目录创建失败"
 		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
@@ -394,16 +416,27 @@ func (this *ResourceController) VoiceUpload() {
 
 	savePath := utils.Upload_Audio_Dir + time.Now().Format("200601/20060102/")
 	savePath += fileName
-	//上传到阿里云
-	err = services.UploadVideoAliyun(fileName, fpath, savePath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
+	//上传到阿里云 和 minio
+	resourceUrl := ``
+	if utils.ObjectStorageClient == "minio" {
+		err = services.UploadVideoToMinIo(fileName, fpath, savePath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+		resourceUrl = utils.MinIoImghost + savePath
+	} else {
+		err = services.UploadVideoAliyun(fileName, fpath, savePath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+		resourceUrl = utils.Imghost + savePath
 	}
 	utils.FileLog.Info(fmt.Sprintf("%s:", time.Now().Format(utils.FormatDateTime)))
 	utils.FileLog.Info("end update oss ")
-	resourceUrl := utils.Imghost + savePath
 
 	item := new(models.Resource)
 	item.ResourceUrl = resourceUrl
@@ -546,18 +579,29 @@ func (this *ResourceController) UploadImageBase64() {
 	hzUploadDir := "static/images/"
 	savePath := hzUploadDir + time.Now().Format("200601/20060102/")
 	savePath += fileName
-	//上传到阿里云
-	err = services.UploadFileToAliyun(fileName, fpath, savePath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
+
+	//上传到阿里云 和 minio
+	resourceUrl := ``
+	if utils.ObjectStorageClient == "minio" {
+		err = services.UploadFileToMinIo(fileName, fpath, savePath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+		resourceUrl = utils.MinIoImghost + savePath
+	} else {
+		err = services.UploadFileToAliyun(fileName, fpath, savePath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+		resourceUrl = utils.Imghost + savePath
 	}
 	utils.FileLog.Info("%s:", time.Now().Format(utils.FormatDateTime))
 	utils.FileLog.Info("end update oss ")
 
-	resourceUrl := utils.Imghost + savePath
-
 	item := new(models.Resource)
 	item.ResourceUrl = resourceUrl
 	item.ResourceType = 1
@@ -661,7 +705,7 @@ func (this *ResourceController) UploadV2() {
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
-	err = os.MkdirAll(uploadDir, 766)
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		br.Msg = "存储目录创建失败"
 		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
@@ -677,12 +721,22 @@ func (this *ResourceController) UploadV2() {
 		br.ErrMsg = "文件上传失败,Err:" + err.Error()
 		return
 	}
-	//上传到阿里云
-	resourceUrl, err := services.UploadAliyunV2(fileName, fpath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
+	resourceUrl := ``
+	//上传到阿里云 和 minio
+	if utils.ObjectStorageClient == "minio" {
+		resourceUrl, err = services.UploadImgToMinIo(fileName, fpath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+	} else {
+		resourceUrl, err = services.UploadAliyunV2(fileName, fpath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
 	}
 
 	defer func() {
@@ -720,14 +774,30 @@ func (this *ResourceController) OssSTSToken() {
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
-	resp, err := services.GetOssSTSToken()
-	if err != nil {
-		br.Msg = "获取失败"
-		br.ErrMsg = "获取STSToken失败, Err: " + err.Error()
-		return
+
+	source, _ := this.GetInt("StorageSource")
+
+	if source == utils.STORAGESOURCE_OSS {
+		resp, err := services.GetOssSTSToken()
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取STSToken失败, Err: " + err.Error()
+			return
+		}
+		br.Data = resp
+		br.Msg = "获取成功"
+		br.Ret = 200
+		br.Success = true
+	} else if source == utils.STORAGESOURCE_MINIO {
+		resp, err := services.GetMinIOSTSToken()
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取STSToken失败, Err: " + err.Error()
+			return
+		}
+		br.Data = resp
+		br.Msg = "获取成功"
+		br.Ret = 200
+		br.Success = true
 	}
-	br.Msg = "获取成功"
-	br.Ret = 200
-	br.Success = true
-	br.Data = resp
 }

+ 20 - 15
controllers/sys_admin.go

@@ -366,8 +366,13 @@ func (this *SysAdminController) Add() {
 			br.Msg = "邮箱格式有误, 请检查"
 			return
 		}
-		_, e := system.GetSysUserByEmail(req.Email)
-		if e.Error() != utils.ErrNoRow() {
+		emailUser, e := system.GetSysUserByEmail(req.Email)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "邮箱获取用户失败"
+			return
+		}
+		if emailUser != nil && emailUser.AdminId > 0 {
 			br.Msg = "邮箱已存在, 请重新填写"
 			return
 		}
@@ -526,13 +531,13 @@ func (this *SysAdminController) Add() {
 		syncData.Source = utils.SOURCE_ETA_FLAG
 		syncData.AdminName = admin.AdminName
 		_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
-	}
 
-	err = services.UpdateResearcherTagGroup(admin.AdminId, req.ResearchGroupIds)
-	if err != nil {
-		br.Msg = "新增失败"
-		br.ErrMsg = "新增失败,Err:" + err.Error()
-		return
+		e := services.UpdateResearcherTagGroup(admin.AdminId, req.ResearchGroupIds)
+		if e != nil {
+			br.Msg = "新增失败"
+			br.ErrMsg = "新增用户研究方向分组失败, Err:" + e.Error()
+			return
+		}
 	}
 
 	// 清楚系统用户列表缓存key
@@ -747,18 +752,18 @@ func (this *SysAdminController) Edit() {
 		syncData.Source = utils.SOURCE_ETA_FLAG
 		syncData.AdminName = adminInfo.AdminName
 		_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
+
+		// 研究方向分组
+		if e := services.UpdateResearcherTagGroup(req.AdminId, req.ResearchGroupIds); e != nil {
+			br.Msg = "编辑失败"
+			br.ErrMsg = "更新用户研究方向失败, Err:" + e.Error()
+			return
+		}
 	}
 
 	// 用户登出
 	logOutSystemUser(adminInfo.AdminId)
 
-	// 研究方向分组
-	if e := services.UpdateResearcherTagGroup(req.AdminId, req.ResearchGroupIds); e != nil {
-		br.Msg = "编辑失败"
-		br.ErrMsg = "更新用户研究方向失败, Err:" + e.Error()
-		return
-	}
-
 	// 清楚系统用户列表缓存key
 	utils.Rc.Delete(utils.CACHE_KEY_ADMIN)
 	utils.Rc.Delete(utils.CACHE_KEY_ADMIN_ID)

+ 9 - 0
controllers/sys_role.go

@@ -707,6 +707,15 @@ func (this *SysRoleController) SystemConfig() {
 		ConfVal: conf["ChartViewUrl"],
 	})
 
+	osc := system.BusinessConf{
+		ConfKey: "ObjectStorageClient",
+		ConfVal: utils.ObjectStorageClient,
+	}
+	if osc.ConfVal == "" {
+		osc.ConfVal = "oss"
+	}
+	list = append(list, osc)
+
 	br.Data = list
 	br.Ret = 200
 	br.Success = true

+ 11 - 0
controllers/target.go

@@ -3483,6 +3483,17 @@ func (this *TargetController) ExcelDataAdd() {
 						continue
 					}
 					valueListMap[index] = valueMap["m"].(string)
+
+					// 09-27 千位分隔符时用 "m" 取字符串存数据库会把逗号当小数点,现在换用 "v" 直接取数字再转为字符串,看看会不会有问题
+					if ct, ok := valueMap["ct"].(map[string]interface{}); ok{
+						fa, _ := ct["fa"]
+						if fa == "#,##0.000" {
+							value = valueMap["v"]
+							floatValue := valueMap["v"].(float64)
+							valueListMap[index] = strconv.FormatFloat(floatValue, 'f', -1, 64)
+						}
+					}
+
 					//valueList = append(valueList, valueMap["m"].(string))
 					//fmt.Println("valueList:", valueList)
 				}

+ 22 - 10
controllers/voice.go

@@ -2,12 +2,12 @@ package controllers
 
 import (
 	"archive/zip"
-	"fmt"
-	"github.com/rdlucklib/rdluck_tools/file"
-	"github.com/rdlucklib/rdluck_tools/http"
 	"eta/eta_api/models"
 	"eta/eta_api/services"
 	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/file"
+	"github.com/rdlucklib/rdluck_tools/http"
 	"io/ioutil"
 	"os"
 	"path"
@@ -56,7 +56,7 @@ func (this *VoiceController) Upload() {
 	ext := path.Ext(h.Filename)
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
-	err = os.MkdirAll(uploadDir, 777)
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		br.Msg = "存储目录创建失败"
 		br.ErrMsg = "存储目录创建失败,Err:" + err.Error()
@@ -72,13 +72,25 @@ func (this *VoiceController) Upload() {
 		br.ErrMsg = "文件上传失败,Err:" + err.Error()
 		return
 	}
-	//上传到阿里云
-	resourceUrl, err := services.UploadAudioAliyun(fileName, fpath)
-	if err != nil {
-		br.Msg = "文件上传失败"
-		br.ErrMsg = "文件上传失败,Err:" + err.Error()
-		return
+
+	resourceUrl := ``
+	//上传到阿里云 和 minio
+	if utils.ObjectStorageClient == "minio" {
+		resourceUrl, err = services.UploadAudioToMinIo(fileName, fpath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
+	} else {
+		resourceUrl, err = services.UploadAudioAliyun(fileName, fpath)
+		if err != nil {
+			br.Msg = "文件上传失败"
+			br.ErrMsg = "文件上传失败,Err:" + err.Error()
+			return
+		}
 	}
+
 	defer func() {
 		os.Remove(fpath)
 	}()

+ 0 - 0
etalogs/binlog/20231023.log


+ 14 - 5
go.mod

@@ -22,18 +22,21 @@ require (
 	github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b
 	github.com/gorilla/websocket v1.5.0
 	github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53
+	github.com/minio/minio-go/v7 v7.0.63
 	github.com/mojocn/base64Captcha v1.3.5
 	github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19
 	github.com/olivere/elastic/v7 v7.0.30
 	github.com/rdlucklib/rdluck_tools v1.0.3
 	github.com/shopspring/decimal v1.3.1
 	github.com/silenceper/wechat/v2 v2.1.3
+	github.com/sirupsen/logrus v1.9.3
 	github.com/tealeg/xlsx v1.0.5
 	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.541
 	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ses v1.0.541
 	github.com/xuri/excelize/v2 v2.7.1
 	github.com/yidane/formula v0.0.0-20210902154546-0782e1736717
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
+	gopkg.in/natefinch/lumberjack.v2 v2.2.1
 )
 
 require (
@@ -57,6 +60,7 @@ require (
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
 	github.com/clbanning/mxj/v2 v2.5.5 // indirect
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
+	github.com/dustin/go-humanize v1.0.1 // indirect
 	github.com/fatih/structs v1.1.0 // indirect
 	github.com/fsnotify/fsnotify v1.6.0 // indirect
 	github.com/garyburd/redigo v1.6.3 // indirect
@@ -69,13 +73,18 @@ require (
 	github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 // indirect
 	github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9 // indirect
 	github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 // indirect
+	github.com/google/uuid v1.3.0 // indirect
 	github.com/hashicorp/golang-lru v0.5.4 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
 	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/compress v1.16.7 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.5 // indirect
 	github.com/lib/pq v1.10.7 // indirect
 	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
+	github.com/minio/md5-simd v1.1.2 // indirect
+	github.com/minio/sha256-simd v1.0.1 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -87,9 +96,9 @@ require (
 	github.com/prometheus/procfs v0.9.0 // indirect
 	github.com/richardlehane/mscfb v1.0.4 // indirect
 	github.com/richardlehane/msoleps v1.0.3 // indirect
+	github.com/rs/xid v1.5.0 // indirect
 	github.com/satori/go.uuid v1.2.0 // indirect
 	github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
-	github.com/sirupsen/logrus v1.9.0 // indirect
 	github.com/spf13/cast v1.5.0 // indirect
 	github.com/stretchr/testify v1.8.1 // indirect
 	github.com/tidwall/gjson v1.14.1 // indirect
@@ -98,11 +107,11 @@ require (
 	github.com/tjfoc/gmsm v1.3.2 // indirect
 	github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect
 	github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect
-	golang.org/x/crypto v0.8.0 // indirect
+	golang.org/x/crypto v0.12.0 // indirect
 	golang.org/x/image v0.5.0 // indirect
-	golang.org/x/net v0.9.0 // indirect
-	golang.org/x/sys v0.7.0 // indirect
-	golang.org/x/text v0.9.0 // indirect
+	golang.org/x/net v0.14.0 // indirect
+	golang.org/x/sys v0.11.0 // indirect
+	golang.org/x/text v0.12.0 // indirect
 	golang.org/x/time v0.0.0-20220609170525-579cf78fd858 // indirect
 	google.golang.org/protobuf v1.28.1 // indirect
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect

+ 29 - 6
go.sum

@@ -119,6 +119,8 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
 github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
@@ -218,6 +220,8 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
+github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -252,6 +256,11 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
 github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53 h1:+8X3HMX8A2QhvNg3dImiQTCiVUt6BQXz1mW+/DrWI+k=
 github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53/go.mod h1:E61jD6q4yJ6Cu9uDGRAfiENM1G5TVZhOog0Y3+GgTpQ=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
+github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
+github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
+github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -273,6 +282,12 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
 github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
+github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
+github.com/minio/minio-go/v7 v7.0.63 h1:GbZ2oCvaUdgT5640WJOpyDhhDxvknAJU2/T3yurwcbQ=
+github.com/minio/minio-go/v7 v7.0.63/go.mod h1:Q6X7Qjb7WMhvG65qKf4gUgA5XaiSox74kR1uAEjxRS4=
+github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
+github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
 github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
 github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -356,6 +371,8 @@ github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN
 github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
 github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
 github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
+github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
+github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
 github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
@@ -373,8 +390,8 @@ github.com/silenceper/wechat/v2 v2.1.3/go.mod h1:FoU0YvegD+Z85TBGQhjkXjY8BMb0+ca
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
-github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
 github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
@@ -445,8 +462,9 @@ golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPh
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
 golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
+golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
+golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.5.0 h1:5JMiNunQeQw++mMOz48/ISeNu3Iweh/JaZU8ZLqHRrI=
@@ -483,8 +501,9 @@ golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
 golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
+golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
+golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -525,8 +544,9 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
 golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
+golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -538,8 +558,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
+golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20220609170525-579cf78fd858 h1:Dpdu/EMxGMFgq0CeYMh4fazTD2vtlZRYE7wyynxJb9U=
@@ -607,6 +628,8 @@ gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
 gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
+gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

+ 7 - 0
models/data_manage/chart_classify.go

@@ -287,3 +287,10 @@ func EditChartClassifySysUser(classifyId, sysUserId int, chartClassifyName strin
 	_, err = o.Raw(sql, sysUserId, chartClassifyName, classifyId).Exec()
 	return
 }
+
+func GetAllChartClassifyItemsBySource(source int) (items []*ChartClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM chart_classify WHERE source = ? order by sort ASC,chart_classify_id ASC`
+	_, err = o.Raw(sql, source).QueryRows(&items)
+	return
+}

+ 131 - 58
models/data_manage/chart_info.go

@@ -2,44 +2,46 @@ package data_manage
 
 import (
 	"errors"
+	"eta/eta_api/utils"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/rdlucklib/rdluck_tools/paging"
-	"eta/eta_api/utils"
 	"strconv"
 	"strings"
 	"time"
 )
 
 type ChartInfo struct {
-	ChartInfoId     int    `orm:"column(chart_info_id);pk"`
-	ChartName       string `description:"来源名称"`
-	ChartNameEn     string `description:"英文图表名称"`
-	ChartClassifyId int    `description:"图表分类id"`
-	SysUserId       int
-	SysUserRealName string
-	UniqueCode      string `description:"图表唯一编码"`
-	CreateTime      time.Time
-	ModifyTime      time.Time
-	DateType        int    `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间"`
-	StartDate       string `description:"自定义开始日期"`
-	EndDate         string `description:"自定义结束日期"`
-	IsSetName       int    `description:"设置名称"`
-	EdbInfoIds      string `description:"指标id"`
-	ChartType       int    `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图,8:商品价格曲线图,9:相关性图"`
-	Calendar        string `description:"公历/农历"`
-	SeasonStartDate string `description:"季节性图开始日期"`
-	SeasonEndDate   string `description:"季节性图开始日期"`
-	ChartImage      string `description:"图表图片"`
-	Sort            int    `description:"排序字段,数字越小越排前面"`
-	LeftMin         string `description:"图表左侧最小值"`
-	LeftMax         string `description:"图表左侧最大值"`
-	RightMin        string `description:"图表右侧最小值"`
-	RightMax        string `description:"图表右侧最大值"`
-	Disabled        int    `description:"是否禁用,0:启用,1:禁用,默认:0"`
-	BarConfig       string `description:"柱方图的配置,json数据"`
-	Source          int    `description:"1:ETA图库;2:商品价格曲线"`
-	ExtraConfig     string `description:"图表额外配置,json数据"`
+	ChartInfoId       int    `orm:"column(chart_info_id);pk"`
+	ChartName         string `description:"来源名称"`
+	ChartNameEn       string `description:"英文图表名称"`
+	ChartClassifyId   int    `description:"图表分类id"`
+	SysUserId         int
+	SysUserRealName   string
+	UniqueCode        string `description:"图表唯一编码"`
+	CreateTime        time.Time
+	ModifyTime        time.Time
+	DateType          int    `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间"`
+	StartDate         string `description:"自定义开始日期"`
+	EndDate           string `description:"自定义结束日期"`
+	IsSetName         int    `description:"设置名称"`
+	EdbInfoIds        string `description:"指标id"`
+	ChartType         int    `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图,8:商品价格曲线图,9:相关性图"`
+	Calendar          string `description:"公历/农历"`
+	SeasonStartDate   string `description:"季节性图开始日期"`
+	SeasonEndDate     string `description:"季节性图开始日期"`
+	ChartImage        string `description:"图表图片"`
+	Sort              int    `description:"排序字段,数字越小越排前面"`
+	LeftMin           string `description:"图表左侧最小值"`
+	LeftMax           string `description:"图表左侧最大值"`
+	RightMin          string `description:"图表右侧最小值"`
+	RightMax          string `description:"图表右侧最大值"`
+	Disabled          int    `description:"是否禁用,0:启用,1:禁用,默认:0"`
+	BarConfig         string `description:"柱方图的配置,json数据"`
+	Source            int    `description:"1:ETA图库;2:商品价格曲线"`
+	ExtraConfig       string `description:"图表额外配置,json数据"`
+	SeasonExtraConfig string `description:"季节性图表中的配置,json数据"`
+	StartYear         int    `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
 }
 
 type ChartInfoMore struct {
@@ -145,13 +147,12 @@ type SaveChartInfoReq struct {
 	StartDate        string           `description:"自定义开始日期"`
 	EndDate          string           `description:"自定义结束日期"`
 	Calendar         string           `description:"公历/农历"`
-	SeasonStartDate  string           `description:"季节性图开始日期"`
-	SeasonEndDate    string           `description:"季节性图开始日期"`
 	LeftMin          string           `description:"图表左侧最小值"`
 	LeftMax          string           `description:"图表左侧最大值"`
 	RightMin         string           `description:"图表右侧最小值"`
 	RightMax         string           `description:"图表右侧最大值"`
 	ExtraConfig      string           `description:"图表额外配置,json数据"`
+	StartYear        int              `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
 }
 
 type ChartSaveItem struct {
@@ -210,12 +211,10 @@ type EditChartInfoReq struct {
 	ChartClassifyId      int                     `description:"分类id"`
 	ChartEdbInfoList     []*ChartSaveItem        `description:"指标及配置信息"`
 	ChartType            int                     `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图,8:商品价格曲线图,9:相关性图,10:截面散点图"`
-	DateType             int                     `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:起始日期至今"`
+	DateType             int                     `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:起始日期至今,20:N年至今"`
 	StartDate            string                  `description:"自定义开始日期"`
 	EndDate              string                  `description:"自定义结束日期"`
 	Calendar             string                  `description:"公历/农历"`
-	SeasonStartDate      string                  `description:"季节性图开始日期"`
-	SeasonEndDate        string                  `description:"季节性图开始日期"`
 	LeftMin              string                  `description:"图表左侧最小值"`
 	LeftMax              string                  `description:"图表左侧最大值"`
 	RightMin             string                  `description:"图表右侧最小值"`
@@ -223,6 +222,8 @@ type EditChartInfoReq struct {
 	BarChartInfo         BarChartInfoReq         `description:"柱方图的配置"`
 	CorrelationChartInfo CorrelationChartInfoReq `description:"相关性图表配置"`
 	ExtraConfig          string                  `description:"图表额外配置信息,json字符串"`
+	SeasonExtraConfig    SeasonExtraItem         `description:"季节性图表中的配置,json数据"`
+	StartYear            int                     `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
 }
 
 type EditChartEnInfoReq struct {
@@ -465,6 +466,16 @@ type QuarterData struct {
 	Year                 int
 	DataList             []*EdbDataList
 	CuttingDataTimestamp int64 `description:"切割的时间戳"`
+	ChartLegend          string
+	Years                string
+}
+
+type QuarterXDateItem struct {
+	StartDate            time.Time
+	EndDate              time.Time
+	ShowName             string
+	ChartLegend          string
+	CuttingDataTimestamp int64 `description:"切割的时间戳"`
 }
 
 type QuarterDataByUniqueCode struct {
@@ -535,8 +546,9 @@ func ModifyChartInfoAndMapping(edbInfoIdStr string, req *SaveChartInfoReq, chart
  			  left_min=?,
 			  left_max=?,
 			  right_min=?,
-			  right_max=?`
-		pars := []interface{}{edbInfoIdStr, req.DateType, req.StartDate, req.EndDate, req.LeftMin, req.LeftMax, req.RightMin, req.RightMax}
+			  right_max=?, 
+              start_year=?`
+		pars := []interface{}{edbInfoIdStr, req.DateType, req.StartDate, req.EndDate, req.LeftMin, req.LeftMax, req.RightMin, req.RightMax, req.StartYear}
 		if req.ExtraConfig != `` {
 			updateStr += `,extra_config=? `
 			pars = append(pars, req.ExtraConfig)
@@ -555,14 +567,18 @@ func ModifyChartInfoAndMapping(edbInfoIdStr string, req *SaveChartInfoReq, chart
               edb_info_ids=?,
 			  modify_time = NOW(),
 			  calendar=?,
+			  date_type=?,
+			  start_date=?,
+			  end_date=?,
 			  season_start_date=?,
 			  season_end_date=?,
 			  left_min=?,
 			  left_max=?,
 			  right_min=?,
-			  right_max=?
+			  right_max=?,
+			  start_year=?
 			WHERE chart_info_id = ?`
-		_, err = to.Raw(sql, edbInfoIdStr, req.Calendar, req.SeasonStartDate, req.SeasonEndDate, req.LeftMin, req.LeftMax, req.RightMin, req.RightMax, req.ChartInfoId).Exec()
+		_, err = to.Raw(sql, edbInfoIdStr, req.Calendar, req.DateType, req.StartDate, req.EndDate, req.StartDate, req.EndDate, req.LeftMin, req.LeftMax, req.RightMin, req.RightMax, req.StartYear, req.ChartInfoId).Exec()
 		if err != nil {
 			fmt.Println("UPDATE  chart_info Err:", err.Error())
 			return err
@@ -645,7 +661,7 @@ func ModifyChartInfoAndMapping(edbInfoIdStr string, req *SaveChartInfoReq, chart
 }
 
 // EditChartInfoAndMapping 修改 ETA图库 的 图表与指标 的关系
-func EditChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr string, calendar string, dateType, disabled int, barChartConf string, chartEdbInfoList []*ChartSaveItem) (err error) {
+func EditChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr string, calendar string, dateType, disabled int, barChartConf string, chartEdbInfoList []*ChartSaveItem, seasonExtra string) (err error) {
 	o := orm.NewOrmUsingDB("data")
 	to, err := o.Begin()
 	if err != nil {
@@ -666,6 +682,8 @@ func EditChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr string, calenda
 	pars = append(pars, disabled)
 	pars = append(pars, barChartConf)
 	pars = append(pars, req.ExtraConfig)
+	pars = append(pars, seasonExtra)
+	pars = append(pars, req.StartYear)
 
 	sql := ` UPDATE  chart_info
 			SET
@@ -675,7 +693,10 @@ func EditChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr string, calenda
 			  chart_classify_id = ?,
 			  modify_time = NOW(),
               disabled = ?,
-              bar_config = ?,extra_config = ?
+              bar_config = ?,
+              extra_config = ?, 
+              season_extra_config = ?,
+ 			  start_year = ?
 			`
 	if calendar != "" {
 		sql += `,calendar = ? `
@@ -693,10 +714,10 @@ func EditChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr string, calenda
 	pars = append(pars, req.EndDate)
 
 	sql += `,season_start_date = ? `
-	pars = append(pars, req.SeasonStartDate)
+	pars = append(pars, req.StartDate)
 
 	sql += `,season_end_date = ? `
-	pars = append(pars, req.SeasonEndDate)
+	pars = append(pars, req.EndDate)
 
 	sql += `,left_min = ? `
 	pars = append(pars, req.LeftMin)
@@ -837,10 +858,10 @@ func EditFutureGoodChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr strin
 	pars = append(pars, req.EndDate)
 
 	sql += `,season_start_date = ? `
-	pars = append(pars, req.SeasonStartDate)
+	pars = append(pars, req.StartDate)
 
 	sql += `,season_end_date = ? `
-	pars = append(pars, req.SeasonEndDate)
+	pars = append(pars, req.EndDate)
 
 	sql += `,left_min = ? `
 	pars = append(pars, req.LeftMin)
@@ -1009,8 +1030,6 @@ type AddChartInfoReq struct {
 	StartDate            string                  `description:"自定义开始日期"`
 	EndDate              string                  `description:"自定义结束日期"`
 	Calendar             string                  `description:"公历/农历"`
-	SeasonStartDate      string                  `description:"季节性图开始日期"`
-	SeasonEndDate        string                  `description:"季节性图开始日期"`
 	LeftMin              string                  `description:"图表左侧最小值"`
 	LeftMax              string                  `description:"图表左侧最大值"`
 	RightMin             string                  `description:"图表右侧最小值"`
@@ -1019,25 +1038,52 @@ type AddChartInfoReq struct {
 	CorrelationChartInfo CorrelationChartInfoReq `description:"相关性图表配置"`
 	ExtraConfig          string                  `description:"图表额外配置信息,json字符串"`
 	ChartImage           string                  `description:"封面图" json:"-"`
+	SeasonExtraConfig    SeasonExtraItem         `description:"季节性图表中的配置,json数据"`
+	StartYear            int                     `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
 }
 
 type PreviewChartInfoReq struct {
-	ChartEdbInfoList []*ChartSaveItem `description:"指标及配置信息"`
-	DateType         int              `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:起始日期至今"`
-	ChartType        int              `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图"`
-	StartDate        string           `description:"自定义开始日期"`
-	EndDate          string           `description:"自定义结束日期"`
-	Calendar         string           `description:"公历/农历"`
-	SeasonStartDate  string           `description:"季节性图开始日期"`
-	SeasonEndDate    string           `description:"季节性图开始日期"`
+	ChartEdbInfoList  []*ChartSaveItem `description:"指标及配置信息"`
+	DateType          int              `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:起始日期至今 20:最近N年"`
+	ChartType         int              `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图"`
+	StartDate         string           `description:"自定义开始日期"`
+	EndDate           string           `description:"自定义结束日期"`
+	Calendar          string           `description:"公历/农历"`
+	SeasonExtraConfig SeasonExtraItem  `description:"季节性图表中的配置,json数据"`
+	StartYear         int              `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
+}
+
+type SeasonExtraItem struct {
+	ChartLegend []SeasonChartLegend `description:"自定义的图例名称"`
+	XStartDate  string              `description:"横坐标显示的起始日"`
+	XEndDate    string              `description:"横坐标显示的截止日"`
+	JumpYear    int                 `description:"横坐标日期是否跨年,1跨年,0不跨年"`
 }
 
+type SeasonChartLegend struct {
+	Name  string
+	Value string
+}
 type AddChartInfoResp struct {
 	ChartInfoId int    `description:"图表id"`
 	UniqueCode  string `description:"图表唯一编码"`
 	ChartType   int    `description:"生成样式:1:曲线图,2:季节性图"`
 }
 
+type QuarterDataList []*QuarterData
+
+func (m QuarterDataList) Len() int {
+	return len(m)
+}
+
+func (m QuarterDataList) Less(i, j int) bool {
+	return m[i].Years < m[j].Years
+}
+
+func (m QuarterDataList) Swap(i, j int) {
+	m[i], m[j] = m[j], m[i]
+}
+
 // 判断图表指标是否已经存在
 func ChartInfoExist(condition, edbInfoIdStr string) (count int, err error) {
 	sql := `SELECT COUNT(1) AS count FROM (
@@ -1282,8 +1328,10 @@ type ChartInfoView struct {
 	BarConfig         string `description:"柱方图的配置,json数据" json:"-"`
 	Source            int    `description:"1:ETA图库;2:商品价格曲线;3:相关性图表"`
 	//CorrelationLeadUnit string `description:"相关性图表-领先单位"`
-	ExtraConfig string          `description:"图表额外配置,json数据"`
-	Button      ChartViewButton `description:"操作按钮"`
+	ExtraConfig       string          `description:"图表额外配置,json数据"`
+	Button            ChartViewButton `description:"操作按钮"`
+	SeasonExtraConfig string          `description:"季节性图表中的配置,json数据"`
+	StartYear         int             `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
 }
 
 type ChartViewButton struct {
@@ -1517,10 +1565,10 @@ func EditCorrelationChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr stri
 	pars = append(pars, req.EndDate)
 
 	sql += `,season_start_date = ? `
-	pars = append(pars, req.SeasonStartDate)
+	pars = append(pars, req.StartDate)
 
 	sql += `,season_end_date = ? `
-	pars = append(pars, req.SeasonEndDate)
+	pars = append(pars, req.EndDate)
 
 	sql += `,left_min = ? `
 	pars = append(pars, req.LeftMin)
@@ -1644,6 +1692,7 @@ type SectionScatterReq struct {
 type SectionScatterSeriesItemReq struct {
 	Name            string `description:"系列名"`
 	NameEn          string `description:"系列名(英文名)"`
+	IsNameDefault   bool   `description:"是否使用默认的系列名 false修改过 true默认  影响自动更新"`
 	Color           string `description:"颜色"`
 	EdbInfoList     []SectionScatterEdbItemReq
 	ShowTrendLine   bool `description:"是否展示趋势线"`
@@ -1687,6 +1736,7 @@ type SectionScatterInfoResp struct {
 type SectionScatterSeriesItemResp struct {
 	Name            string `description:"系列名"`
 	NameEn          string `description:"系列名(英文)"`
+	IsNameDefault   bool   `description:"是否使用默认的系列名 false修改过 true默认  影响自动更新"`
 	Color           string `description:"颜色"`
 	EdbInfoList     []SectionScatterEdbItemResp
 	ShowTrendLine   bool              `description:"是否展示趋势线"`
@@ -1773,5 +1823,28 @@ func GetChartInfoAdminList() (items []int, err error) {
 	sql := ` SELECT sys_user_id FROM chart_info GROUP BY sys_user_id `
 
 	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// EditChartInfoExtraConfig 修改 ETA图库 的 图表额外配置
+func EditChartInfoExtraConfig(chartId int, extraConfig string) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	var pars []interface{}
+	pars = append(pars, extraConfig)
+
+	sql := ` UPDATE  chart_info
+			SET
+			  modify_time = NOW(),
+              extra_config = ?
+			`
+	sql += `WHERE chart_info_id = ?`
+
+	pars = append(pars, chartId)
+	_, err = o.Raw(sql, pars).Exec()
+	if err != nil {
+		fmt.Println("UPDATE  chart_info Err:", err.Error())
+		return err
+	}
+
 	return
 }

+ 89 - 1
models/data_manage/edb_classify.go

@@ -13,6 +13,7 @@ type EdbClassify struct {
 	ClassifyType    uint8     `description:"分类类型,0:普通指标分类,1:预测指标分类"`
 	ClassifyName    string    `description:"分类名称"`
 	ParentId        int       `description:"父级id"`
+	RootId          int       `description:"顶级id"`
 	HasData         int       `description:"是否含有指标数据"`
 	CreateTime      time.Time `description:"创建时间"`
 	ModifyTime      time.Time `description:"修改时间"`
@@ -117,6 +118,26 @@ func GetEdbClassifyByParentId(parentId int, classifyType int8) (items []*EdbClas
 	return
 }
 
+func GetEdbClassifyByRootIdLevel(rootId int, classifyType uint8, orderStr string) (items []*EdbClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM edb_classify WHERE root_id=? AND classify_type = ? `
+	if orderStr != "" {
+		sql += orderStr
+	} else {
+		sql += ` order by level desc, sort asc, classify_id asc`
+	}
+
+	_, err = o.Raw(sql, rootId, classifyType).QueryRows(&items)
+	return
+}
+
+func GetEdbClassifyByRootIdLevelOrder(rootId int, classifyType uint8) (items []*EdbClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM edb_classify WHERE root_id=? AND classify_type = ? order by level desc, sort asc, classify_id asc`
+	_, err = o.Raw(sql, rootId, classifyType).QueryRows(&items)
+	return
+}
+
 // GetEdbClassifyAll 获取普通指标的分类列表
 func GetEdbClassifyAll() (items []*EdbClassifyItems, err error) {
 	o := orm.NewOrmUsingDB("data")
@@ -125,6 +146,14 @@ func GetEdbClassifyAll() (items []*EdbClassifyItems, err error) {
 	return
 }
 
+// GetNormalEdbClassifyAll 获取普通指标的分类列表
+func GetNormalEdbClassifyAll() (items []*EdbClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM edb_classify WHERE  classify_type = 0  order by sort asc,classify_id asc`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
 // GetPredictEdbClassifyAll 获取预测指标的分类列表
 func GetPredictEdbClassifyAll() (items []*EdbClassifyItems, err error) {
 	o := orm.NewOrmUsingDB("data")
@@ -133,12 +162,21 @@ func GetPredictEdbClassifyAll() (items []*EdbClassifyItems, err error) {
 	return
 }
 
+// GetAllEdbClassifyByType 根据类型获取分类列表
+func GetAllEdbClassifyByType(classifyType int) (items []*EdbClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM edb_classify WHERE classify_type = ? order by sort ASC,classify_id ASC`
+	_, err = o.Raw(sql, classifyType).QueryRows(&items)
+	return
+}
+
 type EdbClassifyItems struct {
 	ClassifyId      int `description:"分类id"`
 	EdbInfoId       int `description:"指标id"`
 	ClassifyName    string
 	ClassifyNameEn  string
 	ParentId        int
+	RootId          int    `description:"顶级id"`
 	Level           int    `description:"层级"`
 	Sort            int    `description:"排序字段,越小越靠前,默认值:10"`
 	UniqueCode      string `description:"唯一编码"`
@@ -153,6 +191,29 @@ type EdbClassifyItems struct {
 	Button          EdbClassifyItemsButton `description:"操作权限"`
 }
 
+type EdbClassifyIdItems struct {
+	ClassifyId   int `description:"分类id"`
+	ClassifyName string
+	UniqueCode   string `description:"唯一编码"`
+	ParentId     int    `description:"父级分类id"`
+	Level        int    `description:"层级"`
+	RootId       int    `description:"顶级分类id"`
+}
+
+type EdbClassifyItemList []*EdbClassifyItems
+
+func (m EdbClassifyItemList) Len() int {
+	return len(m)
+}
+
+func (m EdbClassifyItemList) Less(i, j int) bool {
+	return m[i].Sort < m[j].Sort
+}
+
+func (m EdbClassifyItemList) Swap(i, j int) {
+	m[i], m[j] = m[j], m[i]
+}
+
 // EdbClassifyItemsButton 操作按钮
 type EdbClassifyItemsButton struct {
 	AddButton           bool `description:"是否可添加"`
@@ -165,7 +226,7 @@ type EdbClassifyItemsButton struct {
 }
 
 type EdbClassifyListResp struct {
-	AllNodes      []*EdbClassifyItems
+	AllNodes      EdbClassifyItemList
 	CanOpClassify bool   `description:"是否允许操作分类"`
 	Language      string `description:"指标的展示语言,CN:中文,EN:英文"`
 }
@@ -229,6 +290,9 @@ type MoveEdbClassifyReq struct {
 	ParentClassifyId int `description:"父级分类id"`
 	PrevClassifyId   int `description:"上一个兄弟节点分类id"`
 	NextClassifyId   int `description:"下一个兄弟节点分类id"`
+	EdbInfoId        int `description:"指标ID, 如果指标ID有值,则移动对象为指标,否则认为移动对象为分类"`
+	PrevEdbInfoId    int `description:"上一个指标ID"`
+	NextEdbInfoId    int `description:"下一个指标ID"`
 }
 
 // GetFirstEdbClassifyByParentId 获取当前父级分类下,且排序数相同 的排序第一条的数据
@@ -265,6 +329,14 @@ func GetEdbClassifyMaxSort(parentId int, classifyType uint8) (sort int, err erro
 	return
 }
 
+// GetEdbInfoMaxSortByClassifyId 获取分类下指标的最大的排序数
+func GetEdbInfoMaxSortByClassifyId(classifyId int) (sort int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT Max(sort) AS sort FROM edb_info WHERE classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&sort)
+	return
+}
+
 type ClassifyEdbInfoListResp struct {
 	EdbInfoList []*EdbClassifyItems
 }
@@ -448,3 +520,19 @@ func FixPredictEdbClassifySysUser() {
 	}
 	fmt.Println("EditChartClassifySysUser end")
 }
+
+func UpdateEdbClassifyChildByParentClassifyId(classifyIds []int, rootId int, levelStep int) (err error) {
+	o := orm.NewOrmUsingDB("data")
+	var pars []interface{}
+	pars = append(pars, rootId, levelStep)
+	pars = append(pars, classifyIds)
+	// 更新相关联的二级分类的parentId,和classify_name_second
+	sql := `update edb_classify 
+SET root_id = ?, level = level+?
+where classify_id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `)`
+	_, err = o.Raw(sql, pars).Exec()
+	if err != nil {
+		return
+	}
+	return
+}

+ 27 - 7
models/data_manage/edb_info.go

@@ -47,6 +47,11 @@ type EdbInfo struct {
 	DataDateType     string  `orm:"column(data_date_type);size(255);null;default(交易日)"`
 	ManualSave       int     `description:"是否有手动保存过上下限: 0-否; 1-是"`
 }
+type EdbInfoFullClassify struct {
+	*EdbInfo
+	CorrelationStr string `description:"相关性系数字符串"`
+	ClassifyList   []*EdbClassifyIdItems
+}
 
 func AddEdbInfo(item *EdbInfo) (lastId int64, err error) {
 	o := orm.NewOrmUsingDB("data")
@@ -297,7 +302,7 @@ type EdbData struct {
 type EdbInfoListResp struct {
 	Paging       *paging.PagingItem
 	Item         *EdbInfoList
-	ClassifyList []*EdbClassifySimplify
+	ClassifyList []*EdbClassifyIdItems
 }
 
 func GetEdbInfoByCondition(condition string, pars []interface{}) (item *EdbInfoList, err error) {
@@ -727,9 +732,11 @@ func GetFirstEdbInfoByClassifyId(classifyId int) (item *EdbInfo, err error) {
 // UpdateEdbInfoSortByClassifyId 根据分类id更新排序
 func UpdateEdbInfoSortByClassifyId(classifyId, nowSort int, prevEdbInfoId int, updateSort string) (err error) {
 	o := orm.NewOrmUsingDB("data")
-	sql := ` update edb_info set sort = ` + updateSort + ` WHERE classify_id=? AND `
+	sql := ` update edb_info set sort = ` + updateSort + ` WHERE classify_id=?`
 	if prevEdbInfoId > 0 {
-		sql += ` ( sort > ? or ( edb_info_id > ` + fmt.Sprint(prevEdbInfoId) + ` and sort=` + fmt.Sprint(nowSort) + ` )) `
+		sql += ` AND ( sort > ? or ( edb_info_id > ` + fmt.Sprint(prevEdbInfoId) + ` and sort=` + fmt.Sprint(nowSort) + ` )) `
+	} else {
+		sql += ` AND ( sort > ? )`
 	}
 	_, err = o.Raw(sql, classifyId, nowSort).Exec()
 	return
@@ -747,6 +754,11 @@ type EdbInfoDataResp struct {
 	DataList []*EdbDataList
 }
 
+type EdbInfoDataFullClassifyResp struct {
+	EdbInfo  *EdbInfoFullClassify
+	DataList []*EdbDataList
+}
+
 type EdbInfoReplaceReq struct {
 	OldEdbInfoId int `description:"原指标ID"`
 	NewEdbInfoId int `description:"替换为指标ID"`
@@ -1710,11 +1722,19 @@ type SetEdbDataInsertConfigReq struct {
 	Value     float64 `description:"值"`
 }
 
-// GetEdbInfoAll 用于分类展示
-func GetEdbInfoByClassifyId(classifyId, edbInfoType int) (items []*EdbClassifyItems, err error) {
+// GetEdbInfoByClassifyId 用于分类展示
+func GetEdbInfoByClassifyId(classifyId, edbInfoType, adminId int) (items []*EdbClassifyItems, err error) {
 	o := orm.NewOrmUsingDB("data")
-	sql := ` SELECT edb_info_id,classify_id,edb_name_source AS classify_name,edb_name_en AS classify_name_en,unique_code,source_name,source,sys_user_id,sys_user_real_name,start_date,edb_code,edb_type FROM edb_info WHERE classify_id = ? AND edb_info_type = ? order by sort asc,edb_info_id asc`
-	_, err = o.Raw(sql, classifyId, edbInfoType).QueryRows(&items)
+	sql := ` SELECT edb_info_id,classify_id,edb_name_source AS classify_name,edb_name_en AS classify_name_en,unique_code,source_name,source,sys_user_id,sys_user_real_name,start_date,edb_code,edb_type, sort FROM edb_info WHERE classify_id = ? AND edb_info_type = ?`
+
+	pars := []interface{}{classifyId, edbInfoType}
+	// 如果筛选了用户id
+	if adminId > 0 {
+		sql += ` AND sys_user_id = ? `
+		pars = append(pars, adminId)
+	}
+	sql += ` order by sort asc,edb_info_id asc `
+	_, err = o.Raw(sql, pars).QueryRows(&items)
 	return
 }
 

+ 1 - 1
models/data_manage/edb_info_calculate.go

@@ -96,7 +96,7 @@ func GetEdbInfoCalculateDetail(edbInfoId, source int) (list []*EdbInfoCalculateD
 }
 
 type CalculateDetailResp struct {
-	EdbInfoDetail *EdbInfo
+	EdbInfoDetail *EdbInfoFullClassify
 	CalculateList []*EdbInfoCalculateDetail
 }
 

+ 6 - 1
models/data_manage/excel_style.go

@@ -203,4 +203,9 @@ func GetExcelEdbdataMappingCount(classifyId int, tradeCode, frequency string) (c
 //	o := orm.NewOrmUsingDB("edb")
 //	_, err = o.Raw(sql, close, tradeCode, dt).Exec()
 //	return
-//}
+//}
+
+type Ct struct {
+	Fa string `json:"fa"`
+	T  string `json:"t"`
+}

+ 4 - 4
models/data_manage/line_feature/line_feature.go

@@ -1,10 +1,10 @@
 package line_feature
 
 import (
-	"fmt"
-	"github.com/beego/beego/v2/client/orm"
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
 	"strconv"
 	"strings"
 	"time"
@@ -97,10 +97,10 @@ func EditLineFeatureChartAndMapping(req *data_manage.EditChartInfoReq, edbInfoId
 	pars = append(pars, req.EndDate)
 
 	sql += `,season_start_date = ? `
-	pars = append(pars, req.SeasonStartDate)
+	pars = append(pars, req.StartDate)
 
 	sql += `,season_end_date = ? `
-	pars = append(pars, req.SeasonEndDate)
+	pars = append(pars, req.EndDate)
 
 	sql += `,left_min = ? `
 	pars = append(pars, req.LeftMin)

+ 2 - 1
models/data_manage/line_feature/request/line_feature.go

@@ -20,9 +20,10 @@ type ConfigSave struct {
 
 // CurveConfig 曲线图配置
 type CurveConfig struct {
-	DateType  int     `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间"`
+	DateType  int     `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,20最近N年"`
 	StartDate string  `description:"自定义开始日期"`
 	EndDate   string  `description:"自定义结束日期"`
+	StartYear int     `description:"最近N年里的N"`
 	LeftMin   float64 `description:"图表左侧最小值"`
 	LeftMax   float64 `description:"图表左侧最大值"`
 }

+ 1 - 0
models/data_manage/multiple_graph_config.go

@@ -60,6 +60,7 @@ type CurveConfig struct {
 	EdbInfoType bool    `description:"true:标准指标,false:领先指标"`
 	LeadValue   int     `description:"领先值"`
 	LeadUnit    string  `description:"领先单位"`
+	StartYear   int     `description:"最近N年"`
 }
 
 // CorrelationConfig 相关性配置

+ 54 - 0
models/data_manage/request/meeting_probabilities.go

@@ -0,0 +1,54 @@
+package request
+
+type ISheet struct {
+	ScrollTop            int        `json:"scrollTop"`
+	ScrollLeft           int        `json:"scrollLeft"`
+	Index                string     `json:"index"`
+	Status               int        `json:"status"`
+	JfGirdSelectSave     []struct{} `json:"jfgird_select_save"`
+	LuckySheetSelectSave []struct{} `json:"luckysheet_select_save"`
+	Data                 [][]*struct {
+		Ct struct {
+			Fa string `json:"fa"`
+			T  string `json:"t"`
+		} `json:"ct"`
+		M string  `json:"m"`
+		V float64 `json:"v"`
+	} `json:"data"`
+	Config map[string]struct {
+	} `json:"config"`
+	VisibleDataRow           []int            `json:"visibledatarow"`
+	VisibleDataColumn        []int            `json:"visibledatacolumn"`
+	ChWidth                  int              `json:"ch_width"`
+	RhHeight                 int              `json:"rh_height"`
+	LuckySheetSelectionRange []int            `json:"luckysheet_selection_range"`
+	ZoomRatio                int              `json:"zoomRatio"`
+	CellData                 []ISheetCellData `json:"celldata"`
+}
+type ISheetData struct {
+	M  string       `json:"m"`
+	Ct ISheetDataCt `json:"ct"`
+	V  string       `json:"v"`
+}
+
+type ISheetDataCt struct {
+	Fa string `json:"fa"`
+	T  string `json:"t"`
+}
+
+type ISheetCellData struct {
+	R int        `json:"r"`
+	C int        `json:"c"`
+	V ISheetData `json:"v"`
+}
+
+type SheetData struct {
+	Data [][]*struct {
+		Ct struct {
+			Fa string `json:"fa"`
+			T  string `json:"t"`
+		} `json:"ct"`
+		M string  `json:"m"`
+		V float64 `json:"v"`
+	} `json:"data"`
+}

+ 14 - 13
models/data_manage/request/predict_edb_info.go

@@ -4,19 +4,20 @@ import "eta/eta_api/models/data_manage"
 
 // PredictEdbInfoChartDataReq 获取预测指标绘图数据请求
 type PredictEdbInfoChartDataReq struct {
-	SourceEdbInfoId int          `description:"来源指标id"`
-	PredictEndDate  string       `description:"预测截止日期"`
-	RuleType        int          `description:"预测规则,1:最新,2:固定值"`
-	FixedValue      float64      `description:"固定值"`
-	DateType        int          `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:自定义起始时间至今,7:18年至今,8:19年至今,9:20年至今,10:全部"`
-	StartDate       string       `description:"自定义开始日期"`
-	EndDate         string       `description:"自定义结束日期"`
-	Calendar        string       `description:"公历/农历"`
-	ChartType       int          `description:"生成样式:1:曲线图,2:季节性图"`
-	SeasonStartDate string       `description:"季节性图开始日期"`
-	SeasonEndDate   string       `description:"季节性图结束日期"`
-	RuleList        []RuleConfig `description:"配置规则列表"`
-	DataDateType    string       `description:"数据日期类型,枚举值:交易日、自然日"`
+	SourceEdbInfoId int     `description:"来源指标id"`
+	PredictEndDate  string  `description:"预测截止日期"`
+	RuleType        int     `description:"预测规则,1:最新,2:固定值"`
+	FixedValue      float64 `description:"固定值"`
+	DateType        int     `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:自定义起始时间至今,7:18年至今,8:19年至今,9:20年至今,10:全部"`
+	StartDate       string  `description:"自定义开始日期"`
+	EndDate         string  `description:"自定义结束日期"`
+	Calendar        string  `description:"公历/农历"`
+	ChartType       int     `description:"生成样式:1:曲线图,2:季节性图"`
+	//SeasonStartDate string       `description:"季节性图开始日期"`
+	//SeasonEndDate   string       `description:"季节性图结束日期"`
+	RuleList     []RuleConfig `description:"配置规则列表"`
+	DataDateType string       `description:"数据日期类型,枚举值:交易日、自然日"`
+	StartYear    int          `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
 }
 
 // AddPredictEdbInfoReq 添加预测指标请求

+ 10 - 7
models/data_manage/response/predit_edb_info.go

@@ -19,18 +19,21 @@ type PredictEdbInfoListResp struct {
 
 // PredictEdbInfoDataResp 预测指标数据返回
 type PredictEdbInfoDataResp struct {
-	EdbInfo  *data_manage.EdbInfo
-	Button   data_manage.EdbClassifyItemsButton
-	DataList interface{}
+	EdbInfo      *data_manage.EdbInfo
+	Button       data_manage.EdbClassifyItemsButton
+	DataList     interface{}
+	ClassifyList []*data_manage.EdbClassifyIdItems `description:"父级分类列表"`
 }
 
 // PredictEdbInfo 预测指标详情
 type PredictEdbInfo struct {
 	data_manage.EdbInfo
-	RuleType      int                                   `description:"预测规则,1:最新,2:固定值,3:同比,4:同差,5:环比,6:环差,7:N期移动均值,8:N期段线性外推值"`
-	FixedValue    float64                               `description:"固定值"`
-	CalculateList []*data_manage.EdbInfoCalculateDetail `description:"关联指标"`
-	RuleList      []data_manage.PredictEdbConfDetail    `description:"指标规则配置"`
+	RuleType       int                                   `description:"预测规则,1:最新,2:固定值,3:同比,4:同差,5:环比,6:环差,7:N期移动均值,8:N期段线性外推值"`
+	FixedValue     float64                               `description:"固定值"`
+	CalculateList  []*data_manage.EdbInfoCalculateDetail `description:"关联指标"`
+	RuleList       []data_manage.PredictEdbConfDetail    `description:"指标规则配置"`
+	CorrelationStr string                                `description:"相关性系数字符串"`
+	ClassifyList   []*data_manage.EdbClassifyIdItems     `description:"父级分类列表"`
 }
 
 // PredictEdbInfoChartDataResp 获取预测指标绘图数据返回

+ 141 - 52
models/english_report.go

@@ -209,6 +209,8 @@ type EnglishReportDetail struct {
 	Overview           string `description:"英文概述部分"`
 	FromReportId       int    `description:"继承的报告ID(英文策略报告ID)"`
 	KeyTakeaways       string `description:"关键点"`
+	ClassifyIdRoot     int    `description:"顶级分类id"`
+	ClassifyNameRoot   string `description:"顶级分类名称"`
 }
 
 func GetEnglishReportById(reportId int) (item *EnglishReportDetail, err error) {
@@ -259,6 +261,9 @@ type EnglishReportList struct {
 	FromReportId       int       `description:"继承的报告ID(英文策略报告ID)"`
 	AdminId            int       `description:"创建者账号"`
 	AdminRealName      string    `description:"创建者姓名"`
+	FullClassifyName   string    `description:"顶级分类名/父级分类名/当前分类名"`
+	ClassifyIdRoot     int       `description:"顶级分类id"`
+	ClassifyNameRoot   string    `description:"顶级分类名称"`
 }
 
 type EnglishReportListResp struct {
@@ -319,6 +324,17 @@ func GetEnglishReportByCondition(condition string, pars []interface{}) (items []
 	return
 }
 
+func GetEnglishReportCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT count(*) 
+        FROM english_report WHERE 1=1  `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
 // 发布报告
 func PublishEnglishReportById(reportId int, publishTime string) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
@@ -386,13 +402,14 @@ type EnglishClassifyList struct {
 	ClassifyName  string    `description:"分类名称"`
 	Sort          int       `description:"排序"`
 	ParentId      int       `description:"父级分类id"`
+	RootId        int       `description:"一级分类ID"`
 	CreateTime    time.Time `description:"创建时间"`
 	ModifyTime    time.Time `description:"修改时间"`
 	ClassifyLabel string    `description:"分类标签"`
 	ShowType      int       `description:"展示类型:1-列表 2-专栏"`
 	IsShow        int       `description:"是否在小程序显示:1-显示 0-隐藏"`
-	ClassifyType  int       `description:"分类类型:0英文报告,1英文线上路演"`
-	EnPermissions []int     `description:"英文权限IDs"`
+	//ClassifyType  int       `description:"分类类型:0英文报告,1英文线上路演"`
+	EnPermissions []int `description:"英文权限IDs"`
 	Child         []*EnglishClassifyList
 }
 
@@ -401,75 +418,77 @@ type EnglishClassifyListResp struct {
 	Paging *paging.PagingItem `description:"分页数据"`
 }
 
-// 获取分类列表
-func GetEnglishClassifyList(startSize, pageSize int, keyWord string, classifyType int) (items []*EnglishClassifyList, err error) {
+// GetEnglishClassifyRootId 获取一级分类列表
+func GetEnglishClassifyRootId(startSize, pageSize int, keyWord string) (items []*EnglishClassifyList, err error) {
 	sql := ``
 	o := orm.NewOrmUsingDB("rddp")
 	if keyWord != "" {
 		sql = `SELECT * FROM (
-                   SELECT * FROM english_classify
-                   WHERE parent_id=0 AND classify_name LIKE '%` + keyWord + `%' AND classify_type = ?
+			                   SELECT * FROM english_classify
+                   WHERE parent_id=0 AND classify_name LIKE '%` + keyWord + `%'
                    UNION
                    SELECT * FROM english_classify
                    WHERE id IN(SELECT parent_id FROM english_classify
-                   WHERE parent_id>0 AND classify_name LIKE '%` + keyWord + `%') AND classify_type = ?
+                   WHERE parent_id>0 AND classify_name LIKE '%` + keyWord + `%')
                    )AS t
                    ORDER BY sort ASC,create_time ASC
                    LIMIT ?,? `
-		_, err = o.Raw(sql, classifyType, classifyType, startSize, pageSize).QueryRows(&items)
+		_, err = o.Raw(sql, startSize, pageSize).QueryRows(&items)
 	} else {
-		sql = `SELECT * FROM english_classify WHERE parent_id=0 AND classify_type = ? ORDER BY sort ASC,create_time ASC LIMIT ?,? `
-		_, err = o.Raw(sql, classifyType, startSize, pageSize).QueryRows(&items)
+		sql = `SELECT * FROM english_classify WHERE parent_id=0 ORDER BY sort ASC,create_time ASC LIMIT ?,? `
+		_, err = o.Raw(sql, startSize, pageSize).QueryRows(&items)
 	}
 	return
 }
 
-func GetEnglishClassifyListCount(keyWord string, classifyType int) (count int, err error) {
+func GetEnglishClassifyListCount(keyWord string) (count int, err error) {
 	sqlCount := ``
 	o := orm.NewOrmUsingDB("rddp")
 	if keyWord != "" {
 		sqlCount = `SELECT  COUNT(1) AS count FROM (
                SELECT * FROM english_classify
-               WHERE parent_id=0 AND classify_name LIKE '%` + keyWord + `%' AND classify_type = ?
+               WHERE parent_id=0 AND classify_name LIKE '%` + keyWord + `%'
                UNION
                SELECT * FROM english_classify
                WHERE id IN(SELECT parent_id FROM english_classify
-               WHERE parent_id>0 AND classify_name LIKE '%` + keyWord + `%' AND classify_type = ?)
+               WHERE parent_id>0 AND classify_name LIKE '%` + keyWord + `%')
                )AS t `
-		err = o.Raw(sqlCount, classifyType, classifyType).QueryRow(&count)
+		err = o.Raw(sqlCount).QueryRow(&count)
 	} else {
-		sqlCount = `SELECT COUNT(1) AS count FROM english_classify WHERE parent_id=0 AND classify_type = ?`
-		err = o.Raw(sqlCount, classifyType).QueryRow(&count)
+		sqlCount = `SELECT COUNT(1) AS count FROM english_classify WHERE parent_id=0`
+		err = o.Raw(sqlCount).QueryRow(&count)
 	}
 
 	return
 }
 
-func GetEnglishClassifyChild(parentId int, keyWord string, classifyType int) (items []*EnglishClassify, err error) {
-	o := orm.NewOrmUsingDB("rddp")
+func GetEnglishClassifyListByRootId(rootIds []int, keyWord string) (items []*EnglishClassifyList, err error) {
 	sql := ``
+	o := orm.NewOrmUsingDB("rddp")
 	if keyWord != "" {
-		sql = `SELECT * FROM english_classify WHERE parent_id=? AND classify_type = ? AND classify_name LIKE '%` + keyWord + `%' ORDER BY create_time ASC `
+		sql = `SELECT
+	a.*
+FROM
+	english_classify a
+	LEFT JOIN english_classify b ON a.root_id = b.id
+	LEFT JOIN english_classify c ON a.parent_id = c.id
+	WHERE a.parent_id>0 and a.classify_name LIKE '%` + keyWord + `%' and a.root_id IN (` + utils.GetOrmInReplace(len(rootIds)) + `)`
+		_, err = o.Raw(sql, rootIds).QueryRows(&items)
 	} else {
-		sql = `SELECT * FROM english_classify WHERE parent_id=? AND classify_type = ? ORDER BY create_time ASC `
+		sql = `SELECT * FROM english_classify WHERE parent_id>0 and root_id IN (` + utils.GetOrmInReplace(len(rootIds)) + `) `
+		_, err = o.Raw(sql, rootIds).QueryRows(&items)
 	}
-	_, err = o.Raw(sql, parentId, classifyType).QueryRows(&items)
 	return
 }
 
-func GetEnglishClassifyChildByParentIds(parentIds []int, keyWord string, classifyType int) (items []*EnglishClassifyList, err error) {
+func GetEnglishClassifyChildByIds(ids []int) (items []*EnglishClassifyList, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := ``
-	if keyWord != "" {
-		sql = `SELECT * FROM english_classify WHERE parent_id IN (` + utils.GetOrmInReplace(len(parentIds)) + `) AND classify_type = ? AND classify_name LIKE '%` + keyWord + `%' ORDER BY create_time ASC `
-	} else {
-		sql = `SELECT * FROM english_classify WHERE parent_id IN (` + utils.GetOrmInReplace(len(parentIds)) + `) AND classify_type = ? ORDER BY create_time ASC `
-	}
-	_, err = o.Raw(sql, parentIds, classifyType).QueryRows(&items)
+	sql := `SELECT * FROM english_classify WHERE id IN (` + utils.GetOrmInReplace(len(ids)) + `) ORDER BY create_time ASC `
+	_, err = o.Raw(sql, ids).QueryRows(&items)
 	return
 }
 
-func GetEnglishReportDetailByClassifyId(classifyIdFirst, classifyIdSecond int) (item *EnglishReport, err error) {
+func GetEnglishReportDetailByClassifyId(classifyIdFirst, classifyIdSecond int) (item *EnglishReportDetail, err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	sql := ` SELECT * FROM english_report WHERE 1=1 `
 	if classifyIdSecond > 0 {
@@ -506,12 +525,13 @@ type EnglishClassify struct {
 	ClassifyName  string    `description:"分类名称"`
 	Sort          int       `description:"排序"`
 	ParentId      int       `description:"父级分类id"`
+	RootId        int       `description:"一级分类ID"`
 	CreateTime    time.Time `description:"创建时间"`
 	ModifyTime    time.Time `description:"修改时间"`
 	ClassifyLabel string    `description:"分类标签"`
 	ShowType      int       `description:"展示类型:1-列表 2-专栏"`
 	IsShow        int       `description:"是否在小程序显示:1-显示 0-隐藏"`
-	ClassifyType  int       `description:"分类类型:0英文报告,1英文线上路演"`
+	//ClassifyType  int       `description:"分类类型:0英文报告,1英文线上路演"`
 }
 
 func AddEnglishClassify(item *EnglishClassify) (lastId int64, err error) {
@@ -527,9 +547,18 @@ func ModifyEnglishClassify(item *EnglishClassify) (err error) {
 	classify_name = ?,
 	sort = ?,
 	parent_id = ?,
+	root_id = ?,
 	modify_time = ? 
 	WHERE id = ? `
-	_, err = o.Raw(sql, item.ClassifyName, item.Sort, item.ParentId, item.ModifyTime, item.Id).Exec()
+	_, err = o.Raw(sql, item.ClassifyName, item.Sort, item.ParentId, item.RootId, item.ModifyTime, item.Id).Exec()
+	return
+}
+
+// UpdateEnglishClassifyRootIdByParentId 更新报告分类的顶级ID
+func UpdateEnglishClassifyRootIdByParentId(parentId, rootId int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := " UPDATE english_classify SET root_id = ? WHERE parent_id=? "
+	_, err = o.Raw(sql, rootId, parentId).Exec()
 	return
 }
 
@@ -568,30 +597,33 @@ func GetEnglishReportCounts(classifyId, parentId int) (count int, err error) {
 	return
 }
 
-func GetEnglishClassifyCountsByName(name string, parentId int, classifyType int) (count int, err error) {
+func GetEnglishClassifyCountsByName(name string, parentId int) (count int, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `SELECT COUNT(1) AS count FROM english_classify WHERE classify_name=? AND parent_id = ? AND classify_type = ? `
-	err = o.Raw(sql, name, parentId, classifyType).QueryRow(&count)
+	sql := `SELECT COUNT(1) AS count FROM english_classify WHERE classify_name=? AND parent_id = ? `
+	err = o.Raw(sql, name, parentId).QueryRow(&count)
 	return
 }
 
-// 获取分类列表
-func GetEnglishFirstClassifyList(classifyType, startSize, pageSize int) (items []*EnglishClassifyList, err error) {
-	sql := ``
-
-	sql = `SELECT * FROM english_classify WHERE parent_id=0 AND classify_type = ?  ORDER BY sort ASC,create_time ASC LIMIT ?,? `
-
+// GetEnglishFirstClassifyList 获取一级、二级分类列表
+func GetEnglishFirstClassifyList(startSize, pageSize int) (items []*EnglishClassifyList, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	_, err = o.Raw(sql, classifyType, startSize, pageSize).QueryRows(&items)
+	sql := `SELECT * FROM english_classify WHERE parent_id=0  ORDER BY sort ASC,create_time ASC LIMIT ?,? `
+	_, err = o.Raw(sql, startSize, pageSize).QueryRows(&items)
 	return
 }
 
-func GetEnglishFirstClassifyListCount(classifyType int) (count int, err error) {
-	sqlCount := ``
-	sqlCount = `SELECT COUNT(1) AS count FROM english_classify WHERE parent_id=0 AND classify_type = ? `
+// GetEnglishSecondClassifyList 获取一级、二级分类列表
+func GetEnglishSecondClassifyList(rootIds []int) (items []*EnglishClassifyList, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM english_classify WHERE root_id IN (` + utils.GetOrmInReplace(len(rootIds)) + `) and parent_id>0 and root_id=parent_id ORDER BY sort ASC,create_time ASC`
+	_, err = o.Raw(sql, rootIds).QueryRows(&items)
+	return
+}
 
+func GetEnglishFirstClassifyListCount() (count int, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	err = o.Raw(sqlCount, classifyType).QueryRow(&count)
+	sqlCount := `SELECT COUNT(1) AS count FROM english_classify WHERE parent_id=0`
+	err = o.Raw(sqlCount).QueryRow(&count)
 	return
 }
 
@@ -633,6 +665,35 @@ func UpdateEnglishReportByClassifyId(classifyFirstName, classifySecondName strin
 	return
 }
 
+func UpdateEnglishReportClassifyByFirstSecondClassifyId(classifyId, parentId int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	// 更新相关联的二级分类的parentId,和classify_name_second
+	sql := `update english_report r
+LEFT JOIN english_classify c ON r.classify_id_second = c.id
+SET r.classify_id_first=c.parent_id, r.classify_name_second=c.classify_name
+where (r.classify_id_first != c.parent_id or r.classify_name_second != c.classify_name) and r.classify_id_second =?`
+	_, err = o.Raw(sql, classifyId).Exec()
+	if err != nil {
+		return
+	}
+	//更新一级分类名
+	sql = `update english_report r
+    LEFT JOIN english_classify c ON r.classify_id_first = c.id
+    SET r.classify_name_first=c.classify_name
+where r.classify_name_first	!= c.classify_name and r.classify_id_first=?`
+	_, err = o.Raw(sql, parentId).Exec()
+	if err != nil {
+		return
+	}
+	//更新一级分类名
+	sql = `update english_report r
+    LEFT JOIN english_classify c ON r.classify_id_first = c.id
+    SET r.classify_name_first=c.classify_name
+where r.classify_name_first	!= c.classify_name and r.classify_id_first=?`
+	_, err = o.Raw(sql, classifyId).Exec()
+	return
+}
+
 // FetchEnglishReportById 主键获取英文报告
 func FetchEnglishReportById(reportId int) (item *EnglishReport, err error) {
 	o := orm.NewOrmUsingDB("rddp")
@@ -650,10 +711,10 @@ func (reportInfo *EnglishReport) UpdateReport(cols []string) (err error) {
 }
 
 // GetAllEnglishClassify 获取所有英文分类
-func GetAllEnglishClassify(classifyType int) (list []*Classify, err error) {
+func GetAllEnglishClassify() (list []*Classify, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := ` SELECT * FROM english_classify where classify_type = ? `
-	_, err = o.Raw(sql, classifyType).QueryRows(&list)
+	sql := ` SELECT * FROM english_classify `
+	_, err = o.Raw(sql).QueryRows(&list)
 	return
 }
 
@@ -728,9 +789,37 @@ func (m RSChildClassifyList) Swap(i, j int) {
 }
 
 // GetEnglishClassifyByClassifyNameParentId 获取英文分类
-func GetEnglishClassifyByClassifyNameParentId(classifyName string, parentId, classifyType int) (item *Classify, err error) {
+func GetEnglishClassifyByClassifyNameParentId(classifyName string, parentId int) (item *Classify, err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := ` SELECT * FROM english_classify where classify_name = ? and parent_id = ? and classify_type = ? `
-	err = o.Raw(sql, classifyName, parentId, classifyType).QueryRow(&item)
+	sql := ` SELECT * FROM english_classify where classify_name = ? and parent_id = ? `
+	err = o.Raw(sql, classifyName, parentId).QueryRow(&item)
+	return
+}
+
+type EnglishClassifyFullName struct {
+	Id           int    `description:"分类ID"`
+	ParentId     int    `description:"父级分类id"`
+	RootId       int    `description:"一级分类ID"`
+	RootName     string `description:"一级分类名"`
+	ParentName   string `description:"二级分类名"`
+	ClassifyName string `description:"分类名称"`
+}
+
+// GetEnglishClassifyFullNameByIds 获取英文分类名一级/二级/三级
+func GetEnglishClassifyFullNameByIds(classifyIds []int) (list []*EnglishClassifyFullName, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` SELECT
+	a.id,
+	a.parent_id,
+	a.root_id,
+	a.classify_name,
+	b.classify_name AS root_name,
+	c.classify_name AS parent_name 
+FROM
+	english_classify a
+	LEFT JOIN english_classify b ON a.root_id = b.id
+	LEFT JOIN english_classify c ON a.parent_id = c.id 
+where a.id IN (` + utils.GetOrmInReplace(len(classifyIds)) + `)`
+	_, err = o.Raw(sql, classifyIds).QueryRows(&list)
 	return
 }

+ 1 - 0
models/english_report_email.go

@@ -187,6 +187,7 @@ type EnglishReportEmailSendReq struct {
 	EmailIds      string `description:"邮箱IDs"`
 	Theme         string `description:"邮件主题"`
 	EnPermissions []int  `description:"品种权限IDs"`
+	NoCompanyIds  []int  `description:"禁止接收邮件的英文客户IDs"`
 }
 
 // EnglishReportEmailConf 英文研报邮件配置

+ 24 - 8
models/english_video.go

@@ -240,16 +240,32 @@ func DeleteEnglishVideo(Id int) (err error) {
 	return
 }
 
-func GetEnglishVideoDetailByClassifyId(classifyIdFirst, classifyIdSecond int) (item *EnglishVideo, err error) {
+func UpdateEnglishVideoClassifyByFirstSecondClassifyId(classifyId, parentId int) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := ` SELECT * FROM english_video WHERE 1=1 `
-	if classifyIdSecond > 0 {
-		sql = sql + ` AND classify_id_second=?   ORDER BY stage DESC LIMIT 1`
-		err = o.Raw(sql, classifyIdSecond).QueryRow(&item)
-	} else {
-		sql = sql + ` AND classify_id_first=?   ORDER BY stage DESC LIMIT 1`
-		err = o.Raw(sql, classifyIdFirst).QueryRow(&item)
+	// 更新相关联的二级分类的parentId,和classify_name_second
+	sql := `update english_video r
+LEFT JOIN english_classify c ON r.classify_id_second = c.id
+SET r.classify_id_first=c.parent_id, r.classify_name_second=c.classify_name
+where (r.classify_id_first != c.parent_id or r.classify_name_second != c.classify_name) and r.classify_id_second =?`
+	_, err = o.Raw(sql, classifyId).Exec()
+	if err != nil {
+		return
+	}
+	//更新一级分类名
+	sql = `update english_video r
+    LEFT JOIN english_classify c ON r.classify_id_first = c.id
+    SET r.classify_name_first=c.classify_name
+where r.classify_name_first	!= c.classify_name and r.classify_id_first=?`
+	_, err = o.Raw(sql, parentId).Exec()
+	if err != nil {
+		return
 	}
+	//更新一级分类名
+	sql = `update english_video r
+    LEFT JOIN english_classify c ON r.classify_id_first = c.id
+    SET r.classify_name_first=c.classify_name
+where r.classify_name_first	!= c.classify_name and r.classify_id_first=?`
+	_, err = o.Raw(sql, classifyId).Exec()
 	return
 }
 

+ 33 - 0
models/meeting_probabilities.go

@@ -0,0 +1,33 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// MeetingProbabilities 加息表格详情表
+type MeetingProbabilities struct {
+	MeetingInfoId int    `orm:"column(meeting_info_id);pk"`
+	DateTime      string // 数据日期
+	Content       string // 表格内容
+	ExcelImage    string // 表格图片
+	IsDelete      int    // 是否删除,0:未删除,1:已删除
+	CreateTime    time.Time
+	ModifyTime    time.Time
+}
+
+// GetMeetingInfoById 根据id 获取加息概率表格详情
+func GetMeetingInfoById(dateTime string) (item *MeetingProbabilities, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ``
+	if dateTime == "" {
+		sql = ` SELECT * FROM meeting_probabilities WHERE is_delete=0 ORDER BY create_time DESC LIMIT 1 `
+		err = o.Raw(sql).QueryRow(&item)
+		return
+	} else {
+		sql = ` SELECT * FROM meeting_probabilities WHERE date_time=? AND is_delete=0 `
+		err = o.Raw(sql, dateTime).QueryRow(&item)
+		return
+	}
+}
+

+ 1 - 1
models/ppt_english/ppt_english_grant.go

@@ -97,7 +97,7 @@ type PptEnglishInfoGrantItem struct {
 	ReportId      int       `description:"关联的报告ID"`
 	ReportCode    string    `description:"关联的报告code"`
 	IsShare       int8      `description:"是否分享,0:不分享,1:分享"`
-
+	PublishTime   time.Time `description:"发布时间"`
 	//GrantId      int64     `orm:"column(grant_id);pk;auto" description:"自增序号"`
 	//PptId        int64     `description:"ppt ID"`
 	//DepartmentId int64     `description:"授权部门id"`

+ 4 - 0
models/ppt_english/ppt_english_group.go

@@ -206,6 +206,8 @@ type RespGroupPptNameListItem struct {
 	ReportId      int    `description:"关联的报告ID"`
 	ReportCode    string `description:"关联的报告code"`
 	PptCreateTime string `description:"ppt创建时间"`
+	PptModifyTime string `description:"ppt修改时间"`
+	PublishTime   string `description:"发布时间"`
 	PptPage       int    `description:"PPT总页数"`
 	IsReceived    int8   `description:"是否收到的共享,0:不是,1:是"`
 	IsGrant       int8   `description:"是否分配了权限,0:不是,1:是"`
@@ -218,6 +220,7 @@ type RespGroupPptListItem struct {
 	BackgroundImg string `description:"背景图片"`
 	Title         string `description:"标题"`
 	PptCreateTime string `description:"ppt创建时间"`
+	PptModifyTime string `description:"ppt修改时间"`
 	AdminId       int    `description:"移动ppt到该目录的系统用户id"`
 	AdminRealName string `description:"系统用户名称"`
 	PptVersion    int8   `description:"是否ppt的旧版本;1:旧的,2:新的"`
@@ -225,6 +228,7 @@ type RespGroupPptListItem struct {
 	PptxUrl       string `description:"pptx下载地址"`
 	ReportId      int    `description:"关联的报告ID"`
 	ReportCode    string `description:"关联的报告code"`
+	PublishTime   string `description:"发布时间"`
 }
 
 func (p RespGroupPptList) Len() int {

+ 1 - 0
models/ppt_v2_grant.go

@@ -97,6 +97,7 @@ type PptV2InfoGrantItem struct {
 	ReportId      int       `description:"关联的报告ID"`
 	ReportCode    string    `description:"关联的报告code"`
 	IsShare       int8      `description:"是否分享,0:不分享,1:分享"`
+	PublishTime   time.Time `description:"发布时间"`
 
 	//GrantId      int64     `orm:"column(grant_id);pk;auto" description:"自增序号"`
 	//PptId        int64     `description:"ppt ID"`

+ 4 - 0
models/ppt_v2_group.go

@@ -206,6 +206,8 @@ type RespGroupPptNameListItem struct {
 	ReportId      int    `description:"关联的报告ID"`
 	ReportCode    string `description:"关联的报告code"`
 	PptCreateTime string `description:"ppt创建时间"`
+	PptModifyTime string `description:"ppt修改时间"`
+	PublishTime   string `description:"发布时间"`
 	PptPage       int    `description:"PPT总页数"`
 	IsReceived    int8   `description:"是否收到的共享,0:不是,1:是"`
 	IsGrant       int8   `description:"是否分配了权限,0:不是,1:是"`
@@ -218,6 +220,7 @@ type RespGroupPptListItem struct {
 	BackgroundImg string `description:"背景图片"`
 	Title         string `description:"标题"`
 	PptCreateTime string `description:"ppt创建时间"`
+	PptModifyTime string `description:"ppt修改时间"`
 	AdminId       int    `description:"移动ppt到该目录的系统用户id"`
 	AdminRealName string `description:"系统用户名称"`
 	PptVersion    int8   `description:"是否ppt的旧版本;1:旧的,2:新的"`
@@ -225,6 +228,7 @@ type RespGroupPptListItem struct {
 	PptxUrl       string `description:"pptx下载地址"`
 	ReportId      int    `description:"关联的报告ID"`
 	ReportCode    string `description:"关联的报告code"`
+	PublishTime   string `description:"发布时间"`
 }
 
 func (p RespGroupPptList) Len() int {

+ 0 - 17
models/response/company.go

@@ -1,17 +0,0 @@
-package response
-
-import (
-	"eta/eta_api/models/company"
-	"github.com/rdlucklib/rdluck_tools/paging"
-)
-
-// 客户所属销售列表
-type CompanySellerListResp struct {
-	List []*company.CompanySellers
-}
-
-// TryOutCompanyListResp 正式转试用客户列表
-type TryOutCompanyListResp struct {
-	List   []*company.TryOutCompanyList
-	Paging *paging.PagingItem
-}

+ 0 - 124
models/response/statistic_report.go

@@ -1,124 +0,0 @@
-package response
-
-import (
-	"github.com/rdlucklib/rdluck_tools/paging"
-	"eta/eta_api/models"
-)
-
-// 首页控制台数据
-type WorktopResp struct {
-	FormalCompanyCount          int                         `description:"正式客户数"`
-	TrialCompanyTotal           int                         `description:"试用客户数"`
-	NewCompanyTotal             int                         `description:"新签客户数"`
-	RenewalCompanyTotal         int                         `description:"续约客户数"`
-	NotRenewalCompanyTotal      int                         `description:"未续约客户数"`
-	IncrementalCompanyChartList IncrementalCompanyChartResp `description:"增量客户图表数据"`
-	WillExpireChartList         WillExpireChartResp         `description:"增量客户图表数据"`
-	IncomeChartList             IncomeChartResp             `description:"收入统计图表数据"`
-	ContractData                ContractChartResp           `description:"当前有效合同存量"`
-}
-
-// 增量客户图表数据
-type IncrementalCompanyChartResp struct {
-	Title               string   `description:"图表名称"`
-	Date                []string `description:"月份"`
-	NewCompanyTotal     []int    `description:"新签客户数"`
-	RenewalCompanyTotal []int    `description:"续约客户数"`
-	//NotRenewalCompanyTotal []int    `description:"未续约客户数"`
-}
-
-// 即将到期客户数
-type WillExpireChartResp struct {
-	Title        string   `description:"图表名称"`
-	Date         []string `description:"月份"`
-	CompanyTotal []int    `description:"客户数"`
-}
-
-// 收入统计图表数据
-type IncomeChartResp struct {
-	Title         string    `description:"图表名称"`
-	Date          []string  `description:"月份"`
-	ContractTotal []int     `description:"合同数"`
-	MoneyTotal    []float64 `description:"合同总金额"`
-}
-
-// 当前有效合同存量
-type ContractChartResp struct {
-	Title              string  `description:"图表名称"`
-	FormalCompanyCount int     `description:"正式客户数"`
-	ContractTotal      int     `description:"合同数"`
-	MoneyTotal         float64 `description:"合同总金额"`
-}
-
-// 即将到期客户
-type WillExpireCompanyListResp struct {
-	List         []*models.WillExpireCompanyList
-	Paging       *paging.PagingItem `description:"分页数据"`
-	TotalCompany int                `description:"总客户数"`
-}
-
-// 合同统计数据接口返回结构体
-type ContractStatListResp struct {
-	List                []*models.ContractStatList
-	TotalContract       int                `description:"合同数量"`
-	TotalMoney          float64            `description:"合同总金额"`
-	TotalCompany        int                `description:"总客户数"`
-	TotalFileContract   int                `description:"总上传附件合同数"`
-	TotalSystemContract int                `description:"总系统生成合同数"`
-	Paging              *paging.PagingItem `description:"分页数据"`
-}
-
-// CompanyContractStatListResp 企业客户的合同统计数据接口返回结构体
-type CompanyContractStatListResp struct {
-	List          []*models.CompanyContractStatList
-	TotalContract int                `description:"合同数量"`
-	TotalMoney    float64            `description:"合同总金额"`
-	TotalCompany  int                `description:"总客户数"`
-	Paging        *paging.PagingItem `description:"分页数据"`
-}
-
-// 收入统计报表返回类
-type IncomeListResp struct {
-	List   []*models.IncomeList
-	Paging *paging.PagingItem `description:"分页数据"`
-	Total  int                `description:"新签合同总数"`
-	Money  float64            `description:"新签合同总金额"`
-}
-
-// 存量客户统计报表返回类
-type StackCompanyListResp struct {
-	List                   []*models.StackCompanyStatisticList
-	Paging                 *paging.PagingItem `description:"分页数据"`
-	NewCompanyTotal        int                `description:"新签客户数"`
-	RenewalCompanyTotal    int                `description:"续约客户数"`
-	NotRenewalCompanyTotal int                `description:"未续约客户数"`
-	NotRenewalTryOut       int                `description:"未续约客户(试用)数"`
-	NotRenewalNotTryOut    int                `description:"未续约客户(非试用)数"`
-}
-
-// 增量客户统计报表返回类
-type IncrementalCompanyListResp struct {
-	List                   []*models.IncrementalList
-	Paging                 *paging.PagingItem `description:"分页数据"`
-	TrialTotal             int                `description:"新增试用客户数"`
-	NewCompanyTotal        int                `description:"新签客户数"`
-	RenewalCompanyTotal    int                `description:"续约客户数"`
-	NotRenewalCompanyTotal int                `description:"未续约客户数"`
-	NotRenewalTryOut       int                `description:"未续约客户(试用)数"`
-	NotRenewalNotTryOut    int                `description:"未续约客户(非试用)数"`
-}
-
-// MoreRenewReasonResp 未续约说明列表返回类
-type MoreRenewReasonResp struct {
-	RenewalReason          string `description:"未续约说明"`
-	RenewalTodo            string `description:"待办事项说明"`
-	CreateTime             string `description:"添加时间"`
-	ExpirationTimeExceeded string `description:"超出到期时间"`
-}
-
-// RenewReasonItemResp 客户未续约说明列表返回类
-type RenewReasonItemResp struct {
-	RenewalReason string `description:"未续约说明"`
-	RenewalTodo   string `description:"待办事项说明"`
-	CreateTime    string `description:"添加时间"`
-}

+ 93 - 0
models/sandbox/sandbox_classify.go

@@ -0,0 +1,93 @@
+package sandbox
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type SandboxClassify struct {
+	SandboxClassifyId   int       `orm:"column(sandbox_classify_id);pk"`
+	SandboxClassifyName string    `description:"分类名称"`
+	ParentId          int       `description:"父级id"`
+	HasData           int       `description:"是否含有指标数据"`
+	CreateTime        time.Time `description:"创建时间"`
+	ModifyTime        time.Time `description:"修改时间"`
+	SysUserId         int       `description:"创建人id"`
+	SysUserRealName   string    `description:"创建人姓名"`
+	Level             int       `description:"层级"`
+	Sort              int       `description:"排序字段,越小越靠前,默认值:10"`
+}
+
+func AddSandboxClassify(item *SandboxClassify) (lastId int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	lastId, err = o.Insert(item)
+	return
+}
+
+// GetSandboxClassifyByParentId
+func GetSandboxClassifyByParentId(parentId int) (items []*SandboxClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM sandbox_classify WHERE parent_id=? order by sort asc,sandbox_classify_id asc`
+	_, err = o.Raw(sql, parentId).QueryRows(&items)
+	return
+}
+
+// GetSandboxClassifyAll
+func GetSandboxClassifyAll() (items []*SandboxClassifyItems, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM sandbox_classify WHERE parent_id<>0 order by sort asc,sandbox_classify_id asc`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+type SandboxClassifyItems struct {
+	SandboxClassifyId   int       `orm:"column(sandbox_classify_id);pk"`
+	SandboxClassifyName string    `description:"分类名称"`
+	ParentId            int       `description:"父级id"`
+	HasData             int       `description:"是否含有指标数据"`
+	CreateTime          time.Time `description:"创建时间"`
+	ModifyTime          time.Time `description:"修改时间"`
+	SysUserId           int       `description:"创建人id"`
+	SysUserRealName     string    `description:"创建人姓名"`
+	Level               int       `description:"层级"`
+	Sort                int       `description:"排序字段,越小越靠前,默认值:10"`
+	SandboxId           int       `description:"沙盘id"`
+	Children            []*SandboxClassifyItems
+}
+
+type SandboxClassifyListResp struct {
+	AllNodes []*SandboxClassifyItems
+}
+
+type AddSandboxClassifyReq struct {
+	SandboxClassifyName string `description:"分类名称"`
+	ParentId            int    `description:"父级id,第一级传0"`
+	Level               int    `description:"层级,第一级传0,其余传上一级的层级"`
+}
+
+func GetSandboxClassifyCount(sandboxClassifyName string, parentId int) (count int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT COUNT(1) AS count FROM sandbox_classify WHERE parent_id=? AND sandbox_classify_name=? `
+	err = o.Raw(sql, parentId, sandboxClassifyName).QueryRow(&count)
+	return
+}
+
+// GetSandboxClassifyMaxSort 获取沙盘分类下最大的排序数
+func GetSandboxClassifyMaxSort(parentId int) (sort int, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT Max(sort) AS sort FROM sandbox_classify WHERE parent_id=? `
+	err = o.Raw(sql, parentId).QueryRow(&sort)
+	return
+}
+
+type EditSandboxClassifyReq struct {
+	SandboxClassifyName string `description:"分类名称"`
+	SandboxClassifyId   int    `description:"分类id"`
+}
+
+func GetSandboxClassifyById(classifyId int) (item *SandboxClassify, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := `SELECT * FROM sandbox_classify WHERE sandbox_classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}

+ 54 - 0
routers/commentsRouter.go

@@ -547,6 +547,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/future_good:FutureGoodChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/future_good:FutureGoodChartInfoController"],
+        beego.ControllerComments{
+            Method: "BaseChartInfoDetailFromUniqueCode",
+            Router: `/chart_info/base_detail/from_unique_code`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/future_good:FutureGoodChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage/future_good:FutureGoodChartInfoController"],
         beego.ControllerComments{
             Method: "CopyChartInfo",
@@ -1906,6 +1915,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbClassifyController"],
+        beego.ControllerComments{
+            Method: "SimpleList",
+            Router: `/classify/simple`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbClassifyController"],
+        beego.ControllerComments{
+            Method: "ClassifyTree",
+            Router: `/classify/tree`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbClassifyController"],
         beego.ControllerComments{
             Method: "EdbClassifyMove",
@@ -3301,6 +3328,24 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:PredictEdbClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:PredictEdbClassifyController"],
+        beego.ControllerComments{
+            Method: "SimpleList",
+            Router: `/predict_classify/simple`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:PredictEdbClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:PredictEdbClassifyController"],
+        beego.ControllerComments{
+            Method: "ClassifyTree",
+            Router: `/predict_classify/tree`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:PredictEdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:PredictEdbInfoController"],
         beego.ControllerComments{
             Method: "Add",
@@ -4795,6 +4840,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:MeetingProbabilitiesController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:MeetingProbabilitiesController"],
+        beego.ControllerComments{
+            Method: "Detail",
+            Router: `/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:OutLinkController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:OutLinkController"],
         beego.ControllerComments{
             Method: "AllList",

+ 5 - 0
routers/router.go

@@ -288,6 +288,11 @@ func init() {
 				&controllers.UserLoginAuthController{},
 			),
 		),
+		web.NSNamespace("/meeting_probabilities",
+			web.NSInclude(
+				&controllers.MeetingProbabilitiesController{},
+			),
+		),
 	)
 	web.AddNamespace(ns)
 }

+ 1 - 1
services/data/base_edb_lib.go

@@ -303,7 +303,7 @@ type CalculateComputeCorrelationResp struct {
 	Msg         string
 	ErrMsg      string
 	ErrCode     string
-	Data        float64
+	Data        string
 	Success     bool `description:"true 执行成功,false 执行失败"`
 	IsSendEmail bool `json:"-" description:"true 发送邮件,false 不发送邮件"`
 	IsAddLog    bool `json:"-" description:"true 新增操作日志,false 不新增操作日志" `

+ 547 - 86
services/data/chart_info.go

@@ -501,7 +501,7 @@ func CheckOpChartPermission(sysUser *system.Admin, createUserId int) (ok bool) {
 }
 
 // GetChartEdbData 获取图表的指标数据
-func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*data_manage.ChartEdbInfoMapping, extraConfigStr string) (edbList []*data_manage.ChartEdbInfoMapping, xEdbIdValue []int, yDataList []data_manage.YData, dataResp interface{}, err error, errMsg string) {
+func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*data_manage.ChartEdbInfoMapping, extraConfigStr string, seasonExtraConfig string) (edbList []*data_manage.ChartEdbInfoMapping, xEdbIdValue []int, yDataList []data_manage.YData, dataResp interface{}, err error, errMsg string) {
 	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)
 	xEdbIdValue = make([]int, 0)
 	yDataList = make([]data_manage.YData, 0)
@@ -535,14 +535,16 @@ func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate st
 			err = errors.New(errMsg)
 			return
 		}
+		
 		extraConfig = tmpExtraConfig
+
 	default:
 		xEdbIdValue = make([]int, 0)
 		yDataList = make([]data_manage.YData, 0)
 	}
 
 	// 指标对应的所有数据
-	edbDataListMap, edbList, err := getEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList)
+	edbDataListMap, edbList, err := getEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList, seasonExtraConfig)
 	if err != nil {
 		return
 	}
@@ -570,7 +572,20 @@ func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate st
 		}
 	case 10: // 截面散点图
 		sectionScatterConf := extraConfig.(data_manage.SectionScatterReq)
-		xEdbIdValue, dataResp, err = GetSectionScatterChartData(mappingList, edbDataListMap, sectionScatterConf)
+		xEdbIdValue, dataResp, err = GetSectionScatterChartData(chartInfoId, mappingList, edbDataListMap, sectionScatterConf)
+
+		var tmpExtraConfig data_manage.SectionScatterReq
+		if extraConfigStr == `` {
+			errMsg = "截面散点图未配置"
+			err = errors.New(errMsg)
+			return
+		}
+		err = json.Unmarshal([]byte(extraConfigStr), &tmpExtraConfig)
+		if err != nil {
+			errMsg = "截面散点配置异常"
+			err = errors.New(errMsg)
+			return
+		}
 
 		// 这个数据没有必要返回给前端
 		for _, v := range edbList {
@@ -582,13 +597,13 @@ func GetChartEdbData(chartInfoId, chartType int, calendar, startDate, endDate st
 }
 
 // GetEdbDataMapList 获取指标最后的基础数据
-func GetEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*data_manage.ChartEdbInfoMapping) (edbDataListMap map[int][]*data_manage.EdbDataList, edbList []*data_manage.ChartEdbInfoMapping, err error) {
-	edbDataListMap, edbList, err = getEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList)
+func GetEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*data_manage.ChartEdbInfoMapping, seasonExtra string) (edbDataListMap map[int][]*data_manage.EdbDataList, edbList []*data_manage.ChartEdbInfoMapping, err error) {
+	edbDataListMap, edbList, err = getEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList, seasonExtra)
 	return
 }
 
 // getEdbDataMapList 获取指标最后的基础数据
-func getEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*data_manage.ChartEdbInfoMapping) (edbDataListMap map[int][]*data_manage.EdbDataList, edbList []*data_manage.ChartEdbInfoMapping, err error) {
+func getEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate string, mappingList []*data_manage.ChartEdbInfoMapping, seasonExtraConfig string) (edbDataListMap map[int][]*data_manage.EdbDataList, edbList []*data_manage.ChartEdbInfoMapping, err error) {
 	// 指标对应的所有数据
 	edbDataListMap = make(map[int][]*data_manage.EdbDataList)
 
@@ -706,7 +721,7 @@ func getEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate
 		}
 		dataList := make([]*data_manage.EdbDataList, 0)
 		//fmt.Println("chart:", v.Source, v.EdbInfoId, startDateReal, endDate)
-
+		fmt.Println("calendarPreYear:", calendarPreYear)
 		//var newEdbInfo *data_manage.EdbInfo
 		switch v.EdbInfoCategoryType {
 		case 0:
@@ -729,7 +744,6 @@ func getEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate
 		}
 
 		if chartType == 2 {
-			latestDateStr := v.LatestDate //实际数据的截止日期
 			latestDate, tmpErr := time.Parse(utils.FormatDate, v.LatestDate)
 			if tmpErr != nil {
 				//item.DataList = dataList
@@ -739,7 +753,6 @@ func getEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate
 				err = errors.New(fmt.Sprint("获取最后实际数据的日期失败,Err:" + tmpErr.Error() + ";LatestDate:" + v.LatestDate))
 				return
 			}
-			latestDateYear := latestDate.Year() //实际数据截止年份
 
 			if calendar == "农历" {
 				if len(dataList) <= 0 {
@@ -751,101 +764,470 @@ func getEdbDataMapList(chartInfoId, chartType int, calendar, startDate, endDate
 						err = errors.New("获取农历数据失败,Err:" + tmpErr.Error())
 						return
 					}
+					quarterDataList, tErr := GetSeasonEdbInfoDataListByXDateNong(result, latestDate, seasonExtraConfig, calendarPreYear)
+					if tErr != nil {
+						err = errors.New("获取季节性图表数据失败,Err:" + tErr.Error())
+						return
+					}
+					item.DataList = quarterDataList
+				}
 
-					// 处理季节图的截止日期
-					for k, edbDataItems := range result.List {
-						var cuttingDataTimestamp int64
+			} else {
+				quarterDataList, tErr := GetSeasonEdbInfoDataListByXDate(dataList, latestDate, seasonExtraConfig)
+				if tErr != nil {
+					err = errors.New("获取季节性图表数据失败,Err:" + tErr.Error())
+					return
+				}
+				item.DataList = quarterDataList
+			}
+		} else if chartType == 7 { //柱方图
+			//item.DataList = dataList
+		} else {
+			item.DataList = dataList
+		}
+		edbList = append(edbList, item)
+	}
 
-						// 切割的日期时间字符串
-						cuttingDataTimeStr := latestDate.AddDate(0, 0, edbDataItems.BetweenDay).Format(utils.FormatDate)
-						//如果等于最后的实际日期,那么遍历找到该日期对应的时间戳,并将其赋值为 切割时间戳
-						if edbDataItems.Year >= latestDateYear {
-							for _, tmpData := range edbDataItems.Items {
-								if tmpData.DataTime == cuttingDataTimeStr {
-									cuttingDataTimestamp = tmpData.DataTimestamp
-									break
-								}
-							}
-						}
-						edbDataItems.CuttingDataTimestamp = cuttingDataTimestamp
-						result.List[k] = edbDataItems
-					}
+	return
+}
+
+// GetSeasonEdbInfoDataListByXDate 季节性图的指标数据根据横轴展示
+func GetSeasonEdbInfoDataListByXDate(dataList []*data_manage.EdbDataList, latestDate time.Time, seasonExtraConfig string) (quarterDataListSort data_manage.QuarterDataList, err error) {
+	xStartDate := "01-01"
+	xEndDate := "12-31"
+	jumpYear := 0
+	legends := make([]data_manage.SeasonChartLegend, 0)
+	var seasonExtra data_manage.SeasonExtraItem
+	if seasonExtraConfig != "" {
+		err = json.Unmarshal([]byte(seasonExtraConfig), &seasonExtra)
+		if err != nil {
+			return
+		}
+	}
+
+	if seasonExtra.XStartDate != "" {
+		xStartDate = seasonExtra.XStartDate
+		xEndDate = seasonExtra.XEndDate
+		jumpYear = seasonExtra.JumpYear
+		legends = seasonExtra.ChartLegend
+	}
+
+	length := len(dataList)
+	if length == 0 {
+		return
+	}
+	legendMap := make(map[string]string, 0)
+	if len(legends) > 0 {
+		for _, v := range legends {
+			legendMap[v.Name] = v.Value
+		}
+	}
+	latestDateStr := latestDate.Format(utils.FormatDate)
+
+	//判断横轴的两个时间之间是不是跨年了,如果跨年了,则横轴截止年份比起始年份+1,如果不跨年,截止年份等于起始年份
+	//根据数据确定最早的年份,和最近年份
+	//根据横轴的日期,汇总所有的年份
+	startDate := dataList[0].DataTime
+	startDateT, tmpErr := time.Parse(utils.FormatDate, startDate)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	startYear := startDateT.Year()
+	//获取数据的最新日期
+	lastDate := dataList[length-1].DataTime
+	lastDateT, tmpErr := time.Parse(utils.FormatDate, lastDate)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	endYear := lastDateT.Year()
+	nowYear := time.Now().Year()
+	dataMap := make(map[string]data_manage.QuarterXDateItem, 0)
+
+	quarterDataList := make([]*data_manage.QuarterData, 0)
+	quarterMap := make(map[string][]*data_manage.EdbDataList, 0)
+
+	//整理出日期
+	idx := 1
+	chartLegendMap := make(map[string]int, 0)
+	for currentStartYear := startYear; currentStartYear <= endYear; currentStartYear++ {
+		startStr := fmt.Sprintf("%d-%s", currentStartYear, xStartDate)
+		currentEndYear := currentStartYear
+		if jumpYear == 1 {
+			currentEndYear = currentStartYear + 1
+		}
+		endStr := fmt.Sprintf("%d-%s", currentEndYear, xEndDate)
+		name := fmt.Sprintf("%s_%s", startStr, endStr)
+		showName := fmt.Sprintf("%d_%d", currentStartYear, currentEndYear)
+
+		startT, tEr := time.Parse(utils.FormatDate, startStr)
+		if tEr != nil {
+			err = tEr
+			return
+		}
 
-					if result.List[0].Year != calendarPreYear {
-						itemList := make([]*data_manage.EdbDataList, 0)
-						items := new(data_manage.EdbDataItems)
-						//items.Year = calendarPreYear
-						items.Items = itemList
+		endT, tEr := time.Parse(utils.FormatDate, endStr)
+		if tEr != nil {
+			err = tEr
+			return
+		}
 
-						newResult := new(data_manage.EdbDataResult)
-						newResult.List = append(newResult.List, items)
-						newResult.List = append(newResult.List, result.List...)
-						item.DataList = newResult
+		if lastDateT.Before(startT) {
+			//如果最新的日期在起始日之前,则跳出循环
+			break
+		}
+
+		if endT.Year() > nowYear {
+			//如果最新的日期比真实年份要大,则数据全部按照最大的年份补齐
+			nowYear = endT.Year()
+		}
+
+		item := data_manage.QuarterXDateItem{
+			StartDate: startT,
+			EndDate:   endT,
+			ShowName:  showName,
+		}
+		dataMap[name] = item
+		chartLegendMap[name] = idx
+		idx++
+		if lastDateT.Before(endT) {
+			//如果最新的日期在起始日之前,则跳出循环
+			break
+		}
+	}
+	lenYear := len(dataMap)
+	for k, v := range dataMap {
+		if i, ok := chartLegendMap[k]; ok {
+			v.ChartLegend = strconv.Itoa(endYear - lenYear + i)
+		}
+		dataMap[k] = v
+	}
+
+	for _, v := range dataList {
+		dataTimeT, _ := time.Parse(utils.FormatDate, v.DataTime)
+		year := dataTimeT.Year()
+		newItemDate := dataTimeT.AddDate(nowYear-year, 0, 0)
+		for k, dateItem := range dataMap {
+			tmpVal := data_manage.EdbDataList{
+				EdbDataId:     v.EdbDataId,
+				EdbInfoId:     v.EdbInfoId,
+				DataTime:      v.DataTime,
+				DataTimestamp: v.DataTimestamp,
+				Value:         v.Value,
+			}
+			if (dateItem.StartDate.Before(dataTimeT) && dateItem.EndDate.After(dataTimeT)) || dateItem.StartDate == dataTimeT || dateItem.EndDate == dataTimeT {
+				if jumpYear == 1 {
+					//计算前一年最大的日期, 只补齐数据到去年
+					beforeYearMaxDate := fmt.Sprintf("%d-12-31", dateItem.StartDate.Year())
+					beforeYearMaxDateT, _ := time.Parse(utils.FormatDate, beforeYearMaxDate)
+					if dataTimeT.Before(beforeYearMaxDateT) || dataTimeT == beforeYearMaxDateT {
+						newItemDate = dataTimeT.AddDate(nowYear-year-1, 0, 0)
 					} else {
-						item.DataList = result
+						newItemDate = dataTimeT.AddDate(nowYear-year, 0, 0)
 					}
+				} else {
+					newItemDate = dataTimeT.AddDate(nowYear-year, 0, 0)
+				}
+				timestamp := newItemDate.UnixNano() / 1e6
+				tmpVal.DataTimestamp = timestamp
+				tmpV := &tmpVal
+				if findVal, ok := quarterMap[k]; !ok {
+					findVal = append(findVal, tmpV)
+					quarterMap[k] = findVal
+				} else {
+					findVal = append(findVal, tmpV)
+					quarterMap[k] = findVal
 				}
 
-			} else {
-				currentYear := time.Now().Year()
+				if v.DataTime == latestDateStr {
+					dateItem.CuttingDataTimestamp = timestamp
+					dataMap[k] = dateItem
+				}
+				//break
+			}
+		}
+	}
+	for k, v := range dataMap {
+		itemList := quarterMap[k]
+		quarterItem := new(data_manage.QuarterData)
+		quarterItem.Years = v.ShowName
+		quarterItem.ChartLegend = v.ChartLegend
+		if le, ok := legendMap[v.ShowName]; ok {
+			if le != strconv.Itoa(v.StartDate.Year()) && le != strconv.Itoa(v.EndDate.Year()) {
+				quarterItem.ChartLegend = le
+			}
+		}
+		quarterItem.DataList = itemList
+		quarterItem.CuttingDataTimestamp = v.CuttingDataTimestamp
+
+		//如果等于最后的实际日期,那么将切割时间戳记录
+		if quarterItem.CuttingDataTimestamp == 0 {
+			//如果大于最后的实际日期,那么第一个点就是切割的时间戳
+			if latestDate.Before(v.StartDate) && len(itemList) > 0 {
+				quarterItem.CuttingDataTimestamp = itemList[0].DataTimestamp - 100
+			}
+		}
+		quarterDataList = append(quarterDataList, quarterItem)
+	}
 
-				quarterDataList := make([]*data_manage.QuarterData, 0)
-				quarterMap := make(map[int][]*data_manage.EdbDataList)
-				var quarterArr []int
+	if len(quarterDataList) > 0 {
+		quarterDataListSort = quarterDataList
+		sort.Sort(quarterDataListSort)
+	}
+	return
+}
 
-				for _, v := range dataList {
-					itemDate, tmpErr := time.Parse(utils.FormatDate, v.DataTime)
-					if tmpErr != nil {
-						err = errors.New("季度指标日期转换,Err:" + tmpErr.Error() + ";DataTime:" + v.DataTime)
-						return
+// GetSeasonEdbInfoDataListByXDateNong 季节性图的指标数据根据横轴选择农历时展示
+func GetSeasonEdbInfoDataListByXDateNong(result *data_manage.EdbDataResult, latestDate time.Time, seasonExtraConfig string, calendarPreYear int) (quarterDataListSort data_manage.QuarterDataList, err error) {
+	xStartDate := "01-01"
+	xEndDate := "12-31"
+	jumpYear := 0
+	legends := make([]data_manage.SeasonChartLegend, 0)
+	var seasonExtra data_manage.SeasonExtraItem
+	if seasonExtraConfig != "" {
+		err = json.Unmarshal([]byte(seasonExtraConfig), &seasonExtra)
+		if err != nil {
+			return
+		}
+	}
+
+	if seasonExtra.XStartDate != "" {
+		xStartDate = seasonExtra.XStartDate
+		xEndDate = seasonExtra.XEndDate
+		jumpYear = seasonExtra.JumpYear
+		legends = seasonExtra.ChartLegend
+	}
+
+	length := len(result.List)
+	if length == 0 {
+		return
+	}
+	legendMap := make(map[string]string, 0)
+	if len(legends) > 0 {
+		for _, v := range legends {
+			legendMap[v.Name] = v.Value
+		}
+	}
+	latestDateYear := latestDate.Year()
+	//判断横轴的两个时间之间是不是跨年了,如果跨年了,则横轴截止年份比起始年份+1,如果不跨年,截止年份等于起始年份
+	//根据数据确定最早的年份,和最近年份
+	//根据横轴的日期,汇总所有的年份
+	startYear := result.List[0].Year
+	/*if jumpYear == 1 {
+		if startYear != calendarPreYear {
+			startYear = startYear - 1
+		}
+	}*/
+	itemLength := len(result.List[length-1].Items)
+	//获取数据的最新日期
+	lastDate := result.List[length-1].Items[itemLength-1].DataTime
+	lastDateT, tmpErr := time.Parse(utils.FormatDate, lastDate)
+	if tmpErr != nil {
+		err = tmpErr
+		return
+	}
+	endYear := lastDateT.Year()
+	nowYear := time.Now().Year()
+	dataMap := make(map[string]data_manage.QuarterXDateItem, 0)
+
+	quarterDataList := make([]*data_manage.QuarterData, 0)
+	resultData := make([]*data_manage.QuarterData, 0)
+	quarterMap := make(map[string][]*data_manage.EdbDataList, 0)
+
+	//整理出日期
+	idx := 1
+	chartLegendMap := make(map[string]int, 0)
+	for currentStartYear := startYear; currentStartYear <= endYear; currentStartYear++ {
+		startStr := fmt.Sprintf("%d-%s", currentStartYear, xStartDate)
+		currentEndYear := currentStartYear
+		if jumpYear == 1 {
+			currentEndYear = currentStartYear + 1
+		}
+		endStr := fmt.Sprintf("%d-%s", currentEndYear, xEndDate)
+		showName := fmt.Sprintf("%d_%d", currentStartYear, currentEndYear)
+
+		startT, tEr := time.Parse(utils.FormatDate, startStr)
+		if tEr != nil {
+			err = tEr
+			return
+		}
+
+		endT, tEr := time.Parse(utils.FormatDate, endStr)
+		if tEr != nil {
+			err = tEr
+			return
+		}
+
+		if lastDateT.Before(startT) {
+			//如果最新的日期在起始日之前,则跳出循环
+			break
+		}
+		if endT.Year() > nowYear {
+			//如果最新的日期比真实年份要大,则数据全部按照最大的年份补齐
+			nowYear = endT.Year()
+		}
+		item := data_manage.QuarterXDateItem{
+			StartDate: startT,
+			EndDate:   endT,
+			ShowName:  showName,
+		}
+		dataMap[showName] = item
+		chartLegendMap[showName] = idx
+		idx++
+		if lastDateT.Before(endT) {
+			//如果最新的日期在起始日之前,则跳出循环
+			break
+		}
+	}
+	lenYear := len(dataMap)
+	for k, v := range dataMap {
+		if i, ok := chartLegendMap[k]; ok {
+			v.ChartLegend = strconv.Itoa(endYear - lenYear + i)
+		}
+		dataMap[k] = v
+	}
+
+	yearDataListMap := make(map[int]*data_manage.EdbDataItems, 0)
+
+	for _, lv := range result.List {
+		yearDataListMap[lv.Year] = lv
+	}
+
+	//判断哪些点应该落在同一条时间线上
+	maxY := lastDateT.Year()
+	endTmp := fmt.Sprintf("%d-%s", maxY, xEndDate)
+	endTmpT, _ := time.Parse(utils.FormatDate, endTmp)
+	minY := maxY
+	if jumpYear == 1 {
+		minY = maxY - 1
+	}
+	startTmp := fmt.Sprintf("%d-%s", minY, xStartDate)
+	startTmpT, _ := time.Parse(utils.FormatDate, startTmp)
+
+	for name, dateItem := range dataMap {
+		lv, ok1 := yearDataListMap[dateItem.EndDate.Year()]
+		if dateItem.EndDate.Year() > lastDateT.Year() {
+			lv, ok1 = yearDataListMap[dateItem.StartDate.Year()]
+		}
+		if !ok1 {
+			continue
+		}
+		for _, item := range lv.Items {
+			tmpVal := data_manage.EdbDataList{
+				EdbDataId:     item.EdbDataId,
+				EdbInfoId:     item.EdbInfoId,
+				DataTime:      item.DataTime,
+				DataTimestamp: item.DataTimestamp,
+				Value:         item.Value,
+			}
+			dataTimeT, _ := time.Parse(utils.FormatDate, item.DataTime)
+			year := dataTimeT.Year()
+			newItemDate := dataTimeT.AddDate(nowYear-year, 0, 0)
+			if dateItem.EndDate.Year() > maxY {
+				if (dateItem.StartDate.Before(dataTimeT) && dateItem.EndDate.After(dataTimeT)) || dateItem.StartDate == dataTimeT || dateItem.EndDate == dataTimeT {
+					if jumpYear == 1 {
+						//计算前一年最大的日期, 只补齐数据到去年
+						beforeYearMaxDate := fmt.Sprintf("%d-12-31", dateItem.StartDate.Year())
+						beforeYearMaxDateT, _ := time.Parse(utils.FormatDate, beforeYearMaxDate)
+						if dataTimeT.Before(beforeYearMaxDateT) || dataTimeT == beforeYearMaxDateT {
+							newItemDate = dataTimeT.AddDate(nowYear-year-1, 0, 0)
+						} else {
+							newItemDate = dataTimeT.AddDate(nowYear-year, 0, 0)
+						}
+					} else {
+						newItemDate = dataTimeT.AddDate(nowYear-year, 0, 0)
 					}
-					year := itemDate.Year()
-					newItemDate := itemDate.AddDate(currentYear-year, 0, 0)
 					timestamp := newItemDate.UnixNano() / 1e6
-					v.DataTimestamp = timestamp
-					if findVal, ok := quarterMap[year]; !ok {
-						quarterArr = append(quarterArr, year)
-						findVal = append(findVal, v)
-						quarterMap[year] = findVal
+					tmpVal.DataTimestamp = timestamp
+					tmpV := &tmpVal
+					if findVal, ok := quarterMap[name]; !ok {
+						findVal = append(findVal, tmpV)
+						quarterMap[name] = findVal
 					} else {
-						findVal = append(findVal, v)
-						quarterMap[year] = findVal
+						findVal = append(findVal, tmpV)
+						quarterMap[name] = findVal
+					}
+					if lv.Year >= latestDateYear {
+						// 切割的日期时间字符串
+						cuttingDataTimeStr := latestDate.AddDate(0, 0, lv.BetweenDay).Format(utils.FormatDate)
+						if item.DataTime == cuttingDataTimeStr {
+							dateItem.CuttingDataTimestamp = timestamp
+							dataMap[name] = dateItem
+						}
 					}
 				}
-				for _, v := range quarterArr {
-					itemList := quarterMap[v]
-					quarterItem := new(data_manage.QuarterData)
-					quarterItem.Year = v
-					quarterItem.DataList = itemList
-
-					//如果等于最后的实际日期,那么将切割时间戳记录
-					if v == latestDateYear {
-						var cuttingDataTimestamp int64
-						for _, tmpData := range itemList {
-							if tmpData.DataTime == latestDateStr {
-								cuttingDataTimestamp = tmpData.DataTimestamp
-								break
-							}
+			} else {
+				if (startTmpT.Before(dataTimeT) && endTmpT.After(dataTimeT)) || startTmpT == dataTimeT || endTmpT == dataTimeT {
+					if jumpYear == 1 {
+						//计算前一年最大的日期, 只补齐数据到去年
+						beforeYearMaxDate := fmt.Sprintf("%d-12-31", startTmpT.Year())
+						beforeYearMaxDateT, _ := time.Parse(utils.FormatDate, beforeYearMaxDate)
+						if dataTimeT.Before(beforeYearMaxDateT) || dataTimeT == beforeYearMaxDateT {
+							newItemDate = dataTimeT.AddDate(nowYear-year-1, 0, 0)
+						} else {
+							newItemDate = dataTimeT.AddDate(nowYear-year, 0, 0)
 						}
-						quarterItem.CuttingDataTimestamp = cuttingDataTimestamp
-					} else if v > latestDateYear {
-						//如果大于最后的实际日期,那么第一个点就是切割的时间戳
-						if len(itemList) > 0 {
-							quarterItem.CuttingDataTimestamp = itemList[0].DataTimestamp - 100
+					} else {
+						newItemDate = dataTimeT.AddDate(nowYear-year, 0, 0)
+					}
+					timestamp := newItemDate.UnixNano() / 1e6
+					tmpVal.DataTimestamp = timestamp
+					tmpV := &tmpVal
+					if findVal, ok := quarterMap[name]; !ok {
+						findVal = append(findVal, tmpV)
+						quarterMap[name] = findVal
+					} else {
+						findVal = append(findVal, tmpV)
+						quarterMap[name] = findVal
+					}
+					if lv.Year >= latestDateYear {
+						// 切割的日期时间字符串
+						cuttingDataTimeStr := latestDate.AddDate(0, 0, lv.BetweenDay).Format(utils.FormatDate)
+						if item.DataTime == cuttingDataTimeStr {
+							dateItem.CuttingDataTimestamp = timestamp
+							dataMap[name] = dateItem
 						}
 					}
-					quarterDataList = append(quarterDataList, quarterItem)
 				}
-				item.DataList = quarterDataList
 			}
-		} else if chartType == 7 { //柱方图
-			//item.DataList = dataList
-		} else {
-			item.DataList = dataList
 		}
-		edbList = append(edbList, item)
+
+	}
+	for k, v := range dataMap {
+		itemList := quarterMap[k]
+		quarterItem := new(data_manage.QuarterData)
+		quarterItem.Years = v.ShowName
+		quarterItem.ChartLegend = v.ChartLegend
+		if le, ok := legendMap[v.ShowName]; ok {
+			if le != strconv.Itoa(v.StartDate.Year()) && le != strconv.Itoa(v.EndDate.Year()) {
+				quarterItem.ChartLegend = le
+			}
+		}
+		quarterItem.DataList = itemList
+		quarterItem.CuttingDataTimestamp = v.CuttingDataTimestamp
+
+		quarterDataList = append(quarterDataList, quarterItem)
+	}
+
+	if result.List[0].Year != calendarPreYear {
+		itemList := make([]*data_manage.EdbDataList, 0)
+		items := new(data_manage.QuarterData)
+		//items.Year = calendarPreYear
+		items.DataList = itemList
+
+		newResult := make([]*data_manage.QuarterData, 0)
+		newResult = append(newResult, items)
+		newResult = append(newResult, quarterDataList...)
+		resultData = newResult
+	} else {
+		resultData = quarterDataList
 	}
 
+	if len(quarterDataList) > 0 {
+		quarterDataListSort = resultData
+		sort.Sort(quarterDataListSort)
+	}
 	return
 }
 
@@ -1241,7 +1623,7 @@ func CheckChartExtraConfig(chartType int, extraConfigStr string) (edbIdList []in
 }
 
 // GetSectionScatterChartData 截面散点图的数据处理
-func GetSectionScatterChartData(mappingList []*data_manage.ChartEdbInfoMapping, edbDataListMap map[int][]*data_manage.EdbDataList, extraConfig data_manage.SectionScatterReq) (edbIdList []int, chartDataResp data_manage.SectionScatterInfoResp, err error) {
+func GetSectionScatterChartData(chartInfoId int, mappingList []*data_manage.ChartEdbInfoMapping, edbDataListMap map[int][]*data_manage.EdbDataList, extraConfig data_manage.SectionScatterReq) (edbIdList []int, chartDataResp data_manage.SectionScatterInfoResp, err error) {
 	// 指标数据数组(10086:{"2022-12-02":100.01,"2022-12-01":102.3})
 	edbDataMap := make(map[int]map[string]float64)
 	for edbInfoId, edbDataList := range edbDataListMap {
@@ -1502,6 +1884,7 @@ func GetSectionScatterChartData(mappingList []*data_manage.ChartEdbInfoMapping,
 		dataListResp = append(dataListResp, data_manage.SectionScatterSeriesItemResp{
 			Name:            seriesItem.Name,
 			NameEn:          seriesItem.NameEn,
+			IsNameDefault:   seriesItem.IsNameDefault,
 			Color:           seriesItem.Color,
 			EdbInfoList:     tmpSeriesEdbInfoList,
 			ShowTrendLine:   seriesItem.ShowTrendLine,
@@ -1513,6 +1896,39 @@ func GetSectionScatterChartData(mappingList []*data_manage.ChartEdbInfoMapping,
 		})
 	}
 
+	// 截面散点图点击详情时自动更新系列名
+	if len(extraConfig.SeriesList) > 0 {
+		// 默认名字的时候才自动更新
+		if extraConfig.SeriesList[0].IsNameDefault {
+			firstXEdbInfoId := extraConfig.SeriesList[0].EdbInfoList[0].XEdbInfoId
+			needUpdate := false
+			if v, ok := edbMappingMap[firstXEdbInfoId]; ok {
+				extraConfig.SeriesList[0].Name = v.LatestDate
+				extraConfig.SeriesList[0].NameEn = v.LatestDate
+				dataListResp[0].Name = v.LatestDate
+				dataListResp[0].NameEn = v.LatestDate
+				needUpdate = true
+			}
+
+			extraConfigByte, e := json.Marshal(extraConfig)
+			if e != nil {
+				errMsg := "截面散点系列更新异常"
+				err = errors.New(errMsg)
+				return
+			}
+			extraConfigStr := string(extraConfigByte)
+
+			if needUpdate {
+				err = data_manage.EditChartInfoExtraConfig(chartInfoId, extraConfigStr)
+				if err != nil {
+					errMsg := "截面散点系列更新异常"
+					err = errors.New(errMsg)
+					return
+				}
+			}
+		}
+	}
+
 	chartDataResp = data_manage.SectionScatterInfoResp{
 		XName:       extraConfig.XName,
 		XNameEn:     extraConfig.XNameEn,
@@ -1568,6 +1984,8 @@ func AddChartInfo(req data_manage.AddChartInfoReq, sysUserId int, sysUserRealNam
 	chartType := req.ChartType
 	extraConfig := req.ExtraConfig
 
+	// 季节性图表额外配置信息
+	var seasonExtraConfig string
 	// 关联指标
 	var edbInfoIdArr []int
 	chartEdbInfoList := req.ChartEdbInfoList
@@ -1614,6 +2032,25 @@ func AddChartInfo(req data_manage.AddChartInfoReq, sysUserId int, sysUserRealNam
 				if existCount <= 0 {
 					go data_manage.AddCalculateQuarter(edbInfoId, edbInfo.Source, edbInfo.EdbCode)
 				}
+
+				// 处理季节性图表横轴配置
+				{
+					if req.SeasonExtraConfig.XEndDate != "" {
+						if req.SeasonExtraConfig.XStartDate > req.SeasonExtraConfig.XEndDate && req.SeasonExtraConfig.JumpYear != 1 {
+							errMsg = "季节性图表配置信息异常:横坐标日期配置错误"
+							err = errors.New("季节性图表配置信息异常: 横坐标日期配置错误")
+							return
+						}
+						seasonExtra, tErr := json.Marshal(req.SeasonExtraConfig)
+						if tErr != nil {
+							errMsg = "季节性图表配置信息异常"
+							err = errors.New("季节性图表配置信息异常,Err:" + tErr.Error())
+							return
+						}
+
+						seasonExtraConfig = string(seasonExtra)
+					}
+				}
 			}
 		}
 	} else {
@@ -1758,6 +2195,7 @@ func AddChartInfo(req data_manage.AddChartInfoReq, sysUserId int, sysUserRealNam
 	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
 	chartInfo.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp)
 
+	// todo 判断是否需要重新计算用户的start_date
 	if req.DateType > 0 {
 		chartInfo.DateType = req.DateType
 	} else {
@@ -1772,11 +2210,12 @@ func AddChartInfo(req data_manage.AddChartInfoReq, sysUserId int, sysUserRealNam
 	if calendar == "" {
 		calendar = "公历"
 	}
+
 	chartInfo.Calendar = calendar
 	chartInfo.StartDate = req.StartDate
 	chartInfo.EndDate = req.EndDate
-	chartInfo.SeasonStartDate = req.SeasonStartDate
-	chartInfo.SeasonEndDate = req.SeasonEndDate
+	chartInfo.SeasonStartDate = req.StartDate
+	chartInfo.SeasonEndDate = req.EndDate
 	chartInfo.LeftMin = req.LeftMin
 	chartInfo.LeftMax = req.LeftMax
 	chartInfo.RightMin = req.RightMin
@@ -1784,6 +2223,8 @@ func AddChartInfo(req data_manage.AddChartInfoReq, sysUserId int, sysUserRealNam
 	chartInfo.Disabled = disableVal
 	chartInfo.BarConfig = barChartConf
 	chartInfo.ExtraConfig = extraConfig
+	chartInfo.SeasonExtraConfig = seasonExtraConfig
+	chartInfo.StartYear = req.StartYear
 	chartInfo.Source = utils.CHART_SOURCE_DEFAULT
 	newId, err := data_manage.AddChartInfo(chartInfo)
 	if err != nil {
@@ -1876,6 +2317,8 @@ func EditChartInfo(req data_manage.EditChartInfoReq, sysUser *system.Admin) (cha
 		return
 	}
 
+	// 季节性图表额外配置信息
+	var seasonExtraConfig string
 	// 图表关联指标id
 	chartEdbInfoList := make([]*data_manage.ChartSaveItem, 0)
 	// 关联指标
@@ -1925,6 +2368,25 @@ func EditChartInfo(req data_manage.EditChartInfoReq, sysUser *system.Admin) (cha
 				if existCount <= 0 {
 					go data_manage.AddCalculateQuarter(edbInfoId, edbInfo.Source, edbInfo.EdbCode)
 				}
+
+				// 处理季节性图表横轴配置
+				{
+					if req.SeasonExtraConfig.XEndDate != "" {
+						if req.SeasonExtraConfig.XStartDate > req.SeasonExtraConfig.XEndDate && req.SeasonExtraConfig.JumpYear != 1 {
+							errMsg = "季节性图表配置信息异常:横坐标日期配置错误"
+							err = errors.New("季节性图表配置信息异常: 横坐标日期配置错误")
+							return
+						}
+						seasonExtra, tErr := json.Marshal(req.SeasonExtraConfig)
+						if tErr != nil {
+							errMsg = "季节性图表配置信息异常"
+							err = errors.New("季节性图表配置信息异常,Err:" + tErr.Error())
+							return
+						}
+
+						seasonExtraConfig = string(seasonExtra)
+					}
+				}
 			}
 		}
 		chartEdbInfoList = req.ChartEdbInfoList
@@ -2015,7 +2477,6 @@ func EditChartInfo(req data_manage.EditChartInfoReq, sysUser *system.Admin) (cha
 			dateType = 3
 		}
 	}
-
 	sort.Ints(edbInfoIdArr)
 	var edbInfoIdArrStr []string
 	for _, v := range edbInfoIdArr {
@@ -2087,7 +2548,7 @@ func EditChartInfo(req data_manage.EditChartInfoReq, sysUser *system.Admin) (cha
 	// 图表启用与否
 	disableVal := CheckIsDisableChart(edbInfoIdArr)
 
-	err = data_manage.EditChartInfoAndMapping(&req, edbInfoIdStr, calendar, dateType, disableVal, barChartConf, chartEdbInfoList)
+	err = data_manage.EditChartInfoAndMapping(&req, edbInfoIdStr, calendar, dateType, disableVal, barChartConf, chartEdbInfoList, seasonExtraConfig)
 	if err != nil {
 		errMsg = "保存失败"
 		err = errors.New("保存失败,Err:" + err.Error())

+ 2 - 2
services/data/correlation/chart_info.go

@@ -798,8 +798,8 @@ func AddChartInfo(req data_manage.AddChartInfoReq, source int, sysUser *system.A
 	chartInfo.DateType = 6
 	chartInfo.StartDate = req.StartDate
 	chartInfo.EndDate = req.EndDate
-	chartInfo.SeasonStartDate = req.SeasonStartDate
-	chartInfo.SeasonEndDate = req.SeasonEndDate
+	chartInfo.SeasonStartDate = req.StartDate
+	chartInfo.SeasonEndDate = req.EndDate
 	chartInfo.LeftMin = req.LeftMin
 	chartInfo.LeftMax = req.LeftMax
 	chartInfo.RightMin = req.RightMin

+ 570 - 78
services/data/edb_classify.go

@@ -49,6 +49,125 @@ func edbClassifyHaveChild(allNode []*data_manage.EdbClassifyItems, node *data_ma
 	return
 }
 
+// GetClassifyTreeRecursive 递归获取分类树形结构
+func GetClassifyTreeRecursive(list []*data_manage.EdbClassifyItems, parentId int) []*data_manage.EdbClassifyItems {
+	res := make([]*data_manage.EdbClassifyItems, 0)
+	for _, v := range list {
+		if v.ParentId == parentId {
+			v.Children = GetClassifyTreeRecursive(list, v.ClassifyId)
+			res = append(res, v)
+		}
+	}
+	return res
+}
+
+func GetFullClassifyByClassifyId(targetClassifyId int) (targetList []*data_manage.EdbClassifyIdItems, err error, errMsg string) {
+	//判断是否是挂在顶级目录下
+	targetClassify, err := data_manage.GetEdbClassifyById(targetClassifyId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "当前分类不存在"
+			err = errors.New(errMsg)
+			return
+		}
+		errMsg = "获取失败"
+		err = errors.New("获取分类信息失败,Err:" + err.Error())
+		return
+	}
+	if targetClassify.ParentId == 0 {
+		targetItem := new(data_manage.EdbClassifyIdItems)
+		targetItem.ClassifyId = targetClassify.ClassifyId
+		targetItem.ParentId = targetClassify.ParentId
+		targetItem.RootId = targetClassify.RootId
+		targetItem.UniqueCode = targetClassify.UniqueCode
+		targetItem.Level = targetClassify.Level
+		targetItem.ClassifyName = targetClassify.ClassifyName
+		targetList = append(targetList, targetItem)
+		return
+	}
+	tmpList, err := data_manage.GetEdbClassifyByRootIdLevel(targetClassify.RootId, targetClassify.ClassifyType, "")
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		err = errors.New("获取数据失败,Err:" + err.Error())
+		return
+	}
+	idMap := make(map[int]struct{})
+	if len(tmpList) > 0 {
+		for _, v := range tmpList {
+			if v.ClassifyId == targetClassify.ClassifyId {
+				idMap[v.ClassifyId] = struct{}{}
+				idMap[v.ParentId] = struct{}{}
+			}
+		}
+		for _, v := range tmpList {
+			if _, ok := idMap[v.ClassifyId]; ok {
+				idMap[v.ParentId] = struct{}{}
+			}
+		}
+		for _, v := range tmpList {
+			if _, ok := idMap[v.ClassifyId]; ok {
+				targetItem := new(data_manage.EdbClassifyIdItems)
+				targetItem.ClassifyId = v.ClassifyId
+				targetItem.ParentId = v.ParentId
+				targetItem.RootId = v.RootId
+				targetItem.UniqueCode = v.UniqueCode
+				targetItem.Level = v.Level
+				targetItem.ClassifyName = v.ClassifyName
+				targetList = append(targetList, targetItem)
+			}
+		}
+	}
+	return
+}
+
+func GetChildClassifyByClassifyId(targetClassifyId int) (targetList []*data_manage.EdbClassifyIdItems, err error, errMsg string) {
+	//判断是否是挂在顶级目录下
+	targetClassify, err := data_manage.GetEdbClassifyById(targetClassifyId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "当前分类不存在"
+			err = errors.New(errMsg)
+			return
+		}
+		errMsg = "获取失败"
+		err = errors.New("获取分类信息失败,Err:" + err.Error())
+		return
+	}
+	orderStr := ` order by level asc, sort asc, classify_id asc`
+	tmpList, err := data_manage.GetEdbClassifyByRootIdLevel(targetClassify.RootId, targetClassify.ClassifyType, orderStr)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		err = errors.New("获取数据失败,Err:" + err.Error())
+		return
+	}
+	idMap := make(map[int]struct{})
+	if len(tmpList) > 0 {
+		for _, v := range tmpList {
+			if v.ClassifyId == targetClassify.ClassifyId {
+				idMap[v.ClassifyId] = struct{}{}
+			}
+		}
+		for _, v := range tmpList {
+			if _, ok := idMap[v.ParentId]; ok {
+				idMap[v.ClassifyId] = struct{}{}
+			}
+		}
+		for _, v := range tmpList {
+			if _, ok := idMap[v.ClassifyId]; ok {
+				targetItem := new(data_manage.EdbClassifyIdItems)
+				targetItem.ClassifyId = v.ClassifyId
+				targetItem.ParentId = v.ParentId
+				targetItem.RootId = v.RootId
+				targetItem.UniqueCode = v.UniqueCode
+				targetItem.Level = v.Level
+				targetItem.ClassifyName = v.ClassifyName
+				targetList = append(targetList, targetItem)
+			}
+		}
+	}
+
+	return
+}
 func GetEdbClassifyEdbInfo() (result map[int][]*data_manage.EdbClassifyItems, err error) {
 	cMap := make(map[int][]*data_manage.EdbClassifyItems)
 	items, err := data_manage.GetEdbInfoAll(0)
@@ -135,8 +254,33 @@ func AddEdbClassify(classifyName string, parentId, level int, classifyType uint8
 		errMsg = `分类名称已存在,请重新输入`
 		return
 	}
+	if level > 6 {
+		errMsg = `最高只支持添加6级分类`
+		return
+	}
 	//获取该层级下最大的排序数
-	maxSort, err := data_manage.GetEdbClassifyMaxSort(parentId, classifyType)
+	maxSort, err := GetEdbClassifyMaxSort(parentId, classifyType)
+	if err != nil {
+		errMsg = "获取失败"
+		err = errors.New("查询排序信息失败,Err:" + err.Error())
+		return
+	}
+	//查询顶级rootId
+	rootId := 0
+	if parentId > 0 {
+		parentClassify, tErr := data_manage.GetEdbClassifyById(parentId)
+		if tErr != nil {
+			if tErr.Error() == utils.ErrNoRow() {
+				errMsg = "父级分类不存在"
+				err = errors.New(errMsg)
+				return
+			}
+			errMsg = "获取失败"
+			err = errors.New("获取分类信息失败,Err:" + tErr.Error())
+			return
+		}
+		rootId = parentClassify.RootId
+	}
 
 	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
 	classifyInfo = &data_manage.EdbClassify{
@@ -144,6 +288,7 @@ func AddEdbClassify(classifyName string, parentId, level int, classifyType uint8
 		ClassifyType:    classifyType,
 		ClassifyName:    classifyName,
 		ParentId:        parentId,
+		RootId:          rootId,
 		HasData:         0,
 		CreateTime:      time.Now(),
 		ModifyTime:      time.Now(),
@@ -151,12 +296,23 @@ func AddEdbClassify(classifyName string, parentId, level int, classifyType uint8
 		SysUserRealName: sysUserName,
 		Level:           level + 1,
 		UniqueCode:      utils.MD5(utils.DATA_PREFIX + "_" + timestamp),
-		Sort:            maxSort,
+		Sort:            maxSort + 1,
 	}
-	_, err = data_manage.AddEdbClassify(classifyInfo)
+	newId, err := data_manage.AddEdbClassify(classifyInfo)
 	if err != nil {
 		errMsg = "保存分类失败"
+		return
 	}
+	if parentId == 0 { //一级目录的rootId等于自己本身
+		classifyInfo.ClassifyId = int(newId)
+		classifyInfo.RootId = int(newId)
+		err = classifyInfo.Update([]string{"RootId"})
+		if err != nil {
+			errMsg = "更新分类失败"
+			return
+		}
+	}
+
 	return
 }
 
@@ -492,113 +648,432 @@ func Delete(classifyId, edbInfoId int, sysUser *system.Admin, requestBody, reque
 }
 
 // MoveEdbClassify 移动指标分类
-func MoveEdbClassify(classifyId, parentClassifyId, prevClassifyId, nextClassifyId int, sysUser *system.Admin) (err error, errMsg string) {
-	//判断分类是否存在
-	edbClassifyInfo, err := data_manage.GetEdbClassifyById(classifyId)
-	if err != nil {
-		errMsg = "移动失败"
-		err = errors.New("获取分类信息失败,Err:" + err.Error())
-		return
+func MoveEdbClassify(req data_manage.MoveEdbClassifyReq, sysUser *system.Admin, classifyType uint8) (err error, errMsg string) {
+	// req.ClassifyId, req.ParentClassifyId, req.PrevClassifyId, req.NextClassifyId
+	classifyId := req.ClassifyId
+	parentClassifyId := req.ParentClassifyId
+	prevClassifyId := req.PrevClassifyId
+	nextClassifyId := req.PrevClassifyId
+
+	edbInfoId := req.EdbInfoId
+	prevEdbInfoId := req.PrevEdbInfoId
+	nextEdbInfoId := req.NextEdbInfoId
+
+	//首先确定移动的对象是分类还是指标
+	//判断上一个节点是分类还是指标
+	//判断下一个节点是分类还是指标
+	//同时更新分类目录下的分类sort和指标sort
+	//更新当前移动的分类或者指标sort
+
+	var parentEdbClassifyInfo *data_manage.EdbClassify
+	if parentClassifyId > 0 {
+		parentEdbClassifyInfo, err = data_manage.GetEdbClassifyById(parentClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上级分类信息失败,Err:" + err.Error())
+			return
+		}
 	}
 
-	// 权限校验
-	{
-		if edbClassifyInfo.ClassifyType == 0 { // 普通指标
-			button := GetEdbClassifyOpButton(sysUser, edbClassifyInfo.SysUserId)
-			if !button.MoveButton {
-				errMsg = "无操作权限"
-				err = errors.New(errMsg)
+	//如果有传入 上一个兄弟节点分类id
+	var (
+		edbClassifyInfo *data_manage.EdbClassify
+		prevClassify    *data_manage.EdbClassify
+		nextClassify    *data_manage.EdbClassify
+
+		edbInfo     *data_manage.EdbInfo
+		prevEdbInfo *data_manage.EdbInfo
+		nextEdbInfo *data_manage.EdbInfo
+		prevSort    int
+		nextSort    int
+	)
+
+	// 移动对象为分类, 判断权限
+	if edbInfoId == 0 {
+		edbClassifyInfo, err = data_manage.GetEdbClassifyById(classifyId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = "当前分类不存在"
+				err = errors.New("获取分类信息失败,Err:" + err.Error())
 				return
 			}
-		} else if edbClassifyInfo.ClassifyType == 1 { // 预测指标
-			button := GetPredictEdbClassifyOpButton(sysUser, edbClassifyInfo.SysUserId)
-			if !button.MoveButton {
-				errMsg = "无操作权限"
-				err = errors.New(errMsg)
-				return
+			errMsg = "移动失败"
+			err = errors.New("获取分类信息失败,Err:" + err.Error())
+			return
+		}
+		if parentClassifyId > 0 && parentEdbClassifyInfo.Level == 6 {
+			errMsg = "最高只支持添加6级分类"
+			err = errors.New(errMsg)
+			return
+		}
+		// 权限校验
+		{
+			if edbClassifyInfo.ClassifyType == 0 { // 普通指标
+				button := GetEdbClassifyOpButton(sysUser, edbClassifyInfo.SysUserId)
+				if !button.MoveButton {
+					errMsg = "无操作权限"
+					err = errors.New(errMsg)
+					return
+				}
+			} else if edbClassifyInfo.ClassifyType == 1 { // 预测指标
+				button := GetPredictEdbClassifyOpButton(sysUser, edbClassifyInfo.SysUserId)
+				if !button.MoveButton {
+					errMsg = "无操作权限"
+					err = errors.New(errMsg)
+					return
+				}
 			}
 		}
 
+	} else {
+		edbInfo, err = data_manage.GetEdbInfoById(req.EdbInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = "当前指标不存在"
+				err = errors.New("获取分类信息失败,Err:" + err.Error())
+				return
+			}
+			errMsg = "移动失败"
+			err = errors.New("获取分类信息失败,Err:" + err.Error())
+			return
+		}
+		if parentClassifyId == 0 {
+			errMsg = "移动失败,指标必须挂在分类下"
+			err = errors.New(errMsg)
+			return
+		}
+		// 移动权限校验
+		button := GetEdbOpButton(sysUser, edbInfo.SysUserId, edbInfo.EdbType, edbInfo.EdbInfoType)
+		if !button.MoveButton {
+			errMsg = "无操作权限"
+			err = errors.New(errMsg)
+			return
+		}
 	}
 
-	updateCol := make([]string, 0)
-
-	//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
-	if edbClassifyInfo.ParentId != parentClassifyId && parentClassifyId != 0 {
-		parentEdbClassifyInfo, tmpErr := data_manage.GetEdbClassifyById(parentClassifyId)
-		if tmpErr != nil {
+	if prevClassifyId > 0 {
+		prevClassify, err = data_manage.GetEdbClassifyById(prevClassifyId)
+		if err != nil {
 			errMsg = "移动失败"
-			err = errors.New("获取上级分类信息失败,Err:" + tmpErr.Error())
+			err = errors.New("获取上一个兄弟节点分类信息失败,Err:" + err.Error())
 			return
 		}
-		edbClassifyInfo.ParentId = parentEdbClassifyInfo.ClassifyId
-		edbClassifyInfo.Level = parentEdbClassifyInfo.Level + 1
-		edbClassifyInfo.ModifyTime = time.Now()
-		updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
+		prevSort = prevClassify.Sort
+	} else if prevEdbInfoId > 0 {
+		prevEdbInfo, err = data_manage.GetEdbInfoById(prevEdbInfoId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		prevSort = prevEdbInfo.Sort
 	}
 
-	//如果有传入 上一个兄弟节点分类id
-	if prevClassifyId > 0 {
-		prevClassify, tmpErr := data_manage.GetEdbClassifyById(prevClassifyId)
-		if tmpErr != nil {
+	if nextClassifyId > 0 {
+		//下一个兄弟节点
+		nextClassify, err = data_manage.GetEdbClassifyById(nextClassifyId)
+		if err != nil {
 			errMsg = "移动失败"
-			err = errors.New("获取上一个兄弟节点分类信息失败,Err:" + tmpErr.Error())
+			err = errors.New("获取下一个兄弟节点分类信息失败,Err:" + err.Error())
 			return
 		}
+		nextSort = nextClassify.Sort
+	} else if nextEdbInfoId > 0 {
+		//下一个兄弟节点
+		nextEdbInfo, err = data_manage.GetEdbInfoById(nextEdbInfoId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取下一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		nextSort = nextEdbInfo.Sort
+	}
 
-		//如果是移动在两个兄弟节点之间
-		if nextClassifyId > 0 {
-			//下一个兄弟节点
-			nextClassify, tmpErr := data_manage.GetEdbClassifyById(nextClassifyId)
-			if tmpErr != nil {
+	err, errMsg = moveEdbClassify(parentEdbClassifyInfo, edbClassifyInfo, prevClassify, nextClassify, edbInfo, prevEdbInfo, nextEdbInfo, parentClassifyId, prevSort, nextSort, classifyType)
+	return
+}
+
+// moveEdbClassify 移动指标分类
+func moveEdbClassify(parentEdbClassifyInfo, edbClassifyInfo, prevClassify, nextClassify *data_manage.EdbClassify, edbInfo, prevEdbInfo, nextEdbInfo *data_manage.EdbInfo, parentClassifyId int, prevSort, nextSort int, classifyType uint8) (err error, errMsg string) {
+	updateCol := make([]string, 0)
+
+	// 移动对象为分类, 判断分类是否存在
+	if edbClassifyInfo != nil {
+		oldParentId := edbClassifyInfo.ParentId
+		oldLevel := edbClassifyInfo.Level
+		var classifyIds []int
+		if oldParentId != parentClassifyId {
+			//更新子分类对应的level
+			childList, e, m := GetChildClassifyByClassifyId(edbClassifyInfo.ClassifyId)
+			if e != nil {
 				errMsg = "移动失败"
-				err = errors.New("获取下一个兄弟节点分类信息失败,Err:" + tmpErr.Error())
+				err = errors.New("查询子分类失败,Err:" + e.Error() + m)
 				return
 			}
-			//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
-			if prevClassify.Sort == nextClassify.Sort || prevClassify.Sort == edbClassifyInfo.Sort {
-				//变更兄弟节点的排序
-				updateSortStr := `sort + 2`
-				_ = data_manage.UpdateEdbClassifySortByParentId(prevClassify.ParentId, prevClassify.ClassifyId, prevClassify.Sort, updateSortStr, edbClassifyInfo.ClassifyType)
-			} else {
-				//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
-				if nextClassify.Sort-prevClassify.Sort == 1 {
+
+			if len(childList) > 0 {
+				for _, v := range childList {
+					if v.ClassifyId == edbClassifyInfo.ClassifyId {
+						continue
+					}
+					classifyIds = append(classifyIds, v.ClassifyId)
+				}
+			}
+		}
+		//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
+		if edbClassifyInfo.ParentId != parentClassifyId && parentClassifyId != 0 {
+			edbClassifyInfo.ParentId = parentEdbClassifyInfo.ClassifyId
+			edbClassifyInfo.RootId = parentEdbClassifyInfo.RootId
+			edbClassifyInfo.Level = parentEdbClassifyInfo.Level + 1
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "ParentId", "RootId", "Level", "ModifyTime")
+		} else if edbClassifyInfo.ParentId != parentClassifyId && parentClassifyId == 0 {
+			edbClassifyInfo.ParentId = 0
+			edbClassifyInfo.RootId = edbClassifyInfo.ClassifyId
+			edbClassifyInfo.Level = 1
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "ParentId", "RootId", "Level", "ModifyTime")
+		}
+
+		if prevSort > 0 {
+			//如果是移动在两个兄弟节点之间
+			if nextSort > 0 {
+				//下一个兄弟节点
+				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+				if prevSort == nextSort || prevSort == edbClassifyInfo.Sort {
 					//变更兄弟节点的排序
-					updateSortStr := `sort + 1`
-					_ = data_manage.UpdateEdbClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.Sort, updateSortStr, edbClassifyInfo.ClassifyType)
+					updateSortStr := `sort + 2`
+
+					//变更分类
+					if prevClassify != nil {
+						_ = data_manage.UpdateEdbClassifySortByParentId(parentClassifyId, prevClassify.ClassifyId, prevClassify.Sort, updateSortStr, classifyType)
+					} else {
+						_ = data_manage.UpdateEdbClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr, classifyType)
+					}
+
+					//变更指标
+					if prevEdbInfo != nil {
+						//变更兄弟节点的排序
+						_ = data_manage.UpdateEdbInfoSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.EdbInfoId, updateSortStr)
+					} else {
+						_ = data_manage.UpdateEdbInfoSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+					}
+				} else {
+					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+					if nextSort-prevSort == 1 {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 1`
+
+						//变更分类
+						if prevClassify != nil {
+							_ = data_manage.UpdateEdbClassifySortByParentId(parentClassifyId, prevClassify.ClassifyId, prevSort, updateSortStr, classifyType)
+						} else {
+							_ = data_manage.UpdateEdbClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr, classifyType)
+						}
+
+						//变更指标
+						if prevEdbInfo != nil {
+							//变更兄弟节点的排序
+							_ = data_manage.UpdateEdbInfoSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.EdbInfoId, updateSortStr)
+						} else {
+							_ = data_manage.UpdateEdbInfoSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+						}
+
+					}
+				}
+			}
+
+			edbClassifyInfo.Sort = prevSort + 1
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else if prevClassify == nil && nextClassify == nil && prevEdbInfo == nil && nextEdbInfo == nil && parentClassifyId > 0 {
+			//处理只拖动到目录里,默认放到目录底部的情况
+			var maxSort int
+			maxSort, err = GetEdbClassifyMaxSort(parentClassifyId, classifyType)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询组内排序信息失败,Err:" + err.Error())
+				return
+			}
+			edbClassifyInfo.Sort = maxSort + 1 //那就是排在组内最后一位
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else {
+			// 拖动到父级分类的第一位
+			firstClassify, tmpErr := data_manage.GetFirstEdbClassifyByParentId(parentClassifyId)
+			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+				errMsg = "移动失败"
+				err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = data_manage.UpdateEdbClassifySortByParentId(parentClassifyId, firstClassify.ClassifyId-1, 0, updateSortStr, edbClassifyInfo.ClassifyType)
+				//该分类下的所有指标也需要+1
+				_ = data_manage.UpdateEdbInfoSortByClassifyId(parentClassifyId, 0, 0, updateSortStr)
+			} else {
+				//如果该分类下存在指标,且第一个指标的排序等于0,那么需要调整排序
+				firstEdb, tErr := data_manage.GetFirstEdbInfoByClassifyId(parentClassifyId)
+				if tErr != nil && tErr.Error() != utils.ErrNoRow() {
+					errMsg = "移动失败"
+					err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tErr.Error())
+					return
+				}
+
+				//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+				if firstEdb != nil && firstEdb.Sort == 0 {
+					updateSortStr := ` sort + 1 `
+					_ = data_manage.UpdateEdbInfoSortByClassifyId(parentClassifyId, 0, firstEdb.EdbInfoId-1, updateSortStr)
+					_ = data_manage.UpdateEdbClassifySortByParentId(parentClassifyId, 0, 0, updateSortStr, classifyType)
 				}
 			}
+
+			edbClassifyInfo.Sort = 0 //那就是排在第一位
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
 		}
 
-		edbClassifyInfo.Sort = prevClassify.Sort + 1
-		edbClassifyInfo.ModifyTime = time.Now()
-		updateCol = append(updateCol, "Sort", "ModifyTime")
+		//更新
+		if len(updateCol) > 0 {
+			err = edbClassifyInfo.Update(updateCol)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("修改失败,Err:" + err.Error())
+				return
+			}
+			//更新对应分类的root_id和层级
+			if oldParentId != parentClassifyId {
+				if len(classifyIds) > 0 {
+					levelStep := edbClassifyInfo.Level - oldLevel
+					err = data_manage.UpdateEdbClassifyChildByParentClassifyId(classifyIds, edbClassifyInfo.RootId, levelStep)
+					if err != nil {
+						errMsg = "移动失败"
+						err = errors.New("更新子分类失败,Err:" + err.Error())
+						return
+					}
+				}
+			}
+		}
 	} else {
-		firstClassify, tmpErr := data_manage.GetFirstEdbClassifyByParentId(edbClassifyInfo.ParentId)
-		if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
-			errMsg = "移动失败"
-			err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+		if edbInfo == nil {
+			errMsg = "当前指标不存在"
+			err = errors.New(errMsg)
 			return
 		}
-
-		//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
-		if firstClassify != nil && firstClassify.Sort == 0 {
-			updateSortStr := ` sort + 1 `
-			_ = data_manage.UpdateEdbClassifySortByParentId(firstClassify.ParentId, firstClassify.ClassifyId-1, 0, updateSortStr, edbClassifyInfo.ClassifyType)
+		//如果改变了分类,那么移动该指标数据
+		if edbInfo.ClassifyId != parentClassifyId {
+			edbInfo.ClassifyId = parentClassifyId
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "ClassifyId", "ModifyTime")
 		}
+		if prevSort > 0 {
+			//如果是移动在两个兄弟节点之间
+			if nextSort > 0 {
+				//下一个兄弟节点
+				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+				if prevSort == nextSort || prevSort == edbInfo.Sort {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 2`
 
-		edbClassifyInfo.Sort = 0 //那就是排在第一位
-		edbClassifyInfo.ModifyTime = time.Now()
-		updateCol = append(updateCol, "Sort", "ModifyTime")
-	}
+					//变更分类
+					if prevClassify != nil {
+						_ = data_manage.UpdateEdbClassifySortByParentId(parentClassifyId, prevClassify.ClassifyId, prevClassify.Sort, updateSortStr, classifyType)
+					} else {
+						_ = data_manage.UpdateEdbClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr, classifyType)
+					}
 
-	//更新
-	if len(updateCol) > 0 {
-		err = edbClassifyInfo.Update(updateCol)
-		if err != nil {
-			errMsg = "移动失败"
-			err = errors.New("修改失败,Err:" + err.Error())
-			return
+					//变更指标
+					if prevEdbInfo != nil {
+						//变更兄弟节点的排序
+						_ = data_manage.UpdateEdbInfoSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.EdbInfoId, updateSortStr)
+					} else {
+						_ = data_manage.UpdateEdbInfoSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+					}
+				} else {
+					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+					if nextSort-prevSort == 1 {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 1`
+						//变更分类
+						if prevClassify != nil {
+							_ = data_manage.UpdateEdbClassifySortByParentId(parentClassifyId, prevClassify.ClassifyId, prevSort, updateSortStr, classifyType)
+						} else {
+							_ = data_manage.UpdateEdbClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr, classifyType)
+						}
+
+						//变更指标
+						if prevEdbInfo != nil {
+							//变更兄弟节点的排序
+							_ = data_manage.UpdateEdbInfoSortByClassifyId(parentClassifyId, prevSort, prevEdbInfo.EdbInfoId, updateSortStr)
+						} else {
+							_ = data_manage.UpdateEdbInfoSortByClassifyId(parentClassifyId, prevSort, 0, updateSortStr)
+						}
+					}
+				}
+			}
+
+			edbInfo.Sort = prevSort + 1
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else if prevClassify == nil && nextClassify == nil && prevEdbInfo == nil && nextEdbInfo == nil && parentClassifyId > 0 {
+			//处理只拖动到目录里,默认放到目录底部的情况
+			var maxSort int
+			maxSort, err = GetEdbClassifyMaxSort(parentClassifyId, classifyType)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询组内排序信息失败,Err:" + err.Error())
+				return
+			}
+			edbInfo.Sort = maxSort + 1 //那就是排在组内最后一位
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else {
+			// 拖动到父级分类的第一位
+			firstClassify, tmpErr := data_manage.GetFirstEdbClassifyByParentId(parentClassifyId)
+			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+				errMsg = "移动失败"
+				err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = data_manage.UpdateEdbClassifySortByParentId(parentClassifyId, firstClassify.ClassifyId-1, 0, updateSortStr, edbClassifyInfo.ClassifyType)
+				//该分类下的所有指标也需要+1
+				_ = data_manage.UpdateEdbInfoSortByClassifyId(parentClassifyId, 0, 0, updateSortStr)
+			} else {
+				//如果该分类下存在指标,且第一个指标的排序等于0,那么需要调整排序
+				firstEdb, tErr := data_manage.GetFirstEdbInfoByClassifyId(parentClassifyId)
+				if tErr != nil && tErr.Error() != utils.ErrNoRow() {
+					errMsg = "移动失败"
+					err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tErr.Error())
+					return
+				}
+
+				//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+				if firstEdb != nil && firstEdb.Sort == 0 {
+					updateSortStr := ` sort + 1 `
+					_ = data_manage.UpdateEdbInfoSortByClassifyId(parentClassifyId, 0, firstEdb.EdbInfoId-1, updateSortStr)
+					_ = data_manage.UpdateEdbClassifySortByParentId(parentClassifyId, 0, 0, updateSortStr, classifyType)
+				}
+			}
+
+			edbInfo.Sort = 0 //那就是排在第一位
+			edbInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		}
+
+		//更新
+		if len(updateCol) > 0 {
+			err = edbInfo.Update(updateCol)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("修改失败,Err:" + err.Error())
+				return
+			}
 		}
 	}
 	return
@@ -665,3 +1140,20 @@ func GetPredictEdbClassifyOpButton(sysUser *system.Admin, belongUserId int) (but
 
 	return
 }
+
+func GetEdbClassifyMaxSort(parentId int, classifyType uint8) (maxSort int, err error) {
+	//获取该层级下最大的排序数
+	classifyMaxSort, err := data_manage.GetEdbClassifyMaxSort(parentId, classifyType)
+	if err != nil {
+		return
+	}
+	maxSort = classifyMaxSort
+	edbMaxSort, err := data_manage.GetEdbInfoMaxSortByClassifyId(parentId)
+	if err != nil {
+		return
+	}
+	if maxSort < edbMaxSort {
+		maxSort = edbMaxSort
+	}
+	return
+}

+ 0 - 1
services/data/edb_data.go

@@ -228,7 +228,6 @@ func GetChartEdbSeasonalData(calendar, startDate, endDate string, edbInfo *data_
 				err = errors.New("获取农历数据失败,Err:" + tmpErr.Error())
 				return
 			}
-
 			// 处理季节图的截止日期
 			for k, edbDataItems := range result.List {
 				var cuttingDataTimestamp int64

+ 188 - 127
services/data/edb_info.go

@@ -2337,155 +2337,175 @@ func GetEdbChartClassifyList(source int) (resp data_manage.EdbChartClassifyResp,
 		}
 
 	case 3: //ETA指标库
-		rootList, e := data_manage.GetEdbClassifyByParentId(0, 0)
-		if e != nil && e.Error() != utils.ErrNoRow() {
-			err = e
-			return
-		}
-		classifyAll, e := data_manage.GetEdbClassifyAll()
+		//rootList, e := data_manage.GetEdbClassifyByParentId(0, 0)
+		//if e != nil && e.Error() != utils.ErrNoRow() {
+		//	err = e
+		//	return
+		//}
+
+		// 考虑到后面可以会迭代到10层, 这里直接用递归处理
+		classifyAll, e := data_manage.GetAllEdbClassifyByType(0)
 		if e != nil && e.Error() != utils.ErrNoRow() {
 			err = e
 			return
 		}
-		rootChildMap := make(map[int][]*data_manage.EdbClassifyItems)
-		for _, v := range classifyAll {
-			rootChildMap[v.ParentId] = append(rootChildMap[v.ParentId], v)
-		}
-		nodeAll := make([]*data_manage.EdbClassifyItems, 0)
-		for _, v := range rootList {
-			if existItems, ok := rootChildMap[v.ClassifyId]; ok {
-				v.Children = existItems
-			} else {
-				items := make([]*data_manage.EdbClassifyItems, 0)
-				v.Children = items
-			}
-			nodeAll = append(nodeAll, v)
-		}
+		resp.List = GetEdbMenuTreeRecursive(classifyAll, 0)
 
-		for _, v := range nodeAll {
-			item := data_manage.EdbChartClassify{
-				ClassifyId:   v.ClassifyId,
-				ClassifyName: v.ClassifyName,
-				ParentId:     v.ParentId,
-			}
-			for _, v2 := range v.Children {
-				child := data_manage.EdbChartClassify{
-					ClassifyId:   v2.ClassifyId,
-					ClassifyName: v2.ClassifyName,
-					ParentId:     v2.ParentId,
-				}
-				item.Child = append(item.Child, &child)
-			}
-			resp.List = append(resp.List, &item)
-		}
+		//rootChildMap := make(map[int][]*data_manage.EdbClassifyItems)
+		//for _, v := range classifyAll {
+		//	rootChildMap[v.ParentId] = append(rootChildMap[v.ParentId], v)
+		//}
+		//nodeAll := make([]*data_manage.EdbClassifyItems, 0)
+		//for _, v := range rootList {
+		//	if existItems, ok := rootChildMap[v.ClassifyId]; ok {
+		//		v.Children = existItems
+		//	} else {
+		//		items := make([]*data_manage.EdbClassifyItems, 0)
+		//		v.Children = items
+		//	}
+		//	nodeAll = append(nodeAll, v)
+		//}
+		//
+		//for _, v := range nodeAll {
+		//	item := data_manage.EdbChartClassify{
+		//		ClassifyId:   v.ClassifyId,
+		//		ClassifyName: v.ClassifyName,
+		//		ParentId:     v.ParentId,
+		//	}
+		//	for _, v2 := range v.Children {
+		//		child := data_manage.EdbChartClassify{
+		//			ClassifyId:   v2.ClassifyId,
+		//			ClassifyName: v2.ClassifyName,
+		//			ParentId:     v2.ParentId,
+		//		}
+		//		item.Child = append(item.Child, &child)
+		//	}
+		//	resp.List = append(resp.List, &item)
+		//}
 	case 4: //ETA预测指标
-		rootList, e := data_manage.GetEdbClassifyByParentId(0, 1)
+		// 考虑到后面可以会迭代到10层, 这里直接用递归
+		classifyAll, e := data_manage.GetAllEdbClassifyByType(1)
 		if e != nil && e.Error() != utils.ErrNoRow() {
 			err = e
 			return
 		}
-		nodeAll := make([]*data_manage.EdbClassifyItems, 0)
-		for _, v := range rootList {
-			nodeAll = append(nodeAll, v)
-		}
+		resp.List = GetEdbMenuTreeRecursive(classifyAll, 0)
 
-		for _, v := range nodeAll {
-			item := data_manage.EdbChartClassify{
-				ClassifyId:   v.ClassifyId,
-				ClassifyName: v.ClassifyName,
-				ParentId:     v.ParentId,
-			}
-			for _, v2 := range v.Children {
-				child := data_manage.EdbChartClassify{
-					ClassifyId:   v2.ClassifyId,
-					ClassifyName: v2.ClassifyName,
-					ParentId:     v2.ParentId,
-				}
-				item.Child = append(item.Child, &child)
-			}
-			resp.List = append(resp.List, &item)
-		}
+		//rootList, e := data_manage.GetEdbClassifyByParentId(0, 1)
+		//if e != nil && e.Error() != utils.ErrNoRow() {
+		//	err = e
+		//	return
+		//}
+		//nodeAll := make([]*data_manage.EdbClassifyItems, 0)
+		//for _, v := range rootList {
+		//	nodeAll = append(nodeAll, v)
+		//}
+		//
+		//for _, v := range nodeAll {
+		//	item := data_manage.EdbChartClassify{
+		//		ClassifyId:   v.ClassifyId,
+		//		ClassifyName: v.ClassifyName,
+		//		ParentId:     v.ParentId,
+		//	}
+		//	for _, v2 := range v.Children {
+		//		child := data_manage.EdbChartClassify{
+		//			ClassifyId:   v2.ClassifyId,
+		//			ClassifyName: v2.ClassifyName,
+		//			ParentId:     v2.ParentId,
+		//		}
+		//		item.Child = append(item.Child, &child)
+		//	}
+		//	resp.List = append(resp.List, &item)
+		//}
 	case 5: //图库
 
 		//判断是否存在缓存,如果存在缓存,那么直接从缓存中获取
-		key := utils.CACHE_CHART_CLASSIFY
-		if utils.Re == nil {
-			if utils.Re == nil && utils.Rc.IsExist(key) {
-				rep := new(data_manage.ChartClassifyListResp)
-				if data, err1 := utils.Rc.RedisBytes(key); err1 == nil {
-					e := json.Unmarshal(data, &rep)
-					if e == nil && rep != nil {
-						for _, v := range rep.AllNodes {
-							item := data_manage.EdbChartClassify{
-								ClassifyId:   v.ChartClassifyId,
-								ClassifyName: v.ChartClassifyName,
-								ParentId:     v.ParentId,
-							}
-							for _, v2 := range v.Children {
-								child := data_manage.EdbChartClassify{
-									ClassifyId:   v2.ChartClassifyId,
-									ClassifyName: v2.ChartClassifyName,
-									ParentId:     v2.ParentId,
-								}
-								item.Child = append(item.Child, &child)
-							}
-							resp.List = append(resp.List, &item)
-						}
-						fmt.Println("source redis")
-						return
-					}
-				}
-			}
-		}
-
-		rootList, e := data_manage.GetChartClassifyByParentIdFromETA(0)
-		if e != nil && e.Error() != utils.ErrNoRow() {
-			err = e
-			return
-		}
+		//key := utils.CACHE_CHART_CLASSIFY
+		//if utils.Re == nil {
+		//	if utils.Re == nil && utils.Rc.IsExist(key) {
+		//		rep := new(data_manage.ChartClassifyListResp)
+		//		if data, err1 := utils.Rc.RedisBytes(key); err1 == nil {
+		//			e := json.Unmarshal(data, &rep)
+		//			if e == nil && rep != nil {
+		//				for _, v := range rep.AllNodes {
+		//					item := data_manage.EdbChartClassify{
+		//						ClassifyId:   v.ChartClassifyId,
+		//						ClassifyName: v.ChartClassifyName,
+		//						ParentId:     v.ParentId,
+		//					}
+		//					for _, v2 := range v.Children {
+		//						child := data_manage.EdbChartClassify{
+		//							ClassifyId:   v2.ChartClassifyId,
+		//							ClassifyName: v2.ChartClassifyName,
+		//							ParentId:     v2.ParentId,
+		//						}
+		//						item.Child = append(item.Child, &child)
+		//					}
+		//					resp.List = append(resp.List, &item)
+		//				}
+		//				fmt.Println("source redis")
+		//				return
+		//			}
+		//		}
+		//	}
+		//}
 
-		classifyAll, e := data_manage.GetChartClassifyAllFromETA()
+		// 考虑到后面可以会迭代到10层, 这里直接用递归处理
+		classifyAll, e := data_manage.GetAllChartClassifyItemsBySource(1)
 		if e != nil && e.Error() != utils.ErrNoRow() {
 			err = e
 			return
 		}
+		resp.List = GetChartMenuTreeRecursive(classifyAll, 0)
 
-		rootChildMap := make(map[int][]*data_manage.ChartClassifyItems)
-		for _, v := range classifyAll {
-			rootChildMap[v.ParentId] = append(rootChildMap[v.ParentId], v)
-		}
-		nodeAll := make([]*data_manage.ChartClassifyItems, 0)
-		for _, v := range rootList {
-			if existItems, ok := rootChildMap[v.ChartClassifyId]; ok {
-				v.Children = existItems
-			} else {
-				items := make([]*data_manage.ChartClassifyItems, 0)
-				v.Children = items
-			}
-			nodeAll = append(nodeAll, v)
-		}
-
-		for _, v := range nodeAll {
-			item := data_manage.EdbChartClassify{
-				ClassifyId:   v.ChartClassifyId,
-				ClassifyName: v.ChartClassifyName,
-				ParentId:     v.ParentId,
-			}
-			for _, v2 := range v.Children {
-				child := data_manage.EdbChartClassify{
-					ClassifyId:   v2.ChartClassifyId,
-					ClassifyName: v2.ChartClassifyName,
-					ParentId:     v2.ParentId,
-				}
-				item.Child = append(item.Child, &child)
-			}
-			resp.List = append(resp.List, &item)
-		}
+		//rootList, e := data_manage.GetChartClassifyByParentIdFromETA(0)
+		//if e != nil && e.Error() != utils.ErrNoRow() {
+		//	err = e
+		//	return
+		//}
+		//
+		//classifyAll, e := data_manage.GetChartClassifyAllFromETA()
+		//if e != nil && e.Error() != utils.ErrNoRow() {
+		//	err = e
+		//	return
+		//}
+		//
+		//rootChildMap := make(map[int][]*data_manage.ChartClassifyItems)
+		//for _, v := range classifyAll {
+		//	rootChildMap[v.ParentId] = append(rootChildMap[v.ParentId], v)
+		//}
+		//nodeAll := make([]*data_manage.ChartClassifyItems, 0)
+		//for _, v := range rootList {
+		//	if existItems, ok := rootChildMap[v.ChartClassifyId]; ok {
+		//		v.Children = existItems
+		//	} else {
+		//		items := make([]*data_manage.ChartClassifyItems, 0)
+		//		v.Children = items
+		//	}
+		//	nodeAll = append(nodeAll, v)
+		//}
+		//
+		//for _, v := range nodeAll {
+		//	item := data_manage.EdbChartClassify{
+		//		ClassifyId:   v.ChartClassifyId,
+		//		ClassifyName: v.ChartClassifyName,
+		//		ParentId:     v.ParentId,
+		//	}
+		//	for _, v2 := range v.Children {
+		//		child := data_manage.EdbChartClassify{
+		//			ClassifyId:   v2.ChartClassifyId,
+		//			ClassifyName: v2.ChartClassifyName,
+		//			ParentId:     v2.ParentId,
+		//		}
+		//		item.Child = append(item.Child, &child)
+		//	}
+		//	resp.List = append(resp.List, &item)
+		//}
 
 		// 将数据加入缓存
-		if utils.Re == nil {
-			utils.Rc.Delete(key)
-		}
+		//if utils.Re == nil {
+		//	utils.Rc.Delete(key)
+		//}
 
 	default:
 		return
@@ -2573,6 +2593,14 @@ func EdbInfoAdd(source, classifyId int, edbCode, edbName, frequency, unit, start
 			}
 		}
 	}
+	//获取该层级下最大的排序数
+	maxSort, err := GetEdbClassifyMaxSort(classifyId, 0)
+	if err != nil {
+		errMsg = "获取失败"
+		err = errors.New("查询排序信息失败,Err:" + err.Error())
+		return
+	}
+
 	edbInfo.EdbCode = edbCode
 	edbInfo.EdbName = edbName
 	edbInfo.EdbNameSource = edbName
@@ -2586,6 +2614,7 @@ func EdbInfoAdd(source, classifyId int, edbCode, edbName, frequency, unit, start
 	edbInfo.CreateTime = time.Now()
 	edbInfo.ModifyTime = time.Now()
 	edbInfo.ServerUrl = serverUrl
+	edbInfo.Sort = maxSort + 1
 	edbInfo.DataDateType = `交易日`
 	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
 	edbInfo.UniqueCode = utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
@@ -3009,3 +3038,35 @@ func GetEdbChartAdminList(source int) (list []int, err error) {
 
 	return
 }
+
+// GetEdbMenuTreeRecursive 递归指标库菜单树
+func GetEdbMenuTreeRecursive(list []*data_manage.EdbClassifyItems, parentId int) []*data_manage.EdbChartClassify {
+	res := make([]*data_manage.EdbChartClassify, 0)
+	for _, v := range list {
+		t := new(data_manage.EdbChartClassify)
+		t.ClassifyId = v.ClassifyId
+		t.ClassifyName = v.ClassifyName
+		t.ParentId = v.ParentId
+		if v.ParentId == parentId {
+			t.Child = GetEdbMenuTreeRecursive(list, v.ClassifyId)
+			res = append(res, t)
+		}
+	}
+	return res
+}
+
+// GetChartMenuTreeRecursive 递归指标库菜单树
+func GetChartMenuTreeRecursive(list []*data_manage.ChartClassifyItems, parentId int) []*data_manage.EdbChartClassify {
+	res := make([]*data_manage.EdbChartClassify, 0)
+	for _, v := range list {
+		t := new(data_manage.EdbChartClassify)
+		t.ClassifyId = v.ChartClassifyId
+		t.ClassifyName = v.ChartClassifyName
+		t.ParentId = v.ParentId
+		if v.ParentId == parentId {
+			t.Child = GetChartMenuTreeRecursive(list, v.ChartClassifyId)
+			res = append(res, t)
+		}
+	}
+	return res
+}

+ 23 - 0
services/data/edb_info_calculate.go

@@ -1,6 +1,7 @@
 package data
 
 import (
+	"encoding/json"
 	"errors"
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/utils"
@@ -688,3 +689,25 @@ func handleDataByLinearRegression(edbInfoDataList []*data_manage.EdbDataList, ha
 func HandleDataByLinearRegression(edbInfoDataList []*data_manage.EdbDataList, handleDataMap map[string]float64) (err error) {
 	return handleDataByLinearRegression(edbInfoDataList, handleDataMap)
 }
+
+// CallCalculateComputeCorrelation 调用计算拟合残差的相关系数
+func CallCalculateComputeCorrelation(data *data_manage.EdbInfoCalculateBatchSaveReqByEdbLib) (val string, err error, errMsg string) {
+	errMsg = "计算失败"
+	// 调用指标库去更新
+	reqJson, err := json.Marshal(data)
+	if err != nil {
+		errMsg = "计算相关系数参数解析异常!"
+		err = errors.New("参数解析失败,Err:" + err.Error())
+		return
+	}
+	respItem, err := CalculateComputeCorrelation(string(reqJson))
+	if err != nil {
+		return
+	}
+
+	if respItem.Ret == 200 {
+		val = respItem.Data
+	}
+
+	return
+}

+ 39 - 28
services/data/future_good/chart_info.go

@@ -15,7 +15,7 @@ import (
 )
 
 // GetChartEdbData 获取图表的指标数据
-func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping, futureGoodEdbInfoMapping *data_manage.ChartEdbInfoMapping, barChartInfoDateList []data_manage.BarChartInfoDateReq, barChartInfoSort data_manage.BarChartInfoSortReq) (barConfigEdbInfoIdList []data_manage.BarChartInfoEdbItemReq, edbList []*data_manage.ChartEdbInfoMapping, xEdbIdValue []int, xDataList []data_manage.XData, yDataList []data_manage.YData, err error) {
+func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping, futureGoodEdbInfoMapping *data_manage.ChartEdbInfoMapping, barChartInfoDateList []data_manage.BarChartInfoDateReq, needData bool) (barConfigEdbInfoIdList []data_manage.BarChartInfoEdbItemReq, edbList []*data_manage.ChartEdbInfoMapping, xEdbIdValue []int, xDataList []data_manage.XData, yDataList []data_manage.YData, err error) {
 	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)
 
 	if futureGoodEdbInfoMapping == nil {
@@ -68,12 +68,6 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 
 	// 普通的指标数据
 	{
-		dataList := make([]*data_manage.EdbDataList, 0)
-
-		dataList, err = data_manage.GetEdbDataList(edbInfoMapping.Source, edbInfoMapping.EdbInfoId, startDate, endDate)
-		edbDataListMap[edbInfoMapping.EdbInfoId] = dataList
-		item.DataList = dataList
-
 		edbList = append(edbList, edbInfoMapping)
 
 		barConfigEdbInfoIdList = append(barConfigEdbInfoIdList, data_manage.BarChartInfoEdbItemReq{
@@ -180,27 +174,6 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 		})
 	}
 
-	// 获取数据
-	for _, v := range futureGoodMappingList {
-		dataList := make([]*data_manage.EdbDataList, 0)
-
-		tmpDataList, tmpErr := future_good2.GetFutureGoodEdbDataListByDate(v.EdbInfoId, startDate, endDate)
-		if tmpErr != nil {
-			return
-		}
-		for _, tmpData := range tmpDataList {
-			dataList = append(dataList, &data_manage.EdbDataList{
-				EdbDataId:     tmpData.FutureGoodEdbDataId,
-				EdbInfoId:     tmpData.FutureGoodEdbInfoId,
-				DataTime:      tmpData.DataTime.Format(utils.FormatDate),
-				DataTimestamp: tmpData.DataTimestamp,
-				Value:         tmpData.Close,
-			})
-		}
-		edbDataListMap[v.EdbInfoId] = dataList
-		v.DataList = dataList
-	}
-
 	if regionType == `海外` {
 		zlFutureGoodEdbMapping := &data_manage.ChartEdbInfoMapping{
 			EdbInfoId:           zlFutureGoodEdbInfo.FutureGoodEdbInfoId,
@@ -248,6 +221,44 @@ func GetChartEdbData(chartInfoId int, startDate, endDate string, edbInfoMapping,
 		edbList = append(edbList, futureGoodMappingList...)
 	}
 
+	if !needData {
+		return
+	}
+
+	// 获取数据
+
+	// 现货数据
+	{
+		dataList := make([]*data_manage.EdbDataList, 0)
+		dataList, err = data_manage.GetEdbDataList(edbInfoMapping.Source, edbInfoMapping.EdbInfoId, startDate, endDate)
+		if err != nil {
+			return
+		}
+		edbDataListMap[edbInfoMapping.EdbInfoId] = dataList
+		item.DataList = dataList
+	}
+
+	// 期货数据
+	for _, v := range futureGoodMappingList {
+		dataList := make([]*data_manage.EdbDataList, 0)
+
+		tmpDataList, tmpErr := future_good2.GetFutureGoodEdbDataListByDate(v.EdbInfoId, startDate, endDate)
+		if tmpErr != nil {
+			return
+		}
+		for _, tmpData := range tmpDataList {
+			dataList = append(dataList, &data_manage.EdbDataList{
+				EdbDataId:     tmpData.FutureGoodEdbDataId,
+				EdbInfoId:     tmpData.FutureGoodEdbInfoId,
+				DataTime:      tmpData.DataTime.Format(utils.FormatDate),
+				DataTimestamp: tmpData.DataTimestamp,
+				Value:         tmpData.Close,
+			})
+		}
+		edbDataListMap[v.EdbInfoId] = dataList
+		v.DataList = dataList
+	}
+
 	xEdbIdValue, yDataList, err = BarChartData(edbList[0], futureGoodEdbInfoList, edbDataListMap, barChartInfoDateList, regionType, latestDate)
 
 	xDataList = []data_manage.XData{

+ 1 - 1
services/data/line_equation/chart_info.go

@@ -142,7 +142,7 @@ func GetChartEdbData(chartInfoId int, lineChartInfoConfig request.LineChartInfoR
 	// 指标对应的所有数据
 	chartType := 1 //1:普通图,2:季节性图
 	calendar := "公历"
-	edbDataListMap, edbList, err := data.GetEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList)
+	edbDataListMap, edbList, err := data.GetEdbDataMapList(chartInfoId, chartType, calendar, startDate, endDate, mappingList, "")
 	if err != nil {
 		return
 	}

+ 5 - 5
services/data/line_feature/chart_info.go

@@ -19,7 +19,7 @@ func GetStandardDeviationData(chartInfoId int, startDate, endDate string, mappin
 	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)
 
 	// 指标对应的所有数据
-	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, startDate, endDate, []*data_manage.ChartEdbInfoMapping{mappingInfo})
+	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, startDate, endDate, []*data_manage.ChartEdbInfoMapping{mappingInfo}, "")
 	if err != nil {
 		return
 	}
@@ -92,7 +92,7 @@ func GetPercentileData(chartInfoId int, startDate, endDate string, mappingInfo *
 	}
 	calculateDay := calculateValue * moveUnitDays
 	// 指标对应的所有数据
-	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, startDate, endDate, []*data_manage.ChartEdbInfoMapping{mappingInfo})
+	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, startDate, endDate, []*data_manage.ChartEdbInfoMapping{mappingInfo}, "")
 	if err != nil {
 		return
 	}
@@ -184,7 +184,7 @@ func GetFrequencyDistributionData(chartInfoId int, mappingInfo *data_manage.Char
 	edbList = make([]*data_manage.ChartEdbInfoMapping, 0)
 
 	// 指标对应的所有数据
-	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, startDate, endDate, []*data_manage.ChartEdbInfoMapping{mappingInfo})
+	_, edbList, err = data.GetEdbDataMapList(chartInfoId, 1, `公历`, startDate, endDate, []*data_manage.ChartEdbInfoMapping{mappingInfo}, "")
 	if err != nil {
 		return
 	}
@@ -434,8 +434,8 @@ func AddChartInfo(req data_manage.AddChartInfoReq, edbInfoMapping *data_manage.C
 	chartInfo.DateType = req.DateType
 	chartInfo.StartDate = req.StartDate
 	chartInfo.EndDate = req.EndDate
-	chartInfo.SeasonStartDate = req.SeasonStartDate
-	chartInfo.SeasonEndDate = req.SeasonEndDate
+	chartInfo.SeasonStartDate = req.StartDate
+	chartInfo.SeasonEndDate = req.EndDate
 	chartInfo.ChartImage = req.ChartImage
 	chartInfo.LeftMin = req.LeftMin
 	chartInfo.LeftMax = req.LeftMax

+ 6 - 8
services/data/predict_edb_info.go

@@ -1009,13 +1009,6 @@ func GetChartDataList(dataList []*data_manage.EdbDataList, chartType int, calend
 		newStartDateReal = newStartDateReal.AddDate(-1, 0, 0)
 		startDateReal = newStartDateReal.Format(utils.FormatDate)
 	}
-
-	// 曲线图
-	if chartType == 1 {
-		resultDataList = dataList
-		return
-	}
-
 	//实际数据的截止日期
 	latestDate, tmpErr := time.Parse(utils.FormatDate, latestDateStr)
 	if tmpErr != nil {
@@ -1024,6 +1017,12 @@ func GetChartDataList(dataList []*data_manage.EdbDataList, chartType int, calend
 	}
 	latestDateYear := latestDate.Year() //实际数据截止年份
 
+	// 曲线图
+	if chartType == 1 {
+		resultDataList = dataList
+		return
+	}
+
 	if calendar == "农历" {
 		if len(dataList) <= 0 {
 			resultDataList = data_manage.EdbDataResult{}
@@ -1033,7 +1032,6 @@ func GetChartDataList(dataList []*data_manage.EdbDataList, chartType int, calend
 				err = errors.New("获取农历数据失败,Err:" + tmpErr.Error())
 				return
 			}
-
 			// 处理季节图的截止日期
 			for k, edbDataItems := range result.List {
 				var cuttingDataTimestamp int64

+ 2 - 3
services/data/supply_analysis/variety_edb.go

@@ -2,10 +2,10 @@ package supply_analysis
 
 import (
 	"errors"
-	"fmt"
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/models/data_manage/supply_analysis"
 	"eta/eta_api/utils"
+	"fmt"
 	"time"
 )
 
@@ -56,7 +56,6 @@ func GetChartEdbSeasonalData(varietyEdbId int, calendar, startDate, endDate, lat
 	if err != nil {
 		return
 	}
-
 	//latestDateStr := edbInfo.LatestDate //实际数据的截止日期
 	latestDate, tmpErr := time.Parse(utils.FormatDate, latestDateStr)
 	if tmpErr != nil {
@@ -108,7 +107,6 @@ func GetChartEdbSeasonalData(varietyEdbId int, calendar, startDate, endDate, lat
 				resultData = result
 			}
 		}
-
 	} else {
 		currentYear := time.Now().Year()
 
@@ -135,6 +133,7 @@ func GetChartEdbSeasonalData(varietyEdbId int, calendar, startDate, endDate, lat
 				quarterMap[year] = findVal
 			}
 		}
+
 		for _, v := range quarterArr {
 			itemList := quarterMap[v]
 			quarterItem := new(data_manage.QuarterData)

+ 13 - 5
services/data/trade_analysis/trade_analysis.go

@@ -222,16 +222,24 @@ func getZhengzhouClassifyName(code string) (name string) {
 		name = "苹果"
 		return
 	}
+	if strings.HasPrefix(code, "PX") {
+		name = "PX"
+		return
+	}
+	if strings.HasPrefix(code, "SH") {
+		name = "烧碱"
+		return
+	}
 	return
 }
 
 func GetPositionTopDetail(req trade_analysis.GetPositionTopReq) (ret trade_analysis.GetPositionTopResp, err error, errMsg string) {
 	//定义交易所
 	exchanges := map[string]string{
-		"郑商所":   "zhengzhou",
-		"大商所":   "dalian",
-		"上期所":   "shanghai",
-		"中金所":   "cffex",
+		"郑商所":  "zhengzhou",
+		"大商所":  "dalian",
+		"上期所":  "shanghai",
+		"中金所":  "cffex",
 		"上期能源": "ine",
 	}
 	exchange, ok := exchanges[req.Exchange]
@@ -263,7 +271,7 @@ func GetPositionTopDetail(req trade_analysis.GetPositionTopReq) (ret trade_analy
 		}
 	}
 	dataTimeStr = dataTime.Format(utils.FormatDate)
-	
+
 	ret.DataTime = dataTimeStr
 	ret.LastDataTime = lastDataTime.Format(utils.FormatDate)
 

+ 144 - 102
services/english_report.go

@@ -290,6 +290,74 @@ func UpdateEnglishReportEs(reportId int, publishState int) (err error) {
 	return
 }
 
+// ModifyAllEsEnglishReportVideo 批量修改es里的英文研报信息和线上路演信息
+func ModifyAllEsEnglishReportVideo() (err error) {
+	reportList, err := models.GetEnglishReportByCondition("", []interface{}{})
+	if err != nil {
+		return
+	}
+	for _, reportInfo := range reportList {
+		// 新增报告ES
+		esReport := &models.ElasticEnglishReportDetail{
+			Id:                 strconv.Itoa(reportInfo.Id),
+			ReportId:           reportInfo.Id,
+			Title:              reportInfo.Title,
+			Abstract:           reportInfo.Abstract,
+			BodyContent:        utils.TrimHtml(html.UnescapeString(reportInfo.Content)),
+			PublishTime:        reportInfo.PublishTime.Format(utils.FormatDateTime),
+			CreateTime:         reportInfo.CreateTime,
+			ReportCode:         reportInfo.ReportCode,
+			PublishState:       reportInfo.State,
+			Author:             reportInfo.Author,
+			Frequency:          reportInfo.Frequency,
+			ClassifyIdFirst:    reportInfo.ClassifyIdFirst,
+			ClassifyNameFirst:  reportInfo.ClassifyNameFirst,
+			ClassifyIdSecond:   reportInfo.ClassifyIdSecond,
+			ClassifyNameSecond: reportInfo.ClassifyNameSecond,
+			StageStr:           strconv.Itoa(reportInfo.Stage),
+			Overview:           utils.TrimHtml(html.UnescapeString(reportInfo.Overview)),
+			ContentSub:         utils.TrimHtml(html.UnescapeString(reportInfo.ContentSub)),
+		}
+		docId := fmt.Sprintf("%d", reportInfo.Id)
+		if err = EsAddOrEditEnglishReport(utils.EsEnglishReportIndexName, docId, esReport); err != nil {
+			return
+		}
+	}
+
+	videoList, err := models.GetEnglishVideoByCondition("", []interface{}{})
+	if err != nil {
+		return
+	}
+	for _, videoInfo := range videoList {
+		// 新增报告ES
+		esReport := &models.ElasticEnglishReportDetail{
+			Id:                 "v" + strconv.Itoa(videoInfo.Id),
+			VideoId:            videoInfo.Id,
+			Title:              videoInfo.Title,
+			Abstract:           videoInfo.Abstract,
+			BodyContent:        "",
+			PublishTime:        videoInfo.PublishTime.Format(utils.FormatDateTime),
+			CreateTime:         videoInfo.CreateTime.Format(utils.FormatDateTime),
+			ReportCode:         videoInfo.VideoCode,
+			PublishState:       videoInfo.State,
+			Author:             videoInfo.Author,
+			Frequency:          "",
+			ClassifyIdFirst:    videoInfo.ClassifyIdFirst,
+			ClassifyNameFirst:  videoInfo.ClassifyNameFirst,
+			ClassifyIdSecond:   videoInfo.ClassifyIdSecond,
+			ClassifyNameSecond: videoInfo.ClassifyNameSecond,
+			StageStr:           "",
+			Overview:           utils.TrimHtml(html.UnescapeString(videoInfo.Overview)),
+			ContentSub:         "",
+		}
+		docId := fmt.Sprintf("v%d", videoInfo.Id)
+		if err = EsAddOrEditEnglishReport(utils.EsEnglishReportIndexName, docId, esReport); err != nil {
+			return
+		}
+	}
+	return
+}
+
 // UpdateEnglishVideoEs 更新英文线上路演Es
 func UpdateEnglishVideoEs(videoId int, publishState int) (err error) {
 	if videoId <= 0 {
@@ -327,8 +395,7 @@ func UpdateEnglishVideoEs(videoId int, publishState int) (err error) {
 	return
 }
 
-func UpdateEnglishReportClassifyId(oldItem, newItem, newParent *models.EnglishClassify, classifyId int) (err error) {
-	//一级分类改为二级分类
+func UpdateEnglishReportClassifyId(oldItem, newItem *models.EnglishClassify, classifyId int) (err error) {
 	defer func() {
 		if err != nil {
 			go alarm_msg.SendAlarmMsg("英文报告分类改名-同步更新报告表字段及权限表关键词失败3, Err:"+err.Error(), 3)
@@ -338,64 +405,51 @@ func UpdateEnglishReportClassifyId(oldItem, newItem, newParent *models.EnglishCl
 	//如果二级分类更换了父级分类,则更新报告中的父级分类ID
 	//如果一级分类更换了名称,则更新所有报告中的一级分类名称
 	//如果二级分类更换了名称,则更新所有报告中的二级分类名称
-	if oldItem.ParentId != newItem.ParentId {
-		parentClassifyName := ""
-		parentId := 0
-
-		//二级分类改为一级分类, 或者二级分类的一级分类有变更
-		if newItem.ParentId > 0 {
-			parentClassifyName = newParent.ClassifyName
-			parentId = newParent.Id
-		}
-
-		// 更新报告表分类字段
-		var condition string
-		var pars []interface{}
-
-		condition += ` AND classify_id_second = ? `
-		pars = append(pars, classifyId)
-		list, e := models.GetEnglishReportByCondition(condition, pars)
-		if e != nil {
-			err = e
-			return
-		}
-		if len(list) > 0 {
-			//二级分类改为一级分类
-			if oldItem.ParentId > 0 && newItem.ParentId == 0 {
-				//查询该二级分类下是否存在关联报告,如果存在则不允许变更
-				err = fmt.Errorf("该分类有关联的报告,不允许变更为一级分类")
-				return
+	if oldItem.ParentId > 0 && oldItem.ParentId != newItem.ParentId {
+		//二级分类下的三级分类如果有报告,则该二级分类不允许改成一级分类
+		if oldItem.ParentId > 0 && oldItem.ParentId == oldItem.RootId {
+			if newItem.ParentId == 0 {
+				// 更新报告表分类字段
+				var condition string
+				var pars []interface{}
+
+				condition += ` AND classify_id_first = ? `
+				pars = append(pars, classifyId)
+				count, e := models.GetEnglishReportCountByCondition(condition, pars)
+				if e != nil {
+					err = e
+					return
+				}
+				if count > 0 {
+					err = fmt.Errorf("该分类有关联的报告,不允许变更为一级分类")
+					return
+				}
 			}
-		}
-		var idSlice []string
-		for _, report := range list {
-			idSlice = append(idSlice, strconv.Itoa(report.Id))
-		}
-		ids := strings.Join(idSlice, ",")
-		if ids != "" {
-			if err = models.UpdateEnglishReportByClassifyId(parentClassifyName, newItem.ClassifyName, parentId, oldItem.Id, ids); err != nil {
-				return
-			}
-		}
-	} else if oldItem.ClassifyName != newItem.ClassifyName {
-		if oldItem.ParentId > 0 {
-			//只对二级分类名称作了修改
-			// 更新报告表分类字段
-			if err = models.UpdateEnglishReportSecondClassifyNameByClassifyId(classifyId, newItem.ClassifyName); err != nil {
-				return
-			}
-		} else if oldItem.ParentId == 0 {
-			//只对一级分类名称作了修改
-			// 更新报告表分类字段
-			if err = models.UpdateEnglishReportFirstClassifyNameByClassifyId(classifyId, newItem.ClassifyName); err != nil {
-				return
+		} else if oldItem.ParentId > 0 && oldItem.ParentId != oldItem.RootId {
+			//三级分类下如果有报告,则该三级分类不允许改成二级或或者一级分类
+			if (newItem.ParentId > 0 && newItem.ParentId == newItem.RootId) || newItem.ParentId == 0 {
+				// 更新报告表分类字段
+				var condition string
+				var pars []interface{}
+
+				condition += ` AND classify_id_second = ? `
+				pars = append(pars, classifyId)
+				count, e := models.GetEnglishReportCountByCondition(condition, pars)
+				if e != nil {
+					err = e
+					return
+				}
+				if count > 0 {
+					err = fmt.Errorf("该分类有关联的报告,不允许变更为二级分类或者一级分类")
+					return
+				}
 			}
 		}
 	}
 	return
 }
 
-func UpdateEnglishVideoClassifyId(oldItem, newItem, newParent *models.EnglishClassify, classifyId int) (err error) {
+func UpdateEnglishVideoClassifyId(oldItem, newItem *models.EnglishClassify, classifyId int) (err error) {
 	//一级分类改为二级分类
 	defer func() {
 		if err != nil {
@@ -406,56 +460,44 @@ func UpdateEnglishVideoClassifyId(oldItem, newItem, newParent *models.EnglishCla
 	//如果二级分类更换了父级分类,则更新报告中的父级分类ID
 	//如果一级分类更换了名称,则更新所有报告中的一级分类名称
 	//如果二级分类更换了名称,则更新所有报告中的二级分类名称
-	if oldItem.ParentId != newItem.ParentId {
-		parentClassifyName := ""
-		parentId := 0
-		//二级分类改为一级分类, 或者二级分类的一级分类有变更
-		if newItem.ParentId > 0 {
-			parentClassifyName = newParent.ClassifyName
-			parentId = newParent.Id
-		}
-
-		// 更新报告表分类字段
-		var condition string
-		var pars []interface{}
-
-		condition += ` AND classify_id_second = ? `
-		pars = append(pars, classifyId)
-		list, e := models.GetEnglishVideoByCondition(condition, pars)
-		if e != nil {
-			err = e
-			return
-		}
-		if len(list) > 0 {
-			//二级分类改为一级分类
-			if oldItem.ParentId > 0 && newItem.ParentId == 0 {
-				//查询该二级分类下是否存在关联报告,如果存在则不允许变更
-				err = fmt.Errorf("该分类有关联的视频,不允许变更为一级分类")
-				return
-			}
-		}
-		var idSlice []string
-		for _, report := range list {
-			idSlice = append(idSlice, strconv.Itoa(report.Id))
-		}
-		ids := strings.Join(idSlice, ",")
-		if ids != "" {
-			if err = models.UpdateEnglishVideoByClassifyId(parentClassifyName, newItem.ClassifyName, parentId, oldItem.Id, ids); err != nil {
-				return
-			}
-		}
-	} else if oldItem.ClassifyName != newItem.ClassifyName {
-		if oldItem.ParentId > 0 {
-			//只对二级分类名称作了修改
-			// 更新报告表分类字段
-			if err = models.UpdateEnglishVideoSecondClassifyNameByClassifyId(classifyId, newItem.ClassifyName); err != nil {
-				return
+	if oldItem.ParentId > 0 && oldItem.ParentId != newItem.ParentId {
+		//二级分类下的三级分类如果有报告,则该二级分类不允许改成一级分类
+		if oldItem.ParentId > 0 && oldItem.ParentId == oldItem.RootId {
+			if newItem.ParentId == 0 {
+				// 更新报告表分类字段
+				var condition string
+				var pars []interface{}
+
+				condition += ` AND classify_id_first = ? `
+				pars = append(pars, classifyId)
+				count, e := models.GetEnglishVideoListCount(condition, pars)
+				if e != nil {
+					err = e
+					return
+				}
+				if count > 0 {
+					err = fmt.Errorf("该分类有关联的视频,不允许变更为一级分类")
+					return
+				}
 			}
-		} else if oldItem.ParentId == 0 {
-			//只对一级分类名称作了修改
-			// 更新报告表分类字段
-			if err = models.UpdateEnglishVideoFirstClassifyNameByClassifyId(classifyId, newItem.ClassifyName); err != nil {
-				return
+		} else if oldItem.ParentId > 0 && oldItem.ParentId != oldItem.RootId {
+			//三级分类下如果有报告,则该三级分类不允许改成二级或或者一级分类
+			if (newItem.ParentId > 0 && newItem.ParentId == newItem.RootId) || newItem.ParentId == 0 {
+				// 更新报告表分类字段
+				var condition string
+				var pars []interface{}
+
+				condition += ` AND classify_id_second = ? `
+				pars = append(pars, classifyId)
+				count, e := models.GetEnglishVideoListCount(condition, pars)
+				if e != nil {
+					err = e
+					return
+				}
+				if count > 0 {
+					err = fmt.Errorf("该分类有关联的视频,不允许变更为二级分类或者一级分类")
+					return
+				}
 			}
 		}
 	}

+ 2 - 7
services/english_video.go

@@ -125,14 +125,9 @@ func SaveEnglishVideo(req models.SaveEnglishVideoReq, adminInfo *system.Admin) (
 	firstClassify := new(models.EnglishClassify)
 	secondClassify := new(models.EnglishClassify)
 	for _, v := range classifyList {
-		if v.ClassifyType != 1 {
-			err = fmt.Errorf("分类类型出错")
-			errMsg = fmt.Sprintf("分类类型出错, Err: %v", err)
-			return
-		}
-		if v.ParentId == 0 {
+		if v.ParentId == v.RootId {
 			firstClassify = v
-		} else {
+		} else if v.ParentId > 0 {
 			secondClassify = v
 		}
 	}

+ 1 - 1
services/excel/lucky_sheet.go

@@ -774,7 +774,7 @@ func (item *LuckySheetData) ToExcel() (downloadFilePath string, err error) {
 func getDownloadPath() (fpath string, err error) {
 	dateDir := time.Now().Format("20060102")
 	uploadDir := "static/xls/" + dateDir
-	err = os.MkdirAll(uploadDir, 0766)
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		return
 	}

+ 15 - 6
services/file.go

@@ -14,7 +14,7 @@ import (
 func UploadToOssAndFileName(fileMulti multipart.File, newFileName string) (resourceUrl string, err error) {
 	dateDir := time.Now().Format("20060102")
 	uploadDir := utils.STATIC_DIR + "hongze/" + dateDir
-	err = os.MkdirAll(uploadDir, 766)
+	err = os.MkdirAll(uploadDir, utils.DIR_MOD)
 	if err != nil {
 		err = errors.New("存储目录创建失败,Err:" + err.Error())
 		return
@@ -27,11 +27,20 @@ func UploadToOssAndFileName(fileMulti multipart.File, newFileName string) (resou
 		err = errors.New("文件上传失败,Err:" + err.Error())
 		return
 	}
-	//上传到阿里云
-	resourceUrl, err = UploadAliyunV2(newFileName, fpath)
-	if err != nil {
-		err = errors.New("文件上传失败,Err:" + err.Error())
-		return
+
+	//上传到阿里云 和 minio
+	if utils.ObjectStorageClient == "minio" {
+		resourceUrl, err = UploadImgToMinIo(newFileName, fpath)
+		if err != nil {
+			err = errors.New("文件上传失败,Err:" + err.Error())
+			return
+		}
+	} else {
+		resourceUrl, err = UploadAliyunV2(newFileName, fpath)
+		if err != nil {
+			err = errors.New("文件上传失败,Err:" + err.Error())
+			return
+		}
 	}
 
 	defer func() {

+ 347 - 0
services/minio.go

@@ -0,0 +1,347 @@
+package services
+
+import (
+	"context"
+	"errors"
+	"eta/eta_api/utils"
+	"github.com/minio/minio-go/v7"
+	"github.com/minio/minio-go/v7/pkg/credentials"
+	"log"
+	"os"
+	"time"
+)
+
+func GetMinIOSTSToken() (item *Token, err error) {
+	// MinIO服务的访问信息
+	item = new(Token)
+	//useSSL := false
+	//if utils.MinIoUseSSL == "true" {
+	//	useSSL = true
+	//}
+	// 创建MinIO客户端
+	//minioClient, err := minio.New(utils.MinIoEndpoint, &minio.Options{
+	//	Creds:  credentials.NewStaticV4(utils.MinIoAccessKeyId, utils.MinIoAccessKeySecret, ""),
+	//	Secure: useSSL,
+	//})
+	//if err != nil {
+	//	return nil, err
+	//}
+	// 设置STS凭证请求参数
+	//policy := `{
+	//    "Version": "2012-10-17",
+	//    "Statement": [
+	//        {
+	//            "Sid": "",
+	//            "Effect": "Allow",
+	//            "Principal": {"AWS": "arn:aws:iam::1234567890:root"},
+	//            "Action": "s3:GetObject",
+	//            "Resource": "arn:aws:s3:::<YourBucketName>/*"
+	//        }
+	//    ]
+	//}`
+	//expiry := time.Hour * 24 // STS凭证的过期时间
+	//获取STS凭证
+	//stsCredentials, err := minioClient.PresignedPutObject(context.Background(), "etastatic", "myobject", expiry)
+	//if err != nil {
+	//	return
+	//}
+	item.AccessKeyId = utils.MinIoAccessKeyId
+	item.SecretKeyId = utils.MinIoAccessKeySecret
+	item.Endpoint = utils.MinIoEndpoint
+	item.ImgHost = utils.MinIoImghost
+	item.Bucketname = utils.MinIoBucketname
+	item.UseSSL = utils.MinIoUseSSL
+	item.RegionId = utils.MinIoRegion
+	item.Port = utils.MinIoPort
+	return
+}
+
+type Token struct {
+	AccessKeyId string
+	SecretKeyId string
+	RegionId    string
+	Bucketname  string
+	Endpoint    string
+	ImgHost     string
+	UseSSL      string
+	Port        string
+}
+
+func UploadMinIo() {
+	ctx := context.Background()
+	endpoint := "8.136.199.33:9000/"
+	accessKeyID := "LfQ8uiJiLP7vLxjRrmNW"
+	secretAccessKey := "IszGVHsNicJMQxHC46cYFtbrOiapo0ynwOIJ6c2R"
+	useSSL := false
+
+	// Initialize minio client object.
+	minioClient, err := minio.New(endpoint, &minio.Options{
+		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+		Secure: useSSL,
+	})
+	if err != nil {
+		log.Fatalln(err)
+	}
+
+	// Make a new bucket called mymusic.
+	bucketName := "etastatic"
+	location := "/"
+
+	err = minioClient.MakeBucket(ctx, bucketName, minio.MakeBucketOptions{Region: location})
+	if err != nil {
+		// Check to see if we already own this bucket (which happens if you run this twice)
+		exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+		if errBucketExists == nil && exists {
+			log.Printf("We already own %s\n", bucketName)
+		} else {
+			log.Fatalln(err)
+		}
+	} else {
+		log.Printf("Successfully created %s\n", bucketName)
+	}
+	//buckets, err := minioClient.ListBuckets(ctx)
+	//for _, bucket := range buckets {
+	//	fmt.Println(bucket)
+	//}
+	// Upload the zip file
+	objectName := "1111.xlsx"
+	filePath := "/Users/xi/Desktop/1111.xlsx"
+	contentType := "application/xlsx"
+
+	// Upload the zip file with FPutObject
+	info, err := minioClient.FPutObject(ctx, bucketName, objectName, filePath, minio.PutObjectOptions{ContentType: contentType})
+	if err != nil {
+		log.Fatalln(err)
+	}
+
+	log.Printf("Successfully uploaded %s of size %d\n", objectName, info.Size)
+}
+
+//UploadImgToMinIo 图片上传
+func UploadImgToMinIo(fileName, filePath string) (string, error) {
+	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+		return "0", errors.New("MinIo信息未配置")
+	}
+
+	ctx := context.Background()
+	endpoint := utils.MinIoEndpoint
+	accessKeyID := utils.MinIoAccessKeyId
+	secretAccessKey := utils.MinIoAccessKeySecret
+	useSSL := false
+	if utils.MinIoUseSSL == "true" {
+		useSSL = true
+	}
+	minioClient, err := minio.New(endpoint, &minio.Options{
+		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+		Secure: useSSL,
+	})
+	if err != nil {
+		log.Fatalln(err)
+	}
+	bucketName := utils.MinIoBucketname
+	// Check to see if we already own this bucket (which happens if you run this twice)
+	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+	if errBucketExists == nil && exists {
+		log.Printf("We already own %s\n", bucketName)
+	} else {
+		log.Fatalln(err)
+	}
+	path := utils.MinIoUpload_Audio_Dir + time.Now().Format("200601/20060102/")
+	path += fileName
+	// Upload the zip file with FPutObject
+	//contentType := "application/xlsx"
+	_, err = minioClient.FPutObject(ctx, bucketName, path, filePath, minio.PutObjectOptions{})
+	if err != nil {
+		log.Fatalln(err)
+	}
+
+	path = utils.MinIoImghost + path
+	return path, err
+}
+
+// UploadAudioToMinIo 音频上传
+func UploadAudioToMinIo(fileName, filePath string) (string, error) {
+	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+		return "0", errors.New("MinIo信息未配置")
+	}
+
+
+	ctx := context.Background()
+	endpoint := utils.MinIoEndpoint
+	accessKeyID := utils.MinIoAccessKeyId
+	secretAccessKey := utils.MinIoAccessKeySecret
+	useSSL := false
+	if utils.MinIoUseSSL == "true" {
+		useSSL = true
+	}
+	minioClient, err := minio.New(endpoint, &minio.Options{
+		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+		Secure: useSSL,
+	})
+	if err != nil {
+		log.Fatalln(err)
+		return "1", err
+	}
+	bucketName := utils.MinIoBucketname
+	// Check to see if we already own this bucket (which happens if you run this twice)
+	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+	if errBucketExists == nil && exists {
+		log.Printf("We already own %s\n", bucketName)
+	} else {
+		log.Fatalln(err)
+		return "2", err
+	}
+
+	path := utils.MinIoUpload_Audio_Dir + time.Now().Format("200601/20060102/")
+	path += fileName
+
+	// Upload the zip file with FPutObject
+	//contentType := "application/xlsx"
+	_, err = minioClient.FPutObject(ctx, bucketName, fileName, filePath, minio.PutObjectOptions{})
+	if err != nil {
+		log.Fatalln(err)
+		return "3", err
+	}
+
+	path = utils.MinIoImghost + path
+	return path, err
+}
+
+// UploadVideoToMinIo 视频上传
+func UploadVideoToMinIo(filename, filePath, savePath string) error {
+	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+		return errors.New("MinIo信息未配置")
+	}
+	defer func() {
+		os.Remove(filePath)
+	}()
+
+	ctx := context.Background()
+	endpoint := utils.MinIoEndpoint
+	accessKeyID := utils.MinIoAccessKeyId
+	secretAccessKey := utils.MinIoAccessKeySecret
+	useSSL := false
+	if utils.MinIoUseSSL == "true" {
+		useSSL = true
+	}
+	minioClient, err := minio.New(endpoint, &minio.Options{
+		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+		Secure: useSSL,
+	})
+	if err != nil {
+		log.Fatalln(err)
+		return err
+	}
+	bucketName := utils.MinIoBucketname
+	// Check to see if we already own this bucket (which happens if you run this twice)
+	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+	if errBucketExists == nil && exists {
+		log.Printf("We already own %s\n", bucketName)
+	} else {
+		log.Fatalln(err)
+		return err
+	}
+
+	//path := utils.Upload_Audio_Dir + time.Now().Format("200601/20060102/")
+	//path += filename
+	_, err = minioClient.FPutObject(ctx, bucketName, savePath, filePath, minio.PutObjectOptions{})
+	if err != nil {
+		log.Fatalln(err)
+		return err
+	}
+	//path = utils.Imghost + path
+	//return path,err
+	return err
+}
+
+// UploadFileToMinIo 上传文件
+func UploadFileToMinIo(filename, filePath, savePath string) error {
+	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+		return errors.New("MinIo信息未配置")
+	}
+	defer func() {
+		os.Remove(filePath)
+	}()
+	ctx := context.Background()
+	endpoint := utils.MinIoEndpoint
+	accessKeyID := utils.MinIoAccessKeyId
+	secretAccessKey := utils.MinIoAccessKeySecret
+	useSSL := false
+	if utils.MinIoUseSSL == "true" {
+		useSSL = true
+	}
+	minioClient, err := minio.New(endpoint, &minio.Options{
+		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+		Secure: useSSL,
+	})
+	if err != nil {
+		log.Fatalln(err)
+		return err
+	}
+	bucketName := utils.MinIoBucketname
+	// Check to see if we already own this bucket (which happens if you run this twice)
+	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+	if errBucketExists == nil && exists {
+		log.Printf("We already own %s\n", bucketName)
+	} else {
+		log.Fatalln(err)
+		return err
+	}
+	//path := utils.Upload_Audio_Dir + time.Now().Format("200601/20060102/")
+	//path += filename
+	_, err = minioClient.FPutObject(ctx, bucketName, savePath, filePath, minio.PutObjectOptions{})
+	if err != nil {
+		log.Fatalln(err)
+		return err
+	}
+	//path = utils.Imghost + path
+	//return path,err
+	return err
+}
+
+// UploadMinIoToDir 上传至hzchart
+func UploadMinIoToDir(filename, filePath, uploadDir, fileDir string) (string, error) {
+	if utils.MinIoAccessKeyId == `` || utils.MinIoAccessKeySecret == `` {
+		return "0", errors.New("MinIo信息未配置")
+	}
+	ctx := context.Background()
+	endpoint := utils.MinIoEndpoint
+	accessKeyID := utils.MinIoAccessKeyId
+	secretAccessKey := utils.MinIoAccessKeySecret
+	useSSL := false
+	if utils.MinIoUseSSL == "true" {
+		useSSL = true
+	}
+	minioClient, err := minio.New(endpoint, &minio.Options{
+		Creds:  credentials.NewStaticV4(accessKeyID, secretAccessKey, ""),
+		Secure: useSSL,
+	})
+	if err != nil {
+		log.Fatalln(err)
+		return "1",err
+	}
+	bucketName := utils.MinIoBucketname
+	// Check to see if we already own this bucket (which happens if you run this twice)
+	exists, errBucketExists := minioClient.BucketExists(ctx, bucketName)
+	if errBucketExists == nil && exists {
+		log.Printf("We already own %s\n", bucketName)
+	} else {
+		log.Fatalln(err)
+		return "2",err
+	}
+	if uploadDir == "" {
+		uploadDir = utils.MinIoUploadDir
+	}
+	if fileDir == "" {
+		fileDir = time.Now().Format("200601/20060102/")
+	}
+	path := uploadDir + fileDir
+	path += filename
+	_, err = minioClient.FPutObject(ctx, bucketName, path, filePath, minio.PutObjectOptions{})
+	if err != nil {
+		log.Fatalln(err)
+		return "3", err
+	}
+	path = utils.MinIoImghost + path
+	return path, err
+}

+ 25 - 3
services/ppt.go

@@ -3,13 +3,13 @@ package services
 import (
 	"encoding/json"
 	"errors"
-	"fmt"
 	"eta/eta_api/models"
 	"eta/eta_api/models/ppt_english"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/services/ppt2img"
 	"eta/eta_api/utils"
+	"fmt"
 	"sort"
 	"time"
 )
@@ -143,7 +143,29 @@ func pptContent2Html(content string, isEnglish bool) (htm string, err error) {
 	pageLen := len(contents)
 	htmlContent := ``
 	// iframe图表/表格域名
-	chartRoot := utils.PublicChartHost
+
+	// 获取基础配置, 若未配置则直接返回
+
+	// 获取配置好的短信模版
+	smsCond := ` AND conf_key = ? `
+	smsPars := make([]interface{}, 0)
+	smsPars = append(smsPars, "ChartViewUrl")
+	conf := new(models.BusinessConf)
+	conf, e := conf.GetItemByCondition(smsCond, smsPars)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			err = fmt.Errorf("请先配置公共图库的地址")
+			return
+		}
+		err = fmt.Errorf("获取聚合短信配置信息失败, Err: %s", e.Error())
+		return
+	}
+	if conf.ConfVal == "" {
+		err = fmt.Errorf("请先配置公共图库的地址")
+		return
+	}
+
+	chartRoot := conf.ConfVal
 	if pageLen > 0 {
 		htmlPrefix := `<p style="text-align: left; margin-top: 10px; font-size: 16px;">`
 		htmlSuffix := `</p>`
@@ -321,7 +343,7 @@ func SaveEnglishPPTReport(pptId, classifyIdFirst, classifyIdSecond int, title, a
 	}
 
 	// 分类
-	classifyList, e := models.GetAllEnglishClassify(0)
+	classifyList, e := models.GetAllEnglishClassify()
 	if e != nil {
 		errMsg = "转换失败"
 		err = errors.New("获取分类列表失败, Err: " + e.Error())

+ 15 - 0
services/ppt/ppt_english_group.go

@@ -424,13 +424,16 @@ func GetGroupPptEnglishList(groupId int64, adminId int) (ret ppt_english.RespGro
 							AdminRealName: pptV.AdminRealName,
 							IsSingleShare: 0,
 							PptCreateTime: pptInfo.CreateTime.Format(utils.FormatDateTime),
+							PptModifyTime: pptInfo.ModifyTime.Format(utils.FormatDateTime),
 							PptxUrl:       pptInfo.PptxUrl,
 							ReportId:      pptInfo.ReportId,
 							ReportCode:    pptInfo.ReportCode,
+							PublishTime:   utils.DealDateTimeZero(pptInfo.PublishTime, utils.FormatDateTime),
 						}
 						if pptV.ChildGroupPptId > 0 {
 							tmp.IsSingleShare = 1
 						}
+
 						list = append(list, tmp)
 						hasPpt[pptInfo.PptId] = struct{}{}
 					}
@@ -955,6 +958,8 @@ func GetEnglishGroupsByAdminId(adminId int) (ret ppt_english.RespGroupList, err
 				ReportId:      i.ReportId,
 				ReportCode:    i.ReportCode,
 				PptCreateTime: i.CreateTime.Format(utils.FormatDateTime),
+				PptModifyTime: i.ModifyTime.Format(utils.FormatDateTime),
+				PublishTime:   utils.DealDateTimeZero(i.PublishTime, utils.FormatDateTime),
 				PptPage:       pptPage,
 			}
 
@@ -1009,6 +1014,8 @@ func GetEnglishGroupsByAdminId(adminId int) (ret ppt_english.RespGroupList, err
 			ReportId:      v.ReportId,
 			ReportCode:    v.ReportCode,
 			PptCreateTime: v.CreateTime.Format(utils.FormatDateTime),
+			PptModifyTime: v.ModifyTime.Format(utils.FormatDateTime),
+			PublishTime:   utils.DealDateTimeZero(v.PublishTime, utils.FormatDateTime),
 			PptPage:       pptPage,
 		}
 		publicPptList = append(publicPptList, tmpV)
@@ -1083,6 +1090,8 @@ func GetEnglishGroupsByAdminId(adminId int) (ret ppt_english.RespGroupList, err
 				ReportId:      v.ReportId,
 				ReportCode:    v.ReportCode,
 				PptCreateTime: v.CreateTime.Format(utils.FormatDateTime),
+				PptModifyTime: v.ModifyTime.Format(utils.FormatDateTime),
+				PublishTime:   utils.DealDateTimeZero(v.PublishTime, utils.FormatDateTime),
 				PptPage:       pptPage,
 			}
 
@@ -1211,12 +1220,14 @@ func GetMyPptEnglishList(adminId int, keyword string) (ret ppt_english.RespGroup
 			BackgroundImg: v.BackgroundImg,
 			Title:         v.Title,
 			PptCreateTime: v.CreateTime.Format(utils.FormatDateTime),
+			PptModifyTime: v.ModifyTime.Format(utils.FormatDateTime),
 			AdminId:       v.AdminId,
 			AdminRealName: v.AdminRealName,
 			IsSingleShare: v.IsShare,
 			PptxUrl:       v.PptxUrl,
 			ReportId:      v.ReportId,
 			ReportCode:    v.ReportCode,
+			PublishTime:   utils.DealDateTimeZero(v.PublishTime, utils.FormatDateTime),
 		}
 		list = append(list, tmpV)
 	}
@@ -1266,12 +1277,14 @@ func GetSharePptEnglishList(adminId int, keyword string, isPrivate bool) (ret pp
 			BackgroundImg: v.BackgroundImg,
 			Title:         v.Title,
 			PptCreateTime: v.CreateTime.Format(utils.FormatDateTime),
+			PptModifyTime: v.ModifyTime.Format(utils.FormatDateTime),
 			AdminId:       v.AdminId,
 			AdminRealName: v.AdminRealName,
 			IsSingleShare: v.IsShare,
 			PptxUrl:       v.PptxUrl,
 			ReportId:      v.ReportId,
 			ReportCode:    v.ReportCode,
+			PublishTime:   utils.DealDateTimeZero(v.PublishTime, utils.FormatDateTime),
 		}
 		list = append(list, tmpV)
 	}
@@ -1324,6 +1337,7 @@ func GetGrantPptEnglishList(adminId int, keyword, sourceType string) (ret ppt_en
 			BackgroundImg: v.BackgroundImg,
 			Title:         v.Title,
 			PptCreateTime: v.CreateTime.Format(utils.FormatDateTime),
+			PptModifyTime: v.ModifyTime.Format(utils.FormatDateTime),
 			AdminId:       v.AdminId,
 			AdminRealName: v.AdminRealName,
 			PptVersion:    v.PptVersion,
@@ -1331,6 +1345,7 @@ func GetGrantPptEnglishList(adminId int, keyword, sourceType string) (ret ppt_en
 			PptxUrl:       v.PptxUrl,
 			ReportId:      v.ReportId,
 			ReportCode:    v.ReportCode,
+			PublishTime:   utils.DealDateTimeZero(v.PublishTime, utils.FormatDateTime),
 		}
 		list = append(list, tmpV)
 	}

+ 14 - 0
services/ppt/ppt_group.go

@@ -612,10 +612,12 @@ func GetGroupPptList(groupId int64, adminId int) (ret models.RespGroupPptList, e
 							AdminRealName: pptV.AdminRealName,
 							IsSingleShare: 0,
 							PptCreateTime: pptInfo.CreateTime.Format(utils.FormatDateTime),
+							PptModifyTime: pptInfo.ModifyTime.Format(utils.FormatDateTime),
 							PptxUrl:       pptInfo.PptxUrl,
 							PptVersion:    pptInfo.PptVersion,
 							ReportId:      pptInfo.ReportId,
 							ReportCode:    pptInfo.ReportCode,
+							PublishTime:   utils.DealDateTimeZero(pptInfo.PublishTime, utils.FormatDateTime),
 						}
 						if pptV.ChildGroupPptId > 0 {
 							tmp.IsSingleShare = 1
@@ -1291,6 +1293,8 @@ func GetGroupsByAdminIdV2(IsNewPpt, adminId int) (ret models.RespGroupList, err
 				ReportId:      i.ReportId,
 				ReportCode:    i.ReportCode,
 				PptCreateTime: i.CreateTime.Format(utils.FormatDateTime),
+				PptModifyTime: i.ModifyTime.Format(utils.FormatDateTime),
+				PublishTime:   utils.DealDateTimeZero(i.PublishTime, utils.FormatDateTime),
 				PptPage:       pptPage,
 			}
 
@@ -1346,6 +1350,8 @@ func GetGroupsByAdminIdV2(IsNewPpt, adminId int) (ret models.RespGroupList, err
 			ReportId:      v.ReportId,
 			ReportCode:    v.ReportCode,
 			PptCreateTime: v.CreateTime.Format(utils.FormatDateTime),
+			PptModifyTime: v.ModifyTime.Format(utils.FormatDateTime),
+			PublishTime:   utils.DealDateTimeZero(v.PublishTime, utils.FormatDateTime),
 			PptPage:       pptPage,
 		}
 		publicPptList = append(publicPptList, tmpV)
@@ -1420,6 +1426,8 @@ func GetGroupsByAdminIdV2(IsNewPpt, adminId int) (ret models.RespGroupList, err
 				ReportId:      v.ReportId,
 				ReportCode:    v.ReportCode,
 				PptCreateTime: v.CreateTime.Format(utils.FormatDateTime),
+				PptModifyTime: v.ModifyTime.Format(utils.FormatDateTime),
+				PublishTime:   utils.DealDateTimeZero(v.PublishTime, utils.FormatDateTime),
 				PptPage:       pptPage,
 			}
 			//如果只展示新版ppt,则过滤旧版的Ppt
@@ -1556,6 +1564,7 @@ func GetMyPptList(adminId int, keyword string) (ret models.RespGroupPptList, err
 			BackgroundImg: v.BackgroundImg,
 			Title:         v.Title,
 			PptCreateTime: v.CreateTime.Format(utils.FormatDateTime),
+			PptModifyTime: v.ModifyTime.Format(utils.FormatDateTime),
 			AdminId:       v.AdminId,
 			AdminRealName: v.AdminRealName,
 			PptVersion:    v.PptVersion,
@@ -1563,6 +1572,7 @@ func GetMyPptList(adminId int, keyword string) (ret models.RespGroupPptList, err
 			PptxUrl:       v.PptxUrl,
 			ReportId:      v.ReportId,
 			ReportCode:    v.ReportCode,
+			PublishTime:   utils.DealDateTimeZero(v.PublishTime, utils.FormatDateTime),
 		}
 		list = append(list, tmpV)
 	}
@@ -1612,6 +1622,7 @@ func GetSharePptList(adminId int, keyword string, isPrivate bool) (ret models.Re
 			BackgroundImg: v.BackgroundImg,
 			Title:         v.Title,
 			PptCreateTime: v.CreateTime.Format(utils.FormatDateTime),
+			PptModifyTime: v.ModifyTime.Format(utils.FormatDateTime),
 			AdminId:       v.AdminId,
 			AdminRealName: v.AdminRealName,
 			PptVersion:    v.PptVersion,
@@ -1619,6 +1630,7 @@ func GetSharePptList(adminId int, keyword string, isPrivate bool) (ret models.Re
 			PptxUrl:       v.PptxUrl,
 			ReportId:      v.ReportId,
 			ReportCode:    v.ReportCode,
+			PublishTime:   utils.DealDateTimeZero(v.PublishTime, utils.FormatDateTime),
 		}
 		list = append(list, tmpV)
 	}
@@ -1671,6 +1683,7 @@ func GetGrantPptList(adminId int, keyword, sourceType string) (ret models.RespGr
 			BackgroundImg: v.BackgroundImg,
 			Title:         v.Title,
 			PptCreateTime: v.CreateTime.Format(utils.FormatDateTime),
+			PptModifyTime: v.ModifyTime.Format(utils.FormatDateTime),
 			AdminId:       v.AdminId,
 			AdminRealName: v.AdminRealName,
 			PptVersion:    v.PptVersion,
@@ -1678,6 +1691,7 @@ func GetGrantPptList(adminId int, keyword, sourceType string) (ret models.RespGr
 			PptxUrl:       v.PptxUrl,
 			ReportId:      v.ReportId,
 			ReportCode:    v.ReportCode,
+			PublishTime:   utils.DealDateTimeZero(v.PublishTime, utils.FormatDateTime),
 		}
 		list = append(list, tmpV)
 	}

+ 29 - 9
services/report.go

@@ -636,14 +636,25 @@ func reportBase64ToImg(imageBase64 string) (resourceUrl string, err error) {
 	hzUploadDir := "static/images/"
 	savePath := hzUploadDir + time.Now().Format("200601/20060102/")
 	savePath += fileName
-	//上传到阿里云
-	err = UploadFileToAliyun(fileName, fpath, savePath)
-	if err != nil {
-		err = errors.New("文件上传失败" + err.Error())
-		return
+
+	//上传到阿里云 和 minio
+	if utils.ObjectStorageClient == "minio" {
+		err = UploadFileToMinIo(fileName, fpath, savePath)
+		if err != nil {
+			err = errors.New("文件上传失败" + err.Error())
+			return
+		}
+		resourceUrl = utils.MinIoImghost + savePath
+	} else {
+		err = UploadFileToAliyun(fileName, fpath, savePath)
+		if err != nil {
+			err = errors.New("文件上传失败" + err.Error())
+			return
+		}
+		resourceUrl = utils.Imghost + savePath
 	}
 
-	resourceUrl = utils.Imghost + savePath
+
 
 	item := new(models.Resource)
 	item.ResourceUrl = resourceUrl
@@ -863,9 +874,18 @@ func PcCreateAndUploadSunCode(scene, page string) (imgUrl string, err error) {
 	}()
 	// 上传OSS
 	fileDir := "yb/suncode/"
-	imgUrl, err = UploadAliyunToDir(fileName, fpath, "", fileDir)
-	if err != nil {
-		return
+
+	//上传到阿里云 和 minio
+	if utils.ObjectStorageClient == "minio" {
+		imgUrl, err = UploadMinIoToDir(fileName, fpath, "", fileDir)
+		if err != nil {
+			return
+		}
+	} else {
+		imgUrl, err = UploadAliyunToDir(fileName, fpath, "", fileDir)
+		if err != nil {
+			return
+		}
 	}
 
 	if err != nil {

+ 61 - 19
services/sms.go

@@ -2,6 +2,7 @@ package services
 
 import (
 	"encoding/json"
+	"eta/eta_api/models"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/utils"
 	"fmt"
@@ -27,8 +28,25 @@ func SendSmsCode(mobile, vCode, tplId string) (flag bool) {
 			go alarm_msg.SendAlarmMsg(tips, 2)
 		}
 	}()
-
-	result, e := sendSms(mobile, tplId, vCode)
+	// 获取配置好的短信模版
+	smsCond := ` AND conf_key = ? `
+	smsPars := make([]interface{}, 0)
+	smsPars = append(smsPars, "SmsJhgnAppKey")
+	conf := new(models.BusinessConf)
+	conf, e := conf.GetItemByCondition(smsCond, smsPars)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			err = fmt.Errorf("请先配置聚合短信Appkey")
+			return
+		}
+		err = fmt.Errorf("获取聚合短信配置信息失败, Err: %s", e.Error())
+		return
+	}
+	if conf.ConfVal == "" {
+		err = fmt.Errorf("请先配置聚合短信Appkey")
+		return
+	}
+	result, e := sendSms(conf.ConfVal, mobile, tplId, vCode)
 	if e != nil {
 		err = fmt.Errorf("send sms err: %s", e.Error())
 		return
@@ -58,7 +76,7 @@ func SendSmsCode(mobile, vCode, tplId string) (flag bool) {
 }
 
 // sendSms 发送国内短信
-func sendSms(mobile, tplId, code string) (rs []byte, err error) {
+func sendSms(jhGnAppKey, mobile, tplId, code string) (rs []byte, err error) {
 	var Url *url.URL
 	apiURL := "http://v.juhe.cn/sms/send"
 	//初始化参数
@@ -67,8 +85,8 @@ func sendSms(mobile, tplId, code string) (rs []byte, err error) {
 	param.Set("mobile", mobile) //接受短信的用户手机号码
 	param.Set("tpl_id", tplId)  //您申请的短信模板ID,根据实际情况修改
 	tplVal := fmt.Sprintf(`#code#=%s&#m#=%d`, code, utils.VerifyCodeExpireMinute)
-	param.Set("tpl_value", tplVal)     //您设置的模板变量,根据实际情况
-	param.Set("key", utils.JhGnAppKey) //应用APPKEY(应用详细页查询)
+	param.Set("tpl_value", tplVal) //您设置的模板变量,根据实际情况
+	param.Set("key", jhGnAppKey)   //应用APPKEY(应用详细页查询)
 
 	Url, err = url.Parse(apiURL)
 	if err != nil {
@@ -87,9 +105,34 @@ func sendSms(mobile, tplId, code string) (rs []byte, err error) {
 }
 
 // SendSmsCodeGj 发送国际短信
-func SendSmsCodeGj(mobile, vCode, areaNum string) bool {
-	flag := false
-	result, err := sendSmsGj(mobile, vCode, areaNum)
+func SendSmsCodeGj(mobile, vCode, areaNum, tplId string) bool {
+	var err error
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("短信验证码发送失败, Err: %s", err.Error())
+			utils.FileLog.Info("%s", tips)
+			go alarm_msg.SendAlarmMsg(tips, 2)
+		}
+	}()
+	// 获取配置好的短信模版
+	smsCond := ` AND conf_key = ? `
+	smsPars := make([]interface{}, 0)
+	smsPars = append(smsPars, "SmsJhgjAppKey")
+	conf := new(models.BusinessConf)
+	conf, e := conf.GetItemByCondition(smsCond, smsPars)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			err = fmt.Errorf("请先配置聚合短信Appkey")
+			return false
+		}
+		err = fmt.Errorf("获取聚合短信配置信息失败, Err: %s", e.Error())
+		return false
+	}
+	if conf.ConfVal == "" {
+		err = fmt.Errorf("请先配置聚合短信Appkey")
+		return false
+	}
+	result, err := sendSmsGj(conf.ConfVal, mobile, vCode, areaNum, tplId)
 	if err != nil {
 		fmt.Println("发送短信失败")
 		return false
@@ -98,34 +141,33 @@ func SendSmsCodeGj(mobile, vCode, areaNum string) bool {
 	var netReturn map[string]interface{}
 	err = json.Unmarshal(result, &netReturn)
 	if err != nil {
-		//go SendEmail("短信验证码发送失败", "err:"+err.Error()+" result"+string(result), utils.EmailSendToUsers)
-		go alarm_msg.SendAlarmMsg("短信验证码发送失败, Err:"+err.Error()+";Result:"+string(result), 2)
-		flag = false
+		err = fmt.Errorf("短信验证码发送失败, Err:" + err.Error() + ";Result:" + string(result))
+		return false
 	}
 	if netReturn["error_code"].(float64) == 0 {
 		fmt.Printf("接口返回result字段是:\r\n%v", netReturn["result"])
-		flag = true
+		return true
 	} else {
 		// 忽略错误的手机号码这种错误
 		if netReturn["error_code"].(float64) != 205401 {
-			go alarm_msg.SendAlarmMsg("短信验证码发送失败, Result:"+string(result), 2)
+			err = fmt.Errorf("短信验证码发送失败, Result:" + string(result))
 		}
-		flag = false
+		return false
 	}
-	return flag
 }
 
 // sendSmsGj 发送国际短信
-func sendSmsGj(mobile, code, areaNum string) (rs []byte, err error) {
+func sendSmsGj(jhGjAppKey, mobile, code, areaNum, tplId string) (rs []byte, err error) {
 	var Url *url.URL
 	apiURL := "http://v.juhe.cn/smsInternational/send.php"
 	//初始化参数
 	param := url.Values{}
 	//配置请求参数,方法内部已处理urlencode问题,中文参数可以直接传参
-	param.Set("mobile", mobile)           //接受短信的用户手机号码
-	param.Set("tplId", "10054")           //您申请的短信模板ID,根据实际情况修改
+	param.Set("mobile", mobile) //接受短信的用户手机号码
+	//param.Set("tplId", "10054")           //您申请的短信模板ID,根据实际情况修改
+	param.Set("tplId", tplId)             //您申请的短信模板ID,根据实际情况修改
 	param.Set("tplValue", "#code#="+code) //您设置的模板变量,根据实际情况
-	param.Set("key", utils.JhGjAppKey)    //应用APPKEY(应用详细页查询)
+	param.Set("key", jhGjAppKey)          //应用APPKEY(应用详细页查询)
 	param.Set("areaNum", areaNum)         //应用APPKEY(应用详细页查询)
 
 	Url, err = url.Parse(apiURL)

+ 11 - 0
services/task.go

@@ -465,3 +465,14 @@ func FixEnCompanyPermission() {
 	}
 	fmt.Println("修复完成")
 }
+
+// ModifyEsEnglishReport 批量修改es里的英文研报信息和线上路演信息
+func ModifyEsEnglishReport() {
+	fmt.Println("开始")
+	err := ModifyAllEsEnglishReportVideo()
+	if err != nil {
+		err = fmt.Errorf("重置es中的英文研报信息失败:ModifyAllEnglishReportInEs err: %s", err.Error())
+		return
+	}
+	fmt.Println("结束")
+}

+ 61 - 9
services/user_login.go

@@ -27,12 +27,43 @@ func SendAdminMobileVerifyCode(source int, mobile, areaCode string) (ok bool, er
 		err = fmt.Errorf("新增验证码记录失败, Err: %s", e.Error())
 		return
 	}
+	// 获取配置好的短信模版
+	smsCond := ` AND conf_key in (?,?) `
+	smsPars := make([]interface{}, 0)
+	smsPars = append(smsPars, "LoginSmsTpId", "LoginSmsGjTpId")
+	conf := new(models.BusinessConf)
+	confList, e := conf.GetItemsByCondition(smsCond, smsPars, []string{"conf_key", "conf_val"}, "")
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			err = fmt.Errorf("请先配置短信模版")
+			return
+		}
+		err = fmt.Errorf("获取短信模版失败, Err: %s", e.Error())
+		return
+	}
+
+	tplId := ""
+	gjTplId := ""
+	for _, v := range confList {
+		if v.ConfKey == "LoginSmsTpId" {
+			tplId = v.ConfVal
+		} else if v.ConfKey == "LoginSmsGjTpId" {
+			gjTplId = v.ConfVal
+		}
+	}
 
-	tplId := utils.SmsNewLoginTplId
+	if tplId == "" {
+		err = fmt.Errorf("请先配置短信模版")
+		return
+	}
 	if areaCode == "86" {
 		ok = SendSmsCode(mobile, verifyCode, tplId)
 	} else {
-		ok = SendSmsCodeGj(mobile, verifyCode, areaCode)
+		if gjTplId == "" {
+			err = fmt.Errorf("请先配置国际短信模版")
+			return
+		}
+		ok = SendSmsCodeGj(mobile, verifyCode, areaCode, gjTplId)
 	}
 	record.SendStatus = system.AdminVerifyCodeRecordStatusSuccess
 	if !ok {
@@ -79,24 +110,45 @@ func SendAdminEmailVerifyCode(source int, email string) (ok bool, err error) {
 	}
 
 	// 获取邮箱模板
-	confKey := "admin_verify_code_email_tmp"
-	confTmp, e := company.GetConfigDetailByCode(confKey)
+	// 获取配置好的短信模版
+	cond := ` AND (conf_key = ? OR conf_key = ?)`
+	pars := make([]interface{}, 0)
+	pars = append(pars, "LoginEmailTemplateSubject", "LoginEmailTemplateContent")
+	busiConf := new(models.BusinessConf)
+	emailConfList, e := busiConf.GetItemsByCondition(cond, pars, []string{"conf_key, conf_val"}, "")
 	if e != nil {
-		err = fmt.Errorf("获取邮件模板失败, Err: %s", e.Error())
+		if e.Error() == utils.ErrNoRow() {
+			err = fmt.Errorf("请先配置邮件模版")
+			return
+		}
+		err = fmt.Errorf("获取邮件模版失败, Err: %s", e.Error())
+		return
+	}
+	var emaiContent, emailSubject string
+	for _, v := range emailConfList {
+		if v.ConfKey == "LoginEmailTemplateContent" {
+			emaiContent = v.ConfVal
+		} else if v.ConfKey == "LoginEmailTemplateSubject" {
+			emailSubject = v.ConfVal
+		}
+	}
+	if emailSubject == "" {
+		err = fmt.Errorf("请先配置邮件模版主题")
 		return
 	}
-	if confTmp.ConfigValue == `` {
-		err = fmt.Errorf("邮件模板为空, 不可推送")
+	if emaiContent == "" {
+		err = fmt.Errorf("请先配置邮件模版内容")
 		return
 	}
 
 	req := new(EnglishReportSendEmailRequest)
-	req.Subject = "弘则研究登录验证"
+	req.Subject = emailSubject
 	req.Email = email
+	// todo 发信人昵称
 	req.FromAlias = conf.FromAlias
 	// 填充模板
 	t := time.Now().Format("2006年01月02日")
-	ct := confTmp.ConfigValue
+	ct := emaiContent
 	ct = strings.Replace(ct, "{{VERIFY_CODE}}", verifyCode, 1)
 	ct = strings.Replace(ct, "{{EXPIRED_MINUTE}}", strconv.Itoa(utils.VerifyCodeExpireMinute), 1)
 	ct = strings.Replace(ct, "{{DATE_TIME}}", t, 1)

+ 30 - 8
services/video.go

@@ -97,10 +97,21 @@ func CreateVideo(report *models.ReportDetail) (err error) {
 		}
 		time.Sleep(5 * time.Second)
 	}
-	uploadUrl, err := UploadAudioAliyun(saveName, savePath)
-	if err != nil {
-		err = errors.New("UploadAudioAliyun Err:" + err.Error())
-		return
+
+	uploadUrl := ``
+	//上传到阿里云 和 minio
+	if utils.ObjectStorageClient == "minio" {
+		uploadUrl, err = UploadAudioToMinIo(saveName, savePath)
+		if err != nil {
+			err = errors.New("UploadAudioAliyun Err:" + err.Error())
+			return
+		}
+	} else {
+		uploadUrl, err = UploadAudioAliyun(saveName, savePath)
+		if err != nil {
+			err = errors.New("UploadAudioAliyun Err:" + err.Error())
+			return
+		}
 	}
 
 	fileBody, err := ioutil.ReadFile(savePath)
@@ -288,10 +299,21 @@ func CreateReportVideo(reportTitle, reportContent, reportTime string) (uploadUrl
 		}
 		time.Sleep(5 * time.Second)
 	}
-	uploadUrl, err = UploadAudioAliyun(saveName, savePath)
-	if err != nil {
-		err = errors.New("UploadAudioAliyun Err:" + err.Error())
-		return
+
+
+	//上传到阿里云 和 minio
+	if utils.ObjectStorageClient == "minio" {
+		uploadUrl, err = UploadAudioToMinIo(saveName, savePath)
+		if err != nil {
+			err = errors.New("UploadAudioAliyun Err:" + err.Error())
+			return
+		}
+	} else {
+		uploadUrl, err = UploadAudioAliyun(saveName, savePath)
+		if err != nil {
+			err = errors.New("UploadAudioAliyun Err:" + err.Error())
+			return
+		}
 	}
 
 	fileBody, err := ioutil.ReadFile(savePath)

+ 70 - 1
utils/common.go

@@ -323,7 +323,7 @@ func DownloadImage(imgUrl string) (filePath string, err error) {
 func DownloadFile(fileUrl, fileDir string) (filePath string, err error) {
 	filePathDir := "./static/imgs/"
 	if fileDir != `` {
-		err = os.MkdirAll(fileDir, 0766)
+		err = os.MkdirAll(fileDir, DIR_MOD)
 		if err != nil {
 			return
 		}
@@ -1593,6 +1593,7 @@ func GetLeadUnitEn(unit string) (unitEn string) {
 }
 
 // GetDateByDateType 通过dateType获取需要的开始/结束日期
+// dateType 日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:起始日期至今 20:最近N年
 func GetDateByDateType(dateType int, tmpStartDate, tmpEndDate string) (startDate, endDate string) {
 	startDate = tmpStartDate
 	endDate = tmpEndDate
@@ -1643,6 +1644,67 @@ func GetDateByDateType(dateType int, tmpStartDate, tmpEndDate string) (startDate
 	return
 }
 
+func GetDateByDateTypeV2(dateType int, tmpStartDate, tmpEndDate string, startYear, yearMax int) (startDate, endDate string) {
+	startDate = tmpStartDate
+	endDate = tmpEndDate
+	switch dateType {
+	case 1:
+		startDate = "2000-01-01"
+		endDate = ""
+	case 2:
+		startDate = "2010-01-01"
+		endDate = ""
+	case 3:
+		startDate = "2015-01-01"
+		endDate = ""
+	case 4:
+		//startDate = strconv.Itoa(time.Now().Year()) + "-01-01"
+		startDate = "2021-01-01"
+		endDate = ""
+	case 5:
+		//startDate = startDate + "-01"
+		//endDate = endDate + "-01"
+	case 6:
+		//startDate = startDate + "-01"
+		endDate = ""
+	case 7:
+		startDate = "2018-01-01"
+		endDate = ""
+	case 8:
+		startDate = "2019-01-01"
+		endDate = ""
+	case 9:
+		startDate = "2020-01-01"
+		endDate = ""
+	case 11:
+		startDate = "2022-01-01"
+		endDate = ""
+	case DateTypeNYears:
+		if startYear == 0 { //默认取最近5年
+			startYear = 5
+		}
+		if yearMax == 0 {
+			return
+		}
+		startYear = startYear - 1
+		baseDate, _ := time.Parse(FormatDate, fmt.Sprintf("%d-01-01", yearMax))
+		startDate = baseDate.AddDate(-startYear, 0, 0).Format(FormatDate)
+		endDate = ""
+	}
+
+	// 兼容日期错误
+	{
+		if strings.Count(startDate, "-") == 1 {
+			startDate = startDate + "-01"
+		}
+		if strings.Count(endDate, "-") == 1 {
+			endDate = endDate + "-01"
+		}
+	}
+
+	return
+}
+
 func TimeTransferString(format string, t time.Time) string {
 	str := t.Format(format)
 	if t.IsZero() {
@@ -2072,3 +2134,10 @@ func FormatTableDataShowValue(x float64) (res string) {
 	}
 	return
 }
+
+func DealDateTimeZero(t time.Time, format string) (timeStr string) {
+	if !t.IsZero() {
+		timeStr = t.Format(format)
+	}
+	return
+}

+ 59 - 0
utils/config.go

@@ -122,6 +122,10 @@ var (
 	//XfVcn                   string //发言人
 )
 
+// 对象存储客户端
+var (
+	ObjectStorageClient string       // 目前有oss minio,默认oss
+)
 // 阿里云配置
 var (
 	Bucketname       string
@@ -174,6 +178,32 @@ var (
 // BusinessCode 商家编码
 var BusinessCode string
 
+// 日志配置
+var (
+	LogPath     string //调用过程中的日志存放地址
+	LogFile     string
+	LogDataPath string //调用过程中图表相关的日志存放地址
+	LogDataFile string
+	BinLogPath  string //数据库相关的日志存放地址
+	BinLogFile  string
+	ApiLogPath  string //接口请求地址和接口返回值日志存放地址
+	ApiLogFile  string
+)
+
+// MinIo配置
+var (
+	MinIoBucketname       string
+	MinIoEndpoint         string
+	MinIoImghost          string
+	MinIoUploadDir        string
+	MinIoUpload_Audio_Dir string
+	MinIoAccessKeyId      string
+	MinIoAccessKeySecret  string
+	MinIoUseSSL           string
+	MinIoPort             string
+	MinIoRegion           string
+)
+
 func init() {
 	tmpRunMode, err := web.AppConfig.String("run_mode")
 	if err != nil {
@@ -289,6 +319,18 @@ func init() {
 		ChatUrl = config["chat_url"]
 	}
 
+	//日志配置
+	{
+		LogPath = config["log_path"]
+		LogFile = config["log_file"]
+		LogDataPath = config["log_data_path"]
+		LogDataFile = config["log_data_file"]
+		BinLogPath = config["binlog_path"]
+		BinLogFile = config["binlog_file"]
+		ApiLogPath = config["apilog_path"]
+		ApiLogFile = config["apilog_file"]
+	}
+
 	// ES配置
 	{
 		ES_URL = config["es_url"]
@@ -316,6 +358,9 @@ func init() {
 		}
 	}
 
+	// 对象存储客户端
+	ObjectStorageClient = config["object_storage_client"]
+
 	// OSS相关
 	{
 		Endpoint = config["endpoint"]
@@ -390,6 +435,20 @@ func init() {
 	// 商家编码
 	BusinessCode = config["business_code"]
 
+	// MinIo相关
+	{
+		MinIoEndpoint = config["minio_endpoint"]
+		MinIoBucketname = config["minio_bucket_name"]
+		MinIoImghost = config["minio_img_host"]
+		MinIoUploadDir = config["minio_upload_dir"]
+		MinIoUpload_Audio_Dir = config["minio_upload_audio_dir"]
+		MinIoAccessKeyId = config["minio_access_key_id"]
+		MinIoAccessKeySecret = config["minio_access_key_secret"]
+		MinIoUseSSL = config["minio_use_ssl"]
+		MinIoPort = config["minio_port"]
+		MinIoRegion = config["minio_region"]
+	}
+
 	// 初始化ES
 	initEs()
 }

+ 16 - 0
utils/constants.go

@@ -1,5 +1,9 @@
 package utils
 
+import (
+	"io/fs"
+)
+
 // 常量定义
 const (
 	FormatTime                 = "15:04:05"                //时间格式
@@ -13,6 +17,7 @@ const (
 	EmptyDateTimeStr           = "0000-00-00 00:00:00"     //DateTime零值字符串
 	EmptyDateStr               = "0000-00-00"              //Date零值字符串
 	FormatMonthDayUnSpace      = "0102"                    //日期格式
+	FormatMonthDay             = "01-02"                   //日期格式
 	FormatYearMonthDate        = "2006-01"                 //日期格式
 	FormatYearDate             = "2006"                    //日期格式
 	PageSize15                 = 15                        //列表页每页数据量
@@ -23,6 +28,8 @@ const (
 	PageSize50                 = 50
 )
 
+const DateTypeNYears = 20 //时间类型为最近N年
+
 // 手机号,电子邮箱正则
 const (
 	RegularMobile = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0-9])|(17[0-9])|(16[0-9])|(19[0-9]))\\d{8}$" //手机号码
@@ -327,3 +334,12 @@ const (
 const CrmEtaAuthorization = "NIi1RbEmH0C2rksXtPGDPBBgRgTZY87Q"
 
 const LoginCacheTime = 60 // 登录缓存时长, 分钟
+
+// 对象存储客户端
+const (
+	STORAGESOURCE_OSS   = 1 //阿里云OSS
+	STORAGESOURCE_MINIO = 2 //MinIo
+)
+
+// DIR_MOD 目录创建权限
+const DIR_MOD fs.FileMode = 0766 // Unix permission bits

+ 126 - 23
utils/logs.go

@@ -3,37 +3,53 @@ package utils
 import (
 	"encoding/json"
 	"github.com/beego/beego/v2/core/logs"
+	"github.com/sirupsen/logrus"
+	"gopkg.in/natefinch/lumberjack.v2"
 	"os"
+	"path"
 )
 
-var FileLog *logs.BeeLogger
-var FileLogData *logs.BeeLogger
+const (
+	DefaultLogPath    = "./rdlucklog"
+	DefaultBinlogPath = "./binlog"
+	DefaultBinlogFile = "binlog.log"
+)
+
+var FileLog = logrus.New()
+var ApiLog = logrus.New()
+var FileLogData = logrus.New()
 var Binlog *logs.BeeLogger
 
 func init() {
-	FileLog = logs.NewLogger(1000000)
-	FileLog.SetLogger(logs.AdapterFile, `{"filename":"./rdlucklog/eta_api.log"}`)
-	FileLog.EnableFuncCallDepth(true)
+	logPath := LogPath
+	if logPath == "" {
+		logPath = DefaultLogPath
+	}
+	logFile := LogFile
+	if logFile == "" {
+		logFile = "eta_api.log"
+	}
+	os.MkdirAll(logPath, os.ModePerm)
 
-	FileLogData = logs.NewLogger(1000000)
-	FileLogData.SetLogger(logs.AdapterFile, `{"filename":"./rdlucklog/eta_api_data.log"}`)
+	// 打开文件
+	logFileName := path.Join(logPath, logFile)
+	// 使用滚动压缩方式记录日志
+	rolling(FileLog, logFileName)
+	//rolling(bLogFileName)
+	// 设置日志输出JSON格式
+	jsonFormat := new(logrus.JSONFormatter)
+	jsonFormat.DisableHTMLEscape = true
+	jsonFormat.TimestampFormat = HlbFormatDateTime
+	FileLog.SetFormatter(jsonFormat)
+	FileLog.SetReportCaller(true)
+	//LogInstance.SetFormatter(&logrus.TextFormatter{})
+	// 设置日志记录级别
+	//FileLog.SetLevel(logrus.DebugLevel)
 
-	//初始化binlog日志
+	//FileLog.Info("abc")
 	initBinlog()
-}
-
-func initBinlog() {
-	//binlog日志
-	binLogDir := `./binlog`
-	os.MkdirAll(binLogDir, os.ModePerm)
-	Binlog = logs.NewLogger(1000000)
-	logConfig := getDefaultLogConfig()
-	logConfig.FileName = "./binlog/binlog.log"
-	logConfig.MaxLines = 10000000
-	logConfig.Rotate = true
-	b, _ := json.Marshal(logConfig)
-	Binlog.SetLogger(logs.AdapterFile, string(b))
-	Binlog.EnableFuncCallDepth(true)
+	initApiLog()
+	initFileLogData()
 }
 
 type logConfig struct {
@@ -48,13 +64,100 @@ type logConfig struct {
 	//Perm     string `json:"perm" description:"日志文件权限"`
 }
 
+func initBinlog() {
+	//binlog日志
+	//binlog日志
+	binlogPath := BinLogPath
+	if binlogPath == "" {
+		binlogPath = DefaultBinlogPath
+	}
+	binlogFile := BinLogFile
+	if binlogFile == "" {
+		binlogFile = DefaultBinlogFile
+	}
+	os.MkdirAll(binlogPath, os.ModePerm)
+	logFileName := path.Join(binlogPath, binlogFile)
+	Binlog = logs.NewLogger(1000000)
+	logConf := getDefaultLogConfig()
+
+	logConf.FileName = logFileName
+	logConf.MaxLines = 10000000
+	logConf.Rotate = true
+	b, _ := json.Marshal(logConf)
+	Binlog.SetLogger(logs.AdapterFile, string(b))
+	Binlog.EnableFuncCallDepth(true)
+}
+
+func initApiLog() {
+	logPath := ApiLogPath
+	if logPath == "" {
+		if RunMode == "release" {
+			logPath = `/data/etalogs/eta_api`
+		} else {
+			logPath = `./rdlucklog/api`
+		}
+	}
+	logFile := ApiLogFile
+	if logFile == "" {
+		logFile = "eta_api_api.log"
+	}
+	os.MkdirAll(logPath, os.ModePerm)
+
+	// 打开文件
+	logFileName := path.Join(logPath, logFile)
+	// 使用滚动压缩方式记录日志
+	rolling(ApiLog, logFileName)
+	//rolling(bLogFileName)
+	// 设置日志输出JSON格式
+	jsonFormat := new(logrus.JSONFormatter)
+	jsonFormat.DisableHTMLEscape = true
+	jsonFormat.TimestampFormat = HlbFormatDateTime
+	ApiLog.SetFormatter(jsonFormat)
+}
+
+func initFileLogData() {
+	logPath := LogDataPath
+	if logPath == "" {
+		logPath = DefaultLogPath
+	}
+	logFile := LogDataFile
+	if logFile == "" {
+		logFile = "eta_api_data.log"
+	}
+	os.MkdirAll(logPath, os.ModePerm)
+
+	// 打开文件
+	logFileName := path.Join(logPath, logFile)
+	// 使用滚动压缩方式记录日志
+	rolling(FileLogData, logFileName)
+	//rolling(bLogFileName)
+	// 设置日志输出JSON格式
+	jsonFormat := new(logrus.JSONFormatter)
+	jsonFormat.DisableHTMLEscape = true
+	jsonFormat.TimestampFormat = HlbFormatDateTime
+	FileLogData.SetFormatter(jsonFormat)
+}
+
+// 日志滚动设置
+func rolling(fLog *logrus.Logger, logFile string) {
+	// 设置输出
+	fLog.SetOutput(&lumberjack.Logger{
+		Filename:   logFile, //日志文件位置
+		MaxSize:    100,     // 单文件最大容量,单位是MB
+		MaxBackups: 3,       // 最大保留过期文件个数
+		MaxAge:     7,       // 保留过期文件的最大时间间隔,单位是天
+		Compress:   true,    // 是否需要压缩滚动日志, 使用的 gzip 压缩
+		LocalTime:  true,
+	})
+}
+
 func getDefaultLogConfig() logConfig {
 	return logConfig{
 		FileName: "",
 		MaxLines: 0,
 		MaxSize:  1 << 28,
 		Daily:    true,
-		MaxDays:  31, //我就是喜欢31天,咋滴,不喜欢你就自己改-_-!
+		MaxDays:  7, //我就是喜欢31天,咋滴,不喜欢你就自己改-_-!
 		Rotate:   true,
 		Level:    logs.LevelTrace,
 		//Perm:     "",