浏览代码

Merge branch 'master' into feature/eta1.1.7

xyxie 1 年之前
父节点
当前提交
ab1402fce2
共有 51 个文件被更改,包括 1913 次插入316 次删除
  1. 16 6
      controllers/banner.go
  2. 98 51
      controllers/base_auth.go
  3. 88 10
      controllers/base_common.go
  4. 17 6
      controllers/cloud_disk.go
  5. 22 6
      controllers/data_manage/chart_info.go
  6. 1 1
      controllers/data_manage/edb_classify.go
  7. 8 7
      controllers/data_manage/edb_info.go
  8. 54 17
      controllers/data_manage/edb_info_calculate.go
  9. 13 4
      controllers/data_manage/excel_info.go
  10. 20 6
      controllers/data_manage/future_good/future_good_chart_info.go
  11. 62 6
      controllers/data_manage/predict_edb_info.go
  12. 114 0
      controllers/meeting_probabilities.go
  13. 20 7
      controllers/ppt_english.go
  14. 19 7
      controllers/ppt_v2.go
  15. 29 10
      controllers/report.go
  16. 116 42
      controllers/resource.go
  17. 13 13
      controllers/sys_admin.go
  18. 9 0
      controllers/sys_role.go
  19. 18 6
      controllers/voice.go
  20. 14 5
      go.mod
  21. 30 10
      go.sum
  22. 25 0
      models/data_manage/chart_info.go
  23. 17 4
      models/data_manage/edb_info.go
  24. 1 1
      models/data_manage/edb_info_calculate.go
  25. 54 0
      models/data_manage/request/meeting_probabilities.go
  26. 5 4
      models/data_manage/response/predit_edb_info.go
  27. 33 0
      models/meeting_probabilities.go
  28. 1 1
      models/ppt_english/ppt_english_grant.go
  29. 4 0
      models/ppt_english/ppt_english_group.go
  30. 1 0
      models/ppt_v2_grant.go
  31. 4 0
      models/ppt_v2_group.go
  32. 93 0
      models/sandbox/sandbox_classify.go
  33. 9 0
      routers/commentsRouter.go
  34. 5 0
      routers/router.go
  35. 1 1
      services/data/base_edb_lib.go
  36. 51 2
      services/data/chart_info.go
  37. 23 0
      services/data/edb_info_calculate.go
  38. 13 5
      services/data/trade_analysis/trade_analysis.go
  39. 14 5
      services/file.go
  40. 347 0
      services/minio.go
  41. 23 1
      services/ppt.go
  42. 15 0
      services/ppt/ppt_english_group.go
  43. 14 0
      services/ppt/ppt_group.go
  44. 29 9
      services/report.go
  45. 61 19
      services/sms.go
  46. 61 9
      services/user_login.go
  47. 30 8
      services/video.go
  48. 7 0
      utils/common.go
  49. 59 0
      utils/config.go
  50. 6 0
      utils/constants.go
  51. 126 27
      utils/logs.go

+ 16 - 6
controllers/banner.go

@@ -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
+}

+ 17 - 6
controllers/cloud_disk.go

@@ -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]

+ 22 - 6
controllers/data_manage/chart_info.go

@@ -2218,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
 	}
@@ -2228,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

+ 1 - 1
controllers/data_manage/edb_classify.go

@@ -763,7 +763,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()

+ 8 - 7
controllers/data_manage/edb_info.go

@@ -4281,7 +4281,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)
@@ -4301,7 +4301,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)
 			}
@@ -4310,8 +4310,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,
@@ -4319,7 +4319,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)
 					}
@@ -4328,10 +4328,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
 			}
 		}
 	}

+ 54 - 17
controllers/data_manage/edb_info_calculate.go

@@ -216,8 +216,54 @@ func (this *ChartInfoController) CalculateDetail() {
 		br.ErrMsg = "获取失败,Err:" + err.Error()
 		return
 	}
+
+	fullEdb := new(data_manage.EdbInfoFullClassify)
+	fullEdb.EdbInfo = edbInfo
+
+	// 拟合残差计算相关系数
+	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 +1918,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

+ 13 - 4
controllers/data_manage/excel_info.go

@@ -1140,10 +1140,19 @@ func UpdateExcelInfoFileUrl(excelInfo *data_manage.ExcelInfo) {
 		fmt.Println("err:", err)
 		return
 	}
-	//上传到阿里云
-	resourceUrl, err := services.UploadAliyunV2(fileName, downloadFilePath)
-	if err != nil {
-		return
+
+	resourceUrl := ``
+	//上传到阿里云 和 minio
+	if utils.ObjectStorageClient == "minio" {
+		resourceUrl, err = services.UploadImgToMinIo(fileName, downloadFilePath)
+		if err != nil {
+			return
+		}
+	} else {
+		resourceUrl, err = services.UploadAliyunV2(fileName, downloadFilePath)
+		if err != nil {
+			return
+		}
 	}
 	excelInfo.FileUrl = resourceUrl
 	err = excelInfo.Update([]string{"FileUrl"})

+ 20 - 6
controllers/data_manage/future_good/future_good_chart_info.go

@@ -2207,6 +2207,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
 	}
@@ -2217,13 +2219,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

+ 62 - 6
controllers/data_manage/predict_edb_info.go

@@ -966,12 +966,56 @@ func (this *PredictEdbInfoController) Detail() {
 		}
 	}
 
+	// 拟合残差计算相关系数
+	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,
 	}
 	br.Ret = 200
 	br.Success = true
@@ -1767,9 +1811,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() {
@@ -1785,6 +1831,9 @@ func (this *PredictEdbInfoController) ClassifyEdbInfoItems() {
 		return
 	}
 
+	// 是否只看我的
+	isShowMe, _ := this.GetBool("IsShowMe")
+
 	// 获取当前账号的不可见指标
 	obj := data_manage.EdbInfoNoPermissionAdmin{}
 	confList, err := obj.GetAllListByAdminId(this.SysUser.AdminId)
@@ -1798,7 +1847,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()
@@ -1811,6 +1866,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

+ 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"
+}
+

+ 20 - 7
controllers/ppt_english.go

@@ -516,14 +516,27 @@ 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)

+ 19 - 7
controllers/ppt_v2.go

@@ -517,14 +517,26 @@ 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)

+ 29 - 10
controllers/report.go

@@ -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() {
@@ -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() {

+ 116 - 42
controllers/resource.go

@@ -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() {
@@ -229,16 +239,29 @@ 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
@@ -394,16 +417,28 @@ 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,17 +581,30 @@ 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
@@ -677,12 +725,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 +778,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
 }

+ 13 - 13
controllers/sys_admin.go

@@ -526,13 +526,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 +747,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

+ 18 - 6
controllers/voice.go

@@ -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)
 	}()

+ 14 - 5
go.mod

@@ -20,18 +20,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.6.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 (
@@ -55,6 +58,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
@@ -67,13 +71,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
@@ -85,9 +94,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
@@ -96,11 +105,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.5.0 // indirect
+	golang.org/x/crypto v0.12.0 // indirect
 	golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 // indirect
-	golang.org/x/net v0.5.0 // indirect
-	golang.org/x/sys v0.4.0 // indirect
-	golang.org/x/text v0.6.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

+ 30 - 10
go.sum

@@ -115,6 +115,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=
@@ -214,6 +216,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=
@@ -248,6 +252,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=
@@ -268,6 +277,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=
@@ -350,6 +365,8 @@ github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTK
 github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
 github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
 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/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
@@ -366,8 +383,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=
@@ -437,8 +454,8 @@ 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-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
-golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
+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.0.0-20220413100746-70e8d0d3baa9 h1:LRtI4W37N+KFebI/qV0OFiLUv4GLOWeEW5hn/KEJvxE=
@@ -471,8 +488,8 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
-golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
+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=
@@ -509,8 +526,9 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
-golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -519,8 +537,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 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.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
-golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+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=
@@ -586,6 +604,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=

+ 25 - 0
models/data_manage/chart_info.go

@@ -1692,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:"是否展示趋势线"`
@@ -1735,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:"是否展示趋势线"`
@@ -1823,3 +1825,26 @@ func GetChartInfoAdminList() (items []int, err error) {
 	_, 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
+}

+ 17 - 4
models/data_manage/edb_info.go

@@ -48,6 +48,11 @@ type EdbInfo struct {
 	ManualSave       int     `description:"是否有手动保存过上下限: 0-否; 1-是"`
 }
 
+type EdbInfoFullClassify struct {
+	*EdbInfo
+	CorrelationStr string `description:"相关性系数字符串"`
+}
+
 func AddEdbInfo(item *EdbInfo) (lastId int64, err error) {
 	o := orm.NewOrmUsingDB("data")
 	lastId, err = o.Insert(item)
@@ -1710,11 +1715,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 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
 }
 

+ 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"`
+}

+ 5 - 4
models/data_manage/response/predit_edb_info.go

@@ -27,10 +27,11 @@ type PredictEdbInfoDataResp struct {
 // 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:"相关性系数字符串"`
 }
 
 // PredictEdbInfoChartDataResp 获取预测指标绘图数据返回

+ 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 {

+ 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
+}

+ 9 - 0
routers/commentsRouter.go

@@ -4714,6 +4714,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

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

+ 1 - 1
services/data/base_edb_lib.go

@@ -293,7 +293,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 不新增操作日志" `

+ 51 - 2
services/data/chart_info.go

@@ -535,7 +535,9 @@ 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)
@@ -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 {
@@ -1608,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 {
@@ -1869,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,
@@ -1880,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,

+ 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"
@@ -683,3 +684,25 @@ func handleDataByLinearRegression(edbInfoDataList []*data_manage.EdbDataList, ha
 
 	return
 }
+
+// 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
+}

+ 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)
 

+ 14 - 5
services/file.go

@@ -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
+}

+ 23 - 1
services/ppt.go

@@ -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>`

+ 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)

+ 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)

+ 7 - 0
utils/common.go

@@ -2134,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()
 }

+ 6 - 0
utils/constants.go

@@ -321,3 +321,9 @@ const (
 const CrmEtaAuthorization = "NIi1RbEmH0C2rksXtPGDPBBgRgTZY87Q"
 
 const LoginCacheTime = 60 // 登录缓存时长, 分钟
+
+// 对象存储客户端
+const (
+	STORAGESOURCE_OSS   = 1 //阿里云OSS
+	STORAGESOURCE_MINIO = 2 //MinIo
+)

+ 126 - 27
utils/logs.go

@@ -3,41 +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
-var FileLogChat *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)
-
-	FileLogData = logs.NewLogger(1000000)
-	FileLogData.SetLogger(logs.AdapterFile, `{"filename":"./rdlucklog/eta_api_data.log"}`)
+	logPath := LogPath
+	if logPath == "" {
+		logPath = DefaultLogPath
+	}
+	logFile := LogFile
+	if logFile == "" {
+		logFile = "eta_api.log"
+	}
+	os.MkdirAll(logPath, os.ModePerm)
 
-	FileLogChat = logs.NewLogger(1000000)
-	FileLogChat.SetLogger(logs.AdapterFile, `{"filename":"./rdlucklog/eta_api_chat.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 {
@@ -52,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:     "",