浏览代码

目录管理

xyxie 11 月之前
父节点
当前提交
184f2948c6

+ 27 - 0
cache/admin_operate_record_cache.go

@@ -0,0 +1,27 @@
+package cache
+
+import (
+	"eta/eta_forum_admin/models/system"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+)
+
+func AdminOperateRecord(adminId int, realName, uuid, uri, params, ip, userAgent, header string) bool {
+	record := new(system.AdminOperateRecord)
+	record.AdminId = adminId
+	record.RealName = realName
+	record.Uuid = uuid
+	record.Uri = uri
+	record.Params = params
+	record.Ip = ip
+	record.UserAgent = userAgent
+	record.Header = header
+	if utils.Re == nil {
+		err := utils.Rc.LPush(utils.CACHE_KEY_ADMIN_OPERATE_RECORD, record)
+		if err != nil {
+			fmt.Println("AdminOperateRecord LPush Err:" + err.Error())
+		}
+		return true
+	}
+	return false
+}

+ 14 - 0
cache/logs_cache.go

@@ -0,0 +1,14 @@
+package cache
+
+// record more information
+func RecordNewLogs(adminId int, request, response string, realName, operateIp, model string) bool {
+	/*log := &models.OperateLogs{AdminId: adminId, SysUserRealName: realName, OperateTime: time.Now(), Request: request, Response: response, OperateIp: operateIp, Model: model}
+	if utils.Re == nil {
+		err := utils.Rc.LPush(utils.CACHE_KEY_LOGS, log)
+		if err != nil {
+			fmt.Println("RecordNewLogs LPush Err:" + err.Error())
+		}
+		return true
+	}*/
+	return false
+}

+ 446 - 0
controllers/base_auth.go

@@ -0,0 +1,446 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/cache"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/system"
+	"eta/eta_forum_admin/services/alarm_msg"
+	"fmt"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/beego/beego/v2/server/web"
+
+	"eta/eta_forum_admin/utils"
+)
+
+type AfterHandle func(bodyByte []byte) error
+
+// AfterHandlerUrlMap 结束后待处理的url
+var AfterHandlerUrlMap = map[string][]AfterHandle{
+	/*"/adminapi/datamanage/chart_info/save":           {data.DeleteChartInfoDataRedis, data.DeleteChartClassifyRedis}, //图表保存接口
+	"/adminapi/datamanage/multiple_graph/chart/save": {data.DeleteChartInfoDataRedis, data.DeleteChartClassifyRedis}, //批量图表保存接口
+	//"/adminapi/datamanage/chart_info/detail": data.DeleteChartInfoDataRedis,
+	"/adminapi/datamanage/chart_info/edit":       {data.DeleteChartInfoDataRedis, data.DeleteChartClassifyRedis}, //图表编辑接口
+	"/adminapi/datamanage/chart_info/refresh":    {data.DeleteChartInfoDataRedis},                                //图表刷新接口
+	"/adminapi/datamanage/chart_info/add":        {data.DeleteChartClassifyRedis},                                //图表添加接口
+	"/adminapi/datamanage/chart_classify/delete": {data.DeleteChartInfoDataRedis, data.DeleteChartClassifyRedis}, //图表删除、图表分类删除
+	"/adminapi/datamanage/chart_classify/add":    {data.DeleteChartClassifyRedis},                                //新增图表分类接口
+	"/adminapi/datamanage/chart_classify/edit":   {data.DeleteChartClassifyRedis},                                //编辑图表分类接口
+	"/adminapi/datamanage/chart_classify/move":   {data.DeleteChartClassifyRedis},                                //移动图表分类接口
+	"/adminapi/datamanage/chart_info/move":       {data.DeleteChartClassifyRedis},                                //移动图表接口
+	"/adminapi/datamanage/chart_info/copy":       {data.DeleteChartClassifyRedis},                                //图表另存为接口*/
+}
+
+var AdminOperateRecordMap = map[string]string{
+	"/adminapi/pptv2/saveLog":                                        "/adminapi/pptv2/saveLog",
+	"/adminapi/report/saveReportContent":                             "/adminapi/report/saveReportContent",
+	"/adminapi/datamanage/chart_info/common/detail/from_unique_code": "/adminapi/datamanage/chart_info/common/detail/from_unique_code",
+	"/adminapi/english_report/saveReportContent":                     "/adminapi/english_report/saveReportContent",
+	"/adminapi/custom/message/listV2":                                "/adminapi/custom/message/listV2",
+	"/adminapi/sysuser/check_pwd":                                    "/adminapi/sysuser/check_pwd",
+	"/adminapi/system/menu/list":                                     "/adminapi/system/menu/list",
+}
+
+type BaseAuthController struct {
+	web.Controller
+	SysUser *system.Admin
+	Session *system.SysSession
+	Lang    string `description:"当前语言类型,中文:zh;英文:en;默认:zh"`
+}
+
+func (c *BaseAuthController) Prepare() {
+	//fmt.Println("enter prepare")
+	method := c.Ctx.Input.Method()
+	uri := c.Ctx.Input.URI()
+	//fmt.Println("Url:", uri)
+	if method != "HEAD" {
+		if method == "POST" || method == "GET" {
+			// 当前语言
+			{
+				lang := c.Ctx.Input.Header("Lang")
+				if lang == "" {
+					lang = utils.ZhLangVersion
+				}
+				c.Lang = lang
+			}
+
+			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)
+				}
+			}
+			if authorization == "" {
+				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 {
+			//	c.JSON(models.BaseResponse{Ret: 408, Msg: "请重新授权!", ErrMsg: "请重新授权:Token is empty or account is empty"}, false, false)
+			//	c.StopRun()
+			//	return
+			//}
+			tokenStr := authorization
+			tokenArr := strings.Split(tokenStr, "=")
+			token := tokenArr[1]
+
+			//accountStr := authorizationArr[1]
+			//accountArr := strings.Split(accountStr, "=")
+			//account := accountArr[1]
+
+			session, err := system.GetSysSessionByToken(token)
+			if err != nil {
+				if err.Error() == utils.ErrNoRow() {
+					c.JSON(models.BaseResponse{Ret: 408, Msg: "信息已变更,请重新登陆!", ErrMsg: "Token 信息已变更:Token: " + token}, false, false)
+					c.StopRun()
+					return
+				}
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "网络异常,请稍后重试!", ErrMsg: "获取用户信息异常,Eerr:" + err.Error()}, false, false)
+				c.StopRun()
+				return
+			}
+			if session == nil {
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "网络异常,请稍后重试!", ErrMsg: "sesson is empty "}, false, false)
+				c.StopRun()
+				return
+			}
+			//校验token是否合法
+			// JWT校验Token和Account
+			account := utils.MD5(session.UserName)
+			if !utils.CheckToken(account, token) {
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "鉴权失败,请重新登录!", ErrMsg: "登录失效,请重新登陆!,CheckToken Fail"}, false, false)
+				c.StopRun()
+				return
+			}
+			if time.Now().After(session.ExpiredTime) {
+				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() {
+					c.JSON(models.BaseResponse{Ret: 408, Msg: "信息已变更,请重新登陆!", ErrMsg: "获取admin 信息失败 " + strconv.Itoa(session.SysUserId)}, false, false)
+					c.StopRun()
+					return
+				}
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "网络异常,请稍后重试!", ErrMsg: "获取admin信息异常,Eerr:" + err.Error()}, false, false)
+				c.StopRun()
+				return
+			}
+			if admin == nil {
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "网络异常,请稍后重试!", ErrMsg: "admin is empty "}, false, false)
+				c.StopRun()
+				return
+			}
+			//如果不是启用状态
+			if admin.Enabled != 1 {
+				c.JSON(models.BaseResponse{Ret: 408, Msg: "账户信息异常!", ErrMsg: "账户被禁用,不允许登陆!,CheckToken Fail"}, false, false)
+				c.StopRun()
+				return
+			}
+
+			// 如果当前登录态是不可信设备的,那么需要做过期校验
+			if session.IsRemember != 1 {
+				loginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN, session.Id)
+				loginInfo, _ := utils.Rc.RedisString(loginKey)
+				if loginInfo == `` {
+					c.JSON(models.BaseResponse{Ret: 408, Msg: "超时未操作,系统自动退出!", ErrMsg: "超时未操作,系统自动退出!"}, false, false)
+					c.StopRun()
+					return
+				}
+
+				if loginInfo != "1" {
+					msg := `该账号于` + admin.LastLoginTime + "在其他网络登录。此客户端已退出登录。"
+					c.JSON(models.BaseResponse{Ret: 408, Msg: msg, ErrMsg: msg}, false, false)
+					c.StopRun()
+					return
+				}
+
+				// 如果是ETA体验版-更新活跃时长/更新登录时长的接口请求, 则不更新Token时长
+				if uri != `/adminapi/eta_trial/user/login_duration` && uri != `/adminapi/eta_trial/user/active` {
+					utils.Rc.Put(loginKey, "1", utils.LoginCacheTime*time.Minute)
+					// 不信任名单也同步更新
+					noTrustLoginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN_NO_TRUST, admin.AdminId)
+					utils.Rc.Put(noTrustLoginKey, session.Id, utils.LoginCacheTime*time.Minute)
+				}
+			}
+
+			admin.RoleTypeCode = GetSysUserRoleTypeCode(admin.RoleTypeCode)
+			c.SysUser = admin
+			c.Session = session
+
+			//接口权限校验
+			/*roleId := admin.RoleId
+			list, e := system.GetMenuButtonApisByRoleId(roleId)
+			if e != nil {
+				c.JSON(models.BaseResponse{Ret: 403, Msg: "获取接口权限出错!", ErrMsg: "获取接口权限出错!"}, false, false)
+				c.StopRun()
+				return
+			}
+			var api string
+			for _, v := range list {
+				api += v.Api + "&"
+			}
+			//处理uri请求,去除前缀和参数
+			api = strings.TrimRight(api, "&")
+			uri = strings.Replace(uri, "/adminapi", "", 1)
+			uris := strings.Split(uri, "?")
+			uri = uris[0]
+			//fmt.Println("uri:", uri)
+			apis := strings.Split(api, "&")
+			apiMap := make(map[string]bool, 0)
+			for _, s := range apis {
+				apiMap[s] = true
+			}
+			if !apiMap[uri] {
+				c.JSON(models.BaseResponse{Ret: 403, Msg: "无权访问!", ErrMsg: "无权访问!"}, false, false)
+				c.StopRun()
+				return
+			}*/
+		} else {
+			c.JSON(models.BaseResponse{Ret: 408, Msg: "请求异常,请联系客服!", ErrMsg: "POST之外的请求,暂不支持"}, false, false)
+			c.StopRun()
+			return
+		}
+	}
+}
+
+func (c *BaseAuthController) ServeJSON(encoding ...bool) {
+	urlPath := c.Ctx.Request.URL.Path
+	// 方法处理完后,需要后置处理的业务逻辑
+	if handlerList, ok := AfterHandlerUrlMap[urlPath]; ok {
+		for _, handler := range handlerList {
+			handler(c.Ctx.Input.RequestBody)
+		}
+	}
+
+	var (
+		hasIndent   = false
+		hasEncoding = false
+	)
+	if web.BConfig.RunMode == web.PROD {
+		hasIndent = false
+	}
+	if len(encoding) > 0 && encoding[0] == true {
+		hasEncoding = true
+	}
+	if c.Data["json"] == nil {
+		//go utils.SendEmail("异常提醒:", "接口:"+"URI:"+c.Ctx.Input.URI()+";无返回值", utils.EmailSendToUsers)
+		body := "接口:" + "URI:" + c.Ctx.Input.URI() + ";无返回值"
+		go alarm_msg.SendAlarmMsg(body, 1)
+		return
+	}
+	baseRes := c.Data["json"].(*models.BaseResponse)
+	if baseRes != nil && baseRes.Ret != 408 {
+		body, _ := json.Marshal(baseRes)
+		var requestBody string
+		method := c.Ctx.Input.Method()
+		if method == "GET" {
+			requestBody = c.Ctx.Request.RequestURI
+		} else {
+			//requestBody, _ = url.QueryUnescape(string(c.Ctx.Input.RequestBody))
+			requestBody = string(c.Ctx.Input.RequestBody)
+		}
+		if baseRes.Ret != 200 && baseRes.IsSendEmail {
+			//go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "URI:"+c.Ctx.Input.URI()+"<br/> "+"Params"+requestBody+" <br/>"+"ErrMsg:"+baseRes.ErrMsg+";<br/>Msg:"+baseRes.Msg+";<br/> Body:"+string(body)+"<br/>"+c.SysUser.RealName, utils.EmailSendToUsers)
+			body := "URI:" + c.Ctx.Input.URI() + "<br/> " + "Params" + requestBody + " <br/>" + "ErrMsg:" + baseRes.ErrMsg + ";<br/>Msg:" + baseRes.Msg + ";<br/> Body:" + string(body) + "<br/>" + c.SysUser.RealName
+			go alarm_msg.SendAlarmMsg(body, 1)
+		}
+		if baseRes.IsAddLog && c.SysUser != nil {
+			go cache.RecordNewLogs(c.SysUser.AdminId, requestBody, string(body), c.SysUser.RealName, c.Ctx.Input.IP(), c.Ctx.Input.URI())
+		}
+	}
+
+	//新增uuid记录
+	{
+		if _, ok := AdminOperateRecordMap[urlPath]; !ok && !strings.Contains(urlPath, "cygx") {
+			var adminId int
+			var realName, params, ip, uriStr string
+			ip = c.Ctx.Input.IP()
+			if c.SysUser != nil {
+				adminId = c.SysUser.AdminId
+				realName = c.SysUser.RealName
+			}
+			uuid := c.Ctx.Input.Header("Uuid")
+			userAgent := c.Ctx.Input.Header("User-Agent")
+			uri := c.Ctx.Input.URI()
+			uriStr, _ = url.PathUnescape(uri)
+			if uriStr != "" {
+				uri = uriStr
+			}
+			if c.Ctx.Input.Method() == "GET" {
+				paramsJson, _ := json.Marshal(c.Ctx.Input.Params())
+				params = string(paramsJson)
+			} else {
+				params = string(c.Ctx.Input.RequestBody)
+			}
+			header := c.Ctx.Request.Header
+			headerJson, _ := json.Marshal(header)
+			headerStr := string(headerJson)
+			go cache.AdminOperateRecord(adminId, realName, uuid, uri, params, ip, userAgent, headerStr)
+		}
+	}
+
+	c.JSON(c.Data["json"], hasIndent, hasEncoding)
+}
+
+func (c *BaseAuthController) JSON(data interface{}, hasIndent bool, coding bool) error {
+	c.Ctx.Output.Header("Content-Type", "application/json; charset=utf-8")
+	desEncrypt := utils.DesBase64Encrypt([]byte(utils.DesKey), utils.DesKeySalt)
+	c.Ctx.Output.Header("Dk", string(desEncrypt)) // des3加解密key
+	// 设置Cookie为HTTPOnly
+	c.Ctx.SetCookie("", "", -1, "/", "", false, true, "")
+	//fmt.Println(c.Ctx.ResponseWriter.Header().Get("Set-Cookie"))
+
+	var content []byte
+	var err error
+	if hasIndent {
+		content, err = json.MarshalIndent(data, "", "  ")
+	} else {
+		content, err = json.Marshal(data)
+	}
+	if err != nil {
+		http.Error(c.Ctx.Output.Context.ResponseWriter, err.Error(), http.StatusInternalServerError)
+		return err
+	}
+	ip := c.Ctx.Input.IP()
+	requestBody, err := url.QueryUnescape(string(c.Ctx.Input.RequestBody))
+	if err != nil {
+		requestBody = string(c.Ctx.Input.RequestBody)
+	}
+	if requestBody == "" {
+		requestBody = c.Ctx.Input.URI()
+	}
+	c.logUri(content, requestBody, ip)
+	// 如果不是debug分支的话,那么需要加密返回
+	if utils.RunMode != "debug" {
+		content = utils.DesBase64Encrypt(content, utils.DesKey)
+		// get请求时,不加双引号就获取不到数据,不知道什么原因,所以还是在前后加上双引号吧
+		content = []byte(`"` + string(content) + `"`)
+	}
+	if coding {
+		content = []byte(utils.StringsToJSON(string(content)))
+	}
+	return c.Ctx.Output.Body(content)
+}
+
+func GetSysUserRoleTypeCode(roleTypeCode string) string {
+	if roleTypeCode == utils.ROLE_TYPE_CODE_FICC_SELLER ||
+		roleTypeCode == utils.ROLE_TYPE_CODE_FICC_DEPARTMENT {
+		return utils.ROLE_TYPE_CODE_FICC_SELLER
+	}
+
+	if roleTypeCode == utils.ROLE_TYPE_CODE_RAI_SELLER ||
+		roleTypeCode == utils.ROLE_TYPE_CODE_RAI_DEPARTMENT {
+		return utils.ROLE_TYPE_CODE_RAI_SELLER
+	}
+
+	if roleTypeCode == utils.ROLE_TYPE_CODE_RAI_GROUP {
+		return utils.ROLE_TYPE_CODE_RAI_GROUP
+	}
+
+	if roleTypeCode == utils.ROLE_TYPE_CODE_ADMIN {
+		return utils.ROLE_TYPE_CODE_ADMIN
+	}
+	if roleTypeCode == utils.ROLE_TYPE_CODE_FICC_ADMIN {
+		return utils.ROLE_TYPE_CODE_FICC_ADMIN
+	}
+	if roleTypeCode == utils.ROLE_TYPE_CODE_FICC_TEAM {
+		return utils.ROLE_TYPE_CODE_FICC_TEAM
+	}
+	if roleTypeCode == utils.ROLE_TYPE_CODE_FICC_GROUP {
+		return utils.ROLE_TYPE_CODE_FICC_GROUP
+	}
+	if roleTypeCode == utils.ROLE_TYPE_CODE_RAI_ADMIN {
+		return utils.ROLE_TYPE_CODE_RAI_ADMIN
+	}
+	if roleTypeCode == utils.ROLE_TYPE_CODE_RESEARCHR {
+		return utils.ROLE_TYPE_CODE_FICC_RESEARCHR
+	}
+	if roleTypeCode == utils.ROLE_TYPE_CODE_FICC_RESEARCHR {
+		return utils.ROLE_TYPE_CODE_FICC_RESEARCHR
+	}
+	if roleTypeCode == utils.ROLE_TYPE_CODE_RAI_RESEARCHR {
+		return utils.ROLE_TYPE_CODE_RAI_RESEARCHR
+	}
+	//合规
+	if roleTypeCode == utils.ROLE_TYPE_CODE_COMPLIANCE {
+		return utils.ROLE_TYPE_CODE_COMPLIANCE
+	}
+	//财务
+	if roleTypeCode == utils.ROLE_TYPE_CODE_FINANCE {
+		return utils.ROLE_TYPE_CODE_FINANCE
+	}
+	return ""
+}
+
+func (c *BaseAuthController) logUri(respContent []byte, 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)
+				}
+			}
+		}
+	}
+
+	utils.ApiLog.Info("uri:%s, authorization:%s, requestBody:%s, responseBody:%s, ip:%s", c.Ctx.Input.URI(), authorization, requestBody, respContent, ip)
+	return
+}

+ 872 - 0
controllers/chart_classify.go

@@ -0,0 +1,872 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/system"
+	"eta/eta_forum_admin/services"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"sort"
+	"time"
+)
+
+// 数据管理-分类模块
+type ChartClassifyController struct {
+	BaseAuthController
+}
+
+// ChartClassifyListV2
+// @Title 图表分类列表
+// @Description 图表分类列表接口
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Success 200 {object} models.ChartClassifyListResp
+// @router /chart_classify/list [get]
+func (this *ChartClassifyController) ChartClassifyListV2() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	resp := new(models.ChartClassifyListResp)
+
+	//判断是否存在缓存,如果存在缓存,那么直接从缓存中获取
+	key := utils.CACHE_CHART_CLASSIFY
+	if utils.Re == nil {
+		if utils.Re == nil && utils.Rc.IsExist(key) {
+			if redisData, err1 := utils.Rc.RedisBytes(key); err1 == nil {
+				err := json.Unmarshal(redisData, &resp)
+				if err == nil && resp != nil {
+
+					br.Ret = 200
+					br.Success = true
+					br.Msg = "获取成功"
+					br.Data = resp
+					fmt.Println("source redis")
+					return
+				}
+			}
+		}
+	}
+
+	rootList, err := models.GetChartClassifyByParentId(0, utils.CHART_SOURCE_DEFAULT)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	classifyAll, err := models.GetChartClassifyAll(utils.CHART_SOURCE_DEFAULT)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	allChartInfo, err := models.GetChartInfoAll([]int{utils.CHART_SOURCE_DEFAULT})
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+		return
+	}
+
+	chartInfoMap := make(map[int][]*models.ChartClassifyItems)
+	for _, v := range allChartInfo {
+		chartInfoMap[v.ChartClassifyId] = append(chartInfoMap[v.ChartClassifyId], v)
+	}
+	rootChildMap := make(map[int][]*models.ChartClassifyItems)
+	for _, v := range classifyAll {
+		rootChildMap[v.ParentId] = append(rootChildMap[v.ParentId], v)
+		if existItems, ok := chartInfoMap[v.ChartClassifyId]; ok {
+			v.Children = existItems
+		} else {
+			items := make([]*models.ChartClassifyItems, 0)
+			v.Children = items
+		}
+	}
+	nodeAll := make([]*models.ChartClassifyItems, 0)
+	for _, v := range rootList {
+		if existItems, ok := rootChildMap[v.ChartClassifyId]; ok {
+			v.Children = existItems
+		} else {
+			items := make([]*models.ChartClassifyItems, 0)
+			v.Children = items
+		}
+		nodeAll = append(nodeAll, v)
+	}
+	//
+	//
+	//nodeAll := make([]*models.ChartClassifyItems, 0)
+	//for k := range rootList {
+	//	rootNode := rootList[k]
+	//	services.ChartClassifyListMakeTree(classifyAll, rootNode)
+	//	nodeAll = append(nodeAll, rootNode)
+	//}
+	resp.AllNodes = nodeAll
+
+	// 将数据加入缓存
+	if utils.Re == nil {
+		redisData, _ := json.Marshal(resp)
+		utils.Rc.Put(key, redisData, 2*time.Hour)
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// getChartClassifyListForMe 获取我创建的图表
+func getChartClassifyListForMe(adminInfo system.Admin, resp *models.ChartClassifyListResp) (errMsg string, err error) {
+	rootList, err := models.GetChartClassifyByParentId(0, utils.CHART_SOURCE_DEFAULT)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		return
+	}
+
+	classifyAll, err := models.GetChartClassifyAll(utils.CHART_SOURCE_DEFAULT)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		return
+	}
+
+	allChartInfo, err := models.GetChartInfoByAdminId([]int{utils.CHART_SOURCE_DEFAULT}, adminInfo.AdminId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		return
+	}
+
+	chartInfoMap := make(map[int][]*models.ChartClassifyItems)
+	for _, v := range allChartInfo {
+		chartInfoMap[v.ChartClassifyId] = append(chartInfoMap[v.ChartClassifyId], v)
+	}
+	rootChildMap := make(map[int][]*models.ChartClassifyItems)
+	for _, v := range classifyAll {
+		rootChildMap[v.ParentId] = append(rootChildMap[v.ParentId], v)
+		if existItems, ok := chartInfoMap[v.ChartClassifyId]; ok {
+			v.Children = existItems
+		} else {
+			items := make([]*models.ChartClassifyItems, 0)
+			v.Children = items
+		}
+	}
+	nodeAll := make([]*models.ChartClassifyItems, 0)
+	for _, v := range rootList {
+		if existItems, ok := rootChildMap[v.ChartClassifyId]; ok {
+			v.Children = existItems
+		} else {
+			items := make([]*models.ChartClassifyItems, 0)
+			v.Children = items
+		}
+		nodeAll = append(nodeAll, v)
+	}
+	resp.AllNodes = nodeAll
+
+	return
+}
+
+// SimpleList
+// @Title 单层分类列表
+// @Description 单层分类列表
+// @Success 200 {object} data_manage.EdbClassifyListResp
+// @router /classify/simple [get]
+func (this *ChartClassifyController) SimpleList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	// 默认查一级分类和一级分类下的指标信息,
+	// 如果是 子级分类,查询该子级分类的下一级分类和指标信息
+	// 增加标识判断是文件夹还是指标列表
+	reqParentId, _ := this.GetInt("ParentId")
+	parentId := 0
+	nodeAll := make([]*models.ChartClassifyItems, 0)
+	// 处理未分类
+	//查询未分类的指标列表
+	if reqParentId == -1 {
+		initClassify := &models.ChartClassifyItems{
+			ChartClassifyId:     0,
+			ChartInfoId:         0,
+			ChartClassifyName:   "未分类",
+			ChartClassifyNameEn: "",
+			ParentId:            0,
+			Level:               1,
+			Sort:                0,
+		}
+		nodeAll = append(nodeAll, initClassify)
+	} else {
+		parentId = reqParentId
+	}
+	rootList, err := models.GetChartClassifyByParentId(parentId, utils.CHART_SOURCE_DEFAULT)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+	var sortList models.ChartClassifyItemList
+
+	if reqParentId >= 0 {
+		// 查询挂在当前分类上的图表列表
+		allChartInfo, e := models.GetChartInfoAllByClassifyId(utils.CHART_SOURCE_DEFAULT, parentId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取图表信息失败,Err:" + e.Error()
+			return
+		}
+
+		if len(allChartInfo) > 0 {
+			for _, v := range allChartInfo {
+				v.Children = make([]*models.ChartClassifyItems, 0)
+				v.ParentId = parentId
+				nodeAll = append(nodeAll, v)
+			}
+		}
+	}
+
+	if len(rootList) > 0 {
+		for _, v := range rootList {
+			v.Children = make([]*models.ChartClassifyItems, 0)
+			nodeAll = append(nodeAll, v)
+		}
+	}
+	if len(nodeAll) > 0 {
+		//根据sort值排序
+		sortList = nodeAll
+		sort.Sort(sortList)
+	}
+
+	language := `CN`
+	// 指标显示的语言
+	{
+		configDetail, _ := system.GetConfigDetailByCode(this.SysUser.AdminId, system.EdbLanguageVar)
+		if configDetail != nil {
+			language = configDetail.ConfigValue
+		} else {
+			configDetail, _ = system.GetDefaultConfigDetailByCode(system.EdbLanguageVar)
+			if configDetail != nil {
+				language = configDetail.ConfigValue
+			}
+		}
+	}
+
+	resp := new(models.ChartClassifyListResp)
+	if len(nodeAll) > 0 {
+		resp.AllNodes = sortList
+	} else {
+		resp.AllNodes = nodeAll
+	}
+
+	resp.Language = language
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// ChartClassifyItems
+// @Title 获取所有图表分类接口-不包含图表
+// @Description 获取所有图表分类接口-不包含图表
+// @Success 200 {object} models.ChartClassifyListResp
+// @router /chart_classify/items [get]
+func (this *ChartClassifyController) ChartClassifyItems() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	rootList, err := models.GetChartClassifyByParentId(0, utils.CHART_SOURCE_DEFAULT)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	classifyAll, err := models.GetChartClassifyAll(utils.CHART_SOURCE_DEFAULT)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败,Err:" + err.Error()
+		return
+	}
+
+	nodeAll := make([]*models.ChartClassifyItems, 0)
+	for k := range rootList {
+		rootNode := rootList[k]
+		services.ChartClassifyItemsMakeTree(this.SysUser, classifyAll, rootNode)
+		nodeAll = append(nodeAll, rootNode)
+	}
+
+	language := `CN`
+	// 指标显示的语言
+	{
+		configDetail, _ := system.GetConfigDetailByCode(this.SysUser.AdminId, system.ChartLanguageVar)
+		if configDetail != nil {
+			language = configDetail.ConfigValue
+		} else {
+			configDetail, _ = system.GetDefaultConfigDetailByCode(system.ChartLanguageVar)
+			if configDetail != nil {
+				language = configDetail.ConfigValue
+			}
+		}
+	}
+
+	// 是否允许添加分类
+	canOpClassify := true
+	// 如果不是 超管 或者 ficc管理员 或者 ficc研究员,那么就没有权限
+	//if !utils.InArrayByStr([]string{utils.ROLE_TYPE_CODE_ADMIN, utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_RESEARCHR, utils.ROLE_TYPE_CODE_FICC_RESEARCHR}, this.SysUser.RoleTypeCode) {
+	//	canOpClassify = false
+	//}
+	resp := models.ChartClassifyListResp{
+		AllNodes:      nodeAll,
+		Language:      language,
+		CanOpClassify: canOpClassify,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 新增图表分类
+// @Description 新增图表分类接口
+// @Param	request	body models.AddChartClassifyReq true "type json string"
+// @Success 200 Ret=200 保存成功
+// @router /classify/add [post]
+func (this *ChartClassifyController) AddChartClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.AddChartClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ChartClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		br.IsSendEmail = false
+		return
+	}
+	if req.ParentId < 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 新增图表分类
+	_, err, errMsg, isSendEmail := services.AddChartClassify(req.ChartClassifyName, req.ParentId, utils.CHART_SOURCE_DEFAULT, this.Lang, this.SysUser)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = "添加分类失败,Err:" + err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+
+	br.Ret = 200
+	br.Msg = "保存成功"
+	br.Success = true
+}
+
+// EditChartClassify
+// @Title 修改图表分类
+// @Description 修改图表分类接口
+// @Param	request	body models.EditChartClassifyReq true "type json string"
+// @Success 200 Ret=200 修改成功
+// @router /classify/edit [post]
+func (this *ChartClassifyController) EditChartClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.EditChartClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ChartClassifyName == "" {
+		br.Msg = "请输入分类名称"
+		br.IsSendEmail = false
+		return
+	}
+
+	if req.ChartClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	// 编辑图表分类
+	_, err, errMsg, isSendEmail := services.EditChartClassify(req.ChartClassifyId, utils.CHART_SOURCE_DEFAULT, req.ChartClassifyName, this.Lang, this.SysUser)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = "保存分类失败,Err:" + err.Error()
+		br.IsSendEmail = isSendEmail
+		return
+	}
+
+	br.Ret = 200
+	br.Msg = "保存成功"
+	br.Success = true
+	br.IsAddLog = true
+}
+
+// @Title 删除图表检测接口
+// @Description 删除图表检测接口
+// @Param	request	body models.ChartClassifyDeleteCheckResp true "type json string"
+// @Success 200 Ret=200 检测成功
+// @router /chart_classify/delete/check [post]
+func (this *ChartClassifyController) DeleteChartClassifyCheck() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.ChartClassifyDeleteCheckReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ChartClassifyId < 0 && req.ChartInfoId <= 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+	var deleteStatus int
+	var tipsMsg string
+	//删除分类
+	if req.ChartClassifyId > 0 && req.ChartInfoId == 0 {
+		// 查找分类
+		/*item, err := models.GetChartClassifyById(req.ChartClassifyId)
+		if err != nil {
+			br.Msg = "保存失败"
+			br.Msg = "获取分类信息失败,Err:" + err.Error()
+			return
+		}
+		// 已授权分类id
+		permissionClassifyIdList, err := data_manage_permission.GetUserChartClassifyPermissionList(this.SysUser.AdminId, item.ChartClassifyId)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取已授权分类id数据失败,Err:" + err.Error()
+			return
+		}
+		// todo 删除是否需要加权限校验
+		{
+			haveOperaAuth := data_manage_permission.CheckEdbClassifyPermissionByPermissionIdList(item.IsJoinPermission, item.ChartClassifyId, permissionClassifyIdList)
+			button := services.GetChartClassifyOpButton(this.SysUser, item.SysUserId, haveOperaAuth)
+			if !button.OpButton {
+				br.Msg = "无操作权限"
+				br.IsSendEmail = false
+				return
+			}
+		}*/
+
+		//判断图表分类下,是否含有图表
+		count, err := models.GetChartInfoCountByClassifyId(req.ChartClassifyId)
+		if err != nil {
+			br.Msg = "删除失败"
+			br.ErrMsg = "分类下是否含有指标失败,Err:" + err.Error()
+			return
+		}
+
+		if count > 0 {
+			deleteStatus = 1
+			tipsMsg = "该分类下关联图表不可删除"
+		}
+	}
+
+	if deleteStatus != 1 && req.ChartInfoId == 0 {
+		classifyCount, err := models.GetChartClassifyCountByClassifyId(req.ChartClassifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "删除失败"
+			br.ErrMsg = "分类下是否含有图表失败,Err:" + err.Error()
+			return
+		}
+		if classifyCount > 0 {
+			deleteStatus = 2
+			tipsMsg = "确认删除当前目录及包含的子目录吗"
+		}
+	}
+	if deleteStatus == 0 {
+		tipsMsg = "可删除,进行删除操作"
+	}
+
+	resp := new(models.ChartClassifyDeleteCheckResp)
+	resp.DeleteStatus = deleteStatus
+	resp.TipsMsg = tipsMsg
+	br.Ret = 200
+	br.Msg = "检测成功"
+	br.Success = true
+	br.Data = resp
+}
+
+// @Title 删除图表分类/图表
+// @Description 删除图表分类/图表接口
+// @Param	request	body models.DeleteChartClassifyReq true "type json string"
+// @Success 200 Ret=200 删除成功
+// @router /classify/delete [post]
+func (this *ChartClassifyController) DeleteChartClassify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.DeleteChartClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ChartClassifyId < 0 {
+		br.Msg = "参数错误"
+		br.IsSendEmail = false
+		return
+	}
+
+	//删除分类
+	_, err = models.GetChartClassifyById(req.ChartClassifyId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "分类不存在,请刷新"
+			return
+		}
+		br.Msg = "操作失败"
+		br.Msg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+
+	//判断是否含有指标
+	count, err := models.GetChartInfoCountByClassifyId(req.ChartClassifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "删除失败"
+		br.ErrMsg = "判断名称是否已存在失败,Err:" + err.Error()
+		return
+	}
+
+	if count > 0 {
+		br.Msg = "该目录下存在关联指标,不可删除"
+		br.IsSendEmail = false
+		return
+	}
+
+	err = models.DeleteChartClassify(req.ChartClassifyId)
+	if err != nil {
+		br.Msg = "删除失败"
+		br.ErrMsg = "删除失败,Err:" + err.Error()
+		return
+	}
+
+	resp := new(models.AddChartInfoResp)
+
+	br.Ret = 200
+	br.Msg = "删除成功"
+	br.Success = true
+	br.Data = resp
+	br.IsAddLog = true
+}
+
+// ChartClassifyMove
+// @Title 图表分类移动接口
+// @Description 图表分类移动接口
+// @Success 200 {object} models.MoveChartClassifyReq
+// @router /classify/move [post]
+func (this *ChartClassifyController) ChartClassifyMove() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req models.MoveChartClassifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ClassifyId <= 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "分类id小于等于0"
+		return
+	}
+	//判断分类是否存在
+	/*chartClassifyInfo, err := models.GetChartClassifyById(req.ClassifyId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			br.Msg = "分类不存在,请刷新页面"
+			return
+		}
+		br.Msg = "移动失败"
+		br.ErrMsg = "获取分类信息失败,Err:" + err.Error()
+		return
+	}
+
+	// 校验移动的父级目录下是否有重名分类
+	exists, e := models.GetChartClassifyByParentIdAndName(req.ParentClassifyId, chartClassifyInfo.ChartClassifyName, req.ClassifyId)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		br.Msg = "移动失败"
+		br.ErrMsg = "获取父级目录下的同名分类失败, Err: " + e.Error()
+		return
+	}
+	if exists != nil {
+		br.Msg = "移动失败,分类名称已存在"
+		return
+	}
+
+	if chartClassifyInfo.Source != utils.CHART_SOURCE_DEFAULT {
+		br.Msg = "分类异常"
+		br.ErrMsg = "分类异常,不是ETA图库的分类"
+		return
+	}
+	updateCol := make([]string, 0)
+
+	//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
+	if chartClassifyInfo.ParentId != req.ParentClassifyId && req.ParentClassifyId != 0 {
+		parentChartClassifyInfo, err := models.GetChartClassifyById(req.ParentClassifyId)
+		if err != nil {
+			br.Msg = "移动失败"
+			br.ErrMsg = "获取上级分类信息失败,Err:" + err.Error()
+			return
+		}
+		chartClassifyInfo.ParentId = parentChartClassifyInfo.ChartClassifyId
+		chartClassifyInfo.Level = parentChartClassifyInfo.Level + 1
+		chartClassifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
+	}
+
+	//如果有传入 上一个兄弟节点分类id
+	if req.PrevClassifyId > 0 {
+		//上一个兄弟节点
+		prevClassify, err := models.GetChartClassifyById(req.PrevClassifyId)
+		if err != nil {
+			br.Msg = "移动失败"
+			br.ErrMsg = "获取上一个兄弟节点分类信息失败,Err:" + err.Error()
+			return
+		}
+
+		//如果是移动在两个兄弟节点之间
+		if req.NextClassifyId > 0 {
+			//下一个兄弟节点
+			nextClassify, err := models.GetChartClassifyById(req.NextClassifyId)
+			if err != nil {
+				br.Msg = "移动失败"
+				br.ErrMsg = "获取下一个兄弟节点分类信息失败,Err:" + err.Error()
+				return
+			}
+			//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+			if prevClassify.Sort == nextClassify.Sort || prevClassify.Sort == chartClassifyInfo.Sort {
+				//变更兄弟节点的排序
+				updateSortStr := `sort + 2`
+				_ = models.UpdateChartClassifySortByParentId(prevClassify.ParentId, prevClassify.ChartClassifyId, prevClassify.Sort, updateSortStr)
+			} else {
+				//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+				if nextClassify.Sort-prevClassify.Sort == 1 {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 1`
+					_ = models.UpdateChartClassifySortByParentId(prevClassify.ParentId, 0, prevClassify.Sort, updateSortStr)
+				}
+			}
+		}
+
+		chartClassifyInfo.Sort = prevClassify.Sort + 1
+		chartClassifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+
+	} else {
+		firstClassify, err := models.GetFirstChartClassifyByParentId(chartClassifyInfo.ParentId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "移动失败"
+			br.ErrMsg = "获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + err.Error()
+			return
+		}
+
+		//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+		if firstClassify != nil && firstClassify.Sort == 0 {
+			updateSortStr := ` sort + 1 `
+			_ = models.UpdateChartClassifySortByParentId(firstClassify.ParentId, firstClassify.ChartClassifyId-1, 0, updateSortStr)
+		}
+
+		chartClassifyInfo.Sort = 0 //那就是排在第一位
+		chartClassifyInfo.ModifyTime = time.Now()
+		updateCol = append(updateCol, "Sort", "ModifyTime")
+	}
+
+	//更新
+	if len(updateCol) > 0 {
+		err = chartClassifyInfo.Update(updateCol)
+		if err != nil {
+			br.Msg = "移动失败"
+			br.ErrMsg = "修改失败,Err:" + err.Error()
+			return
+		}
+	}*/
+	err, errMsg := services.MoveChartClassify(req, sysUser, 0)
+	if errMsg != `` {
+		br.Msg = errMsg
+		br.ErrMsg = errMsg
+		if err != nil {
+			br.ErrMsg = err.Error()
+		} else {
+			br.IsSendEmail = false
+		}
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "移动成功"
+}
+
+// getChartClassifyListForMe 获取我创建的图表分类
+func getChartClassifyListForMeV2(adminInfo system.Admin, resp *models.ChartClassifyListResp) (errMsg string, err error) {
+	rootList, err := models.GetChartClassifyByParentId(0, utils.CHART_SOURCE_DEFAULT)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		return
+	}
+
+	classifyAll, err := models.GetChartClassifyAll(utils.CHART_SOURCE_DEFAULT)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		errMsg = "获取失败"
+		return
+	}
+
+	rootChildMap := make(map[int][]*models.ChartClassifyItems)
+	for _, v := range classifyAll {
+		rootChildMap[v.ParentId] = append(rootChildMap[v.ParentId], v)
+	}
+	nodeAll := make([]*models.ChartClassifyItems, 0)
+	for _, v := range rootList {
+		if existItems, ok := rootChildMap[v.ChartClassifyId]; ok {
+			v.Children = existItems
+		} else {
+			items := make([]*models.ChartClassifyItems, 0)
+			v.Children = items
+		}
+		nodeAll = append(nodeAll, v)
+	}
+	resp.AllNodes = nodeAll
+
+	return
+}
+
+// ChartClassifyChartListV2
+// @Title 根据图表分类获取图表列表
+// @Description 根据图表分类获取图表列表接口
+// @Param   IsShowMe   query   bool  true       "是否只看我的,true、false"
+// @Param   ChartClassifyId   query   bool  true       "图片分类id"
+// @Success 200 {object} models.ChartClassifyListResp
+// @router /classify/chart/list [get]
+func (this *ChartClassifyController) ChartClassifyChartListV2() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	resp := new(models.ChartClassifyListResp)
+
+	chartClassifyId, _ := this.GetInt("ChartClassifyId")
+	if chartClassifyId <= 0 {
+		br.Msg = "参数错误"
+		return
+	}
+
+	/*// 获取当前账号的不可见指标
+	noPermissionChartIdMap := make(map[int]bool)
+	{
+		obj := models.EdbInfoNoPermissionAdmin{}
+		confList, err := obj.GetAllChartListByAdminId(this.SysUser.AdminId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取不可见指标配置数据失败,Err:" + err.Error()
+			return
+		}
+		for _, v := range confList {
+			noPermissionChartIdMap[v.ChartInfoId] = true
+		}
+	}*/
+
+	isShowMe, _ := this.GetBool("IsShowMe")
+	if isShowMe {
+		allChartInfo, err := models.GetChartInfoByAdminIdAndClassify([]int{utils.CHART_SOURCE_DEFAULT}, sysUser.AdminId, chartClassifyId)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取全部数据失败,Err:" + err.Error()
+			return
+		}
+		// 移除没有权限的图表
+		//allNodes := services.HandleNoPermissionChart(allChartInfo, noPermissionChartIdMap, this.SysUser.AdminId)
+		resp.AllNodes = allChartInfo
+
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		br.Data = resp
+		fmt.Println("source my classify")
+		return
+	}
+
+	allChartInfo, err := models.GetChartInfoAllByClassifyId(utils.CHART_SOURCE_DEFAULT, chartClassifyId)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+		return
+	}
+	/*// 移除没有权限的图表
+	allNodes := services.HandleNoPermissionChart(allChartInfo, noPermissionChartIdMap, this.SysUser.AdminId)
+
+	for k, item := range allNodes {
+		item.Button = services.GetChartOpButton(this.SysUser, item.SysUserId, item.HaveOperaAuth)
+		item.Button.AddButton = false
+		item.Button.OpButton = false
+		item.Button.DeleteButton = false
+		allNodes[k] = item
+	}*/
+
+	resp.AllNodes = allChartInfo
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 17 - 19
controllers/login.go

@@ -1,8 +1,11 @@
 package controllers
 
 import (
+	"encoding/json"
 	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/system"
 	"eta/eta_forum_admin/services"
+	"eta/eta_forum_admin/utils"
 	"fmt"
 	"github.com/beego/beego/v2/server/web"
 )
@@ -15,7 +18,7 @@ type LoginController struct {
 // @Title 编码登录
 // @Description 编码登录
 // @Success 200 Ret=200 操作成功
-// @router /login/auth_code_login [get]
+// @router /auth_code_login [post]
 func (this *LoginController) AuthCodeLogin() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
@@ -25,29 +28,24 @@ func (this *LoginController) AuthCodeLogin() {
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
-
-	req := make(map[string]interface{})
-	query := this.Ctx.Request.URL.Query()
-	for key, value := range query {
-		req[key] = value[0]
-	}
-	fmt.Println(req)
-	adminName := ""
-	//key := fmt.Sprint(utils.CACHE_ETA_AUTH_CODE_PREFIX, req.AuthCode)
-	//adminName, e := global.Rc.RedisString(key)
-	/*if e != nil {
-		fmt.ErrorfMsg("获取失败", "获取失败, Redis Err: "+e.Error())
+	var req system.AuthCodeLoginReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
-	}*/
-	/*if adminName == "" {
-		fmt.Errorf("获取失败, 无效编码")
+	}
+	key := fmt.Sprint(utils.CACHE_FORUM_AUTH_CODE_PREFIX, req.AuthCode)
+	adminNameRet := utils.Rc.Get(key)
+	if adminNameRet == nil {
+		br.Msg = "获取失败, 无效编码"
 		return
 	}
 	// 清除AuthCode
 	defer func() {
-		_ = global.Rc.Delete(key)
-	}()*/
-
+		//_ = utils.Rc.Delete(key)
+	}()
+	adminName := fmt.Sprintf("%s", adminNameRet)
 	data, e := services.ThirdLogin(adminName)
 	if e != nil {
 		br.Msg = "获取失败"

+ 5 - 0
go.mod

@@ -6,8 +6,10 @@ require (
 	github.com/beego/bee/v2 v2.1.0
 	github.com/beego/beego/v2 v2.1.0
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
+	github.com/go-redis/redis/v8 v8.11.5
 	github.com/go-sql-driver/mysql v1.7.0
 	github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b
+	github.com/olivere/elastic/v7 v7.0.32
 	github.com/shopspring/decimal v1.3.1
 	go.mongodb.org/mongo-driver v1.15.0
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
@@ -16,6 +18,7 @@ require (
 require (
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/cespare/xxhash/v2 v2.2.0 // indirect
+	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 	github.com/golang/protobuf v1.5.3 // indirect
 	github.com/golang/snappy v0.0.1 // indirect
 	github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac // indirect
@@ -25,8 +28,10 @@ require (
 	github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9 // indirect
 	github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 // indirect
 	github.com/hashicorp/golang-lru v0.5.4 // indirect
+	github.com/josharian/intern v1.0.0 // indirect
 	github.com/klauspost/compress v1.13.6 // indirect
 	github.com/kr/text v0.2.0 // indirect
+	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
 	github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
 	github.com/mitchellh/mapstructure v1.5.0 // indirect

+ 24 - 0
go.sum

@@ -11,8 +11,16 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
 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/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
 github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
+github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
+github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
+github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
 github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
 github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -41,6 +49,8 @@ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
 github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
 github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc=
 github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -49,6 +59,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
 github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/lib/pq v1.10.5 h1:J+gdV2cUmX7ZqL2B0lFcW0m+egaHC2V3lpO8nWxyYiQ=
 github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
 github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
 github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
@@ -57,6 +69,14 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
 github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
 github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0=
 github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
+github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
+github.com/olivere/elastic/v7 v7.0.32 h1:R7CXvbu8Eq+WlsLgxmKVKPox0oOwAE/2T9Si5BnvK6E=
+github.com/olivere/elastic/v7 v7.0.32/go.mod h1:c7PVmLe3Fxq77PIfY/bZmxY/TAamBhCzZ8xDOE09a9k=
+github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
+github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
+github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE=
+github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -134,5 +154,9 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
+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.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 2 - 1
models/base.go

@@ -8,6 +8,7 @@ type BaseResponse struct {
 	Data        interface{}
 	Success     bool `description:"true 执行成功,false 执行失败"`
 	IsSendEmail bool `description:"true 发送邮件,false 不发送邮件"`
+	IsAddLog    bool `json:"-" description:"true 新增操作日志,false 不新增操作日志" `
 }
 
 type BaseResponseRef struct {
@@ -27,7 +28,7 @@ type BaseResponseResult struct {
 }
 
 func (r *BaseResponse) Init() *BaseResponse {
-	return &BaseResponse{Ret: 403,IsSendEmail: true}
+	return &BaseResponse{Ret: 403, IsSendEmail: true}
 }
 
 type BaseRequest struct {

+ 68 - 20
models/chart_classify.go

@@ -8,19 +8,20 @@ import (
 )
 
 type ChartClassify struct {
-	ChartClassifyId   int       `orm:"column(chart_classify_id);pk"`
-	ChartClassifyName 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:"层级"`
-	UniqueCode        string    `description:"唯一编码"`
-	Sort              int       `description:"排序字段,越小越靠前,默认值:10"`
-	Source            int       `description:"1:ETA图库;2:商品价格曲线"`
-	IsJoinPermission  int       `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+	ChartClassifyId     int       `orm:"column(chart_classify_id);pk"`
+	ChartClassifyName   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:"层级"`
+	UniqueCode          string    `description:"唯一编码"`
+	Sort                int       `description:"排序字段,越小越靠前,默认值:10"`
+	Source              int       `description:"1:ETA图库;2:商品价格曲线"`
+	IsJoinPermission    int       `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+	ChartClassifyNameEn string    `description:"英文分类名称"`
 }
 
 func AddChartClassify(item *ChartClassify) (lastId int64, err error) {
@@ -32,7 +33,6 @@ func AddChartClassify(item *ChartClassify) (lastId int64, err error) {
 type AddChartClassifyReq struct {
 	ChartClassifyName string `description:"分类名称"`
 	ParentId          int    `description:"父级id,第一级传0"`
-	Level             int    `description:"层级,第一级传0,其余传上一级的层级"`
 }
 
 func GetChartClassifyCount(chartClassifyName string, parentId, source int) (count int, err error) {
@@ -42,6 +42,22 @@ func GetChartClassifyCount(chartClassifyName string, parentId, source int) (coun
 	return
 }
 
+// GetChartClassifyEnCount
+// @Description: 根据图表分类英文名称获取对应的数量
+// @author: Roc
+// @datetime 2024-04-16 18:13:24
+// @param chartClassifyNameEn string
+// @param parentId int
+// @param source int
+// @return count int
+// @return err error
+func GetChartClassifyEnCount(chartClassifyNameEn string, parentId, source int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT COUNT(1) AS count FROM chart_classify WHERE parent_id=? AND source = ? AND chart_classify_name_en = ? `
+	err = o.Raw(sql, parentId, source, chartClassifyNameEn).QueryRow(&count)
+	return
+}
+
 type EditChartClassifyReq struct {
 	ChartClassifyName string `description:"分类名称"`
 	ChartClassifyId   int    `description:"分类id"`
@@ -70,7 +86,7 @@ func EditChartClassify(classifyId, source int, chartClassifyName string) (err er
 
 type DeleteChartClassifyReq struct {
 	ChartClassifyId int `description:"分类id"`
-	ChartInfoId     int `description:"指标id"`
+	//ChartInfoId     int `description:"指标id"`
 }
 
 func GetChartInfoCountByClassifyId(classifyId int) (count int, err error) {
@@ -208,6 +224,10 @@ type MoveChartClassifyReq struct {
 	ParentClassifyId int `description:"父级分类id"`
 	PrevClassifyId   int `description:"上一个兄弟节点分类id"`
 	NextClassifyId   int `description:"下一个兄弟节点分类id"`
+
+	ChartInfoId     int `description:"图表ID"`
+	PrevChartInfoId int `description:"上一个图表ID"`
+	NextChartInfoId int `description:"下一个图表ID"`
 }
 
 // GetFirstChartClassifyByParentId 获取当前父级图表分类下的排序第一条的数据
@@ -227,13 +247,13 @@ func GetFirstChartClassifyByParentIdAndSource(parentId, source int) (item *Chart
 }
 
 // UpdateChartClassifySortByParentId 根据图表父类id更新排序
-func UpdateChartClassifySortByParentId(parentId, classifyId, nowSort int, updateSort string) (err error) {
+func UpdateChartClassifySortByParentId(parentId, classifyId, nowSort int, updateSort string, source int) (err error) {
 	o := orm.NewOrm()
-	sql := ` update chart_classify set sort = ` + updateSort + ` WHERE parent_id=? and sort > ? `
+	sql := ` update chart_classify set sort = ` + updateSort + ` WHERE parent_id=? and source=? and sort > ? `
 	if classifyId > 0 {
 		sql += ` or ( chart_classify_id > ` + fmt.Sprint(classifyId) + ` and sort= ` + fmt.Sprint(nowSort) + `)`
 	}
-	_, err = o.Raw(sql, parentId, nowSort).Exec()
+	_, err = o.Raw(sql, parentId, source, nowSort).Exec()
 	return
 }
 
@@ -245,10 +265,24 @@ func (chartClassify *ChartClassify) Update(cols []string) (err error) {
 }
 
 // GetChartClassifyMaxSort 获取图表分类下最大的排序数
-func GetChartClassifyMaxSort(parentId, source int) (sort int, err error) {
+func GetChartClassifyMaxSort(parentId, source int) (maxSort int, err error) {
 	o := orm.NewOrm()
+	var classifyMaxSort int
+	var chartMaxSort int
 	sql := `SELECT Max(sort) AS sort FROM chart_classify WHERE parent_id=? AND source = ? `
-	err = o.Raw(sql, parentId, source).QueryRow(&sort)
+	err = o.Raw(sql, parentId, source).QueryRow(&classifyMaxSort)
+	if err != nil {
+		return
+	}
+	maxSort = classifyMaxSort
+	sql = `SELECT Max(sort) AS sort FROM chart_info WHERE parent_id=? AND source = ? `
+	err = o.Raw(sql, parentId, source).QueryRow(&chartMaxSort)
+	if err != nil {
+		return
+	}
+	if maxSort < chartMaxSort {
+		maxSort = chartMaxSort
+	}
 	return
 }
 
@@ -348,3 +382,17 @@ func GetChartClassifyBySourceAndIsJoinPermission(source, isJoinPermission int) (
 	_, err = o.Raw(sql, source, isJoinPermission).QueryRows(&items)
 	return
 }
+
+type ChartClassifyItemList []*ChartClassifyItems
+
+func (m ChartClassifyItemList) Len() int {
+	return len(m)
+}
+
+func (m ChartClassifyItemList) Less(i, j int) bool {
+	return m[i].Sort < m[j].Sort
+}
+
+func (m ChartClassifyItemList) Swap(i, j int) {
+	m[i], m[j] = m[j], m[i]
+}

+ 26 - 0
models/chart_info_log.go

@@ -0,0 +1,26 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type ChartInfoLog struct {
+	ChartInfoLogId  int    `orm:"column(chart_info_log_id);pk"`
+	ChartInfoId     int    `description:"图表id"`
+	ChartName       string `description:"来源名称"`
+	ChartClassifyId int    `description:"图表分类id"`
+	SysUserId       int
+	SysUserRealName string
+	UniqueCode      string `description:"图表唯一编码"`
+	CreateTime      time.Time
+	Content         string `description:"内容"`
+	Status          string `description:"状态"`
+	Method          string `description:"执行方法"`
+}
+
+func AddChartInfoLog(item *ChartInfoLog) (lastId int64, err error) {
+	o := orm.NewOrmUsingDB("data")
+	lastId, err = o.Insert(item)
+	return
+}

+ 6 - 3
models/db.go

@@ -1,6 +1,7 @@
 package models
 
 import (
+	"eta/eta_forum_admin/models/system"
 	"eta/eta_forum_admin/utils"
 	"github.com/beego/beego/v2/client/orm"
 	_ "github.com/go-sql-driver/mysql"
@@ -31,6 +32,7 @@ func initChart() {
 		new(ChartEdbMapping),
 		new(ChartTheme),
 		new(ChartThemeType),
+		new(ChartInfoLog),
 	)
 }
 
@@ -45,8 +47,9 @@ func initEdbData() {
 // 初始化用户服务
 func initUser() {
 	orm.RegisterModel(
-		new(SysSession),
-		new(SysUserLoginRecord),
-		new(Admin),
+		new(system.SysSession),
+		new(system.SysUserLoginRecord),
+		new(system.Admin),
+		new(system.AdminOperateRecord),
 	)
 }

+ 3 - 3
models/sys_user.go → models/system/admin.go

@@ -1,4 +1,4 @@
-package models
+package system
 
 import (
 	"eta/eta_forum_admin/utils"
@@ -214,6 +214,6 @@ func GetUserUnexpiredSysSession(userName, expiredTime string) (item *SysSession,
 	return
 }
 
-type TokenLoginReq struct {
-	AuthCode string `json:"auth_code" form:"auth_code" binding:"required"`
+type AuthCodeLoginReq struct {
+	AuthCode string
 }

+ 58 - 0
models/system/admin_config.go

@@ -0,0 +1,58 @@
+package system
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type AdminConfig struct {
+	ConfigId    int       `orm:"column(config_id);pk" description:"配置id"`
+	AdminId     int       `description:"系统客户id"`
+	ConfigCode  string    `description:"配置编码"`
+	ConfigValue string    `description:"配置值"`
+	Remark      string    `description:"备注信息"`
+	CreateTime  time.Time `description:"创建时间"`
+}
+
+// Update 更新基础信息
+func (item *AdminConfig) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(item, cols...)
+	return
+}
+
+func (item *AdminConfig) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(item)
+	if err != nil {
+		return
+	}
+	item.ConfigId = int(id)
+	return
+}
+
+var EdbLanguageVar = `edb_language`
+var PredictEdbLanguageVar = `predict_edb_language`
+var ChartLanguageVar = `chart_language`
+
+// GetConfigDetailByCode 获取详情
+func GetConfigDetailByCode(adminId int, configCode string) (item *AdminConfig, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM admin_config WHERE admin_id = ? AND  config_code=? `
+	err = o.Raw(sql, adminId, configCode).QueryRow(&item)
+	return
+}
+
+// GetDefaultConfigDetailByCode 获取默认配置详情
+func GetDefaultConfigDetailByCode(configCode string) (item *AdminConfig, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM admin_config WHERE admin_id = ? AND  config_code=? `
+	err = o.Raw(sql, 0, configCode).QueryRow(&item)
+	return
+}
+
+// SetAdminConfigReq 设置用户的配置
+type SetAdminConfigReq struct {
+	ConfigCode  string `description:"配置编码"`
+	ConfigValue string `description:"配置值"`
+}

+ 25 - 0
models/system/admin_operate_record.go

@@ -0,0 +1,25 @@
+package system
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type AdminOperateRecord struct {
+	AdminOperateRecordId int       `orm:"column(admin_operate_record_id);pk" description:"id"`
+	AdminId              int       `description:"系统客户id"`
+	RealName             string    `description:"配置编码"`
+	Uuid                 string    `description:"配置值"`
+	Uri                  string    `description:"地址"`
+	Params               string    `description:"参数"`
+	Ip                   string    `description:"IP"`
+	UserAgent            string    `description:"用户代理信息"`
+	CreateTime           time.Time `description:"创建时间"`
+	Header               string
+}
+
+func (item *AdminOperateRecord) Insert() (err error) {
+	o := orm.NewOrm()
+	_, err = o.Insert(item)
+	return
+}

+ 1 - 1
models/sys_session.go → models/system/sys_session.go

@@ -1,4 +1,4 @@
-package models
+package system
 
 import (
 	"github.com/beego/beego/v2/client/orm"

+ 1 - 1
models/sys_user_login_record.go → models/system/sys_user_login_record.go

@@ -1,4 +1,4 @@
-package models
+package system
 
 import (
 	"github.com/beego/beego/v2/client/orm"

+ 116 - 26
routers/commentsRouter.go

@@ -7,31 +7,121 @@ import (
 
 func init() {
 
-	beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartInfoController"],
-		beego.ControllerComments{
-			Method:           "Delete",
-			Router:           `/delete`,
-			AllowHTTPMethods: []string{"post"},
-			MethodParams:     param.Make(),
-			Filters:          nil,
-			Params:           nil})
-
-	beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartInfoController"],
-		beego.ControllerComments{
-			Method:           "Save",
-			Router:           `/save`,
-			AllowHTTPMethods: []string{"post"},
-			MethodParams:     param.Make(),
-			Filters:          nil,
-			Params:           nil})
-
-	beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartInfoController"],
-		beego.ControllerComments{
-			Method:           "Update",
-			Router:           `/update`,
-			AllowHTTPMethods: []string{"post"},
-			MethodParams:     param.Make(),
-			Filters:          nil,
-			Params:           nil})
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"],
+        beego.ControllerComments{
+            Method: "DeleteChartClassifyCheck",
+            Router: `/chart_classify/delete/check`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"],
+        beego.ControllerComments{
+            Method: "ChartClassifyItems",
+            Router: `/chart_classify/items`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"],
+        beego.ControllerComments{
+            Method: "ChartClassifyListV2",
+            Router: `/chart_classify/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"],
+        beego.ControllerComments{
+            Method: "AddChartClassify",
+            Router: `/classify/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"],
+        beego.ControllerComments{
+            Method: "ChartClassifyChartListV2",
+            Router: `/classify/chart/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"],
+        beego.ControllerComments{
+            Method: "DeleteChartClassify",
+            Router: `/classify/delete`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"],
+        beego.ControllerComments{
+            Method: "EditChartClassify",
+            Router: `/classify/edit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"],
+        beego.ControllerComments{
+            Method: "ChartClassifyMove",
+            Router: `/classify/move`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartClassifyController"],
+        beego.ControllerComments{
+            Method: "SimpleList",
+            Router: `/classify/simple`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartInfoController"],
+        beego.ControllerComments{
+            Method: "Delete",
+            Router: `/delete`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartInfoController"],
+        beego.ControllerComments{
+            Method: "Save",
+            Router: `/save`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartInfoController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:ChartInfoController"],
+        beego.ControllerComments{
+            Method: "Update",
+            Router: `/update`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:LoginController"] = append(beego.GlobalControllerRouter["eta/eta_forum_admin/controllers:LoginController"],
+        beego.ControllerComments{
+            Method: "AuthCodeLogin",
+            Router: `/auth_code_login`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
 
 }

+ 6 - 0
routers/router.go

@@ -17,6 +17,12 @@ func init() {
 		web.NSNamespace("/chart",
 			web.NSInclude(
 				&controllers.ChartInfoController{},
+				&controllers.ChartClassifyController{},
+			),
+		),
+		web.NSNamespace("/login",
+			web.NSInclude(
+				&controllers.LoginController{},
 			),
 		),
 	)

+ 739 - 0
services/chart_classify.go

@@ -0,0 +1,739 @@
+package services
+
+import (
+	"errors"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/system"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"strconv"
+	"time"
+)
+
+func ChartClassifyItemsMakeTree(sysUser *system.Admin, allNode []*models.ChartClassifyItems, node *models.ChartClassifyItems) {
+	//	node.HaveOperaAuth = data_manage_permission.CheckEdbClassifyPermissionByPermissionIdList(node.IsJoinPermission, node.ChartClassifyId, permissionClassifyIdList)
+	button := GetChartClassifyOpButton(sysUser, node.SysUserId, node.HaveOperaAuth)
+	node.Button = button
+
+	childs, _ := chartClassifyHaveChild(allNode, node) //判断节点是否有子节点并返回
+	if len(childs) > 0 {
+		for _, child := range childs {
+			//child.HaveOperaAuth = data_manage_permission.CheckEdbClassifyPermissionByPermissionIdList(child.IsJoinPermission, child.ChartClassifyId, permissionClassifyIdList)
+			childButton := GetChartClassifyOpButton(sysUser, child.SysUserId, child.HaveOperaAuth)
+			if child.Level == 3 {
+				childButton.AddButton = false //第三级的话,默认图表都是没有添加按钮的
+			}
+			child.Button = childButton
+			//node.Children = append(node.Children, child)
+		}
+		node.Children = append(node.Children, childs[0:]...) //添加子节点
+		for _, v := range childs {
+			//v.HaveOperaAuth = data_manage_permission.CheckEdbClassifyPermissionByPermissionIdList(v.IsJoinPermission, v.ChartClassifyId, permissionClassifyIdList)
+			//查询子节点的子节点,并添加到子节点
+			_, has := chartClassifyHaveChild(allNode, v)
+			if has {
+				ChartClassifyItemsMakeTree(sysUser, allNode, v) //递归添加节点
+			} else {
+				childrenArr := make([]*models.ChartClassifyItems, 0)
+				v.Children = childrenArr
+			}
+		}
+	} else {
+		childrenArr := make([]*models.ChartClassifyItems, 0)
+		node.Children = childrenArr
+	}
+}
+func chartClassifyHaveChild(allNode []*models.ChartClassifyItems, node *models.ChartClassifyItems) (childs []*models.ChartClassifyItems, yes bool) {
+	for _, v := range allNode {
+		if v.ParentId == node.ChartClassifyId {
+			childs = append(childs, v)
+		}
+	}
+	if len(childs) > 0 {
+		yes = true
+	}
+	return
+}
+
+func FixChartClassify() {
+	//新增三级分类
+	list, err := models.GetChartClassifyByLevel(2, 1)
+	if err != nil {
+		return
+	}
+
+	for _, v := range list {
+		classify := new(models.ChartClassify)
+		classify.ParentId = v.ChartClassifyId
+		classify.ChartClassifyName = v.ChartClassifyName
+		classify.HasData = 0
+		classify.CreateTime = time.Now()
+		classify.ModifyTime = time.Now()
+		if utils.RunMode == "debug" {
+			classify.SysUserId = 11
+		} else {
+			classify.SysUserId = 163
+		}
+		classify.SysUserRealName = "admin"
+		classify.Level = 3
+		timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+		classify.UniqueCode = utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
+		classify.Sort = v.Sort
+		classify.Source = utils.CHART_SOURCE_DEFAULT
+
+		id, err := models.AddChartClassify(classify)
+		if err != nil {
+			return
+		}
+
+		err = models.FIXChartClassifyId(int(id), v.ChartClassifyId)
+		if err != nil {
+			return
+		}
+	}
+}
+
+func FixChartClassifySysUserId() {
+	//获取所有父级分类信息
+	list, err := models.GetChartClassifyByLevel(2, 1)
+	if err != nil {
+		return
+	}
+
+	for _, v := range list {
+		err := models.EditChartClassifySysUser(v.ChartClassifyId, v.SysUserId, v.SysUserRealName)
+		if err != nil {
+			return
+		}
+		fmt.Println("ChartClassifyId:", v.ChartClassifyId)
+	}
+	fmt.Println("FixChartClassifySysUserId end")
+}
+
+// GetChartClassifyOpButton 获取ETA图库分类的操作权限
+func GetChartClassifyOpButton(sysUser *system.Admin, belongUserId int, haveOperaAuth bool) (button models.ChartClassifyItemsButton) {
+	// 没有数据权限就直接返回
+	if !haveOperaAuth {
+		return
+	}
+
+	//ficc管理员和超管和ficc研究员有权限创建和管理分类,可以编辑分类名称(分类名称不允许重复),可以拖动分类,改变分类顺序,可以拖动分类下模型,改变顺序,可以删除分类,若分类下有预测指标,则不允许删除;
+	//if utils.InArrayByStr([]string{utils.ROLE_TYPE_CODE_ADMIN, utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_RESEARCHR, utils.ROLE_TYPE_CODE_FICC_RESEARCHR}, sysUser.RoleTypeCode) {
+	button.AddButton = true
+	button.OpButton = true
+	button.DeleteButton = true
+	button.MoveButton = true
+	//}
+
+	return
+}
+
+// GetCanOpChartClassify 获取是否允许添加图表分类
+func GetCanOpChartClassify(roleTypeCode string) (canOpClassify bool) {
+	// 如果不是 超管 或者 ficc管理员 或者 ficc研究员,那么就没有权限
+	if utils.InArrayByStr([]string{utils.ROLE_TYPE_CODE_ADMIN, utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_RESEARCHR, utils.ROLE_TYPE_CODE_FICC_RESEARCHR}, roleTypeCode) {
+		canOpClassify = true
+	}
+
+	return
+}
+
+// GetChartOpButton 获取ETA图库的操作权限
+func GetChartOpButton(sysUser *system.Admin, belongUserId int, haveOperaAuth bool) (button models.ChartClassifyItemsButton) {
+	// 数据权限判断
+	if !haveOperaAuth {
+		return
+	}
+	//预测指标的添加人对该预测指标有全部操作权限,ficc管理员、超管对所有预测指标有全部操作权限;
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_ADMIN || sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_ADMIN || sysUser.AdminId == belongUserId || sysUser.PredictEdbPermission == 1 {
+		button.AddButton = true
+		button.OpButton = true
+		button.DeleteButton = true
+		button.MoveButton = true
+	}
+
+	return
+}
+
+// AddChartClassify
+// @Description: 添加图表分类
+// @author: Roc
+// @datetime 2024-04-17 14:48:23
+// @param chartClassifyName string
+// @param parentId int
+// @param level int
+// @param source int
+// @param lang string
+// @param sysUser *system.Admin
+// @return classifyInfo *models.ChartClassify
+// @return err error
+// @return errMsg string
+// @return isSendEmail bool
+func AddChartClassify(chartClassifyName string, parentId, source int, lang string, sysUser *system.Admin) (classifyInfo *models.ChartClassify, err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+	errMsg = "保存分类失败"
+
+	// 校验分类名称相同的数量
+	{
+		var count int
+		switch lang {
+		case utils.EnLangVersion:
+			count, err = models.GetChartClassifyEnCount(chartClassifyName, parentId, source)
+		default:
+			count, err = models.GetChartClassifyCount(chartClassifyName, parentId, source)
+		}
+		if err != nil {
+			errMsg = "判断名称是否已存在失败"
+			return
+		}
+		if count > 0 {
+			errMsg = "分类名称已存在,请重新输入"
+			err = errors.New(errMsg)
+			isSendEmail = false
+			return
+		}
+	}
+	// 查询level值
+	level := 0
+	if parentId > 0 {
+		parentClassify, tErr := models.GetChartClassifyById(parentId)
+		if tErr != nil {
+			if tErr.Error() == utils.ErrNoRow() {
+				errMsg = "父级分类不存在"
+				err = errors.New(errMsg)
+				return
+			}
+			errMsg = "获取失败"
+			err = errors.New("获取分类信息失败,Err:" + tErr.Error())
+			return
+		}
+		level = parentClassify.Level
+	}
+
+	//获取该层级下最大的排序数
+	maxSort, err := models.GetChartClassifyMaxSort(parentId, source)
+
+	classifyInfo = new(models.ChartClassify)
+	classifyInfo.ParentId = parentId
+	classifyInfo.ChartClassifyName = chartClassifyName
+	classifyInfo.ChartClassifyNameEn = chartClassifyName
+	classifyInfo.HasData = 0
+	classifyInfo.CreateTime = time.Now()
+	classifyInfo.ModifyTime = time.Now()
+	classifyInfo.SysUserId = sysUser.AdminId
+	classifyInfo.SysUserRealName = sysUser.RealName
+	classifyInfo.Level = level + 1
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+	classifyInfo.UniqueCode = utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
+	classifyInfo.Sort = maxSort + 1
+	classifyInfo.Source = source
+
+	_, err = models.AddChartClassify(classifyInfo)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// EditChartClassify
+// @Description: 修改图表分类
+// @author: Roc
+// @datetime 2024-04-17 14:59:37
+// @param chartClassifyId int
+// @param source int
+// @param chartClassifyName string
+// @param lang string
+// @param sysUser *system.Admin
+// @return classifyInfo *models.ChartClassify
+// @return err error
+// @return errMsg string
+// @return isSendEmail bool
+func EditChartClassify(chartClassifyId, source int, chartClassifyName, lang string, sysUser *system.Admin) (classifyInfo *models.ChartClassify, err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+	errMsg = "保存失败"
+
+	// 获取分类信息
+	classifyInfo, err = models.GetChartClassifyById(chartClassifyId)
+	if err != nil {
+		return
+	}
+
+	// 分类来源校验
+	if classifyInfo.Source != source {
+		errMsg = "图表分类异常"
+		err = errors.New(fmt.Sprintf("图表分类来源异常,修改来源:%d,分类来源:%d", source, classifyInfo.Source))
+		isSendEmail = false
+		return
+	}
+
+	// 需要变更的字段
+	updateCols := make([]string, 0)
+
+	// 语言版本校验
+	switch lang {
+	case utils.EnLangVersion:
+		if classifyInfo.ChartClassifyNameEn != chartClassifyName {
+			count, tmpErr := models.GetChartClassifyEnCount(chartClassifyName, classifyInfo.ParentId, source)
+			if tmpErr != nil {
+				errMsg = "判断名称是否已存在失败"
+				err = errors.New("判断名称是否已存在失败,Err:" + tmpErr.Error())
+				return
+			}
+			if count > 0 {
+				errMsg = "分类名称已存在,请重新输入"
+				err = errors.New(errMsg)
+				isSendEmail = false
+				return
+			}
+
+			classifyInfo.ChartClassifyNameEn = chartClassifyName
+			classifyInfo.ModifyTime = time.Now()
+			updateCols = append(updateCols, "ChartClassifyNameEn", "ModifyTime")
+		}
+	default:
+		if classifyInfo.ChartClassifyName != chartClassifyName {
+			count, tmpErr := models.GetChartClassifyCount(chartClassifyName, classifyInfo.ParentId, source)
+			if tmpErr != nil {
+				errMsg = "判断名称是否已存在失败"
+				err = errors.New("判断名称是否已存在失败,Err:" + tmpErr.Error())
+				return
+			}
+			if count > 0 {
+				errMsg = "分类名称已存在,请重新输入"
+				err = errors.New(errMsg)
+				isSendEmail = false
+				return
+			}
+
+			classifyInfo.ChartClassifyName = chartClassifyName
+			classifyInfo.ModifyTime = time.Now()
+			updateCols = append(updateCols, "ChartClassifyName", "ModifyTime")
+		}
+	}
+
+	if len(updateCols) > 0 {
+		err = classifyInfo.Update(updateCols)
+	}
+
+	return
+}
+
+// MoveChartClassify 移动图表分类
+func MoveChartClassify(req models.MoveChartClassifyReq, sysUser *system.Admin, classifyType int) (err error, errMsg string) {
+	// req.ClassifyId, req.ParentClassifyId, req.PrevClassifyId, req.NextClassifyId
+	classifyId := req.ClassifyId
+	parentClassifyId := req.ParentClassifyId
+	prevClassifyId := req.PrevClassifyId
+	nextClassifyId := req.NextClassifyId
+
+	chartInfoId := req.ChartInfoId
+	prevChartInfoId := req.PrevChartInfoId
+	nextChartInfoId := req.NextChartInfoId
+
+	//首先确定移动的对象是分类还是指标
+	//判断上一个节点是分类还是指标
+	//判断下一个节点是分类还是指标
+	//同时更新分类目录下的分类sort和指标sort
+	//更新当前移动的分类或者指标sort
+
+	var parentEdbClassifyInfo *models.ChartClassify
+	if parentClassifyId > 0 {
+		parentEdbClassifyInfo, err = models.GetChartClassifyById(parentClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上级分类信息失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	//如果有传入 上一个兄弟节点分类id
+	var (
+		edbClassifyInfo *models.ChartClassify
+		prevClassify    *models.ChartClassify
+		nextClassify    *models.ChartClassify
+
+		chartInfo     *models.ChartInfo
+		prevChartInfo *models.ChartInfo
+		nextChartInfo *models.ChartInfo
+		prevSort      int
+		nextSort      int
+	)
+
+	// 移动对象为分类, 判断权限
+	if chartInfoId == 0 {
+		edbClassifyInfo, err = models.GetChartClassifyById(classifyId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = "当前分类不存在"
+				err = errors.New("获取分类信息失败,Err:" + err.Error())
+				return
+			}
+			errMsg = "移动失败"
+			err = errors.New("获取分类信息失败,Err:" + err.Error())
+			return
+		}
+		if parentClassifyId > 0 && parentEdbClassifyInfo.Level == 6 {
+			errMsg = "最高只支持添加6级分类"
+			err = errors.New(errMsg)
+			return
+		}
+		// 如果是移动目录, 那么校验一下父级目录下是否有重名目录
+		exists, e := models.GetChartClassifyByParentIdAndName(parentClassifyId, edbClassifyInfo.ChartClassifyName, classifyId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			errMsg = "移动失败"
+			err = fmt.Errorf("获取父级分类下的同名分类失败, Err: %s", e.Error())
+			return
+		}
+		if exists != nil {
+			errMsg = "移动失败,分类名称已存在"
+			return
+		}
+
+	} else {
+		chartInfo, err = models.GetChartInfoById(req.ChartInfoId)
+		if err != nil {
+			if err.Error() == utils.ErrNoRow() {
+				errMsg = "当前指标不存在"
+				err = errors.New("获取分类信息失败,Err:" + err.Error())
+				return
+			}
+			errMsg = "移动失败"
+			err = errors.New("获取分类信息失败,Err:" + err.Error())
+			return
+		}
+		if parentClassifyId == 0 {
+			errMsg = "移动失败,指标必须挂在分类下"
+			err = errors.New(errMsg)
+			return
+		}
+	}
+
+	if prevClassifyId > 0 {
+		prevClassify, err = models.GetChartClassifyById(prevClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		prevSort = prevClassify.Sort
+	} else if prevChartInfoId > 0 {
+		prevChartInfo, err = models.GetChartInfoById(prevChartInfoId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取上一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		prevSort = prevChartInfo.Sort
+	}
+
+	if nextClassifyId > 0 {
+		//下一个兄弟节点
+		nextClassify, err = models.GetChartClassifyById(nextClassifyId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取下一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		nextSort = nextClassify.Sort
+	} else if nextChartInfoId > 0 {
+		//下一个兄弟节点
+		nextChartInfo, err = models.GetChartInfoById(nextChartInfoId)
+		if err != nil {
+			errMsg = "移动失败"
+			err = errors.New("获取下一个兄弟节点分类信息失败,Err:" + err.Error())
+			return
+		}
+		nextSort = nextChartInfo.Sort
+	}
+
+	err, errMsg = moveChartClassify(parentEdbClassifyInfo, edbClassifyInfo, prevClassify, nextClassify, chartInfo, prevChartInfo, nextChartInfo, parentClassifyId, prevSort, nextSort, classifyType)
+	return
+}
+
+// moveChartClassify 移动图表分类
+func moveChartClassify(parentClassifyInfo, edbClassifyInfo, prevClassify, nextClassify *models.ChartClassify, chartInfo, prevChartInfo, nextChartInfo *models.ChartInfo, parentClassifyId int, prevSort, nextSort int, classifyType int) (err error, errMsg string) {
+	updateCol := make([]string, 0)
+
+	// 移动对象为分类, 判断分类是否存在
+	if edbClassifyInfo != nil {
+		oldParentId := edbClassifyInfo.ParentId
+		//oldLevel := edbClassifyInfo.Level
+		//var classifyIds []int
+		if oldParentId != parentClassifyId {
+			//更新子分类对应的level
+			/*childList, e, m := GetChildClassifyByClassifyId(edbClassifyInfo.ClassifyId)
+			if e != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询子分类失败,Err:" + e.Error() + m)
+				return
+			}
+
+			if len(childList) > 0 {
+				for _, v := range childList {
+					if v.ClassifyId == edbClassifyInfo.ClassifyId {
+						continue
+					}
+					classifyIds = append(classifyIds, v.ClassifyId)
+				}
+			}*/
+		}
+		//判断上级id是否一致,如果不一致的话,那么需要移动该分类层级
+		if edbClassifyInfo.ParentId != parentClassifyId && parentClassifyId != 0 {
+			if edbClassifyInfo.Level != parentClassifyInfo.Level+1 { //禁止层级调整
+				errMsg = "移动失败"
+				err = errors.New("不支持目录层级变更")
+				return
+			}
+			edbClassifyInfo.ParentId = parentClassifyInfo.ChartClassifyId
+			edbClassifyInfo.Level = parentClassifyInfo.Level + 1
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "ParentId", "Level", "ModifyTime")
+		} else if edbClassifyInfo.ParentId != parentClassifyId && parentClassifyId == 0 {
+			errMsg = "移动失败"
+			err = errors.New("不支持目录层级变更")
+			return
+		}
+
+		if prevSort > 0 {
+			//如果是移动在两个兄弟节点之间
+			if nextSort > 0 {
+				//下一个兄弟节点
+				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+				if prevSort == nextSort || prevSort == edbClassifyInfo.Sort {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 2`
+
+					//变更分类
+					if prevClassify != nil {
+						_ = models.UpdateChartClassifySortByParentId(parentClassifyId, prevClassify.ChartClassifyId, prevClassify.Sort, updateSortStr, classifyType)
+					} else {
+						_ = models.UpdateChartClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr, classifyType)
+					}
+
+					//变更指标
+					if prevChartInfo != nil {
+						//变更兄弟节点的排序
+						_ = models.UpdateChartInfoSortByClassifyId(parentClassifyId, prevSort, prevChartInfo.ChartInfoId, []int{classifyType}, updateSortStr)
+					} else {
+						_ = models.UpdateChartInfoSortByClassifyId(parentClassifyId, prevSort, 0, []int{classifyType}, updateSortStr)
+					}
+				} else {
+					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+					if nextSort-prevSort == 1 {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 1`
+
+						//变更分类
+						if prevClassify != nil {
+							_ = models.UpdateChartClassifySortByParentId(parentClassifyId, prevClassify.ChartClassifyId, prevSort, updateSortStr, classifyType)
+						} else {
+							_ = models.UpdateChartClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr, classifyType)
+						}
+
+						//变更指标
+						if prevChartInfo != nil {
+							//变更兄弟节点的排序
+							_ = models.UpdateChartInfoSortByClassifyId(parentClassifyId, prevSort, prevChartInfo.ChartInfoId, []int{classifyType}, updateSortStr)
+						} else {
+							_ = models.UpdateChartInfoSortByClassifyId(parentClassifyId, prevSort, 0, []int{classifyType}, updateSortStr)
+						}
+
+					}
+				}
+			}
+
+			edbClassifyInfo.Sort = prevSort + 1
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else if prevClassify == nil && nextClassify == nil && prevChartInfo == nil && nextChartInfo == nil && parentClassifyId > 0 {
+			//处理只拖动到目录里,默认放到目录底部的情况
+			var maxSort int
+			maxSort, err = models.GetChartClassifyMaxSort(parentClassifyId, classifyType)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询组内排序信息失败,Err:" + err.Error())
+				return
+			}
+			edbClassifyInfo.Sort = maxSort + 1 //那就是排在组内最后一位
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else {
+			// 拖动到父级分类的第一位
+			firstClassify, tmpErr := models.GetFirstChartClassifyByParentId(parentClassifyId)
+			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+				errMsg = "移动失败"
+				err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = models.UpdateChartClassifySortByParentId(parentClassifyId, firstClassify.ChartClassifyId-1, 0, updateSortStr, classifyType)
+				//该分类下的所有指标也需要+1
+				_ = models.UpdateChartInfoSortByClassifyId(parentClassifyId, 0, 0, []int{classifyType}, updateSortStr)
+			} else {
+				//如果该分类下存在指标,且第一个指标的排序等于0,那么需要调整排序
+				firstEdb, tErr := models.GetFirstChartInfoByClassifyId(parentClassifyId)
+				if tErr != nil && tErr.Error() != utils.ErrNoRow() {
+					errMsg = "移动失败"
+					err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tErr.Error())
+					return
+				}
+
+				//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+				if firstEdb != nil && firstEdb.Sort == 0 {
+					updateSortStr := ` sort + 1 `
+					_ = models.UpdateChartInfoSortByClassifyId(parentClassifyId, 0, firstEdb.ChartInfoId-1, []int{classifyType}, updateSortStr)
+					_ = models.UpdateChartClassifySortByParentId(parentClassifyId, 0, 0, updateSortStr, classifyType)
+				}
+			}
+
+			edbClassifyInfo.Sort = 0 //那就是排在第一位
+			edbClassifyInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		}
+
+		//更新
+		if len(updateCol) > 0 {
+			err = edbClassifyInfo.Update(updateCol)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("修改失败,Err:" + err.Error())
+				return
+			}
+			// todo 更新对应分类的root_id和层级
+			/*if oldParentId != parentClassifyId {
+				if len(classifyIds) > 0 {
+					levelStep := edbClassifyInfo.Level - oldLevel
+					err = models.UpdateEdbClassifyChildByParentClassifyId(classifyIds, edbClassifyInfo.RootId, levelStep)
+					if err != nil {
+						errMsg = "移动失败"
+						err = errors.New("更新子分类失败,Err:" + err.Error())
+						return
+					}
+				}
+			}*/
+		}
+	} else {
+		if chartInfo == nil {
+			errMsg = "当前图表不存在"
+			err = errors.New(errMsg)
+			return
+		}
+		//如果改变了分类,那么移动该指标数据
+		if chartInfo.ChartClassifyId != parentClassifyId {
+			chartInfo.ChartClassifyId = parentClassifyId
+			chartInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "ClassifyId", "ModifyTime")
+		}
+		if prevSort > 0 {
+			//如果是移动在两个兄弟节点之间
+			if nextSort > 0 {
+				//下一个兄弟节点
+				//如果上一个兄弟与下一个兄弟的排序权重是一致的,那么需要将下一个兄弟(以及下个兄弟的同样排序权重)的排序权重+2,自己变成上一个兄弟的排序权重+1
+				if prevSort == nextSort || prevSort == chartInfo.Sort {
+					//变更兄弟节点的排序
+					updateSortStr := `sort + 2`
+
+					//变更分类
+					if prevClassify != nil {
+						_ = models.UpdateChartClassifySortByParentId(parentClassifyId, prevClassify.ChartClassifyId, prevClassify.Sort, updateSortStr, classifyType)
+					} else {
+						_ = models.UpdateChartClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr, classifyType)
+					}
+
+					//变更指标
+					if prevChartInfo != nil {
+						//变更兄弟节点的排序
+						_ = models.UpdateChartInfoSortByClassifyId(parentClassifyId, prevSort, prevChartInfo.ChartInfoId, []int{classifyType}, updateSortStr)
+					} else {
+						_ = models.UpdateChartInfoSortByClassifyId(parentClassifyId, prevSort, 0, []int{classifyType}, updateSortStr)
+					}
+				} else {
+					//如果下一个兄弟的排序权重正好是上个兄弟节点的下一层,那么需要再加一层了
+					if nextSort-prevSort == 1 {
+						//变更兄弟节点的排序
+						updateSortStr := `sort + 1`
+						//变更分类
+						if prevClassify != nil {
+							_ = models.UpdateChartClassifySortByParentId(parentClassifyId, prevClassify.ChartClassifyId, prevSort, updateSortStr, classifyType)
+						} else {
+							_ = models.UpdateChartClassifySortByParentId(parentClassifyId, 0, prevSort, updateSortStr, classifyType)
+						}
+
+						//变更指标
+						if prevChartInfo != nil {
+							//变更兄弟节点的排序
+							_ = models.UpdateChartInfoSortByClassifyId(parentClassifyId, prevSort, prevChartInfo.ChartInfoId, []int{classifyType}, updateSortStr)
+						} else {
+							_ = models.UpdateChartInfoSortByClassifyId(parentClassifyId, prevSort, 0, []int{classifyType}, updateSortStr)
+						}
+					}
+				}
+			}
+
+			chartInfo.Sort = prevSort + 1
+			chartInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else if prevClassify == nil && nextClassify == nil && prevChartInfo == nil && nextChartInfo == nil && parentClassifyId > 0 {
+			//处理只拖动到目录里,默认放到目录底部的情况
+			var maxSort int
+			maxSort, err = models.GetChartClassifyMaxSort(parentClassifyId, classifyType)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("查询组内排序信息失败,Err:" + err.Error())
+				return
+			}
+			chartInfo.Sort = maxSort + 1 //那就是排在组内最后一位
+			chartInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		} else {
+			// 拖动到父级分类的第一位
+			firstClassify, tmpErr := models.GetFirstChartClassifyByParentId(parentClassifyId)
+			if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+				errMsg = "移动失败"
+				err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tmpErr.Error())
+				return
+			}
+
+			//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+			if firstClassify != nil && firstClassify.Sort == 0 {
+				updateSortStr := ` sort + 1 `
+				_ = models.UpdateChartClassifySortByParentId(parentClassifyId, firstClassify.ChartClassifyId-1, 0, updateSortStr, classifyType)
+				//该分类下的所有指标也需要+1
+				_ = models.UpdateChartInfoSortByClassifyId(parentClassifyId, 0, 0, []int{classifyType}, updateSortStr)
+			} else {
+				//如果该分类下存在指标,且第一个指标的排序等于0,那么需要调整排序
+				firstEdb, tErr := models.GetFirstChartInfoByClassifyId(parentClassifyId)
+				if tErr != nil && tErr.Error() != utils.ErrNoRow() {
+					errMsg = "移动失败"
+					err = errors.New("获取获取当前父级分类下的排序第一条的分类信息失败,Err:" + tErr.Error())
+					return
+				}
+
+				//如果该分类下存在其他分类,且第一个其他分类的排序等于0,那么需要调整排序
+				if firstEdb != nil && firstEdb.Sort == 0 {
+					updateSortStr := ` sort + 1 `
+					_ = models.UpdateChartInfoSortByClassifyId(parentClassifyId, 0, firstEdb.ChartInfoId-1, []int{classifyType}, updateSortStr)
+					_ = models.UpdateChartClassifySortByParentId(parentClassifyId, 0, 0, updateSortStr, classifyType)
+				}
+			}
+
+			chartInfo.Sort = 0 //那就是排在第一位
+			chartInfo.ModifyTime = time.Now()
+			updateCol = append(updateCol, "Sort", "ModifyTime")
+		}
+
+		//更新
+		if len(updateCol) > 0 {
+			err = chartInfo.Update(updateCol)
+			if err != nil {
+				errMsg = "移动失败"
+				err = errors.New("修改失败,Err:" + err.Error())
+				return
+			}
+		}
+	}
+	return
+}

+ 130 - 0
services/chart_info_elastic.go

@@ -0,0 +1,130 @@
+package services
+
+import (
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/services/elastic"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"strconv"
+)
+
+func CreateChartInfoIndex() {
+	indexName := utils.CHART_INDEX_NAME
+	mappingJson := `{
+  "mappings": {
+    "dynamic": true,
+    "properties": {
+      "ChartInfoId": {
+        "type": "integer"
+      },
+      "ChartName": {
+        "type": "text",
+        "term_vector": "with_positions_offsets",
+        "analyzer": "ik_smart"
+      },
+      "ChartClassifyId": {
+        "type": "integer"
+      },
+      "SysUserId": {
+        "type": "integer"
+      },
+      "SysUserRealName": {
+        "type": "text",
+        "term_vector": "with_positions_offsets",
+        "analyzer": "ik_smart"
+      },
+      "UniqueCode": {
+        "type": "text",
+        "term_vector": "with_positions_offsets"
+      },
+      "Unit": {
+        "type": "text",
+        "term_vector": "with_positions_offsets"
+      },
+      "CreateTime": {
+        "type": "date"
+      },
+      "ModifyTime": {
+        "type": "date"
+      },
+      "DateType": {
+        "type": "integer"
+      },
+      "StartDate": {
+        "type": "text",
+        "term_vector": "with_positions_offsets"
+      },
+      "EndDate": {
+        "type": "text",
+        "term_vector": "with_positions_offsets"
+      },
+      "IsSetName": {
+        "type": "integer"
+      },
+      "EdbInfoIds": {
+        "type": "text",
+        "term_vector": "with_positions_offsets"
+      },
+      "ChartType": {
+        "type": "integer"
+      },
+      "Calendar": {
+        "type": "text",
+        "term_vector": "with_positions_offsets"
+      },
+      "ChartImage": {
+        "type": "text",
+        "term_vector": "with_positions_offsets"
+      }
+    }
+  }
+}`
+	elastic.EsCreateIndex(indexName, mappingJson)
+}
+
+func AddAllChartInfo() {
+	allList, err := models.GetChartInfoAllList()
+	if err != nil {
+		fmt.Println("GetArticleAll Err:", err.Error())
+		return
+	}
+	total := len(allList)
+	for k, v := range allList {
+		EsAddOrEditChartInfo(v.ChartInfoId)
+		//fmt.Println(v.ChartInfoId)
+		fmt.Println("剩余", total-k-1, "条数据,当前图表id:", v.ChartInfoId)
+	}
+}
+
+// EsAddOrEditChartInfo 新增和修改ES中的图表数据
+func EsAddOrEditChartInfo(chartInfoId int) {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println("新增和修改ES中的图表数据失败:", err.Error())
+		}
+	}()
+	itemInfo, _ := models.GetChartInfoById(chartInfoId)
+	//添加es
+	err = elastic.EsAddOrEditChartInfoData(utils.CHART_INDEX_NAME, strconv.Itoa(itemInfo.ChartInfoId), itemInfo)
+	return
+}
+
+// EsDeleteChartInfo 删除ES中的图表数据
+func EsDeleteChartInfo(chartInfoId int) {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println("删除ES中的图表数据失败:", err.Error())
+		}
+	}()
+	//添加es
+	err = elastic.EsDeleteDataV2(utils.CHART_INDEX_NAME, strconv.Itoa(chartInfoId))
+	return
+}
+
+// EsSearchChartInfo 搜索图表信息
+func EsSearchChartInfo(keyword string, showSysId int, sourceList []int, noPermissionChartIdList []int, startSize, pageSize int) (list []*models.ChartInfo, total int64, err error) {
+	list, total, err = elastic.SearchChartInfoData(utils.CHART_INDEX_NAME, keyword, showSysId, sourceList, noPermissionChartIdList, startSize, pageSize)
+	return
+}

+ 1287 - 0
services/elastic/elastic.go

@@ -0,0 +1,1287 @@
+package elastic
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/olivere/elastic/v7"
+	"strconv"
+)
+
+// indexName:索引名称
+// mappingJson:表结构
+func EsCreateIndex(indexName, mappingJson string) (err error) {
+	client := utils.EsClient
+
+	//定义表结构
+	exists, err := client.IndexExists(indexName).Do(context.Background()) //<5>
+	if err != nil {
+		return
+	}
+	if !exists {
+		resp, err := client.CreateIndex(indexName).BodyJson(mappingJson).Do(context.Background())
+		//BodyJson(bodyJson).Do(context.Background())
+		if err != nil {
+			fmt.Println("CreateIndex Err:" + err.Error())
+			return err
+		}
+		fmt.Println(resp.Index, resp.ShardsAcknowledged, resp.Acknowledged)
+	} else {
+		fmt.Println(indexName + " 已存在")
+	}
+	return
+}
+
+// 删除数据
+func EsDeleteData(indexName, docId string) (err error) {
+	client := utils.EsClient
+
+	resp, err := client.Delete().Index(indexName).Id(docId).Do(context.Background())
+	fmt.Println(resp)
+	if err != nil {
+		return
+	}
+	if resp.Status == 0 {
+		fmt.Println("删除成功")
+	} else {
+		fmt.Println("AddData", resp.Status, resp.Result)
+	}
+	return
+}
+
+func MappingModify(indexName, mappingJson string) {
+	client := utils.EsClient
+	result, err := client.PutMapping().Index(indexName).BodyString(mappingJson).Do(context.Background())
+	fmt.Println(err)
+	fmt.Println(result)
+	return
+}
+
+// EsAddOrEditEdbInfoData 新增/修改es中的指标数据
+func EsAddOrEditEdbInfoData(indexName, docId string, item *models.EdbInfoList) (err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("EsAddOrEditData Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	resp, err := client.Index().Index(indexName).Id(docId).BodyJson(item).Do(context.Background())
+	if err != nil {
+		fmt.Println("新增失败:", err.Error())
+		return err
+	}
+	fmt.Println(resp)
+	if resp.Status == 0 {
+		fmt.Println("新增成功", resp.Result)
+		err = nil
+	} else {
+		fmt.Println("AddData", resp.Status, resp.Result)
+	}
+	return
+}
+
+// SearchEdbInfoData 查询es中的指标数据
+func SearchEdbInfoData(indexName, keywordStr string, from, size, filterSource, source int, edbInfoType int8, frequency string, noPermissionEdbInfoIdList []int) (total int64, list []*models.EdbInfoList, err error) {
+	list = make([]*models.EdbInfoList, 0)
+	defer func() {
+		if err != nil {
+			fmt.Println("EsAddOrEditData Err:", err.Error())
+		}
+	}()
+
+	highlight := elastic.NewHighlight()
+	highlight = highlight.Fields(elastic.NewHighlighterField("EdbCode"), elastic.NewHighlighterField("EdbName"))
+	highlight = highlight.PreTags("<font color='red'>").PostTags("</font>")
+
+	//var source map[string]interface{}
+	//source := map[string]interface{}{
+	//	"query": map[string]interface{}{
+	//		"match_all": map[string]interface{}{},
+	//	},
+	//}
+	mustMap := make([]interface{}, 0)
+	mustNotMap := make([]interface{}, 0)
+
+	//source := map[string]interface{}{
+	//	"query": map[string]interface{}{
+	//		"bool": map[string]interface{}{
+	//			"must": map[string]interface{}{
+	//				"query_string": map[string]interface{}{
+	//					"query":  keywordStr,
+	//					"fields": []string{"EdbCode", "EdbName"},
+	//				},
+	//			},
+	//		},
+	//	},
+	//}
+
+	switch filterSource {
+	case 2:
+		//source = map[string]interface{}{
+		//	"query": map[string]interface{}{
+		//		"bool": map[string]interface{}{
+		//			"must": map[string]interface{}{
+		//				"query_string": map[string]interface{}{
+		//					"query": keywordStr,
+		//				},
+		//			},
+		//			"filter": []interface{}{
+		//				map[string]interface{}{
+		//					"term": map[string]interface{}{
+		//						"Frequency.keyword": "月度",
+		//					},
+		//				}},
+		//		},
+		//	},
+		//}
+		mustMap = []interface{}{
+			map[string]interface{}{
+				"term": map[string]interface{}{
+					"Frequency.keyword": "月度",
+					//"Frequency.keyword": "月度",
+				},
+			},
+		}
+	case 3:
+		//source = map[string]interface{}{
+		//	"query": map[string]interface{}{
+		//		"bool": map[string]interface{}{
+		//			"must": map[string]interface{}{
+		//				"query_string": map[string]interface{}{
+		//					"query": keywordStr,
+		//				},
+		//			},
+		//			"must_not": []interface{}{
+		//				map[string]interface{}{
+		//					"match": map[string]interface{}{
+		//						"Frequency.keyword": "日度",
+		//					},
+		//				}},
+		//		},
+		//	},
+		//}
+
+		////注释掉,所有频度都可以变频 2022-08-31 14:31:28
+		//mustNotMap = []interface{}{
+		//	map[string]interface{}{
+		//		"match": map[string]interface{}{
+		//			"Frequency.keyword": "日度",
+		//			//"Frequency.keyword": "月度",
+		//		},
+		//	},
+		//}
+	case 4:
+		//source = map[string]interface{}{
+		//	"query": map[string]interface{}{
+		//		"bool": map[string]interface{}{
+		//			"must": map[string]interface{}{
+		//				"query_string": map[string]interface{}{
+		//					"query": keywordStr,
+		//				},
+		//			},
+		//			"filter": []interface{}{
+		//				map[string]interface{}{
+		//					"term": map[string]interface{}{
+		//						"EdbType": 1,
+		//					},
+		//				}},
+		//		},
+		//	},
+		//}
+		mustMap = []interface{}{
+			map[string]interface{}{
+				"term": map[string]interface{}{
+					"EdbType": 1,
+				},
+			},
+		}
+	case 5:
+		mustMap = []interface{}{
+			map[string]interface{}{
+				"term": map[string]interface{}{
+					"Source": 6,
+				},
+			},
+		}
+	case 6:
+		mustNotMap = []interface{}{
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"Frequency.keyword": "年度",
+				},
+			},
+		}
+	}
+
+	//指标来源
+	if source > 0 {
+		mustMap = append(mustMap, map[string]interface{}{
+			"term": map[string]interface{}{
+				"Source": source,
+				//"Frequency.keyword": "月度",
+			},
+		})
+	}
+
+	if frequency != "" {
+		mustMap = append(mustMap, map[string]interface{}{
+			"term": map[string]interface{}{
+				"Frequency.keyword": frequency,
+				//"Frequency.keyword": "月度",
+			},
+		})
+	}
+
+	// noPermissionEdbInfoIdList 无权限指标id
+	if len(noPermissionEdbInfoIdList) > 0 {
+		mustNotMap = append(mustNotMap, map[string]interface{}{
+			"terms": map[string]interface{}{
+				"EdbInfoId": noPermissionEdbInfoIdList,
+				//"Frequency.keyword": "月度",
+			},
+		})
+	}
+
+	// 指标类型:普通指标、预测指标(小于0 代表不区分指标是普通还是预测)
+	if edbInfoType >= 0 {
+		mustMap = append(mustMap, map[string]interface{}{
+			"term": map[string]interface{}{
+				"EdbInfoType": edbInfoType,
+			},
+		})
+	}
+
+	//普通指标
+	//mustMap = append(mustMap, map[string]interface{}{
+	//	"term": map[string]interface{}{
+	//		"EdbInfoType": 0,
+	//		//"Frequency.keyword": "月度",
+	//	},
+	//})
+
+	//关键字匹配
+	shouldMap := map[string]interface{}{
+		"should": []interface{}{
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"EdbCode": keywordStr,
+					//"Frequency.keyword": "月度",
+				},
+			},
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"EdbName": keywordStr,
+					//"Frequency.keyword": "月度",
+				},
+			},
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"EdbNameEn": keywordStr,
+					//"Frequency.keyword": "月度",
+				},
+			},
+		},
+	}
+	mustMap = append(mustMap, map[string]interface{}{
+		"bool": shouldMap,
+	})
+
+	return searchEdbInfoData(indexName, mustMap, mustNotMap, shouldMap, from, size)
+}
+
+func SearchEdbInfoDataBak(indexName, keywordStr string, from, size, filterSource, source int, frequency string) (total int64, list []*models.EdbInfoList, err error) {
+	list = make([]*models.EdbInfoList, 0)
+	defer func() {
+		if err != nil {
+			fmt.Println("EsAddOrEditData Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	//queryString := elastic.NewQueryStringQuery(keywordStr)
+	//boolQueryJson, err := json.Marshal(queryString)
+	//if err != nil {
+	//	fmt.Println("boolQueryJson err:", err)
+	//} else {
+	//	fmt.Println("boolQueryJson ", string(boolQueryJson))
+	//}
+
+	highlight := elastic.NewHighlight()
+	highlight = highlight.Fields(elastic.NewHighlighterField("EdbCode"), elastic.NewHighlighterField("EdbName"))
+	highlight = highlight.PreTags("<font color='red'>").PostTags("</font>")
+
+	//query := elastic.RawStringQuery(`{"match_all":{}}`)
+
+	//var source map[string]interface{}
+	//source := map[string]interface{}{
+	//	"query": map[string]interface{}{
+	//		"match_all": map[string]interface{}{},
+	//	},
+	//}
+	mustMap := make([]interface{}, 0)
+	mustNotMap := make([]interface{}, 0)
+
+	//source := map[string]interface{}{
+	//	"query": map[string]interface{}{
+	//		"bool": map[string]interface{}{
+	//			"must": map[string]interface{}{
+	//				"query_string": map[string]interface{}{
+	//					"query":  keywordStr,
+	//					"fields": []string{"EdbCode", "EdbName"},
+	//				},
+	//			},
+	//		},
+	//	},
+	//}
+
+	switch filterSource {
+	case 2:
+		//source = map[string]interface{}{
+		//	"query": map[string]interface{}{
+		//		"bool": map[string]interface{}{
+		//			"must": map[string]interface{}{
+		//				"query_string": map[string]interface{}{
+		//					"query": keywordStr,
+		//				},
+		//			},
+		//			"filter": []interface{}{
+		//				map[string]interface{}{
+		//					"term": map[string]interface{}{
+		//						"Frequency.keyword": "月度",
+		//					},
+		//				}},
+		//		},
+		//	},
+		//}
+		mustMap = []interface{}{
+			map[string]interface{}{
+				"term": map[string]interface{}{
+					"Frequency.keyword": "月度",
+					//"Frequency.keyword": "月度",
+				},
+			},
+		}
+	case 3:
+		//source = map[string]interface{}{
+		//	"query": map[string]interface{}{
+		//		"bool": map[string]interface{}{
+		//			"must": map[string]interface{}{
+		//				"query_string": map[string]interface{}{
+		//					"query": keywordStr,
+		//				},
+		//			},
+		//			"must_not": []interface{}{
+		//				map[string]interface{}{
+		//					"match": map[string]interface{}{
+		//						"Frequency.keyword": "日度",
+		//					},
+		//				}},
+		//		},
+		//	},
+		//}
+
+		////注释掉,所有频度都可以变频 2022-08-31 14:31:28
+		//mustNotMap = []interface{}{
+		//	map[string]interface{}{
+		//		"match": map[string]interface{}{
+		//			"Frequency.keyword": "日度",
+		//			//"Frequency.keyword": "月度",
+		//		},
+		//	},
+		//}
+	case 4:
+		//source = map[string]interface{}{
+		//	"query": map[string]interface{}{
+		//		"bool": map[string]interface{}{
+		//			"must": map[string]interface{}{
+		//				"query_string": map[string]interface{}{
+		//					"query": keywordStr,
+		//				},
+		//			},
+		//			"filter": []interface{}{
+		//				map[string]interface{}{
+		//					"term": map[string]interface{}{
+		//						"EdbType": 1,
+		//					},
+		//				}},
+		//		},
+		//	},
+		//}
+		mustMap = []interface{}{
+			map[string]interface{}{
+				"term": map[string]interface{}{
+					"EdbType": 1,
+				},
+			},
+		}
+	case 5:
+		mustMap = []interface{}{
+			map[string]interface{}{
+				"term": map[string]interface{}{
+					"Source": 6,
+				},
+			},
+		}
+	case 6:
+		mustNotMap = []interface{}{
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"Frequency.keyword": "年度",
+				},
+			},
+		}
+	}
+
+	//指标来源
+	if source > 0 {
+		mustMap = append(mustMap, map[string]interface{}{
+			"term": map[string]interface{}{
+				"Source": source,
+				//"Frequency.keyword": "月度",
+			},
+		})
+	}
+
+	if frequency != "" {
+		mustMap = append(mustMap, map[string]interface{}{
+			"term": map[string]interface{}{
+				"Frequency.keyword": frequency,
+				//"Frequency.keyword": "月度",
+			},
+		})
+	}
+
+	//普通指标
+	//mustMap = append(mustMap, map[string]interface{}{
+	//	"term": map[string]interface{}{
+	//		"EdbInfoType": 0,
+	//		//"Frequency.keyword": "月度",
+	//	},
+	//})
+
+	//关键字匹配
+	shouldMap := map[string]interface{}{
+		"should": []interface{}{
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"EdbCode": keywordStr,
+					//"Frequency.keyword": "月度",
+				},
+			},
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"EdbName": keywordStr,
+					//"Frequency.keyword": "月度",
+				},
+			},
+
+			// 因为关键词被分了,所以需要用下面的语句来让他 整个词 查询,从而加重整词的权重
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"EdbCode": map[string]interface{}{
+						"query":    keywordStr,
+						"operator": "and",
+					},
+					//"Frequency.keyword": "月度",
+				},
+			},
+
+			// 因为关键词被分了,所以需要用下面的语句来让他 整个词 查询,从而加重整词的权重
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"EdbName": map[string]interface{}{
+						"query":    keywordStr,
+						"operator": "and",
+					},
+					//"Frequency.keyword": "月度",
+				},
+			},
+		},
+	}
+	mustMap = append(mustMap, map[string]interface{}{
+		"bool": shouldMap,
+	})
+
+	queryMap := map[string]interface{}{
+		"query": map[string]interface{}{
+			"bool": map[string]interface{}{
+				"must":     mustMap,
+				"must_not": mustNotMap,
+				//"should":   shouldMap,
+			},
+		},
+	}
+
+	//根据条件数量统计
+	requestTotalHits := client.Count(indexName).BodyJson(queryMap)
+	total, err = requestTotalHits.Do(context.Background())
+	if err != nil {
+		return
+	}
+
+	queryMap["from"] = from
+	queryMap["size"] = size
+	jsonBytes, _ := json.Marshal(queryMap)
+	fmt.Println(string(jsonBytes))
+
+	//queryStr := fmt.Sprintf(`{"query":{"bool":{"must":{"query_string":{"query":"%s","fields":["EdbCode","EdbName"]}}}}}`, keywordStr)
+	//switch filterSource {
+	//case 2:
+	//	queryStr = fmt.Sprintf(`{"query":{"bool":{"must":{"query_string":{"query":"%s","fields":["EdbCode","EdbName"]}},"filter":{"term":{"Frequency.keyword":"%s"}}}}}`, keywordStr, "月度")
+	//case 3:
+	//	queryStr = fmt.Sprintf(`{"query":{"bool":{"must":{"query_string":{"query":"%s","fields":["EdbCode","EdbName"]}},"must_not":[{"match":{"Frequency.keyword":"%s"}}]}}}`, keywordStr, "日度")
+	//case 4:
+	//	queryStr = fmt.Sprintf(`{"query":{"bool":{"must":{"query_string":{"query":"%s","fields":["EdbCode","EdbName"]}},"must_not":[{"match":{"EdbType":1}}]}}}`, keywordStr)
+	//}
+	//queryString := elastic.RawStringQuery(queryStr)
+	//fmt.Println("queryString:", queryString)
+
+	//queryString := elastic.NewMatchQuery("EdbCode", keywordStr)
+	//request := client.Search(indexName).Highlight(highlight).From(from).Size(size).Query(queryString)
+
+	request := client.Search(indexName).Highlight(highlight).Source(queryMap) // sets the JSON request
+
+	//requestJson, err := json.Marshal(request)
+	//if err != nil {
+	//	fmt.Println("requestJson err:", err)
+	//}
+	//fmt.Println("requestJson ", string(requestJson))
+	searchMap := make(map[string]string)
+
+	searchResp, err := request.Do(context.Background())
+	if err != nil {
+		return
+	}
+	fmt.Println(searchResp)
+	fmt.Println(searchResp.Status)
+	if searchResp.Status != 0 {
+		return
+	}
+	if searchResp.Hits != nil {
+		for _, v := range searchResp.Hits.Hits {
+			if _, ok := searchMap[v.Id]; !ok {
+				itemJson, tmpErr := v.Source.MarshalJSON()
+				if tmpErr != nil {
+					err = tmpErr
+					fmt.Println("movieJson err:", err)
+					return
+				}
+				edbInfoItem := new(models.EdbInfoList)
+				tmpErr = json.Unmarshal(itemJson, &edbInfoItem)
+				if tmpErr != nil {
+					fmt.Println("json.Unmarshal movieJson err:", tmpErr)
+					err = tmpErr
+					return
+				}
+				if len(v.Highlight["EdbCode"]) > 0 {
+					edbInfoItem.EdbCode = v.Highlight["EdbCode"][0]
+				}
+				if len(v.Highlight["EdbName"]) > 0 {
+					edbInfoItem.EdbCode = v.Highlight["EdbName"][0]
+				}
+				list = append(list, edbInfoItem)
+				searchMap[v.Id] = v.Id
+			}
+		}
+	}
+
+	//for _, v := range result {
+	//	fmt.Println(v)
+	//}
+	return
+}
+
+// SearchAddPredictEdbInfoData 查询允许添加预测指标的数据
+func SearchAddPredictEdbInfoData(indexName, keywordStr string, noPermissionEdbInfoIdList []int, from, size int) (total int64, list []*models.EdbInfoList, err error) {
+	list = make([]*models.EdbInfoList, 0)
+	defer func() {
+		if err != nil {
+			fmt.Println("EsAddOrEditData Err:", err.Error())
+		}
+	}()
+
+	highlight := elastic.NewHighlight()
+	highlight = highlight.Fields(elastic.NewHighlighterField("EdbCode"), elastic.NewHighlighterField("EdbName"))
+	highlight = highlight.PreTags("<font color='red'>").PostTags("</font>")
+
+	mustMap := make([]interface{}, 0)
+	mustNotMap := make([]interface{}, 0)
+
+	mustNotMap = []interface{}{
+		//map[string]interface{}{
+		//	"terms": map[string]interface{}{
+		//		"Frequency.keyword": []string{"日度", "周度", "月度"},
+		//	},
+		//	//"match": map[string]interface{}{
+		//	//	"Frequency": []string{"日度", "周度", "月度"},
+		//	//	//"Frequency.keyword": []string{"日度", "周度", "月度"},
+		//	//},
+		//},
+	}
+
+	// 指标类型:普通指标、预算指标
+	mustMap = append(mustMap, map[string]interface{}{
+		"term": map[string]interface{}{
+			"EdbInfoType": 0,
+		},
+	})
+	mustMap = append(mustMap, map[string]interface{}{
+		"terms": map[string]interface{}{
+			"Frequency.keyword": []string{"日度", "周度", "月度"},
+		},
+	})
+
+	//关键字匹配
+	shouldMap := map[string]interface{}{
+		"should": []interface{}{
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"EdbCode": keywordStr,
+					//"Frequency.keyword": "月度",
+				},
+			},
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"EdbName": keywordStr,
+					//"Frequency.keyword": "月度",
+				},
+			},
+		},
+	}
+
+	// noPermissionEdbInfoIdList 无权限指标id
+	if len(noPermissionEdbInfoIdList) > 0 {
+		mustNotMap = append(mustNotMap, map[string]interface{}{
+			"terms": map[string]interface{}{
+				"EdbInfoId": noPermissionEdbInfoIdList,
+				//"Frequency.keyword": "月度",
+			},
+		})
+	}
+	return searchEdbInfoData(indexName, mustMap, mustNotMap, shouldMap, from, size)
+}
+
+// searchEdbInfoData 查询es中的指标数据
+func searchEdbInfoData(indexName string, mustMap, mustNotMap []interface{}, shouldMap map[string]interface{}, from, size int) (total int64, list []*models.EdbInfoList, err error) {
+	list = make([]*models.EdbInfoList, 0)
+	defer func() {
+		if err != nil {
+			fmt.Println("EsAddOrEditData Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	//queryString := elastic.NewQueryStringQuery(keywordStr)
+	//boolQueryJson, err := json.Marshal(queryString)
+	//if err != nil {
+	//	fmt.Println("boolQueryJson err:", err)
+	//} else {
+	//	fmt.Println("boolQueryJson ", string(boolQueryJson))
+	//}
+
+	highlight := elastic.NewHighlight()
+	highlight = highlight.Fields(elastic.NewHighlighterField("EdbCode"), elastic.NewHighlighterField("EdbName"))
+	highlight = highlight.PreTags("<font color='red'>").PostTags("</font>")
+
+	//query := elastic.RawStringQuery(`{"match_all":{}}`)
+
+	//关键字匹配
+	mustMap = append(mustMap, map[string]interface{}{
+		"bool": shouldMap,
+	})
+
+	queryMap := map[string]interface{}{
+		"query": map[string]interface{}{
+			"bool": map[string]interface{}{
+				"must":     mustMap,
+				"must_not": mustNotMap,
+				//"should":   shouldMap,
+			},
+		},
+	}
+
+	//根据条件数量统计
+	requestTotalHits := client.Count(indexName).BodyJson(queryMap)
+	total, err = requestTotalHits.Do(context.Background())
+	if err != nil {
+		return
+	}
+
+	queryMap["from"] = from
+	queryMap["size"] = size
+	jsonBytes, _ := json.Marshal(queryMap)
+	fmt.Println(string(jsonBytes))
+
+	//queryStr := fmt.Sprintf(`{"query":{"bool":{"must":{"query_string":{"query":"%s","fields":["EdbCode","EdbName"]}}}}}`, keywordStr)
+	//switch filterSource {
+	//case 2:
+	//	queryStr = fmt.Sprintf(`{"query":{"bool":{"must":{"query_string":{"query":"%s","fields":["EdbCode","EdbName"]}},"filter":{"term":{"Frequency.keyword":"%s"}}}}}`, keywordStr, "月度")
+	//case 3:
+	//	queryStr = fmt.Sprintf(`{"query":{"bool":{"must":{"query_string":{"query":"%s","fields":["EdbCode","EdbName"]}},"must_not":[{"match":{"Frequency.keyword":"%s"}}]}}}`, keywordStr, "日度")
+	//case 4:
+	//	queryStr = fmt.Sprintf(`{"query":{"bool":{"must":{"query_string":{"query":"%s","fields":["EdbCode","EdbName"]}},"must_not":[{"match":{"EdbType":1}}]}}}`, keywordStr)
+	//}
+	//queryString := elastic.RawStringQuery(queryStr)
+	//fmt.Println("queryString:", queryString)
+
+	//queryString := elastic.NewMatchQuery("EdbCode", keywordStr)
+	//request := client.Search(indexName).Highlight(highlight).From(from).Size(size).Query(queryString)
+
+	request := client.Search(indexName).Highlight(highlight).Source(queryMap) // sets the JSON request
+
+	//requestJson, err := json.Marshal(request)
+	//if err != nil {
+	//	fmt.Println("requestJson err:", err)
+	//}
+	//fmt.Println("requestJson ", string(requestJson))
+	searchMap := make(map[string]string)
+
+	searchResp, err := request.Do(context.Background())
+	if err != nil {
+		return
+	}
+	fmt.Println(searchResp)
+	fmt.Println(searchResp.Status)
+	if searchResp.Status != 0 {
+		return
+	}
+	//total = searchResp.TotalHits()
+	if searchResp.Hits != nil {
+		for _, v := range searchResp.Hits.Hits {
+			if _, ok := searchMap[v.Id]; !ok {
+				itemJson, tmpErr := v.Source.MarshalJSON()
+				if tmpErr != nil {
+					err = tmpErr
+					fmt.Println("movieJson err:", err)
+					return
+				}
+				edbInfoItem := new(models.EdbInfoList)
+				tmpErr = json.Unmarshal(itemJson, &edbInfoItem)
+				if tmpErr != nil {
+					fmt.Println("json.Unmarshal movieJson err:", tmpErr)
+					err = tmpErr
+					return
+				}
+				if len(v.Highlight["EdbCode"]) > 0 {
+					edbInfoItem.EdbCode = v.Highlight["EdbCode"][0]
+				}
+				if len(v.Highlight["EdbName"]) > 0 {
+					edbInfoItem.EdbCode = v.Highlight["EdbName"][0]
+				}
+				list = append(list, edbInfoItem)
+				searchMap[v.Id] = v.Id
+			}
+		}
+	}
+
+	return
+}
+
+// EsDeleteEdbInfoData 删除es中的指标数据
+func EsDeleteEdbInfoData(indexName, docId string) (err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("EsDeleteEdbInfoData Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	resp, err := client.Delete().Index(indexName).Id(docId).Do(context.Background())
+	fmt.Println(resp)
+	if err != nil {
+		return
+	}
+	if resp.Status == 0 {
+		fmt.Println("删除成功")
+	} else {
+		fmt.Println("AddData", resp.Status, resp.Result)
+	}
+	return
+}
+
+// AnalyzeResp 分词接口返回结构体
+type AnalyzeResp struct {
+	Tokens []struct {
+		EndOffset   int64  `json:"end_offset"`
+		Position    int64  `json:"position"`
+		StartOffset int64  `json:"start_offset"`
+		Token       string `json:"token"`
+		Type        string `json:"type"`
+	} `json:"tokens"`
+}
+
+// Analyze 根据输入的文字获取分词后的文字
+func Analyze(content string) (contentList []string, err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("Analyze Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	queryMap := map[string]string{
+		"text":     content,
+		"analyzer": "ik_max_word",
+	}
+	res, err := client.PerformRequest(
+		context.Background(),
+		elastic.PerformRequestOptions{
+			Method: "GET",
+			Path:   "/_analyze",
+			Body:   queryMap,
+			Stream: false,
+		},
+	)
+	if res.StatusCode == 200 {
+		var analyzeResp AnalyzeResp
+		tmpErr := json.Unmarshal(res.Body, &analyzeResp)
+		if tmpErr != nil {
+			err = errors.New("返回数据转结构体失败:" + tmpErr.Error())
+			return
+		}
+		for _, v := range analyzeResp.Tokens {
+			contentList = append(contentList, v.Token)
+		}
+	} else {
+		err = errors.New("分词失败,返回code异常:" + strconv.Itoa(res.StatusCode))
+	}
+	return
+}
+
+// EsAddOrEditChartInfoData 新增/修改es中的图表数据
+func EsAddOrEditChartInfoData(indexName, docId string, item *models.ChartInfo) (err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("EsAddOrEditData Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	resp, err := client.Index().Index(indexName).Id(docId).BodyJson(item).Do(context.Background())
+	if err != nil {
+		fmt.Println("新增失败:", err.Error())
+		return err
+	}
+	fmt.Println(resp)
+	if resp.Status == 0 {
+		fmt.Println("新增成功", resp.Result)
+		err = nil
+	} else {
+		fmt.Println("AddData", resp.Status, resp.Result)
+	}
+	return
+}
+
+// EsDeleteDataV2 删除es中的数据
+func EsDeleteDataV2(indexName, docId string) (err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("EsDeleteEdbInfoData Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	resp, err := client.Delete().Index(indexName).Id(docId).Do(context.Background())
+	fmt.Println(resp)
+	if err != nil {
+		return
+	}
+	if resp.Status == 0 {
+		fmt.Println("删除成功")
+	} else {
+		fmt.Println("AddData", resp.Status, resp.Result)
+	}
+	return
+}
+
+// SearchChartInfoData 查询es中的图表数据
+func SearchChartInfoData(indexName, keywordStr string, showSysId int, sourceList []int, noPermissionChartIdList []int, from, size int) (list []*models.ChartInfo, total int64, err error) {
+	list = make([]*models.ChartInfo, 0)
+	defer func() {
+		if err != nil {
+			fmt.Println("EsAddOrEditData Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	//queryString := elastic.NewQueryStringQuery(keywordStr)
+	//boolQueryJson, err := json.Marshal(queryString)
+	//if err != nil {
+	//	fmt.Println("boolQueryJson err:", err)
+	//} else {
+	//	fmt.Println("boolQueryJson ", string(boolQueryJson))
+	//}
+
+	highlight := elastic.NewHighlight()
+	highlight = highlight.Fields(elastic.NewHighlighterField("ChartName"))
+	highlight = highlight.PreTags("<font color='red'>").PostTags("</font>")
+
+	mustMap := make([]interface{}, 0)
+	mustNotMap := make([]interface{}, 0)
+
+	//指标来源
+	if showSysId > 0 {
+		mustMap = append(mustMap, map[string]interface{}{
+			"term": map[string]interface{}{
+				"SysUserId": showSysId,
+				//"Frequency.keyword": "月度",
+			},
+		})
+	}
+	mustMap = append(mustMap, map[string]interface{}{
+		"terms": map[string]interface{}{
+			"Source": sourceList,
+		},
+	})
+
+	//关键字匹配
+	shouldMap := map[string]interface{}{
+		"should": []interface{}{
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"ChartName": keywordStr,
+					//"Frequency.keyword": "月度",
+				},
+			},
+			// 因为关键词被分了,所以需要用下面的语句来让他 整个词 查询,从而加重整词的权重
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"ChartName": map[string]interface{}{
+						"query":    keywordStr,
+						"operator": "and",
+					},
+					//"Frequency.keyword": "月度",
+				},
+			},
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"ChartNameEn": keywordStr,
+					//"Frequency.keyword": "月度",
+				},
+			},
+			// 因为关键词被分了,所以需要用下面的语句来让他 整个词 查询,从而加重整词的权重
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"ChartNameEn": map[string]interface{}{
+						"query":    keywordStr,
+						"operator": "and",
+					},
+					//"Frequency.keyword": "月度",
+				},
+			},
+		},
+	}
+	mustMap = append(mustMap, map[string]interface{}{
+		"bool": shouldMap,
+	})
+
+	// noPermissionEdbInfoIdList 无权限指标id
+	if len(noPermissionChartIdList) > 0 {
+		mustNotMap = append(mustNotMap, map[string]interface{}{
+			"terms": map[string]interface{}{
+				"ChartInfoId": noPermissionChartIdList,
+				//"Frequency.keyword": "月度",
+			},
+		})
+	}
+
+	queryMap := map[string]interface{}{
+		"query": map[string]interface{}{
+			"bool": map[string]interface{}{
+				"must":     mustMap,
+				"must_not": mustNotMap,
+				//"should":   shouldMap,
+			},
+		},
+	}
+
+	//根据条件数量统计
+	requestTotalHits := client.Count(indexName).BodyJson(queryMap)
+	total, err = requestTotalHits.Do(context.Background())
+	if err != nil {
+		return
+	}
+
+	// 分页查询
+	queryMap["from"] = from
+	queryMap["size"] = size
+	jsonBytes, _ := json.Marshal(queryMap)
+	fmt.Println(string(jsonBytes))
+
+	request := client.Search(indexName).Highlight(highlight).Source(queryMap) // sets the JSON request
+
+	//requestJson, err := json.Marshal(request)
+	//if err != nil {
+	//	fmt.Println("requestJson err:", err)
+	//}
+	//fmt.Println("requestJson ", string(requestJson))
+	searchMap := make(map[string]string)
+
+	searchResp, err := request.Do(context.Background())
+	if err != nil {
+		return
+	}
+	fmt.Println(searchResp)
+	fmt.Println(searchResp.Status)
+	if searchResp.Status != 0 {
+		return
+	}
+
+	if searchResp.Hits != nil {
+		for _, v := range searchResp.Hits.Hits {
+			if _, ok := searchMap[v.Id]; !ok {
+				itemJson, tmpErr := v.Source.MarshalJSON()
+				if tmpErr != nil {
+					err = tmpErr
+					fmt.Println("movieJson err:", err)
+					return
+				}
+				chartInfoItem := new(models.ChartInfo)
+				tmpErr = json.Unmarshal(itemJson, &chartInfoItem)
+				if err != nil {
+					fmt.Println("json.Unmarshal chartInfoJson err:", err)
+					err = tmpErr
+					return
+				}
+				if len(v.Highlight["ChartName"]) > 0 {
+					chartInfoItem.ChartName = v.Highlight["ChartName"][0]
+				}
+				list = append(list, chartInfoItem)
+				searchMap[v.Id] = v.Id
+			}
+		}
+	}
+
+	//for _, v := range result {
+	//	fmt.Println(v)
+	//}
+	return
+}
+
+// EsAddOrEditDataInterface 新增/修改es中的数据
+func EsAddOrEditDataInterface(indexName, docId string, item interface{}) (err error) {
+	defer func() {
+		if err != nil {
+			fmt.Println("EsAddOrEditData Err:", err.Error())
+		}
+	}()
+	client := utils.EsClient
+
+	resp, err := client.Index().Index(indexName).Id(docId).BodyJson(item).Do(context.Background())
+	if err != nil {
+		fmt.Println("新增失败:", err.Error())
+		return err
+	}
+	fmt.Println(resp)
+	if resp.Status == 0 {
+		fmt.Println("新增成功", resp.Result)
+		err = nil
+	} else {
+		fmt.Println("AddData", resp.Status, resp.Result)
+	}
+	return
+}
+
+// SearchEdbInfoDataByAdminId 查询es中的指标数据
+func SearchEdbInfoDataByAdminId(indexName, keywordStr string, from, size, filterSource, source int, edbInfoType uint8, frequency string, adminId int) (total int64, list []*models.EdbInfoList, err error) {
+	list = make([]*models.EdbInfoList, 0)
+	defer func() {
+		if err != nil {
+			fmt.Println("EsAddOrEditData Err:", err.Error())
+		}
+	}()
+
+	highlight := elastic.NewHighlight()
+	highlight = highlight.Fields(elastic.NewHighlighterField("EdbCode"), elastic.NewHighlighterField("EdbName"))
+	highlight = highlight.PreTags("<font color='red'>").PostTags("</font>")
+
+	//var source map[string]interface{}
+	//source := map[string]interface{}{
+	//	"query": map[string]interface{}{
+	//		"match_all": map[string]interface{}{},
+	//	},
+	//}
+	mustMap := make([]interface{}, 0)
+	mustNotMap := make([]interface{}, 0)
+
+	//source := map[string]interface{}{
+	//	"query": map[string]interface{}{
+	//		"bool": map[string]interface{}{
+	//			"must": map[string]interface{}{
+	//				"query_string": map[string]interface{}{
+	//					"query":  keywordStr,
+	//					"fields": []string{"EdbCode", "EdbName"},
+	//				},
+	//			},
+	//		},
+	//	},
+	//}
+
+	switch filterSource {
+	case 2:
+		//source = map[string]interface{}{
+		//	"query": map[string]interface{}{
+		//		"bool": map[string]interface{}{
+		//			"must": map[string]interface{}{
+		//				"query_string": map[string]interface{}{
+		//					"query": keywordStr,
+		//				},
+		//			},
+		//			"filter": []interface{}{
+		//				map[string]interface{}{
+		//					"term": map[string]interface{}{
+		//						"Frequency.keyword": "月度",
+		//					},
+		//				}},
+		//		},
+		//	},
+		//}
+		mustMap = []interface{}{
+			map[string]interface{}{
+				"term": map[string]interface{}{
+					"Frequency.keyword": "月度",
+					//"Frequency.keyword": "月度",
+				},
+			},
+		}
+	case 3:
+		//source = map[string]interface{}{
+		//	"query": map[string]interface{}{
+		//		"bool": map[string]interface{}{
+		//			"must": map[string]interface{}{
+		//				"query_string": map[string]interface{}{
+		//					"query": keywordStr,
+		//				},
+		//			},
+		//			"must_not": []interface{}{
+		//				map[string]interface{}{
+		//					"match": map[string]interface{}{
+		//						"Frequency.keyword": "日度",
+		//					},
+		//				}},
+		//		},
+		//	},
+		//}
+
+		////注释掉,所有频度都可以变频 2022-08-31 14:31:28
+		//mustNotMap = []interface{}{
+		//	map[string]interface{}{
+		//		"match": map[string]interface{}{
+		//			"Frequency.keyword": "日度",
+		//			//"Frequency.keyword": "月度",
+		//		},
+		//	},
+		//}
+	case 4:
+		//source = map[string]interface{}{
+		//	"query": map[string]interface{}{
+		//		"bool": map[string]interface{}{
+		//			"must": map[string]interface{}{
+		//				"query_string": map[string]interface{}{
+		//					"query": keywordStr,
+		//				},
+		//			},
+		//			"filter": []interface{}{
+		//				map[string]interface{}{
+		//					"term": map[string]interface{}{
+		//						"EdbType": 1,
+		//					},
+		//				}},
+		//		},
+		//	},
+		//}
+		mustMap = []interface{}{
+			map[string]interface{}{
+				"term": map[string]interface{}{
+					"EdbType": 1,
+				},
+			},
+		}
+	case 5:
+		mustMap = []interface{}{
+			map[string]interface{}{
+				"term": map[string]interface{}{
+					"Source": 6,
+				},
+			},
+		}
+	case 6:
+		mustNotMap = []interface{}{
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"Frequency.keyword": "年度",
+				},
+			},
+		}
+	}
+
+	//指标来源
+	if source > 0 {
+		mustMap = append(mustMap, map[string]interface{}{
+			"term": map[string]interface{}{
+				"Source": source,
+				//"Frequency.keyword": "月度",
+			},
+		})
+	}
+
+	if frequency != "" {
+		mustMap = append(mustMap, map[string]interface{}{
+			"term": map[string]interface{}{
+				"Frequency.keyword": frequency,
+				//"Frequency.keyword": "月度",
+			},
+		})
+	}
+
+	// 指标类型:普通指标、预算指标
+	mustMap = append(mustMap, map[string]interface{}{
+		"term": map[string]interface{}{
+			"EdbInfoType": edbInfoType,
+		},
+	})
+
+	//普通指标
+	//mustMap = append(mustMap, map[string]interface{}{
+	//	"term": map[string]interface{}{
+	//		"EdbInfoType": 0,
+	//		//"Frequency.keyword": "月度",
+	//	},
+	//})
+
+	//关键字匹配
+	shouldMap := map[string]interface{}{
+		"should": []interface{}{
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"EdbCode": keywordStr,
+					//"Frequency.keyword": "月度",
+				},
+			},
+			map[string]interface{}{
+				"match": map[string]interface{}{
+					"EdbName": keywordStr,
+					//"Frequency.keyword": "月度",
+				},
+			},
+		},
+	}
+	mustMap = append(mustMap, map[string]interface{}{
+		"bool": shouldMap,
+	})
+
+	//创建人
+	if adminId > 0 {
+		mustMap = append(mustMap, map[string]interface{}{
+			"term": map[string]interface{}{
+				"SysUserId": adminId,
+			},
+		})
+	}
+
+	return searchEdbInfoData(indexName, mustMap, mustNotMap, shouldMap, from, size)
+}

+ 12 - 0
services/elastic/elasticsearch.go

@@ -0,0 +1,12 @@
+package elastic
+
+import (
+	"fmt"
+)
+
+type tracelog struct{}
+
+// 实现输出
+func (tracelog) Printf(format string, v ...interface{}) {
+	fmt.Printf(format, v...)
+}

+ 8 - 8
services/login.go

@@ -1,15 +1,15 @@
 package services
 
 import (
-	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/system"
 	"eta/eta_forum_admin/utils"
 	"fmt"
 	"time"
 )
 
-func ThirdLogin(adminName string) (login models.LoginResp, err error) {
+func ThirdLogin(adminName string) (login system.LoginResp, err error) {
 	// 获取用户信息
-	sysUser, e := models.GetSysUserByAdminName(adminName)
+	sysUser, e := system.GetSysUserByAdminName(adminName)
 	if e != nil {
 		if e.Error() == utils.ErrNoRow() {
 			err = fmt.Errorf("用户不存在")
@@ -23,12 +23,12 @@ func ThirdLogin(adminName string) (login models.LoginResp, err error) {
 	account := utils.MD5(adminName)
 	// 获取用户未过期的session, 避免过于频繁生成token
 	expired := time.Now().AddDate(0, 0, 1).Format(utils.FormatDateTime)
-	session, _ := models.GetUserUnexpiredSysSession(adminName, expired)
+	session, _ := system.GetUserUnexpiredSysSession(adminName, expired)
 	if session != nil && session.AccessToken != "" {
 		token = session.AccessToken
 	} else {
 		token = utils.GenToken(account)
-		sysSession := new(models.SysSession)
+		sysSession := new(system.SysSession)
 		sysSession.UserName = adminName
 		sysSession.SysUserId = sysUser.AdminId
 		sysSession.ExpiredTime = time.Now().AddDate(0, 0, 90)
@@ -36,7 +36,7 @@ func ThirdLogin(adminName string) (login models.LoginResp, err error) {
 		sysSession.CreatedTime = time.Now()
 		sysSession.LastUpdatedTime = time.Now()
 		sysSession.AccessToken = token
-		if e = models.AddSysSession(sysSession); e != nil {
+		if e = system.AddSysSession(sysSession); e != nil {
 			err = fmt.Errorf("获取失败,新增session失败, err: " + e.Error())
 			return
 		}
@@ -83,13 +83,13 @@ func ThirdLogin(adminName string) (login models.LoginResp, err error) {
 
 	// 新增登录记录
 	{
-		record := new(models.SysUserLoginRecord)
+		record := new(system.SysUserLoginRecord)
 		record.Uid = sysUser.AdminId
 		record.UserName = adminName
 		record.Ip, _ = utils.GetLocalIP()
 		record.Stage = "login"
 		record.CreateTime = time.Now()
-		go models.AddSysUserLoginRecord(record)
+		go system.AddSysUserLoginRecord(record)
 	}
 	return
 }

+ 45 - 0
utils/config.go

@@ -14,6 +14,21 @@ var (
 	MONGODB_URL        string //mongodb连接
 	MONGODB_COMMUNITY  string //mongodb库名
 	MONGODB_CREDENTIAL string
+	Re                 error       //redis错误
+	Rc                 RedisClient //redis缓存
+)
+
+// ES配置
+var (
+	ES_URL      string // ES服务器地址
+	ES_USERNAME string // ES账号
+	ES_PASSWORD string // ES密码
+)
+
+// ES索引配置
+var (
+	DATA_INDEX_NAME  string //数据指标库索引
+	CHART_INDEX_NAME string //研究图库索引
 )
 
 // 公共api内部服务调用
@@ -54,6 +69,11 @@ var (
 	SyncIndexPath      string // 桥接服务-指标同步服务地址
 )
 
+// 基础配置
+var (
+	DesKey string // 接口返回加密KEY
+)
+
 // AlarmMsgUrl 报警服务地址
 var AlarmMsgUrl string
 
@@ -113,6 +133,31 @@ func init() {
 		AlarmMsgUrl = config["alarm_msg_url"]
 
 	}
+
+	// 初始化缓存
+	redisClient, err := initRedis(config["redis_type"], config["beego_cache"])
+	if err != nil {
+		fmt.Println("redis链接异常:", err)
+		panic(any(Re))
+	}
+
+	Rc = redisClient
+
+	// ES配置
+	{
+		ES_URL = config["es_url"]
+		ES_USERNAME = config["es_username"]
+		ES_PASSWORD = config["es_password"]
+	}
+
+	// 初始化ES
+	initEs()
+
+	// ES 索引
+	{
+		DATA_INDEX_NAME = config["data_index_name"]
+		CHART_INDEX_NAME = config["chart_index_name"]
+	}
 }
 
 //修改接口文档

+ 25 - 0
utils/constants.go

@@ -278,3 +278,28 @@ const (
 	COMPANY_PRODUCT_RAI_ID    = 2
 	COMPANY_PRODUCT_RAI_NAME  = "权益"
 )
+
+// 语言版本
+const (
+	ZhLangVersion = "zh" // 中文语言版本
+	EnLangVersion = "en" // 英文语言版本
+)
+
+// redis缓存
+const (
+	CACHE_ACCESS_TOKEN_LOGIN          = "eta_forum_admin:login:"                    //管理后台登录
+	CACHE_ACCESS_TOKEN_LOGIN_NO_TRUST = "eta_forum_admin:login:no_trust:"           //管理后台登录(不可信登录态)
+	CACHE_CHART_CLASSIFY              = "eta_forum_admin:chart:classify"            //图表分类数据
+	LoginCacheTime                    = 600                                         // 登录缓存时长, 分钟
+	CACHE_KEY_ADMIN_OPERATE_RECORD    = "eta_forum_admin:list:admin_operate_record" //系统用户操作日志队列
+)
+
+const (
+	UserLoginSalt = "MiQM9yusNA9T2uIH"         // 用户登录盐值
+	DesKeySalt    = "JMCqSoUrTAmyNNIRb0TtlrPk" // DesKey盐值
+)
+
+const (
+	CACHE_CRM_AUTH_CODE_PREFIX   = "eta_forum:crm_auth_code:"   // 免密登录Code-CRM
+	CACHE_FORUM_AUTH_CODE_PREFIX = "eta_forum:forum_auth_code:" // 免密登录Code-ETA
+)

+ 21 - 0
utils/elastic.go

@@ -0,0 +1,21 @@
+package utils
+
+import (
+	"github.com/olivere/elastic/v7"
+)
+
+// EsClient es客户端
+var EsClient *elastic.Client
+
+func initEs() {
+	client, err := elastic.NewClient(
+		elastic.SetURL(ES_URL),
+		elastic.SetBasicAuth(ES_USERNAME, ES_PASSWORD),
+		elastic.SetSniff(false))
+	EsClient = client
+	if err != nil {
+		panic("ElasticSearch连接失败,err:" + err.Error())
+		//go alarm_msg.SendAlarmMsg("ElasticSearch连接失败", 2)
+	}
+	return
+}

+ 33 - 0
utils/redis.go

@@ -0,0 +1,33 @@
+package utils
+
+import (
+	"eta/eta_forum_admin/utils/redis"
+	"time"
+)
+
+type RedisClient interface {
+	Get(key string) interface{}
+	RedisBytes(key string) (data []byte, err error)
+	RedisString(key string) (data string, err error)
+	RedisInt(key string) (data int, err error)
+	Put(key string, val interface{}, timeout time.Duration) error
+	SetNX(key string, val interface{}, timeout time.Duration) bool
+	Delete(key string) error
+	IsExist(key string) bool
+	LPush(key string, val interface{}) error
+	Brpop(key string, callback func([]byte))
+	GetRedisTTL(key string) time.Duration
+	Incrby(key string, num int) (interface{}, error)
+	Do(commandName string, args ...interface{}) (reply interface{}, err error)
+}
+
+func initRedis(redisType string, conf string) (redisClient RedisClient, err error) {
+	switch redisType {
+	case "cluster": // 集群
+		redisClient, err = redis.InitClusterRedis(conf)
+	default: // 默认走单机
+		redisClient, err = redis.InitStandaloneRedis(conf)
+	}
+
+	return
+}

+ 265 - 0
utils/redis/cluster_redis.go

@@ -0,0 +1,265 @@
+package redis
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/go-redis/redis/v8"
+	"strings"
+	"time"
+)
+
+// ClusterRedisClient
+// @Description: 集群的redis客户端
+type ClusterRedisClient struct {
+	redisClient *redis.ClusterClient
+}
+
+var DefaultKey = "zcmRedis"
+
+// InitClusterRedis
+// @Description: 初始化集群redis客户端
+// @param config
+// @return clusterRedisClient
+// @return err
+func InitClusterRedis(config string) (clusterRedisClient *ClusterRedisClient, err error) {
+	var cf map[string]string
+	err = json.Unmarshal([]byte(config), &cf)
+	if err != nil {
+		return
+	}
+	//if _, ok := cf["key"]; !ok {
+	//	cf["key"] = DefaultKey
+	//}
+
+	// 集群地址
+	connList := make([]string, 0)
+	if _, ok := cf["conn"]; !ok {
+		err = errors.New("config has no conn key")
+		return
+	} else {
+		connList = strings.Split(cf["conn"], ",")
+		if len(connList) <= 1 {
+			err = errors.New("conn address less than or equal to 1")
+			return
+		}
+	}
+
+	// 密码
+	if _, ok := cf["password"]; !ok {
+		cf["password"] = ""
+	}
+
+	// 创建 Redis 客户端配置对象
+	clusterOptions := &redis.ClusterOptions{
+		Addrs:    connList, // 设置 Redis 节点的 IP 地址和端口号
+		Password: cf["password"],
+	}
+
+	// 创建 Redis 集群客户端
+	client := redis.NewClusterClient(clusterOptions)
+
+	// 测试连接并获取信息
+	_, err = client.Ping(context.TODO()).Result()
+	if err != nil {
+		err = errors.New("redis 链接失败:" + err.Error())
+		return
+	}
+
+	clusterRedisClient = &ClusterRedisClient{redisClient: client}
+
+	return
+}
+
+// Get
+// @Description: 根据key获取数据(其实是返回的字节编码)
+// @receiver rc
+// @param key
+// @return interface{}
+func (rc *ClusterRedisClient) Get(key string) interface{} {
+	data, err := rc.redisClient.Get(context.TODO(), key).Bytes()
+	if err != nil {
+		return nil
+	}
+
+	return data
+}
+
+// RedisBytes
+// @Description: 根据key获取字节编码数据
+// @receiver rc
+// @param key
+// @return data
+// @return err
+func (rc *ClusterRedisClient) RedisBytes(key string) (data []byte, err error) {
+	data, err = rc.redisClient.Get(context.TODO(), key).Bytes()
+
+	return
+}
+
+// RedisString
+// @Description: 根据key获取字符串数据
+// @receiver rc
+// @param key
+// @return data
+// @return err
+func (rc *ClusterRedisClient) RedisString(key string) (data string, err error) {
+	data, err = rc.redisClient.Get(context.TODO(), key).Result()
+
+	return
+}
+
+// RedisInt
+// @Description: 根据key获取int数据
+// @receiver rc
+// @param key
+// @return data
+// @return err
+func (rc *ClusterRedisClient) RedisInt(key string) (data int, err error) {
+	data, err = rc.redisClient.Get(context.TODO(), key).Int()
+
+	return
+}
+
+// Put
+// @Description: put一个数据到redis
+// @receiver rc
+// @param key
+// @param val
+// @param timeout
+// @return error
+func (rc *ClusterRedisClient) Put(key string, val interface{}, timeout time.Duration) error {
+	var err error
+	err = rc.redisClient.SetEX(context.TODO(), key, val, timeout).Err()
+	if err != nil {
+		return err
+	}
+
+	err = rc.redisClient.HSet(context.TODO(), DefaultKey, key, true).Err()
+
+	return err
+}
+
+// SetNX
+// @Description: 设置一个会过期时间的值
+// @receiver rc
+// @param key
+// @param val
+// @param timeout
+// @return bool
+func (rc *ClusterRedisClient) SetNX(key string, val interface{}, timeout time.Duration) bool {
+	result, err := rc.redisClient.SetEX(context.TODO(), key, val, timeout).Result()
+	if err != nil || result != "OK" {
+		return false
+	}
+
+	return true
+}
+
+// Delete
+// @Description: 删除redis中的键值对
+// @receiver rc
+// @param key
+// @return error
+func (rc *ClusterRedisClient) Delete(key string) error {
+	var err error
+
+	err = rc.redisClient.Del(context.TODO(), key).Err()
+	if err != nil {
+		return err
+	}
+
+	err = rc.redisClient.HDel(context.TODO(), DefaultKey, key).Err()
+
+	return err
+}
+
+// IsExist
+// @Description: 根据key判断是否写入缓存中
+// @receiver rc
+// @param key
+// @return bool
+func (rc *ClusterRedisClient) IsExist(key string) bool {
+	result, err := rc.redisClient.Exists(context.TODO(), key).Result()
+	if err != nil {
+		return false
+	}
+	if result == 0 {
+		_ = rc.redisClient.HDel(context.TODO(), DefaultKey, key).Err()
+		return false
+	}
+
+	return true
+}
+
+// LPush
+// @Description: 写入list
+// @receiver rc
+// @param key
+// @param val
+// @return error
+func (rc *ClusterRedisClient) LPush(key string, val interface{}) error {
+	data, _ := json.Marshal(val)
+	err := rc.redisClient.LPush(context.TODO(), key, data).Err()
+
+	return err
+}
+
+// Brpop
+// @Description: 从list中读取
+// @receiver rc
+// @param key
+// @param callback
+func (rc *ClusterRedisClient) Brpop(key string, callback func([]byte)) {
+	values, err := rc.redisClient.BRPop(context.TODO(), 1*time.Second, key).Result()
+	if err != nil {
+		return
+	}
+	if len(values) < 2 {
+		fmt.Println("assert is wrong")
+		return
+	}
+
+	callback([]byte(values[1]))
+
+}
+
+// GetRedisTTL
+// @Description: 获取key的过期时间
+// @receiver rc
+// @param key
+// @return time.Duration
+func (rc *ClusterRedisClient) GetRedisTTL(key string) time.Duration {
+	value, err := rc.redisClient.TTL(context.TODO(), key).Result()
+	if err != nil {
+		return 0
+	}
+
+	return value
+
+}
+
+// Incrby
+// @Description: 设置自增值
+// @receiver rc
+// @param key
+// @param num
+// @return interface{}
+// @return error
+func (rc *ClusterRedisClient) Incrby(key string, num int) (interface{}, error) {
+	return rc.redisClient.IncrBy(context.TODO(), key, int64(num)).Result()
+}
+
+// Do
+// @Description: cmd执行redis命令
+// @receiver rc
+// @param commandName
+// @param args
+// @return reply
+// @return err
+func (rc *ClusterRedisClient) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
+	newArgs := []interface{}{commandName}
+	newArgs = append(newArgs, args...)
+	return rc.redisClient.Do(context.TODO(), newArgs...).Result()
+}

+ 257 - 0
utils/redis/standalone_redis.go

@@ -0,0 +1,257 @@
+package redis
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/go-redis/redis/v8"
+	"strconv"
+	"time"
+)
+
+// StandaloneRedisClient
+// @Description: 单机redis客户端
+type StandaloneRedisClient struct {
+	redisClient *redis.Client
+}
+
+func InitStandaloneRedis(config string) (standaloneRedis *StandaloneRedisClient, err error) {
+	var cf map[string]string
+	err = json.Unmarshal([]byte(config), &cf)
+	if err != nil {
+		return
+	}
+	//if _, ok := cf["key"]; !ok {
+	//	cf["key"] = DefaultKey
+	//}
+
+	if _, ok := cf["conn"]; !ok {
+		err = errors.New("config has no conn key")
+		return
+	}
+
+	// db库
+	dbNum := 0
+	// 如果指定了db库
+	if _, ok := cf["dbNum"]; ok {
+		dbNum, err = strconv.Atoi(cf["dbNum"])
+		if err != nil {
+			return
+		}
+	}
+
+	// 密码
+	if _, ok := cf["password"]; !ok {
+		cf["password"] = ""
+	}
+
+	client := redis.NewClient(&redis.Options{
+		Addr:     cf["conn"],
+		Password: cf["password"],
+		DB:       dbNum,
+		//PoolSize: 10, //连接池最大socket连接数,默认为10倍CPU数, 10 * runtime.NumCPU(暂不配置)
+	})
+
+	_, err = client.Ping(context.TODO()).Result()
+	if err != nil {
+		err = errors.New("redis 链接失败:" + err.Error())
+		return
+	}
+
+	standaloneRedis = &StandaloneRedisClient{redisClient: client}
+
+	return
+}
+
+// Get
+// @Description: 根据key获取数据(其实是返回的字节编码)
+// @receiver rc
+// @param key
+// @return interface{}
+func (rc *StandaloneRedisClient) Get(key string) interface{} {
+	data, err := rc.redisClient.Get(context.TODO(), key).Bytes()
+	if err != nil {
+		return nil
+	}
+
+	return data
+}
+
+// RedisBytes
+// @Description: 根据key获取字节编码数据
+// @receiver rc
+// @param key
+// @return data
+// @return err
+func (rc *StandaloneRedisClient) RedisBytes(key string) (data []byte, err error) {
+	data, err = rc.redisClient.Get(context.TODO(), key).Bytes()
+
+	return
+}
+
+// RedisString
+// @Description: 根据key获取字符串数据
+// @receiver rc
+// @param key
+// @return data
+// @return err
+func (rc *StandaloneRedisClient) RedisString(key string) (data string, err error) {
+	data, err = rc.redisClient.Get(context.TODO(), key).Result()
+
+	return
+}
+
+// RedisInt
+// @Description: 根据key获取int数据
+// @receiver rc
+// @param key
+// @return data
+// @return err
+func (rc *StandaloneRedisClient) RedisInt(key string) (data int, err error) {
+	data, err = rc.redisClient.Get(context.TODO(), key).Int()
+
+	return
+}
+
+// Put
+// @Description: put一个数据到redis
+// @receiver rc
+// @param key
+// @param val
+// @param timeout
+// @return error
+func (rc *StandaloneRedisClient) Put(key string, val interface{}, timeout time.Duration) error {
+	var err error
+	err = rc.redisClient.SetEX(context.TODO(), key, val, timeout).Err()
+	if err != nil {
+		return err
+	}
+
+	err = rc.redisClient.HSet(context.TODO(), DefaultKey, key, true).Err()
+
+	return err
+}
+
+// SetNX
+// @Description: 设置一个会过期时间的值
+// @receiver rc
+// @param key
+// @param val
+// @param timeout
+// @return bool
+func (rc *StandaloneRedisClient) SetNX(key string, val interface{}, timeout time.Duration) bool {
+	result, err := rc.redisClient.SetEX(context.TODO(), key, val, timeout).Result()
+	if err != nil || result != "OK" {
+		return false
+	}
+
+	return true
+}
+
+// Delete
+// @Description: 删除redis中的键值对
+// @receiver rc
+// @param key
+// @return error
+func (rc *StandaloneRedisClient) Delete(key string) error {
+	var err error
+
+	err = rc.redisClient.Del(context.TODO(), key).Err()
+	if err != nil {
+		return err
+	}
+
+	err = rc.redisClient.HDel(context.TODO(), DefaultKey, key).Err()
+
+	return err
+}
+
+// IsExist
+// @Description: 根据key判断是否写入缓存中
+// @receiver rc
+// @param key
+// @return bool
+func (rc *StandaloneRedisClient) IsExist(key string) bool {
+	result, err := rc.redisClient.Exists(context.TODO(), key).Result()
+	if err != nil {
+		return false
+	}
+	if result == 0 {
+		_ = rc.redisClient.HDel(context.TODO(), DefaultKey, key).Err()
+		return false
+	}
+
+	return true
+}
+
+// LPush
+// @Description: 写入list
+// @receiver rc
+// @param key
+// @param val
+// @return error
+func (rc *StandaloneRedisClient) LPush(key string, val interface{}) error {
+	data, _ := json.Marshal(val)
+	err := rc.redisClient.LPush(context.TODO(), key, data).Err()
+
+	return err
+}
+
+// Brpop
+// @Description: 从list中读取
+// @receiver rc
+// @param key
+// @param callback
+func (rc *StandaloneRedisClient) Brpop(key string, callback func([]byte)) {
+	values, err := rc.redisClient.BRPop(context.TODO(), 1*time.Second, key).Result()
+	if err != nil {
+		return
+	}
+	if len(values) < 2 {
+		fmt.Println("assert is wrong")
+		return
+	}
+
+	callback([]byte(values[1]))
+
+}
+
+// GetRedisTTL
+// @Description: 获取key的过期时间
+// @receiver rc
+// @param key
+// @return time.Duration
+func (rc *StandaloneRedisClient) GetRedisTTL(key string) time.Duration {
+	value, err := rc.redisClient.TTL(context.TODO(), key).Result()
+	if err != nil {
+		return 0
+	}
+
+	return value
+
+}
+
+// Incrby
+// @Description: 设置自增值
+// @receiver rc
+// @param key
+// @param num
+// @return interface{}
+// @return error
+func (rc *StandaloneRedisClient) Incrby(key string, num int) (interface{}, error) {
+	return rc.redisClient.IncrBy(context.TODO(), key, int64(num)).Result()
+}
+
+// Do
+// @Description: cmd执行redis命令
+// @receiver rc
+// @param commandName
+// @param args
+// @return reply
+// @return err
+func (rc *StandaloneRedisClient) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
+	newArgs := []interface{}{commandName}
+	newArgs = append(newArgs, args...)
+	return rc.redisClient.Do(context.TODO(), newArgs...).Result()
+}