xyxie 1 年之前
當前提交
acfa86ba6c

+ 9 - 0
.gitignore

@@ -0,0 +1,9 @@
+/.idea
+/rdlucklog
+/etalogs
+/binlog
+/conf
+/*.exe
+/main
+/.DS_Store
+*.tar.gz

+ 1 - 0
controllers/admin.go

@@ -0,0 +1 @@
+package controllers

+ 91 - 0
controllers/base_common.go

@@ -0,0 +1,91 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/services/alarm_msg"
+	"net/http"
+	"net/url"
+
+	"github.com/beego/beego/v2/server/web"
+
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/utils"
+)
+
+type BaseCommonController struct {
+	web.Controller
+}
+
+func (c *BaseCommonController) ServeJSON(encoding ...bool) {
+	// 方法处理完后,需要后置处理的业务逻辑
+	//if handlerList, ok := AfterHandlerUrlMap[c.Ctx.Request.URL.Path]; 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 alarm_msg.SendAlarmMsg("接口:URI:"+c.Ctx.Input.URI()+";无返回值", 3)
+		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))
+		}
+		if baseRes.Ret != 200 && baseRes.IsSendEmail {
+			//go utils.SendEmail(utils.APP_NAME_CN+"【"+utils.RunMode+"】"+"失败提醒", "URI:"+c.Ctx.Input.URI()+"<br/> "+"Params"+requestBody+" <br/>"+"ErrMsg:"+baseRes.ErrMsg+";<br/>Msg:"+baseRes.Msg+";<br/> Body:"+string(body)+"<br/>", utils.EmailSendToUsers)
+			go alarm_msg.SendAlarmMsg("失败提醒:URI:"+c.Ctx.Input.URI()+";请求入参:Params"+requestBody+" ErrMsg:"+baseRes.ErrMsg+";Msg"+baseRes.Msg+"; Body:"+string(body), 3)
+		}
+	}
+	c.JSON(c.Data["json"], hasIndent, hasEncoding)
+}
+
+func (c *BaseCommonController) JSON(data interface{}, hasIndent bool, coding bool) error {
+	c.Ctx.Output.Header("Content-Type", "application/json; charset=utf-8")
+	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()
+	}
+	authorization := c.Ctx.Input.Header("authorization")
+	if authorization == "" {
+		authorization = c.Ctx.Input.Header("Authorization")
+	}
+
+	utils.ApiLog.Info("uri:%s, authorization:%s, requestBody:%s, responseBody:%s, ip:%s", c.Ctx.Input.URI(), authorization, requestBody, content, ip)
+	if coding {
+		content = []byte(utils.StringsToJSON(string(content)))
+	}
+	return c.Ctx.Output.Body(content)
+}

+ 211 - 0
controllers/chart_info.go

@@ -0,0 +1,211 @@
+package controllers
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/services"
+	"strings"
+)
+
+type ChartInfoController struct {
+	BaseCommonController
+}
+
+// Update
+// @Title 图表-更新
+// @Description 图表-更新
+// @Param	request	body data_manage.AddChartReq true "type json string"
+// @Success 200 {object} data_manage.AddChartInfoResp
+// @router /update [post]
+func (this *ChartInfoController) Update() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	/*deleteCache := true
+	cacheKey := "CACHE_CHART_INFO_ADD_" + strconv.Itoa(sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(this.Ctx.Input.RequestBody)
+		return
+	}*/
+	var req models.AddChartReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ChartInfo == nil {
+		br.Msg = "请选择图表"
+		return
+	}
+	if req.ChartInfo.ChartInfoId <= 0 {
+		br.Msg = "请选择图表"
+		return
+	}
+	//校验图表名称是否重复
+	req.ChartInfo.ChartName = strings.Trim(req.ChartInfo.ChartName, " ")
+	if req.ChartInfo.ChartName == "" {
+		br.Msg = "请填写图表名称!"
+		return
+	}
+
+	if len(req.EdbInfoList) <= 0 {
+		br.Msg = "指标信息不能为空"
+		return
+	}
+	for _, edbInfo := range req.EdbInfoList {
+		if edbInfo.EdbName == "" {
+			br.Msg = "指标名称不能为空"
+			return
+		}
+	}
+
+	if len(req.ChartEdbMapping) <= 0 {
+		br.Msg = "图表指标映射不能为空"
+		return
+	}
+
+	chartInfo, err, errMsg, _ := services.UpdateChartInfoAndEdbInfo(&req, 0, "")
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = models.AddChartInfoResp{
+		ChartInfoId: chartInfo.ChartInfoId,
+		UniqueCode:  chartInfo.UniqueCode,
+		ChartType:   chartInfo.ChartType,
+	}
+	return
+}
+
+// Save
+// @Title 图表-保存
+// @Description 图表-保存
+// @Param	request	body data_manage.AddChartReq true "type json string"
+// @Success 200 {object} data_manage.AddChartInfoResp
+// @router /save [post]
+func (this *ChartInfoController) Save() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	/*deleteCache := true
+	cacheKey := "CACHE_CHART_INFO_ADD_" + strconv.Itoa(sysUser.AdminId)
+	defer func() {
+		if deleteCache {
+			utils.Rc.Delete(cacheKey)
+		}
+	}()
+	if !utils.Rc.SetNX(cacheKey, 1, 30*time.Second) {
+		deleteCache = false
+		br.Msg = "系统处理中,请稍后重试!"
+		br.ErrMsg = "系统处理中,请稍后重试!" + sysUser.RealName + ";data:" + string(this.Ctx.Input.RequestBody)
+		return
+	}*/
+	var req models.AddChartReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	if req.ChartInfo == nil {
+		br.Msg = "请选择图表"
+		return
+	}
+	//校验图表名称是否重复
+	req.ChartInfo.ChartName = strings.Trim(req.ChartInfo.ChartName, " ")
+	if req.ChartInfo.ChartName == "" {
+		br.Msg = "请填写图表名称!"
+		return
+	}
+
+	if len(req.EdbInfoList) <= 0 {
+		br.Msg = "指标信息不能为空"
+		return
+	}
+	for _, edbInfo := range req.EdbInfoList {
+		if edbInfo.EdbName == "" {
+			br.Msg = "指标名称不能为空"
+			return
+		}
+	}
+
+	if len(req.ChartEdbMapping) <= 0 {
+		br.Msg = "图表指标映射不能为空"
+		return
+	}
+
+	chartInfo, err, errMsg, _ := services.AddChartInfo(&req, 0, "")
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "保存成功"
+	br.Data = models.AddChartInfoResp{
+		ChartInfoId: chartInfo.ChartInfoId,
+		UniqueCode:  chartInfo.UniqueCode,
+		ChartType:   chartInfo.ChartType,
+	}
+	return
+}
+
+// Delete
+// @Title 图表-删除
+// @Description 图表-删除
+// @Param	request	body data_manage.DeleteChartReq true "type json string"
+// @Success 200 {object}
+// @router /delete [post]
+func (this *ChartInfoController) Delete() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	var req models.DeleteChartReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.ChartInfoId <= 0 {
+		br.Msg = "请选择图表"
+		return
+	}
+
+	err, errMsg := services.DeleteChart(req.ChartInfoId)
+	if err != nil {
+		br.Msg = errMsg
+		br.ErrMsg = err.Error()
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "删除成功"
+}

+ 62 - 0
controllers/login.go

@@ -0,0 +1,62 @@
+package controllers
+
+import (
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/services"
+	"fmt"
+	"github.com/beego/beego/v2/server/web"
+)
+
+type LoginController struct {
+	web.Controller
+}
+
+// AuthCodeLogin
+// @Title 编码登录
+// @Description 编码登录
+// @Success 200 Ret=200 操作成功
+// @router /login/auth_code_login [get]
+func (this *LoginController) AuthCodeLogin() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		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())
+		return
+	}*/
+	/*if adminName == "" {
+		fmt.Errorf("获取失败, 无效编码")
+		return
+	}
+	// 清除AuthCode
+	defer func() {
+		_ = global.Rc.Delete(key)
+	}()*/
+
+	data, e := services.ThirdLogin(adminName)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取登录Token失败, Err: " + e.Error()
+		return
+	}
+
+	br.Data = data
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 53 - 0
go.mod

@@ -0,0 +1,53 @@
+module eta/eta_forum_admin
+
+go 1.21.7
+
+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-sql-driver/mysql v1.7.0
+	github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b
+	github.com/shopspring/decimal v1.3.1
+	go.mongodb.org/mongo-driver v1.15.0
+	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
+)
+
+require (
+	github.com/beorn7/perks v1.0.1 // indirect
+	github.com/cespare/xxhash/v2 v2.2.0 // 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
+	github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 // indirect
+	github.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2 // indirect
+	github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 // indirect
+	github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9 // indirect
+	github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 // indirect
+	github.com/hashicorp/golang-lru v0.5.4 // indirect
+	github.com/klauspost/compress v1.13.6 // indirect
+	github.com/kr/text v0.2.0 // 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
+	github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/prometheus/client_golang v1.15.1 // indirect
+	github.com/prometheus/client_model v0.3.0 // indirect
+	github.com/prometheus/common v0.42.0 // indirect
+	github.com/prometheus/procfs v0.9.0 // indirect
+	github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
+	github.com/stretchr/testify v1.8.4 // indirect
+	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
+	github.com/xdg-go/scram v1.1.2 // indirect
+	github.com/xdg-go/stringprep v1.0.4 // indirect
+	github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
+	golang.org/x/crypto v0.19.0 // indirect
+	golang.org/x/net v0.21.0 // indirect
+	golang.org/x/sync v0.1.0 // indirect
+	golang.org/x/sys v0.17.0 // indirect
+	golang.org/x/text v0.14.0 // indirect
+	google.golang.org/protobuf v1.30.0 // indirect
+	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)

+ 138 - 0
go.sum

@@ -0,0 +1,138 @@
+github.com/beego/bee/v2 v2.1.0 h1:4WngbAnkvVOyKy74WXcRH3clon76wkjhuzrV2mx2fQU=
+github.com/beego/bee/v2 v2.1.0/go.mod h1:wDhKy5TNxv46LHKsK2gyxo38ObCOm9PbCN89lWHK3EU=
+github.com/beego/beego/v2 v2.1.0 h1:Lk0FtQGvDQCx5V5yEu4XwDsIgt+QOlNjt5emUa3/ZmA=
+github.com/beego/beego/v2 v2.1.0/go.mod h1:6h36ISpaxNrrpJ27siTpXBG8d/Icjzsc7pU1bWpp0EE=
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
+github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+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/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/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=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
+github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac h1:Q0Jsdxl5jbxouNs1TQYt0gxesYMU4VXRbsTlgDloZ50=
+github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=
+github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 h1:EvokxLQsaaQjcWVWSV38221VAK7qc2zhaO17bKys/18=
+github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg=
+github.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2 h1:GUSkTcIe1SlregbHNUKbYDhBsS8lNgYfIp4S4cToUyU=
+github.com/gonum/integrate v0.0.0-20181209220457-a422b5c0fdf2/go.mod h1:pDgmNM6seYpwvPos3q+zxlXMsbve6mOIPucUnUOrI7Y=
+github.com/gonum/internal v0.0.0-20181124074243-f884aa714029 h1:8jtTdc+Nfj9AR+0soOeia9UZSvYBvETVHZrugUowJ7M=
+github.com/gonum/internal v0.0.0-20181124074243-f884aa714029/go.mod h1:Pu4dmpkhSyOzRwuXkOgAvijx4o+4YMUJJo9OvPYMkks=
+github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9 h1:7qnwS9+oeSiOIsiUMajT+0R7HR6hw5NegnKPmn/94oI=
+github.com/gonum/lapack v0.0.0-20181123203213-e4cdc5a0bff9/go.mod h1:XA3DeT6rxh2EAE789SSiSJNqxPaC0aE9J8NTOI0Jo/A=
+github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 h1:V2IgdyerlBa/MxaEFRbV5juy/C3MGdj4ePi+g6ePIp4=
+github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=
+github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b h1:fbskpz/cPqWH8VqkQ7LJghFkl2KPAiIFUHrTJ2O3RGk=
+github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+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/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=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+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/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=
+github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/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/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=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI=
+github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
+github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
+github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
+github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM=
+github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
+github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
+github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
+github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
+github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 h1:DAYUYH5869yV94zvCES9F51oYtN5oGlwjxJJz7ZCnik=
+github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
+github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
+github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
+github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
+github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
+github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc=
+go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
+google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
+gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 15 - 0
main.go

@@ -0,0 +1,15 @@
+package main
+
+import (
+	_ "eta/eta_forum_admin/routers"
+	beego "github.com/beego/beego/v2/server/web"
+)
+
+func main() {
+	if beego.BConfig.RunMode == "dev" {
+		beego.BConfig.WebConfig.DirectoryIndex = true
+		beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
+	}
+
+	beego.Run()
+}

+ 38 - 0
models/base.go

@@ -0,0 +1,38 @@
+package models
+
+type BaseResponse struct {
+	Ret         int
+	Msg         string
+	ErrMsg      string
+	ErrCode     string
+	Data        interface{}
+	Success     bool `description:"true 执行成功,false 执行失败"`
+	IsSendEmail bool `description:"true 发送邮件,false 不发送邮件"`
+}
+
+type BaseResponseRef struct {
+	Ret     int
+	Msg     string
+	ErrMsg  string
+	ErrCode string
+	Data    string
+}
+
+type BaseResponseResult struct {
+	Ret     int    `description:"状态:200 成功,408 重新登录,403:为失败"`
+	Msg     string `description:"提示信息,对用户展示"`
+	ErrMsg  string `description:"错误信息,供开发定位问题"`
+	ErrCode string `description:"错误编码,预留"`
+	Data    string `description:"返回数据,json格式字符串"`
+}
+
+func (r *BaseResponse) Init() *BaseResponse {
+	return &BaseResponse{Ret: 403,IsSendEmail: true}
+}
+
+type BaseRequest struct {
+}
+
+func (br *BaseRequest) Init() *BaseRequest {
+	return &BaseRequest{}
+}

+ 350 - 0
models/chart_classify.go

@@ -0,0 +1,350 @@
+package models
+
+import (
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+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"`
+}
+
+func AddChartClassify(item *ChartClassify) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+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) {
+	o := orm.NewOrm()
+	sql := `SELECT COUNT(1) AS count FROM chart_classify WHERE parent_id=? AND source = ? AND chart_classify_name=? `
+	err = o.Raw(sql, parentId, source, chartClassifyName).QueryRow(&count)
+	return
+}
+
+type EditChartClassifyReq struct {
+	ChartClassifyName string `description:"分类名称"`
+	ChartClassifyId   int    `description:"分类id"`
+}
+
+func GetChartClassifyById(classifyId int) (item *ChartClassify, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_classify WHERE chart_classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+func GetChartClassifyCountById(classifyId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT count(1) AS count FROM chart_classify WHERE chart_classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&count)
+	return
+}
+
+func EditChartClassify(classifyId, source int, chartClassifyName string) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE chart_classify SET chart_classify_name=?,modify_time=NOW() WHERE chart_classify_id=? AND source = ? `
+	_, err = o.Raw(sql, chartClassifyName, classifyId, source).Exec()
+	return
+}
+
+type DeleteChartClassifyReq struct {
+	ChartClassifyId int `description:"分类id"`
+	ChartInfoId     int `description:"指标id"`
+}
+
+func GetChartInfoCountByClassifyId(classifyId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(1) AS count FROM chart_info AS a
+				WHERE a.chart_classify_id IN(
+				SELECT t.chart_classify_id FROM 
+				(
+				SELECT rd.*
+				FROM (SELECT * FROM chart_classify WHERE parent_id IS NOT NULL) rd,
+					 (SELECT @pid := ?) pd 
+				WHERE FIND_IN_SET(parent_id, @pid) > 0 
+				  AND @pid := CONCAT(@pid, ',', chart_classify_id) 
+				UNION SELECT * FROM chart_classify WHERE chart_classify_id = @pid
+				)AS t
+				) `
+	err = o.Raw(sql, classifyId).QueryRow(&count)
+	return
+}
+
+func DeleteChartClassify(classifyId int) (err error) {
+	o := orm.NewOrm()
+	sql := ` DELETE FROM chart_classify
+				WHERE chart_classify_id IN(
+				SELECT t.chart_classify_id FROM
+				(
+				SELECT rd.*
+				FROM (SELECT * FROM chart_classify WHERE parent_id IS NOT NULL) rd,
+				(SELECT @pid := ?) pd
+				WHERE FIND_IN_SET(parent_id, @pid) > 0
+				AND @pid := CONCAT(@pid, ',', chart_classify_id)
+				UNION SELECT * FROM chart_classify WHERE chart_classify_id = @pid
+				)AS t
+				) `
+	_, err = o.Raw(sql, classifyId).Exec()
+	return
+}
+
+// GetChartClassifyByParentId
+// @param source int 1:ETA图库;2:商品价格曲线;3:相关性图表
+func GetChartClassifyByParentId(parentId, source int) (items []*ChartClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_classify WHERE parent_id=? AND source = ? order by sort asc,chart_classify_id asc`
+	_, err = o.Raw(sql, parentId, source).QueryRows(&items)
+	return
+}
+
+// GetChartClassifyAll
+// @param source int 1:ETA图库;2:商品价格曲线;3:相关性图表
+func GetChartClassifyAll(source int) (items []*ChartClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_classify WHERE parent_id<>0 AND source = ? order by sort asc,chart_classify_id asc`
+	_, err = o.Raw(sql, source).QueryRows(&items)
+	return
+}
+
+type ChartClassifyItems struct {
+	ChartClassifyId     int `description:"分类id"`
+	ChartInfoId         int `description:"指标id"`
+	ChartClassifyName   string
+	ChartClassifyNameEn string
+	ParentId            int
+	Level               int    `description:"层级"`
+	Sort                int    `description:"排序字段,越小越靠前,默认值:10"`
+	UniqueCode          string `description:"唯一编码"`
+	Source              int    `description:"来源id"`
+	SourceName          string `description:"来源名称"`
+	SysUserId           int    `description:"创建人id"`
+	SysUserRealName     string `description:"创建人姓名"`
+	DateType            int    `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间"`
+	StartDate           string `description:"自定义开始日期"`
+	EndDate             string `description:"自定义结束日期"`
+	ChartType           int    `description:"生成样式:1:曲线图,2:季节性图"`
+	Calendar            string `description:"公历/农历"`
+	SeasonStartDate     string `description:"季节性图开始日期"`
+	SeasonEndDate       string `description:"季节性图开始日期"`
+	Children            []*ChartClassifyItems
+	Button              ChartClassifyItemsButton `description:"按钮权限"`
+	IsJoinPermission    int                      `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+	HaveOperaAuth       bool                     `description:"是否有数据权限,默认:false"`
+}
+
+// ChartClassifyItemsButton 操作按钮
+type ChartClassifyItemsButton struct {
+	AddButton    bool `description:"是否可添加"`
+	OpButton     bool `description:"是否可编辑"`
+	DeleteButton bool `description:"是否可删除"`
+	MoveButton   bool `description:"是否可移动"`
+}
+
+type ChartClassifyListResp struct {
+	AllNodes      []*ChartClassifyItems
+	Language      string `description:"指标的展示语言,CN:中文,EN:英文"`
+	CanOpClassify bool   `description:"是否允许操作分类"`
+}
+
+type ChartClassifyDeleteCheckResp struct {
+	DeleteStatus int    `description:"检测状态:0:默认值,如果为0,继续走其他校验,1:该分类下关联图表不可删除,2:确认删除当前目录及包含的子目录吗"`
+	TipsMsg      string `description:"提示信息"`
+}
+
+type ChartClassifyDeleteCheckReq struct {
+	ChartClassifyId int `description:"分类id"`
+	ChartInfoId     int `description:"指标id"`
+}
+
+func GetChartClassifyCountByClassifyId(chartClassifyId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(1) AS count FROM (
+			SELECT rd.*
+			FROM (SELECT * FROM chart_classify WHERE parent_id IS NOT NULL) rd,
+				 (SELECT @pid := ?) pd 
+			WHERE FIND_IN_SET(parent_id, @pid) > 0 
+			  AND @pid := CONCAT(@pid, ',', chart_classify_id) 
+			UNION SELECT * FROM chart_classify WHERE chart_classify_id = @pid
+			)AS t
+			WHERE t.chart_classify_id<>? `
+	err = o.Raw(sql, chartClassifyId, chartClassifyId).QueryRow(&count)
+	return
+}
+
+func GetChartClassifyByCondition(condition string, pars []interface{}) (item *ChartClassify, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_classify WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+// MoveChartClassifyReq 移动图表分类请求参数
+type MoveChartClassifyReq struct {
+	ClassifyId       int `description:"分类id"`
+	ParentClassifyId int `description:"父级分类id"`
+	PrevClassifyId   int `description:"上一个兄弟节点分类id"`
+	NextClassifyId   int `description:"下一个兄弟节点分类id"`
+}
+
+// GetFirstChartClassifyByParentId 获取当前父级图表分类下的排序第一条的数据
+func GetFirstChartClassifyByParentId(parentId int) (item *ChartClassify, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_classify WHERE parent_id=? order by sort asc,chart_classify_id asc limit 1`
+	err = o.Raw(sql, parentId).QueryRow(&item)
+	return
+}
+
+// GetFirstChartClassifyByParentIdAndSource 获取当前父级图表分类下的排序第一条的数据
+func GetFirstChartClassifyByParentIdAndSource(parentId, source int) (item *ChartClassify, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_classify WHERE parent_id=?  and source = ? order by sort asc,chart_classify_id asc limit 1`
+	err = o.Raw(sql, parentId, source).QueryRow(&item)
+	return
+}
+
+// UpdateChartClassifySortByParentId 根据图表父类id更新排序
+func UpdateChartClassifySortByParentId(parentId, classifyId, nowSort int, updateSort string) (err error) {
+	o := orm.NewOrm()
+	sql := ` update chart_classify set sort = ` + updateSort + ` WHERE parent_id=? 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()
+	return
+}
+
+// Update 更新图表分类基础信息
+func (chartClassify *ChartClassify) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(chartClassify, cols...)
+	return
+}
+
+// GetChartClassifyMaxSort 获取图表分类下最大的排序数
+func GetChartClassifyMaxSort(parentId, source int) (sort int, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT Max(sort) AS sort FROM chart_classify WHERE parent_id=? AND source = ? `
+	err = o.Raw(sql, parentId, source).QueryRow(&sort)
+	return
+}
+
+type ChartClassifyView struct {
+	ChartClassifyId   int    `orm:"column(chart_classify_id);pk"`
+	ChartClassifyName string `description:"分类名称"`
+	ParentId          int    `description:"父级id"`
+}
+
+func GetChartClassifyViewById(classifyId int) (item *ChartClassifyView, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_classify WHERE chart_classify_id=? `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+func GetChartClassifyByParentIdFromETA(parentId int) (items []*ChartClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_classify WHERE parent_id=? AND source = 1 order by sort asc,chart_classify_id asc`
+	_, err = o.Raw(sql, parentId).QueryRows(&items)
+	return
+}
+
+func GetChartClassifyAllFromETA() (items []*ChartClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_classify WHERE parent_id<>0 AND source = 1 order by sort asc,chart_classify_id asc`
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+func GetChartClassifyTwo(source int) (items []*ChartClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_classify WHERE level=3 AND source = ? order by sort asc,chart_classify_id asc`
+	_, err = o.Raw(sql, source).QueryRows(&items)
+	return
+}
+
+func GetChartClassifyByLevel(level, source int) (items []*ChartClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_classify WHERE level=? AND source = ? order by sort asc,chart_classify_id asc`
+	_, err = o.Raw(sql, level, source).QueryRows(&items)
+	return
+}
+
+func EditChartClassifySysUser(classifyId, sysUserId int, chartClassifyName string) (err error) {
+	o := orm.NewOrm()
+	sql := `UPDATE chart_classify SET sys_user_id=?,sys_user_real_name=?,modify_time=NOW() WHERE parent_id=?  AND level=3 `
+	_, err = o.Raw(sql, sysUserId, chartClassifyName, classifyId).Exec()
+	return
+}
+
+func GetAllChartClassifyItemsBySource(source int) (items []*ChartClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_classify WHERE source = ? order by sort ASC,chart_classify_id ASC`
+	_, err = o.Raw(sql, source).QueryRows(&items)
+	return
+}
+
+// GetChartClassifyByParentIdAndName 根据父级ID和名称获取分类
+func GetChartClassifyByParentIdAndName(parentId int, classifyName string, classifyId int) (item *ChartClassify, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_classify WHERE parent_id = ? AND chart_classify_name = ? AND chart_classify_id <> ? LIMIT 1`
+	err = o.Raw(sql, parentId, classifyName, classifyId).QueryRow(&item)
+	return
+}
+
+// GetChartClassifyByIdList
+// @Description:  根据分类ID列表获取分类
+// @author: Roc
+// @datetime 2024-04-01 13:21:48
+// @param classifyId []int
+// @return items []*ChartClassify
+// @return err error
+func GetChartClassifyByIdList(classifyIdList []int) (items []*ChartClassify, err error) {
+	num := len(classifyIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_classify WHERE chart_classify_id in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, classifyIdList).QueryRows(&items)
+
+	return
+}
+
+// GetChartClassifyBySourceAndIsJoinPermission
+// @Description: 根据分类类型和是否涉密获取分类列表
+// @author: Roc
+// @datetime 2024-03-29 10:30:21
+// @param source int
+// @param isJoinPermission int
+// @return items []*ChartClassify
+// @return err error
+func GetChartClassifyBySourceAndIsJoinPermission(source, isJoinPermission int) (items []*ChartClassify, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_classify WHERE source = ? AND is_join_permission = ? `
+	_, err = o.Raw(sql, source, isJoinPermission).QueryRows(&items)
+	return
+}

+ 318 - 0
models/chart_edb_mapping.go

@@ -0,0 +1,318 @@
+package models
+
+import (
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type ChartEdbMapping struct {
+	ChartEdbMappingId int       `orm:"column(chart_edb_mapping_id);pk"`
+	ChartInfoId       int       `description:"图表id"`
+	EdbInfoId         int       `description:"指标id"`
+	CreateTime        time.Time `description:"创建时间"`
+	ModifyTime        time.Time `description:"修改时间"`
+	UniqueCode        string    `description:"唯一编码"`
+	MaxData           float64   `description:"上限"`
+	MinData           float64   `description:"下限"`
+	IsOrder           bool      `description:"true:正序,false:逆序"`
+	IsAxis            int       `description:"true:左轴,false:右轴"`
+	EdbInfoType       int       `description:"true:标准指标,false:领先指标"`
+	LeadValue         int       `description:"领先值"`
+	LeadUnit          string    `description:"领先单位"`
+	ChartStyle        string    `description:"图表类型"`
+	ChartColor        string    `description:"颜色"`
+	PredictChartColor string    `description:"预测数据的颜色"`
+	ChartWidth        float64   `description:"线条大小"`
+	Source            int       `description:"1:ETA图库;2:商品价格曲线"`
+	EdbAliasName      string    `description:"中文别名"`
+	IsConvert         int       `description:"是否数据转换 0不转 1转"`
+	ConvertType       int       `description:"数据转换类型 1乘 2除 3对数"`
+	ConvertValue      float64   `description:"数据转换值"`
+	ConvertUnit       string    `description:"数据转换单位"`
+	ConvertEnUnit     string    `description:"数据转换单位"`
+	EdbCode           string    `description:"指标编码"`
+}
+
+func AddChartEdbMapping(items []*ChartEdbMapping) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func GetChartEdbMappingList(chartInfoId int) (list []*ChartEdbInfoMapping, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT a.*,b.source_name,b.source,b.sub_source,b.classify_id,b.edb_code,b.edb_name,b.edb_name_en,b.frequency,b.unit,b.unit_en,b.start_date,
+b.end_date,b.modify_time,b.latest_date,b.latest_value,b.unique_code,b.edb_info_type AS edb_info_category_type,b.edb_type,b.is_join_permission,
+a.is_convert, a.convert_type, a.convert_value, a.convert_unit, a.convert_en_unit 
+             FROM chart_edb_mapping AS a
+			 INNER JOIN edb_info AS b ON a.edb_info_id=b.edb_info_id
+			 WHERE chart_info_id=? 
+             ORDER BY chart_edb_mapping_id ASC `
+	_, err = o.Raw(sql, chartInfoId).QueryRows(&list)
+	return
+}
+
+func GetChartEdbMappingListByChartInfoIds(chartInfoIds string) (list []*ChartEdbInfoMapping, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT a.*,b.source_name,b.source,b.sub_source,b.edb_code,b.edb_name,b.edb_name_en,b.frequency,b.unit,b.unit_en,b.start_date,b.end_date,b.modify_time,b.latest_date,b.latest_value,b.unique_code,b.edb_info_type AS edb_info_category_type,b.edb_type,b.classify_id,b.is_join_permission
+             FROM chart_edb_mapping AS a
+			 INNER JOIN edb_info AS b ON a.edb_info_id=b.edb_info_id
+			 WHERE chart_info_id in (` + chartInfoIds + `) 
+             ORDER BY chart_edb_mapping_id ASC `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+// GetChartEdbMappingListByEdbInfoIdList 根据指标id列表获取关联关系
+func GetChartEdbMappingListByEdbInfoIdList(edbIdList []int) (list []*ChartEdbInfoMapping, err error) {
+	num := len(edbIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := ` SELECT edb_info_id,source_name,source,sub_source,edb_code,edb_name,edb_name_en,frequency,unit,unit_en,start_date,end_date,modify_time,latest_date,latest_value,unique_code,edb_info_type AS edb_info_category_type,max_value,min_value,edb_type,classify_id,is_join_permission
+             FROM edb_info
+			 WHERE edb_info_id IN(` + utils.GetOrmInReplace(num) + `)
+			ORDER BY FIELD(edb_info_id,` + utils.GetOrmInReplace(num) + `)
+              `
+	_, err = o.Raw(sql, edbIdList, edbIdList).QueryRows(&list)
+	return
+}
+
+// GetChartEdbMappingListByIdList 通过图表id列表获取
+func GetChartEdbMappingListByIdList(chartInfoIdList []int) (list []*ChartEdbInfoMapping, err error) {
+	num := len(chartInfoIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := ` SELECT a.*,b.source_name,b.source,b.sub_source,b.edb_code,b.edb_name,b.edb_name_en,b.frequency,b.unit,b.unit_en,b.start_date,b.end_date,b.modify_time,b.latest_date,b.latest_value,b.unique_code,b.edb_info_type,b.edb_type AS edb_info_category_type,b.classify_id,b.is_join_permission
+             FROM chart_edb_mapping AS a
+			 INNER JOIN edb_info AS b ON a.edb_info_id=b.edb_info_id
+			 WHERE chart_info_id in (` + utils.GetOrmInReplace(num) + `) 
+             ORDER BY chart_edb_mapping_id ASC `
+	_, err = o.Raw(sql, chartInfoIdList).QueryRows(&list)
+	return
+}
+
+func GetRelationEdbInfoListByCondition(condition string, pars []interface{}, startSize, pageSize int) (item []*ChartEdbInfoMapping, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT a.* FROM edb_info AS a 
+	JOIN edb_info_calculate_mapping AS b on a.edb_info_id = b.edb_info_id WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	//sql += " ORDER BY sort ASC,chart_info_id DESC LIMIT ?,? "
+	sql += " ORDER BY a.create_time DESC LIMIT ?,? "
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&item)
+	return
+}
+
+func GetRelationEdbInfoListCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(1) AS count FROM edb_info AS a 
+	JOIN edb_info_calculate_mapping AS b on a.edb_info_id = b.edb_info_id WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+// GetEtaEdbChartEdbMapping       商品曲线图查询对应的普通指标
+func GetEtaEdbChartEdbMapping(chartInfoId int) (item *ChartEdbInfoMapping, err error) {
+	o := orm.NewOrm()
+	aField := `a.chart_edb_mapping_id,a.chart_info_id,a.edb_info_id,a.create_time,a.modify_time,a.unique_code,a.max_data,a.min_data,a.is_order,a.is_axis,a.edb_info_type,a.lead_value,a.lead_unit,a.chart_style,a.chart_color,a.predict_chart_color,a.chart_width,a.source as mapping_source`
+
+	sql := ` SELECT ` + aField + `,b.source_name,b.source,b.sub_source,b.edb_code,b.edb_name,b.edb_name_en,b.frequency,b.unit,b.unit_en,b.start_date,b.end_date,b.modify_time,b.latest_date,b.latest_value,b.unique_code,b.edb_info_type AS edb_info_category_type,b.classify_id,b.is_join_permission
+             FROM chart_edb_mapping AS a
+			 INNER JOIN edb_info AS b ON a.edb_info_id=b.edb_info_id
+			 WHERE a.chart_info_id=? AND a.source = ?
+             ORDER BY chart_edb_mapping_id ASC `
+	err = o.Raw(sql, chartInfoId, utils.CHART_SOURCE_DEFAULT).QueryRow(&item)
+	return
+}
+
+// GetFutureGoodEdbChartEdbMapping       商品曲线图查询对应的商品指标
+func GetFutureGoodEdbChartEdbMapping(chartInfoId int) (item *ChartEdbInfoMapping, err error) {
+	o := orm.NewOrm()
+	aField := `a.chart_edb_mapping_id,a.chart_info_id,a.edb_info_id,a.create_time,a.modify_time,a.unique_code,a.max_data,a.min_data,a.is_order,a.is_axis,a.edb_info_type,a.lead_value,a.lead_unit,a.chart_style,a.chart_color,a.predict_chart_color,a.chart_width,a.source as mapping_source`
+	sql := ` SELECT ` + aField + `,b.future_good_edb_info_id,b.future_good_edb_code as edb_code,b.future_good_edb_name as edb_name,b.start_date,b.end_date,b.modify_time,b.latest_date,b.latest_value
+             FROM chart_edb_mapping AS a
+			 INNER JOIN future_good_edb_info AS b ON a.edb_info_id=b.future_good_edb_info_id
+			 WHERE a.chart_info_id=? AND a.source = ?
+             ORDER BY chart_edb_mapping_id ASC `
+	err = o.Raw(sql, chartInfoId, utils.CHART_SOURCE_FUTURE_GOOD).QueryRow(&item)
+	return
+}
+
+// GetFutureGoodEdbChartEdbMappingList       商品曲线图查询对应的商品指标
+func GetFutureGoodEdbChartEdbMappingList(chartInfoId int) (items []*ChartEdbInfoMapping, err error) {
+	o := orm.NewOrm()
+	aField := `a.chart_edb_mapping_id,a.chart_info_id,a.edb_info_id,a.create_time,a.modify_time,a.unique_code,a.max_data,a.min_data,a.is_order,a.is_axis,a.edb_info_type,a.lead_value,a.lead_unit,a.chart_style,a.chart_color,a.predict_chart_color,a.chart_width,a.source as mapping_source`
+	sql := ` SELECT ` + aField + `,b.future_good_edb_info_id,b.future_good_edb_code as edb_code,b.future_good_edb_name as edb_name,b.start_date,b.end_date,b.modify_time,b.latest_date,b.latest_value
+             FROM chart_edb_mapping AS a
+			 INNER JOIN future_good_edb_info AS b ON a.edb_info_id=b.future_good_edb_info_id
+			 WHERE a.chart_info_id=? AND a.source = ?
+             ORDER BY chart_edb_mapping_id ASC `
+	_, err = o.Raw(sql, chartInfoId, utils.CHART_SOURCE_FUTURE_GOOD).QueryRows(&items)
+	return
+}
+
+// GetChartEdbMappingListV2 根据图表id获取指标信息,不连表查询指标表
+func GetChartEdbMappingListV2(chartInfoId int) (list []*ChartEdbMapping, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT a.* FROM chart_edb_mapping AS a
+			 WHERE chart_info_id=? 
+             ORDER BY chart_edb_mapping_id ASC `
+	_, err = o.Raw(sql, chartInfoId).QueryRows(&list)
+	return
+}
+
+// GetChartEdbMappingByEdbInfoId 根据指标id获取edb_mapping
+func GetChartEdbMappingByEdbInfoId(edbInfoId int) (item *ChartEdbInfoMapping, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT edb_info_id,source_name,classify_id,source,sub_source,edb_code,edb_name,edb_name_en,frequency,unit,unit_en,start_date,end_date,modify_time,latest_date,latest_value,unique_code,edb_info_type AS edb_info_category_type,edb_type,max_value,min_value,is_join_permission
+             FROM edb_info
+			 WHERE edb_info_id = ? limit 1`
+	err = o.Raw(sql, edbInfoId).QueryRow(&item)
+	return
+}
+
+// GetChartEdbMappingByFutureGoodEdbInfoId 根据指标id获取edb_mapping
+func GetChartEdbMappingByFutureGoodEdbInfoId(edbInfoId int) (item *ChartEdbInfoMapping, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT future_good_edb_info_id as edb_info_id,future_good_edb_code as edb_code,b.future_good_edb_name as edb_name,b.start_date,b.end_date,b.modify_time,b.latest_date,b.latest_value,b.region_type
+             FROM future_good_edb_info b
+			 WHERE future_good_edb_info_id = ?  limit 1 `
+	err = o.Raw(sql, edbInfoId).QueryRow(&item)
+	return
+}
+
+// ModifyChartEdbMapping
+// @Description: 修改图表的关系表
+// @author: Roc
+// @datetime 2023-12-11 17:23:32
+// @param chartInfoId int
+// @param edbInfoList []*EdbInfo
+// @return err error
+func ModifyChartEdbMapping(chartInfoId int, edbInfoList []*EdbInfo) (err error) {
+	o, err := orm.NewOrm().Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			fmt.Println("AddCalculateHcz,Err:" + err.Error())
+			_ = o.Rollback()
+		} else {
+			_ = o.Commit()
+		}
+	}()
+	list := make([]*ChartEdbMapping, 0)
+	sql := ` SELECT a.*
+             FROM chart_edb_mapping AS a
+			 WHERE chart_info_id=? 
+             ORDER BY chart_edb_mapping_id ASC `
+	_, err = o.Raw(sql, chartInfoId).QueryRows(&list)
+
+	if err != nil {
+		return
+	}
+
+	mappingIdMap := make(map[int]*ChartEdbMapping)
+	removeMapping := make(map[int]int)
+	for _, v := range list {
+		mappingIdMap[v.EdbInfoId] = v
+		removeMapping[v.EdbInfoId] = v.ChartEdbMappingId
+	}
+
+	addList := make([]*ChartEdbMapping, 0)
+	for _, v := range edbInfoList {
+		_, ok := mappingIdMap[v.EdbInfoId]
+		// 存在该指标关系就不处理了
+		if ok {
+			delete(removeMapping, v.EdbInfoId)
+			continue
+		}
+		timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+		// 不存在就添加
+		addList = append(addList, &ChartEdbMapping{
+			//ChartEdbMappingId: 0,
+			ChartInfoId: chartInfoId,
+			EdbInfoId:   v.EdbInfoId,
+			CreateTime:  time.Now(),
+			ModifyTime:  time.Now(),
+			UniqueCode:  utils.MD5(utils.CHART_PREFIX + "_" + fmt.Sprint(chartInfoId) + "_" + fmt.Sprint(v.EdbInfoId) + "_" + timestamp),
+			MaxData:     v.MaxValue,
+			MinData:     v.MinValue,
+			//IsOrder:     v.IsOrder,
+			//IsAxis:      v.IsAxis,
+			EdbInfoType: v.EdbInfoType,
+			//LeadValue:   v.LeadValue,
+			//LeadUnit:    v.LeadUnit,
+			//ChartStyle:  v.ChartStyle,
+			//ChartColor:  v.ChartColor,
+			//ChartWidth:  v.ChartWidth,
+			Source: v.Source,
+		})
+	}
+
+	// 需要添加的话,那就添加吧
+	if len(addList) > 0 {
+		_, err = o.InsertMulti(len(addList), addList)
+		if err != nil {
+			return
+		}
+	}
+
+	// 移除不必要的mapping
+	if len(removeMapping) > 0 {
+		removeIdList := make([]string, 0) //需要移除的日期
+		for _, v := range removeMapping {
+			removeIdList = append(removeIdList, fmt.Sprint(v))
+		}
+		removeIdStr := strings.Join(removeIdList, `","`)
+		removeIdStr = `"` + removeIdStr + `"`
+		//如果拼接指标变更了,那么需要删除所有的指标数据
+		sql := fmt.Sprintf(` DELETE FROM chart_edb_mapping WHERE chart_edb_mapping_id in (%s) `, removeIdStr)
+
+		_, err = o.Raw(sql).Exec()
+		if err != nil {
+			err = fmt.Errorf("移除不必要的mapping失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	return
+}
+
+func DeleteChartEdbMappingByChartEdbMappingId(removeIdStr string) (err error) {
+	// 删除映射关系
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(` DELETE FROM chart_edb_mapping WHERE chart_edb_mapping_id in (%s) `, removeIdStr)
+	_, err = o.Raw(sql).Exec()
+	if err != nil {
+		err = fmt.Errorf("删除映射关系失败,Err:" + err.Error())
+		return
+	}
+	return
+}
+
+func (c *ChartEdbMapping) Update(col []string) (err error) {
+	// 删除映射关系
+	o := orm.NewOrm()
+	_, err = o.Update(c, col...)
+	return
+}
+
+func GetChartEdbMappingCount(edbInfoId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(1) AS count FROM chart_edb_mapping WHERE edb_info_id=? `
+	err = o.Raw(sql, edbInfoId).QueryRow(&count)
+	return
+}

+ 1928 - 0
models/chart_info.go

@@ -0,0 +1,1928 @@
+package models
+
+import (
+	"eta/eta_forum_admin/models/mgodb"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type ChartInfo struct {
+	ChartInfoId       int    `orm:"column(chart_info_id);pk"`
+	ChartName         string `description:"来源名称"`
+	ChartNameEn       string `description:"英文图表名称"`
+	ChartClassifyId   int    `description:"图表分类id"`
+	SysUserId         int
+	SysUserRealName   string
+	UniqueCode        string `description:"图表唯一编码"`
+	CreateTime        time.Time
+	ModifyTime        time.Time
+	DateType          int    `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间"`
+	StartDate         string `description:"自定义开始日期"`
+	EndDate           string `description:"自定义结束日期"`
+	IsSetName         int    `description:"设置名称"`
+	EdbInfoIds        string `description:"指标id"`
+	ChartType         int    `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图,8:商品价格曲线图,9:相关性图,10:截面散点图, 11:雷达图"`
+	Calendar          string `description:"公历/农历"`
+	SeasonStartDate   string `description:"季节性图开始日期"`
+	SeasonEndDate     string `description:"季节性图开始日期"`
+	ChartImage        string `description:"图表图片"`
+	Sort              int    `description:"排序字段,数字越小越排前面"`
+	XMin              string `description:"图表X轴最小值"`
+	XMax              string `description:"图表X轴最大值"`
+	LeftMin           string `description:"图表左侧最小值"`
+	LeftMax           string `description:"图表左侧最大值"`
+	RightMin          string `description:"图表右侧最小值"`
+	RightMax          string `description:"图表右侧最大值"`
+	Right2Min         string `description:"图表右侧2最小值"`
+	Right2Max         string `description:"图表右侧2最大值"`
+	MinMaxSave        int    `description:"是否手动保存过上下限:0-否;1-是"`
+	Disabled          int    `description:"是否禁用,0:启用,1:禁用,默认:0"`
+	BarConfig         string `description:"柱方图的配置,json数据"`
+	Source            int    `description:"1:ETA图库;2:商品价格曲线"`
+	ExtraConfig       string `description:"图表额外配置,json数据"`
+	SeasonExtraConfig string `description:"季节性图表中的配置,json数据"`
+	StartYear         int    `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
+	ChartThemeId      int    `description:"图表应用主题ID"`
+	SourcesFrom       string `description:"图表来源"`
+	Instructions      string `description:"图表说明"`
+	MarkersLines      string `description:"标识线"`
+	MarkersAreas      string `description:"标识区"`
+	Unit              string `description:"中文单位名称"`
+	UnitEn            string `description:"英文单位名称"`
+	IsJoinPermission  int    `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+}
+
+type ChartInfoMore struct {
+	ChartInfo
+	IsEnChart     bool `description:"是否展示英文标识"`
+	HaveOperaAuth bool `description:"是否有数据权限,默认:false"`
+}
+
+func AddChartInfo(item *ChartInfo) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+func UpdateChartInfo(item *ChartInfo) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(&item)
+	return
+}
+
+type ChartInfoItem struct {
+	ChartInfoId     int    `description:"图表id"`
+	ChartName       string `description:"图表名称"`
+	ChartClassifyId int    `description:"图表分类"`
+}
+
+// GetChartInfoAll 用于分类展示
+// @param source int 1:ETA图库;2:商品价格曲线
+func GetChartInfoAll(sourceList []int) (items []*ChartClassifyItems, err error) {
+	num := len(sourceList)
+	if num <= 0 {
+		return
+	}
+
+	o := orm.NewOrm()
+	sql := ` SELECT chart_info_id,chart_classify_id,chart_name AS chart_classify_name,
+             unique_code,sys_user_id,sys_user_real_name,date_type,start_date,end_date,chart_type,calendar,season_start_date,season_end_date,source
+            FROM chart_info WHERE source in (` + utils.GetOrmInReplace(num) + `)  ORDER BY sort asc,chart_info_id ASC `
+	_, err = o.Raw(sql, sourceList).QueryRows(&items)
+	return
+}
+
+// GetChartInfoByAdminId 获取所有我创建的图表,用于分类展示
+func GetChartInfoByAdminId(sourceList []int, adminId int) (items []*ChartClassifyItems, err error) {
+	num := len(sourceList)
+	if num <= 0 {
+		return
+	}
+
+	o := orm.NewOrm()
+	sql := ` SELECT chart_info_id,chart_classify_id,chart_name AS chart_classify_name,
+             unique_code,sys_user_id,sys_user_real_name,date_type,start_date,end_date,chart_type,calendar,season_start_date,season_end_date
+            FROM chart_info where source in (` + utils.GetOrmInReplace(num) + `)  AND sys_user_id = ? ORDER BY sort asc,create_time ASC `
+	_, err = o.Raw(sql, sourceList, adminId).QueryRows(&items)
+	return
+}
+
+// 用于分类展示
+func GetChartInfoAllList() (items []*ChartInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT *
+            FROM chart_info ORDER BY create_time ASC `
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// 图表检索数据
+type ChartInfoSearch struct {
+	EdbCode   string `description:"图表编码"`
+	StartDate string `description:"起始日期"`
+	EndDate   string `description:"终止日期"`
+	DataList  []*EdbInfoSearchData
+}
+
+type ChartInfoSearchData struct {
+	DataTime string  `description:"数据日期"`
+	Value    float64 `description:"数据"`
+}
+
+type ChartInfoSearchResp struct {
+	SearchItem *EdbInfoSearch `description:"图表分类"`
+	Status     int            `description:"1:数据已存在于弘则数据库,2:新数据"`
+}
+
+func GetChartInfoById(chartInfoId int) (item *ChartInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE chart_info_id=? `
+	err = o.Raw(sql, chartInfoId).QueryRow(&item)
+	return
+}
+
+func GetChartInfoViewById(chartInfoId int) (item *ChartInfoView, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE chart_info_id=? `
+	err = o.Raw(sql, chartInfoId).QueryRow(&item)
+	return
+}
+
+func GetChartInfoViewByIds(chartInfoIds []string) (list []*ChartInfoView, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE chart_info_id in ("` + strings.Join(chartInfoIds, `","`) + `") `
+	_, err = o.Raw(sql).QueryRows(&list)
+	return
+}
+
+type SaveChartInfoReq struct {
+	ChartEdbInfoList []*ChartSaveItem `description:"指标及配置信息"`
+	ChartInfoId      int              `description:"图表id,新增时传0"`
+	DateType         int              `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:起始日期至今"`
+	StartDate        string           `description:"自定义开始日期"`
+	EndDate          string           `description:"自定义结束日期"`
+	Calendar         string           `description:"公历/农历"`
+	LeftMin          string           `description:"图表左侧最小值"`
+	LeftMax          string           `description:"图表左侧最大值"`
+	RightMin         string           `description:"图表右侧最小值"`
+	RightMax         string           `description:"图表右侧最大值"`
+	Right2Min        string           `description:"图表右侧最小值"`
+	Right2Max        string           `description:"图表右侧最大值"`
+	MinMaxSave       int              `description:"是否手动保存过上下限:0-否;1-是"`
+	ExtraConfig      string           `description:"图表额外配置,json数据"`
+	StartYear        int              `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
+}
+
+type ChartSaveItem struct {
+	EdbInfoId         int     `description:"指标id"`
+	MaxData           float64 `description:"上限"`
+	MinData           float64 `description:"下限"`
+	IsOrder           bool    `description:"true:正序,false:逆序"`
+	IsAxis            int     `description:"1:左轴,0:右轴"`
+	EdbInfoType       int     `description:"1:标准指标,0:领先指标"`
+	LeadValue         int     `description:"领先值"`
+	LeadUnit          string  `description:"领先单位"`
+	ChartStyle        string  `description:"图表类型"`
+	ChartColor        string  `description:"颜色"`
+	PredictChartColor string  `description:"预测数据的颜色"`
+	ChartWidth        float64 `description:"线条大小"`
+	Source            int     `description:"1:ETA图库;2:商品价格曲线"`
+	EdbAliasName      string  `description:"中文别名"`
+	IsConvert         int     `description:"是否数据转换 0不转 1转"`
+	ConvertType       int     `description:"数据转换类型 1乘 2除 3对数"`
+	ConvertValue      float64 `description:"数据转换值"`
+	ConvertUnit       string  `description:"数据转换单位"`
+	ConvertEnUnit     string  `description:"数据转换单位"`
+}
+
+func DeleteChartInfoAndData(chartInfoId int) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	sql := ` DELETE FROM chart_info WHERE chart_info_id=? `
+	_, err = to.Raw(sql, chartInfoId).Exec()
+	if err != nil {
+		return
+	}
+	sql = ` DELETE FROM  chart_edb_mapping WHERE chart_info_id=? `
+	_, err = to.Raw(sql, chartInfoId).Exec()
+	return
+}
+
+func GetChartInfoCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(1) AS count FROM chart_info WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+type EditChartInfoReq struct {
+	ChartInfoId          int                     `description:"图表ID"`
+	ChartName            string                  `description:"图表名称"`
+	ChartClassifyId      int                     `description:"分类id"`
+	ChartEdbInfoList     []*ChartSaveItem        `description:"指标及配置信息"`
+	ChartType            int                     `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图,8:商品价格曲线图,9:相关性图,10:截面散点图"`
+	DateType             int                     `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:起始日期至今,20:N年至今"`
+	StartDate            string                  `description:"自定义开始日期"`
+	EndDate              string                  `description:"自定义结束日期"`
+	Calendar             string                  `description:"公历/农历"`
+	LeftMin              string                  `description:"图表左侧最小值"`
+	LeftMax              string                  `description:"图表左侧最大值"`
+	RightMin             string                  `description:"图表右侧最小值"`
+	RightMax             string                  `description:"图表右侧最大值"`
+	Right2Min            string                  `description:"图表右侧最小值"`
+	Right2Max            string                  `description:"图表右侧最大值"`
+	MinMaxSave           int                     `description:"是否手动保存过上下限:0-否;1-是"`
+	BarChartInfo         BarChartInfoReq         `description:"柱方图的配置"`
+	CorrelationChartInfo CorrelationChartInfoReq `description:"相关性图表配置"`
+	ExtraConfig          string                  `description:"图表额外配置信息,json字符串"`
+	SeasonExtraConfig    SeasonExtraItem         `description:"季节性图表中的配置,json数据"`
+	StartYear            int                     `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
+	ChartThemeId         int                     `description:"图表应用主题ID"`
+	SourcesFrom          string                  `description:"图表来源"`
+	Instructions         string                  `description:"图表说明"`
+	MarkersLines         string                  `description:"标识线"`
+	MarkersAreas         string                  `description:"标识区"`
+	Unit                 string                  `description:"中文单位名称"`
+	UnitEn               string                  `description:"英文单位名称"`
+}
+
+type EditChartEnInfoReq struct {
+	ChartInfoId      int                       `description:"图表ID"`
+	ChartNameEn      string                    `description:"英文图表名称"`
+	ChartEdbInfoList []*EditChartEnInfoEdbItem `description:"指标及配置信息"`
+	ExtraConfig      string                    `description:"图表额外配置信息,json字符串"`
+}
+type EditChartEnInfoEdbItem struct {
+	EdbInfoId int    `description:"指标ID"`
+	EdbNameEn string `description:"英文指标名称"`
+	UnitEn    string `description:"英文单位"`
+}
+
+func ModifyChartInfo(item *EditChartInfoReq) (err error) {
+	o := orm.NewOrm()
+	sql := ` UPDATE  chart_info
+			SET
+			  chart_name =?,
+			  chart_classify_id = ?,
+              is_set_name=1,
+			  modify_time = NOW()
+			WHERE chart_info_id = ?`
+	_, err = o.Raw(sql, item.ChartName, item.ChartClassifyId, item.ChartInfoId).Exec()
+	return
+}
+
+type ChartInfoList struct {
+	ChartInfoId          int    `orm:"column(chart_info_id);pk"`
+	ChartName            string `description:"来源名称"`
+	ChartChartClassifyId int    `description:"图表分类id"`
+	SysUserId            int
+	SysUserRealName      string
+	UniqueCode           string `description:"图表唯一编码"`
+	CreateTime           time.Time
+	ModifyTime           time.Time
+	EdbInfoList          []*EdbInfoList
+}
+
+type ChartInfoListResp struct {
+	Item *ChartInfoList
+}
+
+func GetChartInfoByCondition(condition string, pars []interface{}) (item *ChartInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func GetChartInfoByNewest(source int) (item *ChartInfoList, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE 1=1 AND source = ? ORDER BY modify_time DESC LIMIT 1 `
+	err = o.Raw(sql, source).QueryRow(&item)
+	return
+}
+
+func ModifyChartInfoModifyTime(chartInfoId int64) (err error) {
+	o := orm.NewOrm()
+	sql := ` UPDATE  chart_info SET modify_time = NOW() WHERE chart_info_id = ? `
+	_, err = o.Raw(sql, chartInfoId).Exec()
+	return
+}
+
+type MoveChartInfoReq struct {
+	ChartInfoId     int `description:"图表ID"`
+	PrevChartInfoId int `description:"上一个图表ID"`
+	NextChartInfoId int `description:"下一个图表ID"`
+	ChartClassifyId int `description:"分类id"`
+}
+
+func MoveChartInfo(chartInfoId, classifyId int) (err error) {
+	o := orm.NewOrm()
+	sql := ` UPDATE  chart_info
+			SET
+			  chart_classify_id = ?
+			WHERE chart_info_id = ?`
+	_, err = o.Raw(sql, classifyId, chartInfoId).Exec()
+	return
+}
+
+type DeleteChartInfoReq struct {
+	ChartInfoId int `description:"图表ID"`
+}
+
+type EdbDataList struct {
+	EdbDataId     int     `description:" 指标数据ID"`
+	EdbInfoId     int     `description:"指标ID"`
+	DataTime      string  //`json:"-" description:"数据日期"`
+	DataTimestamp int64   `description:"数据日期"`
+	Value         float64 `description:"数据值"`
+}
+
+type EdbDataListByUniqueCode struct {
+	EdbDataId     int     `description:" 指标数据ID" json:"-"`
+	EdbInfoId     int     `description:"指标ID" json:"-"`
+	DataTime      string  //`json:"-" description:"数据日期"`
+	DataTimestamp int64   `description:"数据日期"`
+	Value         float64 `description:"数据值"`
+}
+
+func GetEdbDataLunarList(endInfoId int, startDate, endDate string) (list []*EdbDataList, err error) {
+	tableName := "edb_data_quarter"
+	sql := `SELECT edb_data_id,edb_info_id,data_time,TRUNCATE(value,2) AS value,data_timestamp FROM %s WHERE edb_info_id=? AND data_time>=? AND data_time<=? ORDER BY data_time ASC `
+	sql = fmt.Sprintf(sql, tableName)
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, endInfoId, startDate, endDate).QueryRows(&list)
+	return
+}
+
+type ChartEdbInfoMapping struct {
+	EdbInfoId           int     `description:"指标id"`
+	SourceName          string  `description:"来源名称"`
+	Source              int     `description:"来源id"`
+	SubSource           int     `description:"来源id"`
+	EdbCode             string  `description:"指标编码"`
+	EdbName             string  `description:"指标名称"`
+	EdbAliasName        string  `description:"指标名称(别名)"`
+	EdbNameEn           string  `description:"英文指标名称"`
+	EdbAliasNameEn      string  `description:"英文指标名称(别名)"`
+	EdbType             int     `description:"指标类型:1:基础指标,2:计算指标"`
+	Frequency           string  `description:"频率"`
+	FrequencyEn         string  `description:"英文频率"`
+	Unit                string  `description:"单位"`
+	UnitEn              string  `description:"英文单位"`
+	StartDate           string  `description:"起始日期"`
+	EndDate             string  `description:"终止日期"`
+	ModifyTime          string  `description:"指标最后更新时间"`
+	ChartEdbMappingId   int     `description:"图表指标id"`
+	ChartInfoId         int     `description:"图表id"`
+	MaxData             float64 `description:"上限"`
+	MinData             float64 `description:"下限"`
+	IsOrder             bool    `description:"true:正序,false:逆序"`
+	IsAxis              int     `description:"1:左轴,0:右轴"`
+	EdbInfoType         int     `description:"1:标准指标,0:领先指标"`
+	EdbInfoCategoryType int     `description:"0:普通指标,1:预测指标"`
+	LeadValue           int     `description:"领先值"`
+	LeadUnit            string  `description:"领先单位"`
+	LeadUnitEn          string  `description:"领先英文单位"`
+	ChartStyle          string  `description:"图表类型"`
+	ChartColor          string  `description:"颜色"`
+	PredictChartColor   string  `description:"预测数据的颜色"`
+	ChartWidth          float64 `description:"线条大小"`
+	ChartType           int     `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图,8:商品价格曲线图,9:相关性图"`
+	LatestDate          string  `description:"数据最新日期"`
+	LatestValue         float64 `description:"数据最新值"`
+	MoveLatestDate      string  `description:"移动后的数据最新日期"`
+	UniqueCode          string  `description:"指标唯一编码"`
+	MinValue            float64 `json:"-" description:"最小值"`
+	MaxValue            float64 `json:"-" description:"最大值"`
+	DataList            interface{}
+	IsNullData          bool    `json:"-" description:"是否空数据"`
+	MappingSource       int     `description:"1:ETA图库;2:商品价格曲线"`
+	RegionType          string  `description:"交易所来源,海外还是国内" json:"-"`
+	ClassifyId          int     `description:"分类id"`
+	IsConvert           int     `description:"是否数据转换 0不转 1转"`
+	ConvertType         int     `description:"数据转换类型 1乘 2除 3对数"`
+	ConvertValue        float64 `description:"数据转换值"`
+	ConvertUnit         string  `description:"数据转换单位"`
+	ConvertEnUnit       string  `description:"数据转换单位"`
+	IsJoinPermission    int     `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+	HaveOperaAuth       bool    `description:"是否有数据权限,默认:false"`
+}
+
+type QuarterData struct {
+	Year                 int
+	DataList             []*EdbDataList
+	CuttingDataTimestamp int64 `description:"切割的时间戳"`
+	ChartLegend          string
+	Years                string
+}
+
+type QuarterXDateItem struct {
+	StartDate            time.Time
+	EndDate              time.Time
+	ShowName             string
+	ChartLegend          string
+	CuttingDataTimestamp int64 `description:"切割的时间戳"`
+}
+
+type QuarterDataByUniqueCode struct {
+	Year     int
+	DataList []*EdbDataListByUniqueCode
+}
+
+type ChartEdbInfoDetailResp struct {
+	EdbInfo *ChartEdbInfoMapping
+}
+
+type ChartInfoDetailResp struct {
+	ChartInfo    *ChartInfoView
+	EdbInfoList  []*ChartEdbInfoMapping
+	XEdbIdValue  []int           `description:"柱方图的x轴数据,指标id"`
+	YDataList    []YData         `description:"柱方图的y轴数据"`
+	XDataList    []XData         `description:"商品价格曲线的X轴数据"`
+	BarChartInfo BarChartInfoReq `description:"柱方图的配置"`
+	//CorrelationChartInfo *CorrelationInfo `description:"相关性图表信息"`
+	DataResp interface{} `description:"图表数据,根据图的类型而定的,没有确定的数据格式"`
+}
+
+// XData 商品价格曲线的的x轴数据
+type XData struct {
+	Name   string `description:"别名"`
+	NameEn string `description:"英文别名"`
+}
+
+// YData 柱方图的y轴数据
+type YData struct {
+	Date           string          `description:"数据日期"`
+	ConfigDate     time.Time       `description:"配置的日期" json:"-"`
+	Color          string          `description:"数据颜色"`
+	Name           string          `description:"别名"`
+	NameEn         string          `description:"英文别名"`
+	Value          []float64       `description:"每个指标的值"`
+	NoDataEdbList  []int           `description:"没有数据的指标列表"`
+	XEdbInfoIdList []int           `description:"对应X轴的指标id列表"`
+	NameList       []string        `description:"每个值对应的名称"`
+	EnNameList     []string        `description:"每个值对应的英文名称"`
+	EdbValMap      map[int]float64 `description:"指标与值的对应" json:"-"`
+	M              []int           `description:"对应开始日期的间隔值" json:"-"`
+	Unit           string          `description:"中文单位名称"`
+	UnitEn         string          `description:"英文单位名称"`
+}
+
+func ModifyChartInfoAndMapping(edbInfoIdStr string, req *SaveChartInfoReq, chartType int) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	//非季节图
+	if chartType != 2 {
+		updateStr := `edb_info_ids=?,
+			  modify_time = NOW(),
+              date_type=?,
+			  start_date=?,
+			  end_date=?,
+ 			  left_min=?,
+			  left_max=?,
+			  right_min=?,
+			  right_max=?,
+			  right2_min=?,
+			  right2_max=?,
+			  min_max_save=?,
+              start_year=?`
+		pars := []interface{}{edbInfoIdStr, req.DateType, req.StartDate, req.EndDate, req.LeftMin, req.LeftMax, req.RightMin, req.RightMax, req.Right2Min, req.Right2Max, req.MinMaxSave, req.StartYear}
+		if req.ExtraConfig != `` {
+			updateStr += `,extra_config=? `
+			pars = append(pars, req.ExtraConfig)
+		}
+		pars = append(pars, req.ChartInfoId)
+
+		sql := ` UPDATE  chart_info SET ` + updateStr + ` WHERE chart_info_id = ?`
+		_, err = to.Raw(sql, pars).Exec()
+		if err != nil {
+			fmt.Println("UPDATE  chart_info Err:", err.Error())
+			return err
+		}
+	} else {
+		sql := ` UPDATE  chart_info
+			SET
+              edb_info_ids=?,
+			  modify_time = NOW(),
+			  calendar=?,
+			  date_type=?,
+			  start_date=?,
+			  end_date=?,
+			  season_start_date=?,
+			  season_end_date=?,
+			  left_min=?,
+			  left_max=?,
+			  right_min=?,
+			  right_max=?,
+			  right2_min=?,
+			  right2_max=?,
+			  min_max_save=?,
+			  start_year=?
+			WHERE chart_info_id = ?`
+		_, err = to.Raw(sql, edbInfoIdStr, req.Calendar, req.DateType, req.StartDate, req.EndDate, req.StartDate, req.EndDate, req.LeftMin, req.LeftMax, req.RightMin, req.RightMax, req.Right2Min, req.Right2Max, req.MinMaxSave, req.StartYear, req.ChartInfoId).Exec()
+		if err != nil {
+			fmt.Println("UPDATE  chart_info Err:", err.Error())
+			return err
+		}
+	}
+
+	var edbInfoIdArr []string
+	mapList := make([]*ChartEdbMapping, 0)
+	for _, v := range req.ChartEdbInfoList {
+		edbInfoIdArr = append(edbInfoIdArr, strconv.Itoa(v.EdbInfoId))
+		var count int
+		csql := `SELECT COUNT(1) AS count FROM chart_edb_mapping WHERE chart_info_id=? AND edb_info_id=? `
+		err = to.Raw(csql, req.ChartInfoId, v.EdbInfoId).QueryRow(&count)
+		if err != nil {
+			fmt.Println("QueryRow Err:", err.Error())
+			return err
+		}
+		if count > 0 {
+			msql := `UPDATE  chart_edb_mapping
+				SET
+                 modify_time = NOW(),
+				 max_data = ?,
+				 min_data = ?,
+				 is_order = ?,
+				 is_axis = ?,
+				 edb_info_type = ?,
+				 lead_value = ?,
+				 lead_unit = ?,
+				 chart_style = ?,
+				 chart_color = ?,
+				 predict_chart_color = ?,
+				 chart_width = ?
+				WHERE chart_info_id =? AND edb_info_id=? `
+			_, err = to.Raw(msql, v.MaxData, v.MinData, v.IsOrder, v.IsAxis, v.EdbInfoType, v.LeadValue, v.LeadUnit, v.ChartStyle, v.ChartColor, v.PredictChartColor, v.ChartWidth, req.ChartInfoId, v.EdbInfoId).Exec()
+			if err != nil {
+				fmt.Println("chart_edb_mapping Err:" + err.Error())
+				return err
+			}
+		} else {
+			mapItem := new(ChartEdbMapping)
+			mapItem.ChartInfoId = req.ChartInfoId
+			mapItem.EdbInfoId = v.EdbInfoId
+			mapItem.CreateTime = time.Now()
+			mapItem.ModifyTime = time.Now()
+			timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+			mapItem.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp)
+			mapItem.MaxData = v.MaxData
+			mapItem.MinData = v.MinData
+			mapItem.IsOrder = v.IsOrder
+			mapItem.IsAxis = v.IsAxis
+			mapItem.EdbInfoType = v.EdbInfoType
+			mapItem.LeadValue = v.LeadValue
+			mapItem.LeadUnit = v.LeadUnit
+			mapItem.ChartStyle = v.ChartStyle
+			mapItem.ChartColor = v.ChartColor
+			mapItem.PredictChartColor = v.PredictChartColor
+			mapItem.ChartWidth = v.ChartWidth
+			mapList = append(mapList, mapItem)
+		}
+	}
+	if len(mapList) > 0 {
+		_, err = to.InsertMulti(1, mapList)
+		if err != nil {
+			fmt.Println("AddChartEdbMapping Err:" + err.Error())
+			return err
+		}
+	}
+	if len(edbInfoIdArr) > 0 {
+		edbInfoIdStr := strings.Join(edbInfoIdArr, ",")
+		if edbInfoIdStr != "" {
+			dsql := `DELETE FROM chart_edb_mapping WHERE chart_info_id=? AND edb_info_id NOT IN(` + edbInfoIdStr + `)`
+			_, err = to.Raw(dsql, req.ChartInfoId).Exec()
+			if err != nil {
+				fmt.Println("delete err:" + err.Error())
+				return err
+			}
+		}
+	}
+	return
+}
+
+// EditChartInfoAndMapping 修改 ETA图库 的 图表与指标 的关系
+func EditChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr string, calendar string, dateType, disabled int, barChartConf string, chartEdbInfoList []*ChartSaveItem, seasonExtra string) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	var pars []interface{}
+	pars = append(pars, req.ChartName)
+	pars = append(pars, edbInfoIdStr)
+	pars = append(pars, req.ChartType)
+	pars = append(pars, req.ChartClassifyId)
+	pars = append(pars, disabled)
+	pars = append(pars, barChartConf)
+	pars = append(pars, req.ExtraConfig)
+	pars = append(pars, seasonExtra)
+	pars = append(pars, req.StartYear)
+	pars = append(pars, req.ChartThemeId)
+	pars = append(pars, req.SourcesFrom)
+	pars = append(pars, req.Instructions)
+	pars = append(pars, req.MarkersLines)
+	pars = append(pars, req.MarkersAreas)
+	pars = append(pars, req.Unit)
+	pars = append(pars, req.UnitEn)
+
+	sql := ` UPDATE  chart_info
+			SET
+			  chart_name =?,
+              edb_info_ids=?,
+			  chart_type=?,
+			  chart_classify_id = ?,
+			  modify_time = NOW(),
+              disabled = ?,
+              bar_config = ?,
+              extra_config = ?, 
+              season_extra_config = ?,
+ 			  start_year = ?,
+ 			  chart_theme_id = ?,
+ 			  sources_from = ?,
+ 			  instructions = ?,
+ 			  markers_lines = ?,
+ 			  markers_areas = ?,
+ 			  unit = ?,
+ 			  unit_en = ?
+			`
+	if calendar != "" {
+		sql += `,calendar = ? `
+		pars = append(pars, calendar)
+	}
+	if dateType > 0 {
+		sql += `,date_type = ? `
+		pars = append(pars, dateType)
+	}
+
+	sql += `,start_date = ? `
+	pars = append(pars, req.StartDate)
+
+	sql += `,end_date = ? `
+	pars = append(pars, req.EndDate)
+
+	sql += `,season_start_date = ? `
+	pars = append(pars, req.StartDate)
+
+	sql += `,season_end_date = ? `
+	pars = append(pars, req.EndDate)
+
+	sql += `,left_min = ? `
+	pars = append(pars, req.LeftMin)
+
+	sql += `,left_max = ? `
+	pars = append(pars, req.LeftMax)
+
+	sql += `,right_min = ? `
+	pars = append(pars, req.RightMin)
+
+	sql += `,right_max = ? `
+	pars = append(pars, req.RightMax)
+
+	sql += `,right2_min = ? `
+	pars = append(pars, req.Right2Min)
+
+	sql += `,right2_max = ? `
+	pars = append(pars, req.Right2Max)
+
+	sql += `,min_max_save = ? `
+	pars = append(pars, req.MinMaxSave)
+
+	sql += `WHERE chart_info_id = ?`
+
+	pars = append(pars, req.ChartInfoId)
+	_, err = to.Raw(sql, pars).Exec()
+	if err != nil {
+		fmt.Println("UPDATE  chart_info Err:", err.Error())
+		return err
+	}
+	chartEdbMappingIdList := make([]string, 0)
+
+	for _, v := range chartEdbInfoList {
+		// 查询该指标是否存在,如果存在的话,那么就去修改,否则新增
+		var tmpChartEdbMapping *ChartEdbMapping
+		csql := `SELECT *  FROM chart_edb_mapping WHERE chart_info_id=? AND edb_info_id=? AND source = ? `
+		err = to.Raw(csql, req.ChartInfoId, v.EdbInfoId, utils.CHART_SOURCE_DEFAULT).QueryRow(&tmpChartEdbMapping)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			fmt.Println("QueryRow Err:", err.Error())
+			return err
+		}
+		if tmpChartEdbMapping != nil {
+			chartEdbMappingIdList = append(chartEdbMappingIdList, strconv.Itoa(tmpChartEdbMapping.ChartEdbMappingId))
+			tmpChartEdbMapping.ModifyTime = time.Now()
+			tmpChartEdbMapping.MaxData = v.MaxData
+			tmpChartEdbMapping.MinData = v.MinData
+			tmpChartEdbMapping.IsOrder = v.IsOrder
+			tmpChartEdbMapping.IsAxis = v.IsAxis
+			tmpChartEdbMapping.EdbInfoType = v.EdbInfoType
+			tmpChartEdbMapping.LeadValue = v.LeadValue
+			tmpChartEdbMapping.LeadUnit = v.LeadUnit
+			tmpChartEdbMapping.ChartStyle = v.ChartStyle
+			tmpChartEdbMapping.ChartColor = v.ChartColor
+			tmpChartEdbMapping.PredictChartColor = v.PredictChartColor
+			tmpChartEdbMapping.ChartWidth = v.ChartWidth
+			tmpChartEdbMapping.EdbAliasName = v.EdbAliasName
+			tmpChartEdbMapping.IsConvert = v.IsConvert
+			tmpChartEdbMapping.ConvertType = v.ConvertType
+			tmpChartEdbMapping.ConvertValue = v.ConvertValue
+			tmpChartEdbMapping.ConvertUnit = v.ConvertUnit
+			tmpChartEdbMapping.ConvertEnUnit = v.ConvertEnUnit
+			_, err = to.Update(tmpChartEdbMapping, "ModifyTime", "MaxData", "MinData", "IsOrder", "IsAxis", "EdbInfoType", "LeadValue", "LeadUnit", "ChartStyle", "ChartColor", "PredictChartColor", "ChartWidth", "EdbAliasName",
+				"IsConvert", "ConvertType", "ConvertValue", "ConvertUnit", "ConvertEnUnit")
+			if err != nil {
+				fmt.Println("chart_edb_mapping Err:" + err.Error())
+				return err
+			}
+		} else {
+			mapItem := new(ChartEdbMapping)
+			mapItem.ChartInfoId = req.ChartInfoId
+			mapItem.EdbInfoId = v.EdbInfoId
+			mapItem.CreateTime = time.Now()
+			mapItem.ModifyTime = time.Now()
+			timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+			mapItem.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp + "_" + strconv.Itoa(v.EdbInfoId))
+			mapItem.MaxData = v.MaxData
+			mapItem.MinData = v.MinData
+			mapItem.IsOrder = v.IsOrder
+			mapItem.IsAxis = v.IsAxis
+			mapItem.EdbInfoType = v.EdbInfoType
+			mapItem.LeadValue = v.LeadValue
+			mapItem.LeadUnit = v.LeadUnit
+			mapItem.ChartStyle = v.ChartStyle
+			mapItem.ChartColor = v.ChartColor
+			mapItem.PredictChartColor = v.PredictChartColor
+			mapItem.ChartWidth = v.ChartWidth
+			mapItem.Source = utils.CHART_SOURCE_DEFAULT
+			mapItem.EdbAliasName = v.EdbAliasName
+			mapItem.IsConvert = v.IsConvert
+			mapItem.ConvertType = v.ConvertType
+			mapItem.ConvertValue = v.ConvertValue
+			mapItem.ConvertUnit = v.ConvertUnit
+			mapItem.ConvertEnUnit = v.ConvertEnUnit
+			tmpId, err := to.Insert(mapItem)
+			if err != nil {
+				fmt.Println("AddChartEdbMapping Err:" + err.Error())
+				return err
+			}
+			mapItem.ChartEdbMappingId = int(tmpId)
+			chartEdbMappingIdList = append(chartEdbMappingIdList, strconv.Itoa(mapItem.ChartEdbMappingId))
+		}
+	}
+	if len(chartEdbMappingIdList) > 0 {
+		chartEdbMappingIdStr := strings.Join(chartEdbMappingIdList, ",")
+		dsql := `DELETE FROM chart_edb_mapping WHERE chart_info_id=? AND chart_edb_mapping_id NOT IN(` + chartEdbMappingIdStr + `)`
+		_, err = to.Raw(dsql, req.ChartInfoId).Exec()
+		if err != nil {
+			fmt.Println("delete err:" + err.Error())
+			return err
+		}
+	}
+	return
+}
+
+// EditFutureGoodChartInfoAndMapping 修改商品价格曲线的 图表与指标 的关系
+func EditFutureGoodChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr string, calendar string, dateType, disabled int, barChartConf string) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	var pars []interface{}
+	pars = append(pars, req.ChartName)
+	pars = append(pars, edbInfoIdStr)
+	pars = append(pars, req.ChartType)
+	pars = append(pars, req.ChartClassifyId)
+	pars = append(pars, disabled)
+	pars = append(pars, barChartConf)
+
+	sql := ` UPDATE  chart_info
+			SET
+			  chart_name =?,
+              edb_info_ids=?,
+			  chart_type=?,
+			  chart_classify_id = ?,
+			  modify_time = NOW(),
+              disabled = ?,
+              bar_config = ?
+			`
+	if calendar != "" {
+		sql += `,calendar = ? `
+		pars = append(pars, calendar)
+	}
+	if dateType > 0 {
+		sql += `,date_type = ? `
+		pars = append(pars, dateType)
+	}
+
+	sql += `,start_date = ? `
+	pars = append(pars, req.StartDate)
+
+	sql += `,end_date = ? `
+	pars = append(pars, req.EndDate)
+
+	sql += `,season_start_date = ? `
+	pars = append(pars, req.StartDate)
+
+	sql += `,season_end_date = ? `
+	pars = append(pars, req.EndDate)
+
+	sql += `,left_min = ? `
+	pars = append(pars, req.LeftMin)
+
+	sql += `,left_max = ? `
+	pars = append(pars, req.LeftMax)
+
+	sql += `,right_min = ? `
+	pars = append(pars, req.RightMin)
+
+	sql += `,right_max = ? `
+	pars = append(pars, req.RightMax)
+
+	sql += `,right2_min = ? `
+	pars = append(pars, req.Right2Min)
+
+	sql += `,right2_max = ? `
+	pars = append(pars, req.Right2Max)
+
+	sql += `WHERE chart_info_id = ?`
+
+	pars = append(pars, req.ChartInfoId)
+	_, err = to.Raw(sql, pars).Exec()
+	if err != nil {
+		fmt.Println("UPDATE  chart_info Err:", err.Error())
+		return err
+	}
+	chartEdbMappingIdList := make([]string, 0)
+	for _, v := range req.BarChartInfo.EdbInfoIdList {
+		// 查询该指标是否存在,如果存在的话,那么就去修改,否则新增
+		var tmpChartEdbMapping *ChartEdbMapping
+		csql := `SELECT *  FROM chart_edb_mapping WHERE chart_info_id=? AND edb_info_id=? AND source = ? `
+		err = to.Raw(csql, req.ChartInfoId, v.EdbInfoId, v.Source).QueryRow(&tmpChartEdbMapping)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			fmt.Println("QueryRow Err:", err.Error())
+			return err
+		}
+		if tmpChartEdbMapping != nil {
+			chartEdbMappingIdList = append(chartEdbMappingIdList, strconv.Itoa(tmpChartEdbMapping.ChartEdbMappingId))
+			//tmpChartEdbMapping.ModifyTime = time.Now()
+			//tmpChartEdbMapping.MaxData = v.MaxData
+			//tmpChartEdbMapping.MinData = v.MinData
+			//tmpChartEdbMapping.IsOrder = v.IsOrder
+			//tmpChartEdbMapping.IsAxis = v.IsAxis
+			//tmpChartEdbMapping.EdbInfoType = v.EdbInfoType
+			//tmpChartEdbMapping.LeadValue = v.LeadValue
+			//tmpChartEdbMapping.LeadUnit = v.LeadUnit
+			//tmpChartEdbMapping.ChartStyle = v.ChartStyle
+			//tmpChartEdbMapping.ChartColor = v.ChartColor
+			//tmpChartEdbMapping.PredictChartColor = v.PredictChartColor
+			//tmpChartEdbMapping.ChartWidth = v.ChartWidth
+			//_, err = to.Update(tmpChartEdbMapping, "ModifyTime", "MaxData", "MinData", "IsOrder", "IsAxis", "EdbInfoType", "LeadValue", "LeadUnit", "ChartStyle", "ChartColor", "PredictChartColor", "ChartWidth")
+			//if err != nil {
+			//	fmt.Println("chart_edb_mapping Err:" + err.Error())
+			//	return err
+			//}
+		} else {
+			mapItem := new(ChartEdbMapping)
+			mapItem.ChartInfoId = req.ChartInfoId
+			mapItem.EdbInfoId = v.EdbInfoId
+			mapItem.CreateTime = time.Now()
+			mapItem.ModifyTime = time.Now()
+			timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+			mapItem.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp + "_" + strconv.Itoa(v.EdbInfoId))
+			//mapItem.MaxData = v.MaxData
+			//mapItem.MinData = v.MinData
+			mapItem.IsOrder = true
+			mapItem.IsAxis = 1
+			mapItem.EdbInfoType = 1
+			//mapItem.LeadValue = v.LeadValue
+			//mapItem.LeadUnit = v.LeadUnit
+			//mapItem.ChartStyle = v.ChartStyle
+			//mapItem.ChartColor = v.ChartColor
+			//mapItem.PredictChartColor = v.PredictChartColor
+			//mapItem.ChartWidth = v.ChartWidth
+			mapItem.Source = v.Source
+			tmpId, err := to.Insert(mapItem)
+			if err != nil {
+				fmt.Println("AddChartEdbMapping Err:" + err.Error())
+				return err
+			}
+			mapItem.ChartEdbMappingId = int(tmpId)
+			chartEdbMappingIdList = append(chartEdbMappingIdList, strconv.Itoa(mapItem.ChartEdbMappingId))
+		}
+	}
+	if len(chartEdbMappingIdList) > 0 {
+		chartEdbMappingIdStr := strings.Join(chartEdbMappingIdList, ",")
+		dsql := `DELETE FROM chart_edb_mapping WHERE chart_info_id=? AND chart_edb_mapping_id NOT IN(` + chartEdbMappingIdStr + `)`
+		_, err = to.Raw(dsql, req.ChartInfoId).Exec()
+		if err != nil {
+			fmt.Println("delete err:" + err.Error())
+			return err
+		}
+	}
+	return
+}
+
+func EditChartEnInfoAndEdbEnInfo(req *EditChartEnInfoReq) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	var pars []interface{}
+	updateField := ` chart_name_en =?, `
+	pars = append(pars, req.ChartNameEn)
+
+	if req.ExtraConfig != `` {
+		updateField += ` extra_config =?, `
+		pars = append(pars, req.ExtraConfig)
+	}
+
+	sql := ` UPDATE  chart_info
+			SET ` + updateField + ` modify_time = NOW()
+			WHERE chart_info_id = ?`
+
+	pars = append(pars, req.ChartInfoId)
+	_, err = to.Raw(sql, pars).Exec()
+	if err != nil {
+		fmt.Println("UPDATE  chart_info Err:", err.Error())
+		return err
+	}
+	var edbInfoIdArr []string
+	for _, v := range req.ChartEdbInfoList {
+		edbInfoIdArr = append(edbInfoIdArr, strconv.Itoa(v.EdbInfoId))
+		var count int
+		csql := `SELECT COUNT(1) AS count FROM chart_edb_mapping WHERE chart_info_id=? AND edb_info_id=? `
+		err = to.Raw(csql, req.ChartInfoId, v.EdbInfoId).QueryRow(&count)
+		if err != nil {
+			fmt.Println("QueryRow Err:", err.Error())
+			return err
+		}
+		if count > 0 {
+			msql := ` UPDATE  edb_info
+			SET
+			  edb_name_en =?,
+			  unit_en = ?,
+			  modify_time = NOW()
+			WHERE edb_info_id = ? `
+			_, err = to.Raw(msql, v.EdbNameEn, v.UnitEn, v.EdbInfoId).Exec()
+			if err != nil {
+				fmt.Println("edb_info Err:" + err.Error())
+				return err
+			}
+		}
+	}
+	return
+}
+
+type AddChartInfoReq struct {
+	ChartEdbInfoList []*ChartSaveItem `description:"指标及配置信息"`
+	//ChartClassifyId      int                     `description:"分类id"`
+	ChartName            string                  `description:"图表名称"`
+	ChartType            int                     `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图"`
+	DateType             int                     `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:起始日期至今"`
+	StartDate            string                  `description:"自定义开始日期"`
+	EndDate              string                  `description:"自定义结束日期"`
+	Calendar             string                  `description:"公历/农历"`
+	LeftMin              string                  `description:"图表左侧最小值"`
+	LeftMax              string                  `description:"图表左侧最大值"`
+	RightMin             string                  `description:"图表右侧最小值"`
+	RightMax             string                  `description:"图表右侧最大值"`
+	Right2Min            string                  `description:"图表右侧2最小值"`
+	Right2Max            string                  `description:"图表右侧2最大值"`
+	MinMaxSave           int                     `description:"是否手动保存过上下限:0-否;1-是"`
+	Disabled             int                     `description:"是否禁用,0:启用,1:禁用,默认:0"`
+	BarConfig            string                  `description:"柱方图的配置,json数据"`
+	CorrelationChartInfo CorrelationChartInfoReq `description:"相关性图表配置"`
+	ExtraConfig          string                  `description:"图表额外配置信息,json字符串"`
+	ChartImage           string                  `description:"封面图" json:"-"`
+	SeasonExtraConfig    string                  `description:"季节性图表中的配置,json数据"`
+	StartYear            int                     `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
+	Source               int                     `description:"1:ETA图库;2:商品价格曲线"`
+	ChartThemeId         int                     `description:"图表应用主题ID"`
+	SourcesFrom          string                  `description:"图表来源"`
+	Instructions         string                  `description:"图表说明"`
+	MarkersLines         string                  `description:"标识线"`
+	MarkersAreas         string                  `description:"标识区"`
+	Unit                 string                  `description:"中文单位名称"`
+	UnitEn               string                  `description:"英文单位名称"`
+}
+
+type AddChartReq struct {
+	ChartInfo               *ChartInfo
+	Description             string `description:"逻辑简述"`
+	EdbInfoList             []*EdbInfo
+	ChartThemeList          []*ChartTheme
+	EdbInfoDataList         []*AddEdbDataReq
+	ChartEdbMapping         []*ChartEdbMapping
+	EdbInfoCalculateMapping []*EdbInfoCalculateMapping
+}
+
+type AddEdbDataReq struct {
+	EdbCode  string
+	DataList []*mgodb.EdbDataBase
+}
+
+type DeleteChartReq struct {
+	ChartInfoId int `description:"图表id"`
+}
+
+type PreviewChartInfoReq struct {
+	ChartEdbInfoList  []*ChartSaveItem `description:"指标及配置信息"`
+	DateType          int              `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:起始日期至今 20:最近N年"`
+	ChartType         int              `description:"生成样式:1:曲线图,2:季节性图,3:面积图,4:柱状图,5:散点图,6:组合图,7:柱方图"`
+	StartDate         string           `description:"自定义开始日期"`
+	EndDate           string           `description:"自定义结束日期"`
+	Calendar          string           `description:"公历/农历"`
+	SeasonExtraConfig SeasonExtraItem  `description:"季节性图表中的配置,json数据"`
+	StartYear         int              `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
+}
+
+type SeasonExtraItem struct {
+	ChartLegend []SeasonChartLegend `description:"自定义的图例名称"`
+	XStartDate  string              `description:"横坐标显示的起始日"`
+	XEndDate    string              `description:"横坐标显示的截止日"`
+	JumpYear    int                 `description:"横坐标日期是否跨年,1跨年,0不跨年"`
+}
+
+type SeasonChartLegend struct {
+	Name  string
+	Value string
+}
+type AddChartInfoResp struct {
+	ChartInfoId int    `description:"图表id"`
+	UniqueCode  string `description:"图表唯一编码"`
+	ChartType   int    `description:"生成样式:1:曲线图,2:季节性图"`
+}
+
+type QuarterDataList []*QuarterData
+
+func (m QuarterDataList) Len() int {
+	return len(m)
+}
+
+func (m QuarterDataList) Less(i, j int) bool {
+	return m[i].Years < m[j].Years
+}
+
+func (m QuarterDataList) Swap(i, j int) {
+	m[i], m[j] = m[j], m[i]
+}
+
+// 判断图表指标是否已经存在
+func ChartInfoExist(condition, edbInfoIdStr string) (count int, err error) {
+	sql := `SELECT COUNT(1) AS count FROM (
+		SELECT GROUP_CONCAT(edb_info_id ORDER BY edb_info_id ASC  SEPARATOR ',') AS edb_info_id_str 
+		 FROM chart_edb_mapping AS a
+         WHERE 1=1`
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` GROUP BY chart_info_id 
+		)AS t
+		WHERE t.edb_info_id_str=? 
+        GROUP BY t.edb_info_id_str `
+	o := orm.NewOrm()
+	err = o.Raw(sql, edbInfoIdStr).QueryRow(&count)
+	return
+}
+
+func ChartInfoSearchByKeyWord(keyword string, showSysId int) (searchList []*ChartInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE 1=1 `
+
+	var pars []interface{}
+
+	if keyword != "" {
+		sql += `AND chart_name LIKE ?  `
+		pars = append(pars, utils.GetLikeKeyword(keyword))
+	}
+
+	if showSysId > 0 {
+		sql += ` AND sys_user_id = ? `
+		pars = append(pars, showSysId)
+	}
+	sql += ` ORDER BY create_time DESC `
+	if keyword == "" {
+		sql += ` LIMIT 100 `
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&searchList)
+	return
+}
+
+// ChartInfoSearchByEmptyKeyWord 没有关键字的时候获取默认100条数据
+func ChartInfoSearchByEmptyKeyWord(showSysId int, sourceList []int, noPermissionChartIdList []int, startSize, pageSize int) (total int64, searchList []*ChartInfo, err error) {
+	num := len(sourceList)
+	o := orm.NewOrm()
+
+	baseSql := `  FROM chart_info WHERE 1=1 AND source in (` + utils.GetOrmInReplace(num) + `)`
+
+	var basePars []interface{}
+	basePars = append(basePars, sourceList)
+
+	if showSysId > 0 {
+		baseSql += ` AND sys_user_id = ? `
+		basePars = append(basePars, showSysId)
+	}
+
+	lenNoPermissionChartIdList := len(noPermissionChartIdList)
+	if lenNoPermissionChartIdList > 0 {
+		baseSql += ` AND chart_info_id not in (` + utils.GetOrmInReplace(lenNoPermissionChartIdList) + `) `
+		basePars = append(basePars, noPermissionChartIdList)
+	}
+
+	// 查找数量
+	totalSql := " SELECT count(1) as total " + baseSql
+	err = o.Raw(totalSql, basePars).QueryRow(&total)
+	if err != nil {
+		return
+	}
+
+	// 查找列表数据
+	sql := " SELECT *  " + baseSql + ` ORDER BY create_time DESC LIMIT ?,? `
+	basePars = append(basePars, startSize, pageSize)
+
+	_, err = o.Raw(sql, basePars).QueryRows(&searchList)
+
+	return
+}
+
+func GetNextChartInfo(classifyId int) (item *ChartInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT b.* FROM chart_classify AS a
+			INNER JOIN chart_info AS b ON a.chart_classify_id=b.chart_classify_id
+			WHERE a.chart_classify_id>?
+			ORDER BY a.chart_classify_id ASC
+			LIMIT 1 `
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+type SetChartInfoImageReq struct {
+	ChartInfoId int    `description:"图表ID"`
+	ImageUrl    string `description:"图表图片地址"`
+}
+
+func EditChartInfoImage(req *SetChartInfoImageReq) (err error) {
+	o := orm.NewOrm()
+
+	sql := ` UPDATE  chart_info SET chart_image=?, modify_time = NOW() WHERE chart_info_id = ? `
+	_, err = o.Raw(sql, req.ImageUrl, req.ChartInfoId).Exec()
+	if err != nil {
+		fmt.Println("EditChartInfoImage Err:", err.Error())
+		return err
+	}
+
+	return
+}
+
+type ChartInfoDetailFromUniqueCodeResp struct {
+	ChartInfo    *ChartInfoView
+	Status       bool `description:"true:图表存在,false:图表不存在"`
+	EdbInfoList  []*ChartEdbInfoMapping
+	XEdbIdValue  []int           `description:"柱方图的x轴数据,指标id"`
+	YDataList    []YData         `description:"柱方图的y轴数据"`
+	XDataList    []XData         `description:"商品价格曲线的X轴数据"`
+	BarChartInfo BarChartInfoReq `description:"柱方图的配置"`
+	//CorrelationChartInfo *CorrelationInfo `description:"相关性图表信息"`
+	DataResp interface{} `description:"图表数据,根据图的类型而定的,没有确定的数据格式"`
+}
+
+func GetChartInfoByUniqueCode(uniqueCode string) (item *ChartInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE unique_code=? `
+	err = o.Raw(sql, uniqueCode).QueryRow(&item)
+	return
+}
+
+func GetChartInfoViewByUniqueCode(uniqueCode string) (item *ChartInfoView, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE unique_code=? `
+	err = o.Raw(sql, uniqueCode).QueryRow(&item)
+	return
+}
+
+// GetFirstChartInfoByClassifyId 获取当前分类下,且排序数相同 的排序第一条的数据
+func GetFirstChartInfoByClassifyId(classifyId int) (item *ChartInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE chart_classify_id=? order by sort asc,chart_info_id asc limit 1`
+	err = o.Raw(sql, classifyId).QueryRow(&item)
+	return
+}
+
+// UpdateChartInfoSortByClassifyId 根据图表id更新排序
+func UpdateChartInfoSortByClassifyId(classifyId, nowSort, prevChartInfoId int, sourceList []int, updateSort string) (err error) {
+	o := orm.NewOrm()
+	num := len(sourceList)
+	if num <= 0 {
+		return
+	}
+	sql := ` update chart_info set sort = ` + updateSort + ` WHERE chart_classify_id=? AND source in (` + utils.GetOrmInReplace(num) + `) AND `
+	if prevChartInfoId > 0 {
+		sql += ` (sort > ? or (chart_info_id > ` + fmt.Sprint(prevChartInfoId) + ` and sort = ` + fmt.Sprint(nowSort) + `))`
+	}
+	_, err = o.Raw(sql, classifyId, sourceList, nowSort).Exec()
+	return
+}
+
+// Update 更新图表基础信息
+func (chartInfo *ChartInfo) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(chartInfo, cols...)
+	return
+}
+
+type ChartInfoView struct {
+	ChartInfoId       int    `orm:"column(chart_info_id);pk"`
+	ChartName         string `description:"来源名称"`
+	ChartNameEn       string `description:"英文图表名称"`
+	Unit              string `description:"中文单位名称"`
+	UnitEn            string `description:"英文单位名称"`
+	ChartClassifyId   int    `description:"图表分类id"`
+	ChartClassifyName string `description:"图表名称"`
+	SysUserId         int
+	SysUserRealName   string
+	UniqueCode        string `description:"图表唯一编码"`
+	CreateTime        time.Time
+	ModifyTime        time.Time
+	DateType          int    `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间"`
+	StartDate         string `description:"自定义开始日期"`
+	EndDate           string `description:"自定义结束日期"`
+	IsSetName         int    `description:"设置名称"`
+	EdbInfoIds        string `description:"指标id"`
+	ChartType         int    `description:"生成样式:1:曲线图,2:季节性图"`
+	Calendar          string `description:"公历/农历"`
+	SeasonStartDate   string `description:"季节性图开始日期"`
+	SeasonEndDate     string `description:"季节性图开始日期"`
+	ChartImage        string `description:"图表图片"`
+	Sort              int    `description:"排序字段,数字越小越排前面"`
+	IsAdd             bool   `description:"true:已加入我的图库,false:未加入我的图库"`
+	MyChartId         int
+	MyChartClassifyId string `description:"我的图表分类,多个用逗号隔开"`
+	ChartClassify     []*ChartClassifyView
+	EdbEndDate        string `description:"指标最新更新日期"`
+	XMin              string `description:"图表X轴最小值"`
+	XMax              string `description:"图表X轴最大值"`
+	LeftMin           string `description:"图表左侧最小值"`
+	LeftMax           string `description:"图表左侧最大值"`
+	RightMin          string `description:"图表右侧最小值"`
+	RightMax          string `description:"图表右侧最大值"`
+	Right2Min         string `description:"图表右侧最小值"`
+	Right2Max         string `description:"图表右侧最大值"`
+	MinMaxSave        int    `description:"是否手动保存过上下限:0-否;1-是"`
+	IsEdit            bool   `description:"是否有编辑权限"`
+	IsEnChart         bool   `description:"是否展示英文标识"`
+	WarnMsg           string `description:"错误信息"`
+	Disabled          int    `description:"是否禁用,0:启用,1:禁用,默认:0"`
+	BarConfig         string `description:"柱方图的配置,json数据" json:"-"`
+	Source            int    `description:"1:ETA图库;2:商品价格曲线;3:相关性图表"`
+	//CorrelationLeadUnit string `description:"相关性图表-领先单位"`
+	ExtraConfig       string          `description:"图表额外配置,json数据"`
+	ChartSource       string          `description:"图表来源str"`
+	ChartSourceEn     string          `description:"图表来源(英文)"`
+	Button            ChartViewButton `description:"操作按钮"`
+	SeasonExtraConfig string          `description:"季节性图表中的配置,json数据"`
+	StartYear         int             `description:"当选择的日期类型为最近N年类型时,即date_type=20, 用start_year表示N"`
+	ChartThemeId      int             `description:"图表应用主题ID"`
+	ChartThemeStyle   string          `description:"图表应用主题样式"`
+	SourcesFrom       string          `description:"图表来源"`
+	Instructions      string          `description:"图表说明"`
+	MarkersLines      string          `description:"标识线"`
+	MarkersAreas      string          `description:"标识区"`
+	IsJoinPermission  int             `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+	HaveOperaAuth     bool            `description:"是否有数据权限,默认:false"`
+}
+
+type ChartViewButton struct {
+	IsEdit    bool `description:"是否有编辑权限"`
+	IsEnChart bool `description:"是否展示英文标识"`
+	IsAdd     bool `description:"true:已加入我的图库,false:未加入我的图库"`
+	IsCopy    bool `description:"是否有另存为按钮"`
+	IsSetName int  `description:"设置名称"`
+}
+
+type ImageSvgToPngResp struct {
+	Data struct {
+		ResourceURL string `json:"ResourceUrl"`
+	} `json:"Data"`
+	ErrCode     string `json:"ErrCode"`
+	ErrMsg      string `json:"ErrMsg"`
+	IsSendEmail bool   `json:"IsSendEmail"`
+	Msg         string `json:"Msg"`
+	Ret         int64  `json:"Ret"`
+	Success     bool   `json:"Success"`
+}
+
+// GetChartInfoByClassifyIdAndName 根据分类id和图表名获取图表信息
+func GetChartInfoByClassifyIdAndName(classifyId int, chartName string) (item *ChartInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE chart_classify_id = ? and chart_name=? `
+	err = o.Raw(sql, classifyId, chartName).QueryRow(&item)
+	return
+}
+
+// BatchChartRefreshReq 批量刷新图表请求
+type BatchChartRefreshReq struct {
+	ChartInfoCode   []string `description:"图表编码数组"`
+	ReportId        int      `description:"报告id"`
+	ReportChapterId int      `description:"报告章节id"`
+	Source          string   `description:"来源,枚举值:report、english_report、smart_report"`
+}
+
+// GetChartInfoListByUniqueCodeSlice 根据图表编码获取图表列表数据
+func GetChartInfoListByUniqueCodeSlice(uniqueCodeSlice []string) (total int64, items []*ChartInfo, err error) {
+	if len(uniqueCodeSlice) <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE unique_code in ("` + strings.Join(uniqueCodeSlice, `","`) + `") `
+	total, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// CopyAddChartInfoReq 复制并新增图表
+type CopyAddChartInfoReq struct {
+	ChartInfoId     int    `description:"待复制的图表id"`
+	ChartClassifyId int    `description:"分类id"`
+	ChartName       string `description:"图表名称"`
+}
+
+// GetChartInfoListByCondition 根据条件获取图表数据了列表
+func GetChartInfoListByCondition(condition string, pars []interface{}, startSize, pageSize int) (items []*ChartInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` ORDER BY chart_info_id DESC LIMIT ?,? `
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
+// GetChartInfoListGroupByUserId 根据指标id列表、用户分组获取指标信息
+func GetChartInfoListGroupByUserId(edbIdList []string) (items []*ChartInfo, err error) {
+	num := len(edbIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE chart_info_id in (` + utils.GetOrmInReplace(num) + `) ORDER BY chart_info_id desc `
+
+	_, err = o.Raw(sql, edbIdList).QueryRows(&items)
+	return
+}
+
+// GetChartInfoListByChartIdList
+// @Description: 根据图表id列表获取列表信息
+// @param edbIdList
+// @return items
+// @return err
+func GetChartInfoListByChartIdList(charIdList []string) (items []*ChartInfo, err error) {
+	num := len(charIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE chart_info_id in (` + utils.GetOrmInReplace(num) + `) ORDER BY chart_info_id desc `
+
+	_, err = o.Raw(sql, charIdList).QueryRows(&items)
+	return
+}
+
+// GetChartInfoListByUserId
+// @Description: 根据图表id列表获取列表信息
+// @param userIdList []int
+// @param source int
+// @return items
+// @return err
+func GetChartInfoListByUserId(userIdList []int, source int) (items []*ChartInfo, err error) {
+	num := len(userIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_info WHERE source = ? AND  sys_user_id in (` + utils.GetOrmInReplace(num) + `) ORDER BY chart_info_id desc `
+
+	_, err = o.Raw(sql, source, userIdList).QueryRows(&items)
+	return
+}
+
+// ModifyChartInfoUserIdByCodeList 根据指标code列表修改创建人
+func ModifyChartInfoUserIdByCodeList(edbIdList []string, userId int, userName string) (err error) {
+	num := len(edbIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `UPDATE chart_info SET sys_user_id=?,sys_user_real_name=? WHERE chart_info_id in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, userId, userName, edbIdList).Exec()
+	return
+}
+
+// ModifyChartInfoUserIdByOldUserId
+// @Description:  根据旧的用户id修改创建人
+// @author: Roc
+// @datetime 2024-03-25 19:14:59
+// @param oldUserId int
+// @param userId int
+// @param userName string
+// @return err error
+func ModifyChartInfoUserIdByOldUserId(oldUserIdList []int, userId int, userName string) (err error) {
+	num := len(oldUserIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `UPDATE chart_info SET sys_user_id=?,sys_user_real_name=? WHERE sys_user_id in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, userId, userName, oldUserIdList).Exec()
+
+	return
+}
+
+// BarChartInfoReq 柱方图预览请求数据
+type BarChartInfoReq struct {
+	EdbInfoIdList []BarChartInfoEdbItemReq `description:"指标信息"`
+	DateList      []BarChartInfoDateReq    `description:"日期配置"`
+	Sort          BarChartInfoSortReq      `description:"排序"`
+	XEdbList      []BarChartInfoEdbItemReq `description:"X轴选择的指标列表"`
+	YEdbList      []BarChartInfoEdbItemReq `description:"Y轴选择的指标列表"`
+	Unit          string                   `description:"中文单位"`
+	UnitEn        string                   `description:"英文单位"`
+}
+
+// BarChartInfoEdbItemReq 柱方图预览请求数据(指标相关)
+type BarChartInfoEdbItemReq struct {
+	EdbInfoId     int     `description:"指标ID"`
+	Name          string  `description:"别名"`
+	NameEn        string  `description:"英文别名"`
+	Source        int     `description:"1:ETA图库;2:商品价格"`
+	IsConvert     int     `description:"是否数据转换 0不转 1转"`
+	ConvertType   int     `description:"数据转换类型 1乘 2除 3对数"`
+	ConvertValue  float64 `description:"数据转换值"`
+	ConvertUnit   string  `description:"数据转换单位"`
+	ConvertEnUnit string  `description:"数据转换单位"`
+}
+
+// BarChartInfoDateReq 柱方图预览请求数据(日期相关)
+type BarChartInfoDateReq struct {
+	Type  int    `description:"配置类型"`
+	Date  string `description:"固定日期"`
+	Value int    `description:"N天的值"`
+	Color string `description:"颜色"`
+	Name  string `description:"别名"`
+}
+
+// BarChartInfoSortReq 柱方图预览请求数据(排序相关)
+type BarChartInfoSortReq struct {
+	Sort      int `description:"排序类型,0:默认,1:升序,2:降序"`
+	DateIndex int `description:"日期数据的下标,从0开始"`
+}
+
+// CorrelationChartInfoReq 相关性图表请求体
+type CorrelationChartInfoReq struct {
+	LeadValue          int                              `description:"领先期数"`
+	LeadUnit           string                           `description:"频度"`
+	CalculateValue     int                              `description:"计算窗口"`
+	CalculateUnit      string                           `description:"计算频度"`
+	BaseCalculateValue int                              `description:"基础计算窗口(滚动相关性的时候用到)"`
+	BaseCalculateUnit  string                           `description:"基础计算频度(滚动相关性的时候用到)"`
+	DateType           int                              `description:"日期类型:1:00年至今,2:10年至今,3:15年至今,4:年初至今,5:自定义时间,6:起始日期至今"`
+	StartDate          string                           `description:"开始日期"`
+	EndDate            string                           `description:"结束日期"`
+	EdbInfoIdList      []CorrelationChartInfoEdbItemReq `description:"指标信息"`
+}
+
+// CorrelationChartInfoEdbItemReq 相关性图表请求指标
+type CorrelationChartInfoEdbItemReq struct {
+	EdbInfoId int    `description:"指标ID"`
+	Name      string `description:"别名"`
+	NameEn    string `description:"英文别名"`
+	//Source    int    `description:"1:ETA图库;2:商品价格"`
+}
+
+/*
+// EditCorrelationChartInfoAndMapping 修改相关性图表的 图表与指标 的关系
+func EditCorrelationChartInfoAndMapping(req *EditChartInfoReq, edbInfoIdStr string, calendar string, dateType, disabled int, barChartConf string, correlationChart *ChartInfoCorrelation, correlationUpdateCols []string) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	var pars []interface{}
+	pars = append(pars, req.ChartName)
+	pars = append(pars, edbInfoIdStr)
+	pars = append(pars, req.ChartType)
+	pars = append(pars, req.ChartClassifyId)
+	pars = append(pars, disabled)
+	pars = append(pars, barChartConf)
+
+	sql := ` UPDATE  chart_info
+			SET
+			  chart_name =?,
+              edb_info_ids=?,
+			  chart_type=?,
+			  chart_classify_id = ?,
+			  modify_time = NOW(),
+              disabled = ?,
+              bar_config = ?
+			`
+	if calendar != "" {
+		sql += `,calendar = ? `
+		pars = append(pars, calendar)
+	}
+	if dateType > 0 {
+		sql += `,date_type = ? `
+		pars = append(pars, dateType)
+	}
+
+	sql += `,start_date = ? `
+	pars = append(pars, req.StartDate)
+
+	sql += `,end_date = ? `
+	pars = append(pars, req.EndDate)
+
+	sql += `,season_start_date = ? `
+	pars = append(pars, req.StartDate)
+
+	sql += `,season_end_date = ? `
+	pars = append(pars, req.EndDate)
+
+	sql += `,left_min = ? `
+	pars = append(pars, req.LeftMin)
+
+	sql += `,left_max = ? `
+	pars = append(pars, req.LeftMax)
+
+	sql += `,right_min = ? `
+	pars = append(pars, req.RightMin)
+
+	sql += `,right_max = ? `
+	pars = append(pars, req.RightMax)
+
+	sql += `WHERE chart_info_id = ?`
+
+	pars = append(pars, req.ChartInfoId)
+	_, err = to.Raw(sql, pars).Exec()
+	if err != nil {
+		fmt.Println("UPDATE  chart_info Err:", err.Error())
+		return err
+	}
+	chartEdbMappingIdList := make([]string, 0)
+	for _, v := range req.CorrelationChartInfo.EdbInfoIdList {
+		// 查询该指标是否存在,如果存在的话,那么就去修改,否则新增
+		var tmpChartEdbMapping *ChartEdbMapping
+		csql := `SELECT *  FROM chart_edb_mapping WHERE chart_info_id=? AND edb_info_id=? AND source = ? `
+		err = to.Raw(csql, req.ChartInfoId, v.EdbInfoId, utils.CHART_SOURCE_CORRELATION).QueryRow(&tmpChartEdbMapping)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			fmt.Println("QueryRow Err:", err.Error())
+			return err
+		}
+		if tmpChartEdbMapping != nil {
+			chartEdbMappingIdList = append(chartEdbMappingIdList, strconv.Itoa(tmpChartEdbMapping.ChartEdbMappingId))
+			//tmpChartEdbMapping.ModifyTime = time.Now()
+			//tmpChartEdbMapping.MaxData = v.MaxData
+			//tmpChartEdbMapping.MinData = v.MinData
+			//tmpChartEdbMapping.IsOrder = v.IsOrder
+			//tmpChartEdbMapping.IsAxis = v.IsAxis
+			//tmpChartEdbMapping.EdbInfoType = v.EdbInfoType
+			//tmpChartEdbMapping.LeadValue = v.LeadValue
+			//tmpChartEdbMapping.LeadUnit = v.LeadUnit
+			//tmpChartEdbMapping.ChartStyle = v.ChartStyle
+			//tmpChartEdbMapping.ChartColor = v.ChartColor
+			//tmpChartEdbMapping.PredictChartColor = v.PredictChartColor
+			//tmpChartEdbMapping.ChartWidth = v.ChartWidth
+			//_, err = to.Update(tmpChartEdbMapping, "ModifyTime", "MaxData", "MinData", "IsOrder", "IsAxis", "EdbInfoType", "LeadValue", "LeadUnit", "ChartStyle", "ChartColor", "PredictChartColor", "ChartWidth")
+			//if err != nil {
+			//	fmt.Println("chart_edb_mapping Err:" + err.Error())
+			//	return err
+			//}
+		} else {
+			mapItem := new(ChartEdbMapping)
+			mapItem.ChartInfoId = req.ChartInfoId
+			mapItem.EdbInfoId = v.EdbInfoId
+			mapItem.CreateTime = time.Now()
+			mapItem.ModifyTime = time.Now()
+			timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+			mapItem.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp + "_" + strconv.Itoa(v.EdbInfoId))
+			//mapItem.MaxData = v.MaxData
+			//mapItem.MinData = v.MinData
+			mapItem.IsOrder = true
+			mapItem.IsAxis = 1
+			mapItem.EdbInfoType = 1
+			//mapItem.LeadValue = v.LeadValue
+			//mapItem.LeadUnit = v.LeadUnit
+			//mapItem.ChartStyle = v.ChartStyle
+			//mapItem.ChartColor = v.ChartColor
+			//mapItem.PredictChartColor = v.PredictChartColor
+			//mapItem.ChartWidth = v.ChartWidth
+			mapItem.Source = utils.CHART_SOURCE_CORRELATION
+			tmpId, err := to.Insert(mapItem)
+			if err != nil {
+				fmt.Println("AddChartEdbMapping Err:" + err.Error())
+				return err
+			}
+			mapItem.ChartEdbMappingId = int(tmpId)
+			chartEdbMappingIdList = append(chartEdbMappingIdList, strconv.Itoa(mapItem.ChartEdbMappingId))
+		}
+	}
+	if len(chartEdbMappingIdList) > 0 {
+		chartEdbMappingIdStr := strings.Join(chartEdbMappingIdList, ",")
+		dsql := `DELETE FROM chart_edb_mapping WHERE chart_info_id=? AND chart_edb_mapping_id NOT IN(` + chartEdbMappingIdStr + `)`
+		_, err = to.Raw(dsql, req.ChartInfoId).Exec()
+		if err != nil {
+			fmt.Println("delete err:" + err.Error())
+			return err
+		}
+	}
+
+	// 相关性图表
+	_, err = to.Update(correlationChart, correlationUpdateCols...)
+	return
+}*/
+
+// PreviewSectionScatterChartReq 预览截面散点图的请求
+type PreviewSectionScatterChartReq struct {
+	ChartName       string `description:"图表名称"`
+	ChartClassifyId int    `description:"分类id"`
+	ExtraConfig     string `description:"图表额外配置信息,json字符串"`
+}
+
+// SectionScatterReq 截面散点请求
+type SectionScatterReq struct {
+	XName       string `description:"x轴名称"`
+	XNameEn     string `description:"x轴名称(英文)"`
+	XUnitName   string `description:"x轴单位名称"`
+	XUnitNameEn string `description:"x轴单位名称(英文)"`
+	YName       string `description:"y轴名称"`
+	YNameEn     string `description:"y轴名称(英文)"`
+	YUnitName   string `description:"y轴单位名称"`
+	YUnitNameEn string `description:"y轴单位名称(英文)"`
+	XMinValue   string `description:"X轴的最小值"`
+	XMaxValue   string `description:"X轴的最大值"`
+	YMinValue   string `description:"Y轴的最小值"`
+	YMaxValue   string `description:"Y轴的最大值"`
+	//EdbList     []SectionScatterEdbItemReq    `description:"指标数据"`
+	SeriesList []SectionScatterSeriesItemReq `description:"系列数据"`
+}
+
+// SectionScatterSeriesItemReq 系列的请求
+type SectionScatterSeriesItemReq struct {
+	Name            string `description:"系列名"`
+	NameEn          string `description:"系列名(英文名)"`
+	IsNameDefault   bool   `description:"是否使用默认的系列名 false修改过 true默认  影响自动更新"`
+	Color           string `description:"颜色"`
+	EdbInfoList     []SectionScatterEdbItemReq
+	ShowTrendLine   bool `description:"是否展示趋势线"`
+	ShowFitEquation bool `description:"是否展示方程式"`
+	ShowRSquare     bool `description:"是否展示R平方"`
+}
+
+// SectionScatterEdbItemReq 截面散点请求的指标
+type SectionScatterEdbItemReq struct {
+	XEdbInfoId int    `description:"X轴的指标ID"`
+	YEdbInfoId int    `description:"Y轴的指标ID"`
+	Name       string `description:"别名"`
+	NameEn     string `description:"英文别名"`
+	XDateType  int    `description:"X轴的日期配置类型"`
+	XDate      string `description:"X轴的日期固定日期"`
+	XDateValue int    `description:"X轴的日期N天的值"`
+	YDateType  int    `description:"Y轴的日期配置类型"`
+	YDate      string `description:"Y轴的日期固定日期"`
+	YDateValue int    `description:"Y轴的日期N天的值"`
+	IsShow     bool   `description:"是否展示"`
+}
+
+// SectionScatterInfoResp 截面散点图数据
+type SectionScatterInfoResp struct {
+	XName       string                         `description:"x轴名称"`
+	XNameEn     string                         `description:"x轴名称(英文)"`
+	XUnitName   string                         `description:"x轴单位名称"`
+	XUnitNameEn string                         `description:"x轴单位名称(英文)"`
+	YName       string                         `description:"y轴名称"`
+	YNameEn     string                         `description:"y轴名称(英文)"`
+	YUnitName   string                         `description:"y轴单位名称"`
+	YUnitNameEn string                         `description:"y轴单位名称(英文)"`
+	XMinValue   string                         `description:"X轴的最小值"`
+	XMaxValue   string                         `description:"X轴的最大值"`
+	YMinValue   string                         `description:"Y轴的最小值"`
+	YMaxValue   string                         `description:"Y轴的最大值"`
+	DataList    []SectionScatterSeriesItemResp `description:"数据列"`
+}
+
+// SectionScatterSeriesItemResp 系列的返回
+type SectionScatterSeriesItemResp struct {
+	Name            string `description:"系列名"`
+	NameEn          string `description:"系列名(英文)"`
+	IsNameDefault   bool   `description:"是否使用默认的系列名 false修改过 true默认  影响自动更新"`
+	Color           string `description:"颜色"`
+	EdbInfoList     []SectionScatterEdbItemResp
+	ShowTrendLine   bool              `description:"是否展示趋势线"`
+	ShowFitEquation bool              `description:"是否展示方程式"`
+	ShowRSquare     bool              `description:"是否展示R平方"`
+	TrendLine       string            `description:"方程式"`
+	RSquare         string            `description:"R平方的值(决定系数R2)"`
+	TrendLimitData  []CoordinatePoint `description:"趋势线的前后坐标点"`
+}
+
+// SectionScatterEdbItemResp 截面散点的返回参数
+type SectionScatterEdbItemResp struct {
+	XEdbInfoId int     `description:"X轴指标id"`
+	XDate      string  `description:"X轴指标实际日期"`
+	XName      string  `description:"X轴指标名称"`
+	XNameEn    string  `description:"X轴指标英文名称"`
+	XValue     float64 `description:"X轴实际值"`
+	YEdbInfoId int     `description:"Y轴指标id"`
+	YDate      string  `description:"Y轴指标实际日期"`
+	YName      string  `description:"Y轴指标名称"`
+	YNameEn    string  `description:"Y轴指标英文名称"`
+	YValue     float64 `description:"Y轴实际值"`
+	IsShow     bool    `description:"是否展示"`
+	Name       string  `description:"标签名称"`
+	NameEn     string  `description:"英文标签名称"`
+}
+
+// CoordinatePoint 坐标点
+type CoordinatePoint struct {
+	X float64
+	Y float64
+}
+
+// RollingCorrelationChartDataResp 滚动相关性图
+type RollingCorrelationChartDataResp struct {
+	XDateTimeValue []string `description:"滚动相关性图的x轴数据,日期数据"`
+	YDataList      []YData  `description:"滚动相关性图的y轴数据"`
+}
+
+// GetChartInfoAll 用于分类展示
+// @param source int 1:ETA图库;2:商品价格曲线
+func GetChartInfoAllByClassifyId(source, classifyId int) (items []*ChartClassifyItems, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT chart_info_id,chart_classify_id,chart_name AS chart_classify_name,chart_name_en AS chart_classify_name_en,
+             unique_code,sys_user_id,sys_user_real_name,date_type,start_date,end_date,chart_type,calendar,season_start_date,season_end_date,is_join_permission
+            FROM chart_info WHERE chart_classify_id = ? AND source = ?  ORDER BY sort asc,create_time DESC `
+	_, err = o.Raw(sql, classifyId, source).QueryRows(&items)
+	return
+}
+
+// ProfitFutureGoodChartResp 商品利润图
+type ProfitFutureGoodChartResp struct {
+	XDataList    []XData
+	YDataList    []YData
+	ProfitName   string `description:"利润的名称"`
+	ProfitNameEn string `description:"利润的英文名称"`
+}
+
+func FIXChartClassifyId(newId, oldId int) (err error) {
+	o := orm.NewOrm()
+	sql := ` UPDATE chart_info SET chart_classify_id=? WHERE chart_classify_id=? `
+	_, err = o.Raw(sql, newId, oldId).Exec()
+	return
+}
+
+// GetChartInfoByAdminIdAndClassify 获取所有我创建的图表,用于分类展示
+func GetChartInfoByAdminIdAndClassify(sourceList []int, adminId, classifyId int) (items []*ChartClassifyItems, err error) {
+	num := len(sourceList)
+	if num <= 0 {
+		return
+	}
+
+	o := orm.NewOrm()
+	sql := ` SELECT chart_info_id,chart_classify_id,chart_name AS chart_classify_name,
+             unique_code,sys_user_id,sys_user_real_name,date_type,start_date,end_date,chart_type,calendar,season_start_date,season_end_date,is_join_permission
+            FROM chart_info where source in (` + utils.GetOrmInReplace(num) + `)  AND sys_user_id = ? AND chart_classify_id = ? ORDER BY sort asc,create_time ASC `
+	_, err = o.Raw(sql, sourceList, adminId, classifyId).QueryRows(&items)
+	return
+}
+
+// GetChartInfoAdminList 根据条件获取图表数据了列表
+func GetChartInfoAdminList() (items []int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT sys_user_id FROM chart_info GROUP BY sys_user_id `
+
+	_, err = o.Raw(sql).QueryRows(&items)
+	return
+}
+
+// EditChartInfoExtraConfig 修改 ETA图库 的 图表额外配置
+func EditChartInfoExtraConfig(chartId int, extraConfig string) (err error) {
+	o := orm.NewOrm()
+	var pars []interface{}
+	pars = append(pars, extraConfig)
+
+	sql := ` UPDATE  chart_info
+			SET
+			  modify_time = NOW(),
+              extra_config = ?
+			`
+	sql += `WHERE chart_info_id = ?`
+
+	pars = append(pars, chartId)
+	_, err = o.Raw(sql, pars).Exec()
+	if err != nil {
+		fmt.Println("UPDATE  chart_info Err:", err.Error())
+		return err
+	}
+
+	return
+}
+
+// PreviewRadarChartReq 预览雷达图的请求入参
+type PreviewRadarChartReq struct {
+	ChartEdbInfoList []*ChartSaveItem `description:"指标及配置信息"`
+	ExtraConfig      string           `description:"图表额外配置信息,json字符串"`
+}
+
+// RadarChartInfoReq 雷达图预览请求数据
+type RadarChartInfoReq struct {
+	DateList []RadarChartInfoDateReq `description:"日期配置"`
+}
+
+// RadarChartInfoEdbItemReq 雷达图预览请求数据(指标相关)
+type RadarChartInfoEdbItemReq struct {
+	EdbInfoId int    `description:"指标ID"`
+	Name      string `description:"别名"`
+	//NameEn    string `description:"英文别名"`
+	//Source    int    `description:"1:ETA图库;2:商品价格"`
+}
+
+// RadarChartInfoDateReq 雷达图预览请求数据(日期相关)
+type RadarChartInfoDateReq struct {
+	Type  int    `description:"配置类型"`
+	Date  string `description:"固定日期"`
+	Value int    `description:"N天的值"`
+	Color string `description:"颜色"`
+	Name  string `description:"别名"`
+}
+
+// RadarChartInfoResp 雷达图数据
+type RadarChartInfoResp struct {
+	YDataList   []RadarYData `description:"数据列"`
+	XEdbIdValue []int
+}
+
+// RadarYData 雷达图的y轴数据
+type RadarYData struct {
+	Date  string    `description:"数据日期"`
+	Color string    `description:"数据颜色"`
+	Name  string    `description:"别名"`
+	Value []float64 `description:"每个指标的值"`
+}

+ 151 - 0
models/chart_theme.go

@@ -0,0 +1,151 @@
+package models
+
+import (
+	"errors"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// ChartTheme
+// @Description: 图表主题表
+type ChartTheme struct {
+	ChartThemeId     int       `description:"图表主题类型ID" orm:"column(chart_theme_id);pk"`
+	ChartThemeName   string    `description:"图表主题名称"`
+	ChartThemeTypeId int       `description:"图表主题类型ID"`
+	ChartImage       string    `description:"缩略图"`
+	Config           string    `description:"配置"`
+	IsDelete         int       `description:"是否删除,0:未删除;1:已删除"`
+	SysUserId        int       `description:"操作人"`
+	SysUserRealName  string    `description:"操作人的真实名称"`
+	IsSystemTheme    int       `description:"是否是系统主题,0:不是;1:是"`
+	ModifyTime       time.Time `description:"修改时间"`
+	CreateTime       time.Time `description:"创建时间"`
+}
+
+// GetChartThemeId
+// @Description: 根据id获取主题
+// @author: Roc
+// @datetime 2023-12-14 16:05:36
+// @param chartThemeId int
+// @return item *ChartTheme
+// @return err error
+func GetChartThemeId(chartThemeId int) (item *ChartTheme, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_theme where chart_theme_id = ? AND is_delete = 0`
+	err = o.Raw(sql, chartThemeId).QueryRow(&item)
+
+	return
+}
+
+// Add
+// @Description: 添加
+// @author: Roc
+// @receiver m
+// @datetime 2023-12-14 16:11:10
+// @param cols []string
+// @return err error
+func (m *ChartTheme) Add() (err error) {
+	if m.ChartThemeId > 0 {
+		err = errors.New("该配置已存在")
+		return
+	}
+	o := orm.NewOrm()
+	lastId, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.ChartThemeId = int(lastId)
+
+	return
+}
+
+// Update
+// @Description: 更新
+// @author: Roc
+// @receiver m
+// @datetime 2023-12-14 16:11:10
+// @param cols []string
+// @return err error
+func (m *ChartTheme) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+// GetChartThemeListByTypeId
+// @Description: 根据图表类型获取关联的图表主题列表
+// @author: Roc
+// @datetime 2023-12-13 17:39:48
+// @return list []*ChartTheme
+// @return err error
+func GetChartThemeListByTypeId(chartThemeTypeId int) (list []*ChartTheme, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_theme WHERE chart_theme_type_id = ? AND  is_delete=0 ORDER BY chart_theme_id ASC `
+	_, err = o.Raw(sql, chartThemeTypeId).QueryRows(&list)
+
+	return
+}
+
+// GetAllChartThemeList
+// @Description: 获取所有图表主题列表
+// @author: Roc
+// @datetime 2023-12-13 17:39:48
+// @return list []*ChartTheme
+// @return err error
+func GetAllChartThemeList() (list []*ChartTheme, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_theme WHERE  is_delete=0 ORDER BY chart_theme_id ASC `
+	_, err = o.Raw(sql).QueryRows(&list)
+
+	return
+}
+
+// ChartThemeItem
+// @Description: 图表主题配置
+type ChartThemeItem struct {
+	ChartThemeId     int    `description:"图表主题类型ID" orm:"column(chart_theme_id);pk"`
+	ChartThemeName   string `description:"图表主题名称"`
+	ChartThemeTypeId int    `description:"图表主题类型ID"`
+	Config           string `description:"配置"`
+	ChartImage       string `description:"缩略图"`
+	//IsDelete            int       `description:"是否删除,0:未删除;1:已删除"`
+	IsSystemTheme       int       `description:"是否是系统主题,0:不是;1:是"`
+	SysUserId           int       `description:"操作人"`
+	SysUserRealName     string    `description:"操作人的真实名称"`
+	ModifyTime          time.Time `description:"修改时间"`
+	CreateTime          time.Time `description:"创建时间"`
+	DefaultChartThemeId int       `description:"默认使用的图表主题ID"`
+}
+
+// GetChartThemeItemList
+// @Description: 根据图表类型id获取配置列表
+// @author: Roc
+// @datetime 2023-12-14 14:26:35
+// @param chartThemeTypeId int
+// @return list []*ChartThemeConfig
+// @return err error
+func GetChartThemeItemList(chartThemeTypeId int) (list []*ChartThemeItem, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT a.*,b.default_chart_theme_id FROM chart_theme a 
+         JOIN chart_theme_type b on a.chart_theme_type_id =b.chart_theme_type_id 
+         WHERE a.chart_theme_type_id = ? AND a.is_delete=0 ORDER BY a.chart_theme_id ASC `
+	_, err = o.Raw(sql, chartThemeTypeId).QueryRows(&list)
+
+	return
+}
+
+// GetSystemChartTheme
+// @Description: 根据图表类型id获取系统配置
+// @author: Roc
+// @datetime 2023-12-14 14:26:35
+// @param chartThemeTypeId int
+// @return item *ChartTheme
+// @return err error
+func GetSystemChartTheme(chartThemeTypeId int) (item *ChartTheme, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT a.* FROM chart_theme a
+         WHERE a.chart_theme_type_id = ? AND a.is_system_theme=1 ORDER BY a.chart_theme_id ASC `
+	err = o.Raw(sql, chartThemeTypeId).QueryRow(&item)
+
+	return
+}

+ 30 - 0
models/chart_theme/request/theme.go

@@ -0,0 +1,30 @@
+package request
+
+// AddThemeReq
+// @Description: 新增主题请求参数
+type AddThemeReq struct {
+	ChartThemeName   string `description:"主题名称"`
+	ChartThemeTypeId int    `description:"图表类型id"`
+}
+
+// EditThemeReq
+// @Description: 编辑主题请求参数
+type EditThemeReq struct {
+	ChartThemeId   int    `description:"主题id"`
+	ChartThemeName string `description:"主题名称"`
+	Config         string `description:"配置的值"`
+	ChartImage     string `description:"缩略图"`
+}
+
+// DeleteThemeReq
+// @Description: 删除主题请求参数
+type DeleteThemeReq struct {
+	ChartThemeId int `description:"配置id"`
+}
+
+// SetDefaultThemeReq
+// @Description: 配置默认主题请求参数
+type SetDefaultThemeReq struct {
+	ChartThemeId     int `description:"主题id"`
+	ChartThemeTypeId int `description:"主题类型id"`
+}

+ 91 - 0
models/chart_theme_default_data.go

@@ -0,0 +1,91 @@
+package models
+
+import (
+	"eta/eta_forum_admin/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// ChartThemeDefaultData
+// @Description: 图表默认数据
+type ChartThemeDefaultData struct {
+	EdbDataId     int       `description:"指标数据ID" orm:"column(edb_data_id);pk"`
+	EdbInfoId     int       `description:"指标ID"`
+	EdbCode       string    `description:"指标编码"`
+	DataTime      string    //`json:"-" description:"数据日期"`
+	DataTimestamp int64     `description:"数据日期"`
+	Value         float64   `description:"数据值"`
+	ModifyTime    time.Time `description:"修改时间"`
+	CreateTime    time.Time `description:"创建时间"`
+}
+
+// GetChartThemeDefaultDataList
+// @Description: 获取指标的数据(日期正序返回)
+// @author: Roc
+// @datetime 2023-12-13 16:40:14
+// @param endInfoId int
+// @return list []*ChartThemeDefaultData
+// @return err error
+func GetChartThemeDefaultDataList(endInfoId int) (list []*ChartThemeDefaultData, err error) {
+	sql := `SELECT * FROM chart_theme_default_data WHERE edb_info_id=? `
+
+	sql += ` ORDER BY data_time ASC `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, endInfoId).QueryRows(&list)
+	return
+}
+
+// Update
+// @Description: 更新
+// @author: Roc
+// @receiver m
+// @datetime 2023-12-13 16:40:04
+// @param cols []string
+// @return err error
+func (m *ChartThemeDefaultData) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+
+	return
+}
+
+// GetChartThemeDefaultDataItemList
+// @Description: 获取指标的数据(日期正序返回)
+// @author: Roc
+// @datetime 2023-12-13 16:40:22
+// @param endInfoId int
+// @return list []*ChartThemeDefaultDataItem
+// @return err error
+func GetChartThemeDefaultDataItemList(endInfoId int, startDate string) (list []*EdbDataList, err error) {
+	sql := `SELECT edb_data_id,edb_info_id,data_time,value,data_timestamp FROM chart_theme_default_data WHERE edb_info_id=? `
+	var pars []interface{}
+	if startDate != "" {
+		sql += ` AND data_time>=? `
+		pars = append(pars, startDate)
+	}
+
+	sql += ` ORDER BY data_time ASC `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, endInfoId, pars).QueryRows(&list)
+	return
+}
+
+// GetChartEdbMappingListByEdbInfoId
+// @Description: 返回模拟的mapping假数据
+// @author: Roc
+// @datetime 2023-12-14 10:03:20
+// @param edbInfoIdList []int
+// @return list []*models.ChartEdbInfoMapping
+// @return err error
+func GetChartEdbMappingListByEdbInfoId(edbInfoIdList []int) (list []*ChartEdbInfoMapping, err error) {
+	num := len(edbInfoIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM chart_theme_default_edb_info WHERE edb_info_id IN(` + utils.GetOrmInReplace(num) + `)
+              `
+	_, err = o.Raw(sql, edbInfoIdList).QueryRows(&list)
+
+	return
+}

+ 90 - 0
models/chart_theme_type.go

@@ -0,0 +1,90 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// ChartThemeType
+// @Description: 图表主题类型表
+type ChartThemeType struct {
+	ChartThemeTypeId    int       `description:"图表主题类型ID" orm:"column(chart_theme_type_id);pk"`
+	ChartTypeName       string    `description:"类型名称"`
+	ChartType           int       `description:"图表类型"`
+	ChartSource         int       `description:"图表来源"`
+	DefaultChartThemeId int       `description:"默认使用的主题id"`
+	ModifyTime          time.Time `description:"修改时间"`
+	CreateTime          time.Time `description:"创建时间"`
+}
+
+// Update
+// @Description: 更新
+// @author: Roc
+// @receiver m
+// @datetime 2023-12-14 16:11:10
+// @param cols []string
+// @return err error
+func (m *ChartThemeType) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+// GetAllChartThemeTypeList
+// @Description: 获取所有图表主题类型列表
+// @author: Roc
+// @datetime 2023-12-13 17:31:03
+// @return list []*ChartThemeType
+// @return err error
+func GetAllChartThemeTypeList() (list []*ChartThemeType, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_theme_type ORDER BY chart_theme_type_id ASC `
+	_, err = o.Raw(sql).QueryRows(&list)
+
+	return
+}
+
+// GetChartThemeTypeListBySource
+// @Description: 根据来源获取图表主题类型列表
+// @author: Roc
+// @datetime 2023-12-13 17:31:03
+// @param source int
+// @return list []*ChartThemeType
+// @return err error
+func GetChartThemeTypeListBySource(source int) (list []*ChartThemeType, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_theme_type WHERE chart_source = ?  ORDER BY chart_theme_type_id ASC `
+	_, err = o.Raw(sql, source).QueryRows(&list)
+
+	return
+}
+
+// GetChartThemeTypeById
+// @Description: 通过类型id获取类型
+// @author: Roc
+// @datetime 2023-12-14 09:53:58
+// @param chartThemeTypeId int
+// @return item *ChartThemeType
+// @return err error
+func GetChartThemeTypeById(chartThemeTypeId int) (item *ChartThemeType, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_theme_type where chart_theme_type_id = ? `
+	err = o.Raw(sql, chartThemeTypeId).QueryRow(&item)
+
+	return
+}
+
+// GetChartThemeTypeByChartTypeAndSource
+// @Description: 通过图表类型和来源获取类型
+// @author: Roc
+// @datetime 2023-12-14 09:53:58
+// @param chartThemeTypeId int
+// @return item *ChartThemeType
+// @return err error
+func GetChartThemeTypeByChartTypeAndSource(chartType, source int) (item *ChartThemeType, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM chart_theme_type where chart_type = ? AND chart_source = ? `
+	err = o.Raw(sql, chartType, source).QueryRow(&item)
+
+	return
+}

+ 52 - 0
models/db.go

@@ -0,0 +1,52 @@
+package models
+
+import (
+	"eta/eta_forum_admin/utils"
+	"github.com/beego/beego/v2/client/orm"
+	_ "github.com/go-sql-driver/mysql"
+	"time"
+)
+
+func init() {
+	_ = orm.RegisterDataBase("default", "mysql", utils.MYSQL_URL)
+	orm.SetMaxIdleConns("default", 50)
+	orm.SetMaxOpenConns("default", 100)
+
+	db, _ := orm.GetDB("default")
+	db.SetConnMaxLifetime(10 * time.Minute)
+
+	orm.Debug = true
+	orm.DebugLog = orm.NewLog(utils.Binlog)
+
+	initChart()
+	initEdbData()
+	initUser()
+}
+
+// initChart 图表 数据表
+func initChart() {
+	orm.RegisterModel(
+		new(ChartClassify),
+		new(ChartInfo),
+		new(ChartEdbMapping),
+		new(ChartTheme),
+		new(ChartThemeType),
+	)
+}
+
+// initEdbData 指标服务 数据表
+func initEdbData() {
+	orm.RegisterModel(
+		new(EdbInfoCalculateMapping),
+		new(EdbInfo),
+	)
+}
+
+// 初始化用户服务
+func initUser() {
+	orm.RegisterModel(
+		new(SysSession),
+		new(SysUserLoginRecord),
+		new(Admin),
+	)
+}

+ 309 - 0
models/edb_info.go

@@ -0,0 +1,309 @@
+package models
+
+import (
+	"eta/eta_forum_admin/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type EdbInfo struct {
+	EdbInfoId        int    `orm:"column(edb_info_id);pk"`
+	EdbInfoType      int    `description:"指标类型,0:普通指标,1:预测指标"`
+	SourceName       string `description:"来源名称"`
+	Source           int    `description:"来源id"`
+	EdbCode          string `description:"指标编码"`
+	EdbName          string `description:"指标名称"`
+	EdbNameEn        string `description:"英文指标名称"`
+	EdbNameSource    string `description:"指标名称来源"`
+	Frequency        string `description:"频率"`
+	Unit             string `description:"单位"`
+	UnitEn           string `description:"英文单位"`
+	StartDate        string `description:"起始日期"`
+	EndDate          string `description:"终止日期"`
+	ClassifyId       int    `description:"分类id"`
+	SysUserId        int
+	SysUserRealName  string
+	UniqueCode       string `description:"指标唯一编码"`
+	CreateTime       time.Time
+	ModifyTime       time.Time
+	MinValue         float64 `description:"指标最小值"`
+	MaxValue         float64 `description:"指标最大值"`
+	CalculateFormula string  `description:"计算公式"`
+	EdbType          int     `description:"指标类型:1:基础指标,2:计算指标"`
+	Sort             int     `description:"排序字段"`
+	LatestDate       string  `description:"数据最新日期(实际日期)"`
+	LatestValue      float64 `description:"数据最新值(实际值)"`
+	EndValue         float64 `description:"数据的最新值(预测日期的最新值)"`
+	MoveType         int     `description:"移动方式:1:领先(默认),2:滞后"`
+	MoveFrequency    string  `description:"移动频度"`
+	NoUpdate         int8    `description:"是否停止更新,0:继续更新;1:停止更新"`
+	ServerUrl        string  `description:"服务器地址"`
+	ChartImage       string  `description:"图表图片"`
+	Calendar         string  `description:"公历/农历" orm:"default(公历);"`
+	DataDateType     string  `orm:"column(data_date_type);size(255);null;default(交易日)"`
+	ManualSave       int     `description:"是否有手动保存过上下限: 0-否; 1-是"`
+	EmptyType        int     `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
+	MaxEmptyType     int     `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
+	TerminalCode     string  `description:"终端编码,用于配置在机器上"`
+	DataUpdateTime   string  `description:"最近一次数据发生变化的时间"`
+	ErDataUpdateDate string  `description:"本次更新,数据发生变化的最早日期"`
+	SourceIndexName  string  `description:"数据源中的指标名称"`
+	SubSource        int     `description:"子数据来源:0:经济数据库,1:日期序列"`
+	SubSourceName    string  `description:"子数据来源名称"`
+	IndicatorCode    string  `description:"指标代码"`
+	StockCode        string  `description:"证券代码"`
+	Extra            string  `description:"指标额外配置"`
+	IsJoinPermission int     `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+}
+
+type EdbInfoFullClassify struct {
+	*EdbInfo
+	CorrelationStr string `description:"相关性系数字符串"`
+	HaveOperaAuth  bool   `description:"是否有数据权限,默认:false"`
+}
+
+func AddEdbInfo(item *EdbInfo) (lastId int64, err error) {
+	o := orm.NewOrm()
+	lastId, err = o.Insert(item)
+	return
+}
+
+// 更新指标信息
+func (e *EdbInfo) Update(col []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(e, col...)
+	return
+}
+
+type BaseEdbNameItem struct {
+	EdbInfoId     int    `description:"指标id"`
+	EdbInfoType   int    `description:"指标类型,0:普通指标,1:预测指标"`
+	SourceName    string `description:"来源名称"`
+	Source        int    `description:"来源id"`
+	EdbCode       string `description:"指标编码"`
+	EdbName       string `description:"指标名称"`
+	Frequency     string `description:"频率"`
+	Unit          string `description:"单位"`
+	HaveOperaAuth bool   `description:"是否有数据权限,默认:false"`
+}
+
+type BaseEdbInfoResp struct {
+	List []*BaseEdbNameItem
+}
+
+// 指标检索数据
+type EdbInfoSearch struct {
+	EdbCode   string `description:"指标编码"`
+	StartDate string `description:"起始日期"`
+	EndDate   string `description:"终止日期"`
+	EdbName   string `description:"指标名称"`
+	Unit      string `description:"单位"`
+	Frequency string `description:"频率"`
+	DataList  []*EdbInfoSearchData
+	StockList []*StockInfo `description:"时序数据"`
+}
+
+type StockInfo struct {
+	StockCode string
+	EdbCode   string
+	DataList  []*EdbInfoSearchData
+}
+
+type EdbInfoSearchData struct {
+	DataTime string  `description:"数据日期"`
+	Value    float64 `description:"数据"`
+}
+
+type BaseIndexInfo struct {
+	IndexName string `description:"数据日期"`
+	Unit      string `description:"单位"`
+	Frequency string `description:"频率"`
+}
+
+type EdbInfoSearchResp struct {
+	SearchItem      *EdbInfoSearch `description:"指标分类"`
+	Status          int            `description:"1:数据已存在于弘则数据库,2:新数据,3:数据已存在于弘则数据库,但是当前账号无权限"`
+	StockSearchList []*EdbInfoSearch
+}
+
+func GetEdbInfoByEdbCode(source int, edbCode string) (item *EdbInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM edb_info WHERE source=? AND edb_code=? `
+	err = o.Raw(sql, source, edbCode).QueryRow(&item)
+	return
+}
+
+func GetEdbInfoById(edbInfoId int) (item *EdbInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM edb_info WHERE edb_info_id=? `
+	err = o.Raw(sql, edbInfoId).QueryRow(&item)
+	return
+}
+
+// GetEdbInfoByUniqueCode
+// @Description: 根据uniqueCode获取指标详情
+// @author: Roc
+// @datetime 2024-01-18 13:40:01
+// @param uniqueCode string
+// @return item *EdbInfo
+// @return err error
+func GetEdbInfoByUniqueCode(uniqueCode string) (item *EdbInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM edb_info WHERE unique_code=? `
+	err = o.Raw(sql, uniqueCode).QueryRow(&item)
+	return
+}
+
+// GetEdbInfoByIdList 根据指标id集合 获取 指标列表
+func GetEdbInfoByIdList(edbInfoIdList []int) (items []*EdbInfo, err error) {
+	num := len(edbInfoIdList)
+	if num <= 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := ` SELECT * FROM edb_info WHERE edb_info_id in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, edbInfoIdList).QueryRows(&items)
+	return
+}
+
+func DeleteEdbInfo(edbInfoId int) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+
+	// 删除指标信息
+	sql := ` DELETE FROM edb_info WHERE edb_info_id=? `
+	_, err = to.Raw(sql, edbInfoId).Exec()
+	if err != nil {
+		return
+	}
+
+	// 删除计算指标的关系
+	sql = ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id=? `
+	_, err = to.Raw(sql, edbInfoId).Exec()
+
+	return
+}
+
+func GetEdbInfoCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(1) AS count FROM edb_info WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+type EditEdbInfoReq struct {
+	EdbInfoId        int    `description:"指标ID"`
+	EdbName          string `description:"指标名称"`
+	Frequency        string `description:"频率"`
+	Unit             string `description:"单位"`
+	ClassifyId       int    `description:"分类id"`
+	CalculateFormula string `description:"计算公式"`
+}
+
+type EditEdbEnInfoReq struct {
+	EdbInfoId int    `description:"指标ID"`
+	EdbNameEn string `description:"英文指标名称"`
+	UnitEn    string `description:"英文单位"`
+}
+
+func ModifyEdbInfo(item *EditEdbInfoReq) (err error) {
+	o := orm.NewOrm()
+	sql := ` UPDATE  edb_info
+			SET
+			  edb_name =?,
+			  edb_name_source =?,
+			  frequency = ?,
+			  unit = ?,
+			  classify_id = ?,
+			  modify_time = NOW()
+			WHERE edb_info_id = ?`
+	_, err = o.Raw(sql, item.EdbName, item.EdbName, item.Frequency, item.Unit, item.ClassifyId, item.EdbInfoId).Exec()
+	return
+}
+
+// ModifyEdbEnInfo 修改指标英文信息
+func ModifyEdbEnInfo(item *EditEdbEnInfoReq) (err error) {
+	o := orm.NewOrm()
+	sql := ` UPDATE  edb_info
+			SET
+			  edb_name_en =?,
+			  unit_en = ?,
+			  modify_time = NOW()
+			WHERE edb_info_id = ?`
+	_, err = o.Raw(sql, item.EdbNameEn, item.UnitEn, item.EdbInfoId).Exec()
+	return
+}
+
+type EdbInfoList struct {
+	EdbInfoId        int                     `orm:"column(edb_info_id);pk"`
+	EdbInfoType      int                     `description:"指标类型,0:普通指标,1:预测指标"`
+	SourceName       string                  `description:"来源名称"`
+	Source           int                     `description:"来源id"`
+	EdbCode          string                  `description:"指标编码"`
+	EdbNameEn        string                  `description:"英文指标名称"`
+	EdbName          string                  `description:"指标名称"`
+	Frequency        string                  `description:"频率"`
+	FrequencyEn      string                  `description:"英文频率"`
+	Unit             string                  `description:"单位"`
+	UnitEn           string                  `description:"英文单位"`
+	StartDate        string                  `description:"起始日期"`
+	EndDate          string                  `description:"终止日期"`
+	LatestDate       string                  `description:"数据最新日期(实际日期)"`
+	LatestValue      float64                 `description:"数据最新值(实际值)"`
+	EndValue         float64                 `description:"数据的最新值(预测日期的最新值)"`
+	ClassifyId       int                     `description:"分类id"`
+	UniqueCode       string                  `description:"指标唯一编码"`
+	SysUserId        int                     `description:"创建人id"`
+	SysUserRealName  string                  `description:"创建人姓名"`
+	ModifyTime       string                  `description:"最新修改时间"`
+	CreateTime       string                  `description:"创建时间"`
+	EdbNameAlias     string                  `json:"-" description:"指标名称,别名"`
+	EdbType          int                     `description:"指标类型:1:基础指标,2:计算指标"`
+	ChartImage       string                  `description:"图表图片"`
+	RuleType         int                     `description:"预测规则,1:最新,2:固定值"`
+	FixedValue       float64                 `description:"固定值"`
+	DataList         []*EdbData              `description:"实际指标数据"`
+	PredictDataList  []*EdbData              `description:"预测指标数据"`
+	IsEnEdb          bool                    `description:"是否展示英文标识"`
+	DataInsertConfig EdbDataInsertConfigItem `description:"指标数据插入配置"`
+	DataDateType     string                  `description:"数据日期类型,枚举值:交易日、自然日"`
+	EmptyType        int                     `description:"空值处理类型(0查找前后35天,1不计算,2前值填充,3后值填充,4等于0)"`
+	MaxEmptyType     int                     `description:"MAX、MIN公式空值处理类型(1、等于0;2、跳过空值)"`
+	SubSource        int                     `description:"子数据来源:0:经济数据库,1:日期序列"`
+	SubSourceName    string                  `description:"子数据来源名称"`
+	IndicatorCode    string                  `description:"指标代码"`
+	StockCode        string                  `description:"证券代码"`
+	NoUpdate         int8                    `description:"是否停止更新,0:继续更新;1:停止更新"`
+	IsJoinPermission int                     `description:"是否加入权限管控,0:不加入;1:加入;默认:0"`
+	HaveOperaAuth    bool                    `description:"是否有数据权限,默认:false"`
+}
+
+type EdbDataInsertConfigItem struct {
+	Date     string `description:"插入的日期"`
+	RealDate string `description:"实际最晚的日期"`
+	Value    string `description:"插入的值"`
+}
+
+type EdbData struct {
+	EdbDataId int `orm:"column(edb_data_id);pk"`
+	EdbInfoId int
+	DataTime  string
+	Value     float64
+}
+
+type EdbInfoListResp struct {
+	Item *EdbInfoList
+}

+ 112 - 0
models/edb_info_calculate_mapping.go

@@ -0,0 +1,112 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// EdbInfoCalculateMapping 计算指标于基础指标,关系表
+type EdbInfoCalculateMapping struct {
+	EdbInfoCalculateMappingId int       `orm:"column(edb_info_calculate_mapping_id);pk"`
+	EdbInfoId                 int       `description:"计算指标id"`
+	Source                    int       `description:"计算指标来源"`
+	SourceName                string    `description:"计算指标来源名称"`
+	EdbCode                   string    `description:"计算指标编码"`
+	FromEdbInfoId             int       `description:"基础指标id"`
+	FromEdbCode               string    `description:"基础指标编码"`
+	FromEdbName               string    `description:"基础指标名称"`
+	FromSource                int       `description:"基础指标来源"`
+	FromSourceName            string    `description:"基础指标来源名称"`
+	FromTag                   string    `description:"来源指标标签"`
+	Sort                      int       `description:"计算指标名称排序"`
+	CreateTime                time.Time `description:"创建时间"`
+	ModifyTime                time.Time `description:"修改时间"`
+	FromSubSource             int       `description:"渠道子数据库来源"`
+}
+
+func AddEdbInfoCalculateMappingMulti(items []*EdbInfoCalculateMapping) (err error) {
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(1, items)
+	return
+}
+
+func DeleteEdbInfoMapping(edbInfoId int64) (err error) {
+	o := orm.NewOrm()
+	// 删除计算指标的关系
+	sql := ` DELETE FROM edb_info_calculate_mapping WHERE edb_info_id=? `
+	_, err = o.Raw(sql, edbInfoId).Exec()
+	return
+}
+
+func GetEdbInfoCalculateMappingCount(edbInfoId int) (count int, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT COUNT(1) AS count FROM edb_info_calculate_mapping WHERE from_edb_info_id=? `
+	err = o.Raw(sql, edbInfoId).QueryRow(&count)
+	return
+}
+
+type EdbInfoCalculateMappingView struct {
+	EdbInfoCalculateMappingId int       `orm:"column(edb_info_calculate_mapping_id);pk"`
+	EdbInfoId                 int       `description:"计算指标id"`
+	Source                    int       `description:"计算指标来源"`
+	SourceName                string    `description:"计算指标来源名称"`
+	EdbCode                   string    `description:"计算指标编码"`
+	FromEdbInfoId             int       `description:"基础指标id"`
+	FromEdbCode               string    `description:"基础指标编码"`
+	FromEdbName               string    `description:"基础指标名称"`
+	FromSource                int       `description:"基础指标来源"`
+	FromSourceName            string    `description:"基础指标来源名称"`
+	FromTag                   string    `description:"来源指标标签"`
+	Sort                      int       `description:"计算指标名称排序"`
+	CreateTime                time.Time `description:"创建时间"`
+	ModifyTime                time.Time `description:"修改时间"`
+	StartDate                 string    `description:"开始日期"`
+	EndDate                   string    `description:"结束日期"`
+	CalculateFormula          string    `description:"N值"`
+	MoveType                  int       `description:"移动方式:1:领先(默认),2:滞后"`
+	MoveFrequency             string    `description:"移动频度"`
+}
+
+func GetEdbInfoCalculateMappingDetail(edbInfoId int) (item *EdbInfoCalculateMappingView, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT a.*,b.start_date,b.end_date,b.calculate_formula,b.move_type,b.move_frequency FROM edb_info_calculate_mapping AS a
+			INNER JOIN edb_info AS b ON a.edb_info_id=b.edb_info_id
+			WHERE a.edb_info_id=? `
+	err = o.Raw(sql, edbInfoId).QueryRow(&item)
+	return
+}
+
+// EdbInfoCalculateMappingInfo
+// @Description: 计算指标与基础指标关系表
+type EdbInfoCalculateMappingInfo struct {
+	EdbInfoCalculateMappingId int       `orm:"column(edb_info_calculate_mapping_id);pk"`
+	EdbInfoId                 int       `description:"计算指标id"`
+	Source                    int       `description:"计算指标来源"`
+	SourceName                string    `description:"计算指标来源名称"`
+	EdbCode                   string    `description:"计算指标编码"`
+	FromEdbInfoId             int       `description:"基础指标id"`
+	FromEdbCode               string    `description:"基础指标编码"`
+	FromEdbName               string    `description:"基础指标名称"`
+	FromSource                int       `description:"基础指标来源"`
+	FromSourceName            string    `description:"基础指标来源名称"`
+	MoveValue                 int       `description:"领先值"`
+	FromTag                   string    `description:"来源指标标签"`
+	Sort                      int       `description:"计算指标名称排序"`
+	CreateTime                time.Time `description:"创建时间"`
+	ModifyTime                time.Time `description:"修改时间"`
+	FromEdbType               int       `description:"来源指标类型:1:基础指标,2:计算指标"`
+	FromEdbInfoType           int       `description:"来源指标类型: 0-基础指标; 1-预测指标"`
+	FromClassifyId            int       `description:"来源指标分类ID"`
+	FromUniqueCode            string    `description:"来源指标唯一编码"`
+	NoUpdate                  int8      `description:"是否停止更新,0:继续更新;1:停止更新"`
+}
+
+// GetEdbInfoCalculateMappingListByEdbInfoId 根据生成的指标id获取来源的指标id列表
+func GetEdbInfoCalculateMappingListByEdbInfoId(edbInfoId int) (items []*EdbInfoCalculateMappingInfo, err error) {
+	o := orm.NewOrm()
+	sql := ` SELECT a.*,b.edb_type as from_edb_type,b.edb_info_type as from_edb_info_type, b.unique_code AS from_unique_code, b.classify_id AS from_classify_id,b.no_update FROM edb_info_calculate_mapping AS a
+			INNER JOIN edb_info AS b ON a.from_edb_info_id=b.edb_info_id
+			WHERE a.edb_info_id=? `
+	_, err = o.Raw(sql, edbInfoId).QueryRows(&items)
+	return
+}

+ 39 - 0
models/mgodb/db.go

@@ -0,0 +1,39 @@
+package mgodb
+
+import (
+	"context"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+	"go.mongodb.org/mongo-driver/mongo/readpref"
+	"time"
+)
+
+var MgoClient *mongo.Client
+
+func init() {
+	var err error
+	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+	defer cancel()
+	credential := options.Credential{
+		AuthMechanism: utils.MONGODB_CREDENTIAL,
+		Username:      "hzeta",
+		Password:      "hzeta2023",
+	}
+	clientOpts := options.Client().ApplyURI(utils.MONGODB_URL).SetAuth(credential)
+	clientOpts.SetMinPoolSize(0)
+	clientOpts.SetMaxPoolSize(100)
+	clientOpts.SetConnectTimeout(10 * time.Second)
+
+	MgoClient, err = mongo.Connect(context.TODO(), clientOpts)
+	if err != nil {
+		panic(err)
+	}
+
+	if err = MgoClient.Ping(ctx, readpref.Primary()); err != nil {
+		panic(err)
+	}
+
+	fmt.Println("Connected to MongoDB!")
+}

+ 119 - 0
models/mgodb/edb_data_base.go

@@ -0,0 +1,119 @@
+package mgodb
+
+import (
+	"context"
+	"eta/eta_forum_admin/utils"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/mongo/options"
+	"time"
+)
+
+type EdbDataBase struct {
+	EdbInfoId     int    `bson:"edb_info_id"`
+	EdbCode       string `bson:"edb_code"`
+	DataTime      string `bson:"data_time"`
+	Value         string `bson:"value"`
+	Status        int    `bson:"status"`
+	CreateTime    string `bson:"create_time"`
+	ModifyTime    string `bson:"modify_time"`
+	DataTimestamp int64  `bson:"data_timestamp"`
+}
+
+type EdbInfoSearchData struct {
+	DataTime string  `description:"数据日期" bson:"data_time"`
+	Value    float64 `description:"数据" bson:"value"`
+	EdbCode  string  `description:"指标编码" bson:"edb_code"`
+}
+
+func GetEdbDataBaseByEdbInfoId(edbInfoId int) (items []*EdbDataBase, err error) {
+	findOptions := options.Find()
+	db := NewMgo(utils.MONGODB_COMMUNITY, "edb_data_base", MgoClient)
+	filter := bson.D{{"edb_info_id", edbInfoId}}
+	ctx := context.TODO()
+	cur, err := db.Find(filter, findOptions)
+	if err != nil {
+		return
+	}
+	// Close the cursor once finished
+	defer cur.Close(ctx)
+	for cur.Next(ctx) {
+		// create a value into which the single document can be decoded
+		var elem EdbDataBase
+		err = cur.Decode(&elem)
+		if err != nil {
+			return
+		}
+
+		items = append(items, &elem)
+	}
+
+	if err = cur.Err(); err != nil {
+		return
+	}
+	return
+}
+
+func GetEdbDataBaseByEdbCode(edbCode string) (items []*EdbDataBase, err error) {
+	findOptions := options.Find()
+	db := NewMgo(utils.MONGODB_COMMUNITY, "edb_data_base", MgoClient)
+	filter := bson.D{{"edb_code", edbCode}}
+	ctx := context.TODO()
+	cur, err := db.Find(filter, findOptions)
+	if err != nil {
+		return
+	}
+	// Close the cursor once finished
+	defer cur.Close(ctx)
+	for cur.Next(ctx) {
+		// create a value into which the single document can be decoded
+		var elem EdbDataBase
+		err = cur.Decode(&elem)
+		if err != nil {
+			return
+		}
+
+		items = append(items, &elem)
+	}
+
+	if err = cur.Err(); err != nil {
+		return
+	}
+	return
+}
+
+func InsertBatch(items []interface{}) (err error) {
+	db := NewMgo(utils.MONGODB_COMMUNITY, "edb_data_base", MgoClient)
+	_, err = db.InsertMany(items)
+	if err != nil {
+		return
+	}
+	return
+}
+
+func ModifyEdbInfoDataStatus(edbInfoId int64, edbCode string) (err error) {
+	filter := bson.D{{"edb_code", edbCode}}
+	update := bson.D{{"$set", bson.D{{"edb_info_id", edbInfoId}, {"modify_time", time.Now().Format("2006-01-02 15:04:05")}}}}
+	db := NewMgo(utils.MONGODB_COMMUNITY, "edb_data_base", MgoClient)
+	_, err = db.UpdateMany(filter, update)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// 删除
+func DeleteEdbInfoDataByEdbInfoId(edbInfoId int) (err error) {
+	filter := bson.D{{"edb_info_id", edbInfoId}}
+	db := NewMgo(utils.MONGODB_COMMUNITY, "edb_data_base", MgoClient)
+	_, err = db.DeleteMany(filter)
+	return
+}
+
+// 更新指标的值
+func ModifyValueEdbInfoDataStatus(edbCode string, dataTime string, value string) (err error) {
+	filter := bson.D{{"edb_code", edbCode}, {"data_time", dataTime}}
+	update := bson.D{{"$set", bson.D{{"value", value}, {"modify_time", time.Now().Format("2006-01-02 15:04:05")}}}}
+	db := NewMgo(utils.MONGODB_COMMUNITY, "edb_data_base", MgoClient)
+	_, err = db.UpdateOne(filter, update)
+	return
+}

+ 156 - 0
models/mgodb/mgo.go

@@ -0,0 +1,156 @@
+package mgodb
+
+import (
+	"context"
+	"fmt"
+	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+	"strconv"
+	"time"
+)
+
+type mgo struct {
+	database   string
+	collection string
+	client     *mongo.Client
+}
+
+func NewMgo(database, collection string, client *mongo.Client) *mgo {
+	return &mgo{
+		database,
+		collection,
+		client,
+	}
+}
+
+// 查询单个
+func (m *mgo) FindOne(key string, value interface{}) *mongo.SingleResult {
+	collection, _ := m.client.Database(m.database).Collection(m.collection).Clone()
+	filter := bson.D{{key, value}}
+	singleResult := collection.FindOne(context.TODO(), filter)
+	return singleResult
+}
+
+// 查询单个
+func (m *mgo) Find(filter bson.D, findOptions *options.FindOptions) (cur *mongo.Cursor, err error) {
+	collection := m.client.Database(m.database).Collection(m.collection)
+
+	cur, err = collection.Find(context.TODO(), filter, findOptions)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// 插入单个
+func (m *mgo) InsertOne(value interface{}) (insertResult *mongo.InsertOneResult, err error) {
+	collection := m.client.Database(m.database).Collection(m.collection)
+	insertResult, err = collection.InsertOne(context.TODO(), value)
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+	return
+}
+
+func (m *mgo) InsertMany(value []interface{}) (insertManyResult *mongo.InsertManyResult, err error) {
+	collection := m.client.Database(m.database).Collection(m.collection)
+	insertManyResult, err = collection.InsertMany(context.TODO(), value)
+	if err != nil {
+		return
+	}
+
+	fmt.Println("Inserted multiple documents: ", insertManyResult.InsertedIDs)
+	return
+}
+
+// 查询集合里有多少数据
+func (m *mgo) CollectionCount() (string, int64) {
+	collection := m.client.Database(m.database).Collection(m.collection)
+	name := collection.Name()
+	size, _ := collection.EstimatedDocumentCount(context.TODO())
+	return name, size
+}
+
+// 按选项查询集合 Skip 跳过 Limit 读取数量 sort 1 ,-1 . 1 为最初时间读取 , -1 为最新时间读取
+func (m *mgo) CollectionDocuments(Skip, Limit int64, sort int) *mongo.Cursor {
+	collection := m.client.Database(m.database).Collection(m.collection)
+	SORT := bson.D{{"_id", sort}} //filter := bson.D{{key,value}}
+	filter := bson.D{{}}
+	findOptions := options.Find().SetSort(SORT).SetLimit(Limit).SetSkip(Skip)
+	//findOptions.SetLimit(i)
+	temp, _ := collection.Find(context.Background(), filter, findOptions)
+	return temp
+}
+
+// 获取集合创建时间和编号
+func (m *mgo) ParsingId(result string) (time.Time, uint64) {
+	temp1 := result[:8]
+	timestamp, _ := strconv.ParseInt(temp1, 16, 64)
+	dateTime := time.Unix(timestamp, 0) //这是截获情报时间 时间格式 2019-04-24 09:23:39 +0800 CST
+	temp2 := result[18:]
+	count, _ := strconv.ParseUint(temp2, 16, 64) //截获情报的编号
+	return dateTime, count
+}
+
+// 删除文章和查询文章
+func (m *mgo) DeleteAndFind(key string, value interface{}) (int64, *mongo.SingleResult) {
+	collection := m.client.Database(m.database).Collection(m.collection)
+	filter := bson.D{{key, value}}
+	singleResult := collection.FindOne(context.TODO(), filter)
+	DeleteResult, err := collection.DeleteOne(context.TODO(), filter, nil)
+	if err != nil {
+		fmt.Println("删除时出现错误,你删不掉的~")
+	}
+	return DeleteResult.DeletedCount, singleResult
+}
+
+// 删除文章
+func (m *mgo) Delete(key string, value interface{}) int64 {
+	collection := m.client.Database(m.database).Collection(m.collection)
+	filter := bson.D{{key, value}}
+	count, err := collection.DeleteOne(context.TODO(), filter, nil)
+	if err != nil {
+		fmt.Println(err)
+	}
+	return count.DeletedCount
+
+}
+
+// 删除多个
+func (m *mgo) DeleteMany(filter bson.D) (num int64, err error) {
+	collection := m.client.Database(m.database).Collection(m.collection)
+	//filter := bson.D{{key, value}}
+
+	count, err := collection.DeleteMany(context.TODO(), filter)
+	if err != nil {
+		return
+	}
+	num = count.DeletedCount
+	return
+}
+
+func (m *mgo) UpdateMany(filter, update bson.D) (result *mongo.UpdateResult, err error) {
+	collection := m.client.Database(m.database).Collection(m.collection)
+	//filter := bson.D{{"name", "123456"}}
+	//update := bson.D{{"$set", bson.D{{"name", "张三"}}}}
+	result, err = collection.UpdateMany(context.TODO(), filter, update)
+	if err != nil {
+		return
+	}
+	fmt.Println(result)
+	return
+}
+
+func (m *mgo) UpdateOne(filter, update bson.D) (result *mongo.UpdateResult, err error) {
+	collection := m.client.Database(m.database).Collection(m.collection)
+	//filter := bson.D{{"name", "123456"}}
+	//update := bson.D{{"$set", bson.D{{"name", "张三"}}}}
+	result, err = collection.UpdateOne(context.TODO(), filter, update)
+	if err != nil {
+		return
+	}
+	fmt.Println(result)
+	return
+}

+ 59 - 0
models/sys_session.go

@@ -0,0 +1,59 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type SysSession struct {
+	Id              int `orm:"column(id);pk"`
+	SysUserId       int
+	UserName        string
+	AccessToken     string
+	IsRemember      int `description:"是否属于受信设备"`
+	ExpiredTime     time.Time
+	CreatedTime     time.Time
+	LastUpdatedTime time.Time
+}
+
+// AddSysSession 新增用户登录session信息
+func AddSysSession(item *SysSession) (err error) {
+	o := orm.NewOrm()
+	lastId, err := o.Insert(item)
+	if err != nil {
+		return
+	}
+	item.Id = int(lastId)
+	return
+}
+
+func GetSysSessionBySysUserId(sysUserId int) (item *SysSession, err error) {
+	sql := `SELECT * FROM sys_session WHERE sys_user_id=? AND expired_time> NOW() ORDER BY expired_time DESC LIMIT 1 `
+	o := orm.NewOrm()
+	err = o.Raw(sql, sysUserId).QueryRow(&item)
+	return
+}
+
+// GetSysSessionByToken 根据token获取session
+func GetSysSessionByToken(token string) (item *SysSession, err error) {
+	sql := `SELECT * FROM sys_session WHERE access_token=? AND expired_time> NOW() ORDER BY expired_time DESC LIMIT 1 `
+	o := orm.NewOrm()
+	err = o.Raw(sql, token).QueryRow(&item)
+	return
+}
+
+// ExpiredSysSessionByAdminId 过期掉用户token
+func ExpiredSysSessionByAdminId(adminId int) (err error) {
+	sql := `update sys_session set expired_time = NOW()  WHERE sys_user_id=? `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, adminId).Exec()
+	return
+}
+
+// UnBindAdminRecordByUserId 根据系统用户id解除绑定用户关系
+func UnBindAdminRecordByUserId(userId int) (err error) {
+	o := orm.NewOrm()
+	msql := ` UPDATE admin_record SET user_id = 0 WHERE user_id = ? `
+	_, err = o.Raw(msql, userId).Exec()
+	return
+}

+ 219 - 0
models/sys_user.go

@@ -0,0 +1,219 @@
+package models
+
+import (
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+type LoginReq struct {
+	Username   string `description:"账号"`
+	Password   string `description:"密码"`
+	IsRemember bool   `description:"是否属于受信设备"`
+}
+
+type LoginResp struct {
+	Authorization   string
+	AdminName       string `description:"系统用户名称"`
+	RealName        string `description:"系统用户姓名"`
+	RoleName        string `description:"角色名称"`
+	RoleTypeCode    string `description:"角色类型编码"`
+	SysRoleTypeCode string `description:"角色类型编码"`
+	AdminId         int    `description:"系统用户id"`
+	ProductName     string `description:"产品名称:admin,ficc,权益"`
+	Authority       int    `description:"管理权限,0:无,1:部门负责人,2:小组负责人,或者ficc销售主管,4:ficc销售组长"`
+}
+
+type Admin struct {
+	AdminId                   int    `orm:"column(admin_id);pk" description:"系统用户id"`
+	AdminName                 string `description:"系统用户名称"`
+	AdminAvatar               string `description:"用户头像"`
+	RealName                  string `description:"系统用户姓名"`
+	Password                  string `json:"-"`
+	LastUpdatedPasswordTime   string `json:"-"`
+	Enabled                   int
+	Email                     string `description:"系统用户邮箱"`
+	LastLoginTime             string
+	CreatedTime               time.Time
+	LastUpdatedTime           string
+	Role                      string    `description:"系统用户角色"`
+	Mobile                    string    `description:"手机号"`
+	RoleType                  int       `description:"角色类型:1需要录入指标,0:不需要"`
+	RoleId                    int       `description:"角色ID"`
+	RoleName                  string    `description:"角色名称"`
+	RoleTypeCode              string    `description:"角色类型编码"`
+	DepartmentId              int       `description:"部门id"`
+	DepartmentName            string    `description:"部门名称"`
+	GroupId                   int       `description:"分组id"`
+	GroupName                 string    `description:"分组名称"`
+	Authority                 int       `description:"管理权限,0:无,1:部门负责人,2:小组负责人,或者ficc销售主管,3:超级管理员,4:ficc销售组长"`
+	Position                  string    `description:"职位"`
+	DisableTime               time.Time `description:"禁用时间"`
+	ChartPermission           int8      `description:"图表权限id"`
+	OpenId                    string    `description:"弘则部门公众号的openid"`
+	UnionId                   string    `description:"微信公众平台唯一标识"`
+	EdbPermission             int8      `description:"指标库操作权限,0:只能操作 自己的,1:所有指标可操作"`
+	MysteelChemicalPermission int8      `description:"钢联化工指标操作权限,0:只能操作 自己的,1:所有指标可操作"`
+	PredictEdbPermission      int8      `description:"预测指标库操作权限,0:只能操作 自己的,1:所有预测指标可操作"`
+	Province                  string    `description:"省"`
+	ProvinceCode              string    `description:"省编码"`
+	City                      string    `description:"市"`
+	CityCode                  string    `description:"市编码"`
+	EmployeeId                string    `description:"员工工号(钉钉/每刻报销)"`
+	TelAreaCode               string    `description:"手机区号"`
+	IsLdap                    int       `description:"是否为域用户:0-系统账户;1-域用户"`
+}
+
+// Update 更新用户基础信息
+func (item *Admin) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(item, cols...)
+	return
+}
+
+func CheckSysUser(userName, password string) (item *Admin, err error) {
+	sql := ` SELECT a.*,b.role_type_code FROM admin AS a
+			 INNER JOIN sys_role AS b ON a.role_id=b.role_id WHERE a.admin_name=? AND a.password=? LIMIT 1`
+	o := orm.NewOrm()
+	err = o.Raw(sql, userName, password).QueryRow(&item)
+	return
+}
+
+func GetSysUserById(sysUserId int) (item *Admin, err error) {
+	sql := `SELECT * FROM admin WHERE admin_id=? LIMIT 1`
+	o := orm.NewOrm()
+	err = o.Raw(sql, sysUserId).QueryRow(&item)
+	return
+}
+
+func ModifyPwd(sysUserId int, newPwd string) (err error) {
+	sql := `UPDATE admin SET password=?,last_updated_time=NOW() WHERE admin_id=? `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, newPwd, sysUserId).Exec()
+	return
+}
+
+type ModifyPwdReq struct {
+	OldPwd string `description:"旧密码"`
+	NewPwd string `description:"新密码"`
+}
+
+// AdminWxUserRelation 管理员-微信用户关联
+type AdminWxUserRelation struct {
+	AdminId   int    `json:"admin_id"`
+	AdminName string `json:"admin_name"`
+	RealName  string `json:"real_name"`
+	UserId    int    `json:"user_id"`
+}
+
+// GetAdminListByIdList 根据用户id列表获取系统用户列表
+func GetAdminListByIdList(idList []int) (items []*Admin, err error) {
+	lenNum := len(idList)
+	if lenNum <= 0 {
+		return
+	}
+	sql := `SELECT * FROM admin WHERE admin_id in (` + utils.GetOrmInReplace(lenNum) + `) and enabled=1 `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, idList).QueryRows(&items)
+	return
+}
+
+// GetAdminListByIdList 根据用户id列表获取系统用户列表
+func GetAdminListByIdListWithoutEnable(idList []int) (items []*Admin, err error) {
+	lenNum := len(idList)
+	if lenNum <= 0 {
+		return
+	}
+	sql := `SELECT * FROM admin WHERE admin_id in (` + utils.GetOrmInReplace(lenNum) + `) `
+	o := orm.NewOrm()
+	_, err = o.Raw(sql, idList).QueryRows(&items)
+	return
+}
+
+func (item *Admin) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM admin WHERE 1=1 %s`, condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (item *Admin) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*Admin, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY created_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM admin WHERE 1=1 %s %s`, fields, condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// GetSysUserByMobile 手机号获取用户
+func GetSysUserByMobile(mobile string) (item *Admin, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+				a.*, b.role_type_code
+			FROM
+				admin AS a
+			INNER JOIN sys_role AS b ON a.role_id = b.role_id
+			WHERE
+				a.mobile = ?
+			LIMIT 1`
+	err = o.Raw(sql, mobile).QueryRow(&item)
+	return
+}
+
+// GetSysUserByEmail 邮箱获取用户
+func GetSysUserByEmail(email string) (item *Admin, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+				a.*, b.role_type_code
+			FROM
+				admin AS a
+			INNER JOIN sys_role AS b ON a.role_id = b.role_id
+			WHERE
+				a.email = ?
+			LIMIT 1`
+	err = o.Raw(sql, email).QueryRow(&item)
+	return
+}
+
+// GetSysUserByAdminName 账号获取用户
+func GetSysUserByAdminName(adminName string) (item *Admin, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+				a.*, b.role_type_code
+			FROM
+				admin AS a
+			INNER JOIN sys_role AS b ON a.role_id = b.role_id
+			WHERE
+				a.admin_name = ?
+			LIMIT 1`
+	err = o.Raw(sql, adminName).QueryRow(&item)
+	return
+}
+
+// GetUserUnexpiredSysSession 获取用户未过期的Token
+func GetUserUnexpiredSysSession(userName, expiredTime string) (item *SysSession, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+				*
+			FROM
+				sys_session
+			WHERE
+				user_name = ? AND expired_time > ?
+				        ORDER BY expired_time DESC
+			LIMIT 1`
+	err = o.Raw(sql, userName, expiredTime).QueryRow(&item)
+	return
+}
+
+type TokenLoginReq struct {
+	AuthCode string `json:"auth_code" form:"auth_code" binding:"required"`
+}

+ 22 - 0
models/sys_user_login_record.go

@@ -0,0 +1,22 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type SysUserLoginRecord struct {
+	Id         int `orm:"column(id);pk"`
+	Uid        int
+	UserName   string
+	Ip         string
+	Stage      string
+	CreateTime time.Time
+}
+
+// AddSysUserLoginRecord 新增登录记录
+func AddSysUserLoginRecord(item *SysUserLoginRecord) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Insert(item)
+	return
+}

+ 37 - 0
routers/commentsRouter.go

@@ -0,0 +1,37 @@
+package routers
+
+import (
+	beego "github.com/beego/beego/v2/server/web"
+	"github.com/beego/beego/v2/server/web/context/param"
+)
+
+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})
+
+}

+ 24 - 0
routers/router.go

@@ -0,0 +1,24 @@
+// @APIVersion 1.0.0
+// @Title beego Test API
+// @Description beego has a very cool tools to autogenerate documents for your API
+// @Contact astaxie@gmail.com
+// @TermsOfServiceUrl http://beego.me/
+// @License Apache 2.0
+// @LicenseUrl http://www.apache.org/licenses/LICENSE-2.0.html
+package routers
+
+import (
+	"eta/eta_forum_admin/controllers"
+	"github.com/beego/beego/v2/server/web"
+)
+
+func init() {
+	ns := web.NewNamespace("/v1",
+		web.NSNamespace("/chart",
+			web.NSInclude(
+				&controllers.ChartInfoController{},
+			),
+		),
+	)
+	web.AddNamespace(ns)
+}

+ 91 - 0
services/alarm_msg/alarm_msg.go

@@ -0,0 +1,91 @@
+package alarm_msg
+
+import (
+	"crypto/tls"
+	"encoding/json"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strings"
+	"time"
+)
+
+// SendAlarmMsg
+// projectName-项目名称
+// runMode-运行模式
+// msgBody-消息内容
+// level:消息基本,1:提示消息,2:警告消息,3:严重错误信息,默认为1 提示消息
+func SendAlarmMsg(msgBody string, level int) {
+	if utils.AlarmMsgUrl == `` {
+		return
+	}
+	params := make(map[string]interface{})
+	params["ProjectName"] = utils.APP_NAME_CN
+	params["RunMode"] = utils.RunMode
+	params["MsgBody"] = msgBody
+	params["Level"] = level
+	param, err := json.Marshal(params)
+	if err != nil {
+		utils.FileLog.Info("SendAlarmMsg json.Marshal Err:" + err.Error())
+		return
+	}
+	_, _ = Post(utils.AlarmMsgUrl, string(param), "application/json")
+}
+
+func HttpPost(url, postData string, params ...string) ([]byte, error) {
+	contentType := "application/x-www-form-urlencoded;charset=utf-8"
+	if len(params) > 0 && params[0] != "" {
+		contentType = params[0]
+	}
+	sTime := time.Now()
+	resp, err := http.Post(url,
+		contentType,
+		strings.NewReader(postData))
+	eTime := time.Now()
+	t := eTime.Sub(sTime).Nanoseconds() / 1000000
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	b, err := ioutil.ReadAll(resp.Body)
+	fmt.Println(t, "ms", "POST", "URL", url, "DATA", postData, "RESPONSE", string(b))
+	return b, err
+}
+
+func Post(url, postData string, params ...string) ([]byte, error) {
+	if strings.HasPrefix(url, "https://") {
+		return HttpsPost(url, postData, params...)
+	} else {
+		return HttpPost(url, postData, params...)
+	}
+}
+
+func HttpsPost(url, postData string, params ...string) ([]byte, error) {
+	body := ioutil.NopCloser(strings.NewReader(postData))
+	tr := &http.Transport{
+		TLSClientConfig:    &tls.Config{InsecureSkipVerify: true},
+		DisableCompression: true,
+	}
+	client := &http.Client{Transport: tr}
+	req, err := http.NewRequest("POST", url, body)
+	if err != nil {
+		return nil, err
+	}
+	contentType := "application/x-www-form-urlencoded;charset=utf-8"
+	if len(params) > 0 && params[0] != "" {
+		contentType = params[0]
+	}
+	req.Header.Set("Content-Type", contentType)
+	sTime := time.Now()
+	resp, err := client.Do(req)
+	eTime := time.Now()
+	t := eTime.Sub(sTime).Nanoseconds() / 1000000
+	if err != nil {
+		return nil, err
+	}
+	defer resp.Body.Close()
+	b, err := ioutil.ReadAll(resp.Body)
+	fmt.Println(t, "ms", "POST", "URL", url, "DATA", postData, "RESPONSE", string(b))
+	return b, err
+}

+ 49 - 0
services/base.go

@@ -0,0 +1,49 @@
+package services
+
+import (
+	"encoding/json"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strings"
+)
+
+// PostEdbLib 调用指标接口
+func PostEdbLib(param map[string]interface{}, method string) (result []byte, err error) {
+	postUrl := utils.EDB_LIB_URL + method
+	postData, err := json.Marshal(param)
+	if err != nil {
+		return
+	}
+	result, err = HttpPost(postUrl, string(postData), "application/json")
+	if err != nil {
+		return
+	}
+	return
+}
+
+func HttpPost(url, postData string, params ...string) ([]byte, error) {
+	fmt.Println("HttpPost Url:" + url)
+	body := ioutil.NopCloser(strings.NewReader(postData))
+	client := &http.Client{}
+	req, err := http.NewRequest("POST", url, body)
+	if err != nil {
+		return nil, err
+	}
+	contentType := "application/x-www-form-urlencoded;charset=utf-8"
+	if len(params) > 0 && params[0] != "" {
+		contentType = params[0]
+	}
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("authorization", utils.MD5(utils.APP_EDB_LIB_NAME_EN+utils.EDB_LIB_Md5_KEY))
+	resp, err := client.Do(req)
+	if err != nil {
+		fmt.Println("client.Do err:" + err.Error())
+		return nil, err
+	}
+	defer resp.Body.Close()
+	b, err := ioutil.ReadAll(resp.Body)
+	fmt.Println("HttpPost:" + string(b))
+	return b, err
+}

+ 476 - 0
services/chart_info.go

@@ -0,0 +1,476 @@
+package services
+
+import (
+	"errors"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"sort"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// AddChartInfo 添加图表
+func AddChartInfo(req *models.AddChartReq, sysUserId int, sysUserRealName string) (chartInfo *models.ChartInfo, err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true // 默认错误的时候要发送邮件
+
+	req.ChartInfo.ChartName = strings.Trim(req.ChartInfo.ChartName, " ")
+	if req.ChartInfo.ChartName == "" {
+		errMsg = "请填写图表名称!"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+
+	//判断图表是否存在
+	var condition string
+	var pars []interface{}
+
+	// todo 判断同一个平台上的图表是否重复
+	condition += " AND chart_name=? AND source = ? "
+	pars = append(pars, req.ChartInfo.ChartName, req.ChartInfo.Source)
+
+	count, err := models.GetChartInfoCountByCondition(condition, pars)
+	if err != nil {
+		errMsg = "判断图表名称是否存在失败"
+		err = errors.New("判断图表名称是否存在失败,Err:" + err.Error())
+		return
+	}
+
+	if count > 0 {
+		errMsg = "图表已存在"
+		err = errors.New("图表已存在")
+		isSendEmail = false
+		return
+	}
+
+	// 添加指标数据
+	err = BatchAddOrUpdateEdbData(req.EdbInfoDataList)
+	if err != nil {
+		errMsg = "添加指标数据失败"
+		err = errors.New("添加指标数据失败,Err:" + err.Error())
+		return
+	}
+	// 查询指标ID和指标编码之间的关系
+	originEdbInfoIdMap := make(map[int]string)
+	for _, v := range req.EdbInfoList {
+		originEdbInfoIdMap[v.EdbInfoId] = v.EdbCode
+	}
+	// 添加关联指标
+	var edbInfoIdArr []int
+	edbInfoList := make([]*models.EdbInfo, 0)
+	edbInfoList, err, errMsg, isSendEmail = BatchAddOrUpdateEdbInfo(req.EdbInfoList, req.EdbInfoCalculateMapping, sysUserId, sysUserRealName)
+	if err != nil {
+		errMsg = "添加关联指标失败"
+		err = errors.New("添加关联指标失败,Err:" + err.Error())
+		return
+	}
+	edbCodeMap := make(map[string]*models.EdbInfo, 0)
+	for _, v := range edbInfoList {
+		edbCodeMap[v.EdbCode] = v
+		edbInfoIdArr = append(edbInfoIdArr, v.EdbInfoId)
+	}
+
+	// todo 批量新增指标
+	sort.Ints(edbInfoIdArr)
+	var edbInfoIdArrStr []string
+	for _, v := range edbInfoIdArr {
+		edbInfoIdArrStr = append(edbInfoIdArrStr, strconv.Itoa(v))
+	}
+	edbInfoIdStr := strings.Join(edbInfoIdArrStr, ",")
+
+	chartInfo = new(models.ChartInfo)
+	chartInfo.ChartName = req.ChartInfo.ChartName
+	chartInfo.EdbInfoIds = edbInfoIdStr
+	chartInfo.SysUserId = req.ChartInfo.SysUserId
+	chartInfo.SysUserRealName = req.ChartInfo.SysUserRealName
+	chartInfo.CreateTime = time.Now()
+	chartInfo.ModifyTime = time.Now()
+	chartInfo.IsSetName = req.ChartInfo.IsSetName
+	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+
+	// todo 是否需要单独生成一个指标图表序列ID
+	chartInfo.UniqueCode = utils.MD5(utils.CHART_PREFIX + "_" + timestamp)
+	chartInfo.DateType = req.ChartInfo.DateType
+	chartInfo.ChartType = req.ChartInfo.ChartType
+
+	calendar := req.ChartInfo.Calendar
+	chartInfo.Calendar = calendar
+	chartInfo.StartDate = req.ChartInfo.StartDate
+	chartInfo.EndDate = req.ChartInfo.EndDate
+	chartInfo.SeasonStartDate = req.ChartInfo.StartDate
+	chartInfo.SeasonEndDate = req.ChartInfo.EndDate
+	chartInfo.LeftMin = req.ChartInfo.LeftMin
+	chartInfo.LeftMax = req.ChartInfo.LeftMax
+	chartInfo.RightMin = req.ChartInfo.RightMin
+	chartInfo.RightMax = req.ChartInfo.RightMax
+	chartInfo.Right2Min = req.ChartInfo.Right2Min
+	chartInfo.Right2Max = req.ChartInfo.Right2Max
+	chartInfo.MinMaxSave = req.ChartInfo.MinMaxSave
+	chartInfo.Disabled = req.ChartInfo.Disabled
+	chartInfo.BarConfig = req.ChartInfo.BarConfig
+	chartInfo.ExtraConfig = req.ChartInfo.ExtraConfig
+	chartInfo.SeasonExtraConfig = req.ChartInfo.SeasonExtraConfig
+	chartInfo.StartYear = req.ChartInfo.StartYear
+	chartInfo.Source = req.ChartInfo.Source
+	chartInfo.ChartThemeId = req.ChartInfo.ChartThemeId
+	chartInfo.SourcesFrom = req.ChartInfo.SourcesFrom
+	chartInfo.Instructions = req.ChartInfo.Instructions
+	chartInfo.MarkersLines = req.ChartInfo.MarkersLines
+	chartInfo.MarkersAreas = req.ChartInfo.MarkersAreas
+	chartInfo.Unit = req.ChartInfo.Unit
+	chartInfo.UnitEn = req.ChartInfo.UnitEn
+	newId, err := models.AddChartInfo(chartInfo)
+	if err != nil {
+		errMsg = `保存失败`
+		err = errors.New("保存失败,Err:" + err.Error())
+		return
+	}
+	chartInfo.ChartInfoId = int(newId)
+
+	// todo 先整理好指标信息,生成指标ID,再将图表和指标ID进行绑定
+	chartEdbInfoList := req.ChartEdbMapping
+	mapList := make([]*models.ChartEdbMapping, 0)
+	for _, v := range chartEdbInfoList {
+		mapItem := new(models.ChartEdbMapping)
+		mapItem.ChartInfoId = int(newId)
+		edbCode, ok := originEdbInfoIdMap[v.EdbInfoId]
+		if !ok {
+			err = fmt.Errorf("指标代码不存在,EdbCode:%s", v.EdbCode)
+			errMsg = "指标代码不存在"
+			return
+		}
+		edbBase, ok := edbCodeMap[edbCode]
+		if !ok {
+			err = fmt.Errorf("指标代码不存在,EdbCode:%s", v.EdbCode)
+			errMsg = "指标代码不存在"
+			return
+		}
+		mapItem.EdbInfoId = edbBase.EdbInfoId
+		mapItem.CreateTime = time.Now()
+		mapItem.ModifyTime = time.Now()
+		timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+		mapItem.UniqueCode = utils.MD5(fmt.Sprint(utils.CHART_PREFIX, "_", edbBase.EdbInfoId, "_", timestamp))
+		mapItem.MaxData = v.MaxData
+		mapItem.MinData = v.MinData
+		mapItem.IsOrder = v.IsOrder
+		mapItem.IsAxis = v.IsAxis
+		mapItem.EdbInfoType = v.EdbInfoType
+		mapItem.LeadValue = v.LeadValue
+		mapItem.LeadUnit = v.LeadUnit
+		mapItem.ChartStyle = v.ChartStyle
+		mapItem.ChartColor = v.ChartColor
+		mapItem.PredictChartColor = v.PredictChartColor
+		mapItem.ChartWidth = v.ChartWidth
+		mapItem.Source = v.Source
+		mapItem.EdbAliasName = v.EdbAliasName
+		mapItem.IsConvert = v.IsConvert
+		mapItem.ConvertType = v.ConvertType
+		mapItem.ConvertValue = v.ConvertValue
+		mapItem.ConvertUnit = v.ConvertUnit
+		mapItem.ConvertEnUnit = v.ConvertEnUnit
+		mapItem.EdbCode = edbCode
+		mapList = append(mapList, mapItem)
+	}
+	if len(mapList) <= 0 {
+		errMsg = `图表指标映射不能为空`
+		err = errors.New("图表指标映射不能为空")
+		return
+	}
+	err = models.AddChartEdbMapping(mapList)
+	if err != nil {
+		errMsg = `保存失败`
+		err = errors.New("保存失败,Err:" + err.Error())
+		return
+	}
+	return
+}
+
+// UpdateChartInfoAndEdbInfo 刷新图表信息和指标信息
+func UpdateChartInfoAndEdbInfo(req *models.AddChartReq, sysUserId int, sysUserRealName string) (chartInfo *models.ChartInfo, err error, errMsg string, isSendEmail bool) {
+
+	isSendEmail = true // 默认错误的时候要发送邮件
+
+	req.ChartInfo.ChartName = strings.Trim(req.ChartInfo.ChartName, " ")
+	if req.ChartInfo.ChartName == "" {
+		errMsg = "请填写图表名称!"
+		err = errors.New(errMsg)
+		isSendEmail = false
+		return
+	}
+
+	chartInfo, err = models.GetChartInfoById(req.ChartInfo.ChartInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "图表已被删除,请刷新页面"
+			err = errors.New(errMsg)
+			isSendEmail = false
+			return
+		}
+		errMsg = "获取图表信息失败"
+		err = errors.New("获取图表信息失败,Err:" + err.Error())
+		return
+	}
+
+	//判断图表是否存在
+	var condition string
+	var pars []interface{}
+
+	// todo 判断同一个平台上的图表是否重复
+	condition += " AND chart_name=? AND source = ? AND chart_info_id<>? "
+	pars = append(pars, req.ChartInfo.ChartName, req.ChartInfo.Source, req.ChartInfo.ChartInfoId)
+
+	count, err := models.GetChartInfoCountByCondition(condition, pars)
+	if err != nil {
+		errMsg = "判断图表名称是否存在失败"
+		err = errors.New("判断图表名称是否存在失败,Err:" + err.Error())
+		return
+	}
+
+	if count > 0 {
+		errMsg = "图表已存在"
+		err = errors.New("图表已存在")
+		isSendEmail = false
+		return
+	}
+
+	// 添加指标数据
+	err = BatchAddOrUpdateEdbData(req.EdbInfoDataList)
+	if err != nil {
+		errMsg = "添加指标数据失败"
+		err = errors.New("添加指标数据失败,Err:" + err.Error())
+		return
+	}
+	// 查询指标ID和指标编码之间的关系
+	originEdbInfoIdMap := make(map[int]string)
+	for _, v := range req.EdbInfoList {
+		originEdbInfoIdMap[v.EdbInfoId] = v.EdbCode
+	}
+	// 添加关联指标
+	var edbInfoIdArr []int
+	edbInfoList := make([]*models.EdbInfo, 0)
+	edbInfoList, err, errMsg, isSendEmail = BatchAddOrUpdateEdbInfo(req.EdbInfoList, req.EdbInfoCalculateMapping, sysUserId, sysUserRealName)
+	if err != nil {
+		errMsg = "添加关联指标失败"
+		err = errors.New("添加关联指标失败,Err:" + err.Error())
+		return
+	}
+	edbCodeMap := make(map[string]*models.EdbInfo, 0)
+	for _, v := range edbInfoList {
+		edbCodeMap[v.EdbCode] = v
+		edbInfoIdArr = append(edbInfoIdArr, v.EdbInfoId)
+	}
+
+	// todo 批量新增指标
+	sort.Ints(edbInfoIdArr)
+	var edbInfoIdArrStr []string
+	for _, v := range edbInfoIdArr {
+		edbInfoIdArrStr = append(edbInfoIdArrStr, strconv.Itoa(v))
+	}
+	edbInfoIdStr := strings.Join(edbInfoIdArrStr, ",")
+
+	chartInfo.ChartName = req.ChartInfo.ChartName
+	chartInfo.EdbInfoIds = edbInfoIdStr
+	chartInfo.SysUserId = req.ChartInfo.SysUserId
+	chartInfo.SysUserRealName = req.ChartInfo.SysUserRealName
+	chartInfo.ModifyTime = time.Now()
+	chartInfo.IsSetName = req.ChartInfo.IsSetName
+	chartInfo.DateType = req.ChartInfo.DateType
+	chartInfo.ChartType = req.ChartInfo.ChartType
+
+	calendar := req.ChartInfo.Calendar
+	chartInfo.Calendar = calendar
+	chartInfo.StartDate = req.ChartInfo.StartDate
+	chartInfo.EndDate = req.ChartInfo.EndDate
+	chartInfo.SeasonStartDate = req.ChartInfo.StartDate
+	chartInfo.SeasonEndDate = req.ChartInfo.EndDate
+	chartInfo.LeftMin = req.ChartInfo.LeftMin
+	chartInfo.LeftMax = req.ChartInfo.LeftMax
+	chartInfo.RightMin = req.ChartInfo.RightMin
+	chartInfo.RightMax = req.ChartInfo.RightMax
+	chartInfo.Right2Min = req.ChartInfo.Right2Min
+	chartInfo.Right2Max = req.ChartInfo.Right2Max
+	chartInfo.MinMaxSave = req.ChartInfo.MinMaxSave
+	chartInfo.Disabled = req.ChartInfo.Disabled
+	chartInfo.BarConfig = req.ChartInfo.BarConfig
+	chartInfo.ExtraConfig = req.ChartInfo.ExtraConfig
+	chartInfo.SeasonExtraConfig = req.ChartInfo.SeasonExtraConfig
+	chartInfo.StartYear = req.ChartInfo.StartYear
+	chartInfo.Source = req.ChartInfo.Source
+	chartInfo.ChartThemeId = req.ChartInfo.ChartThemeId
+	chartInfo.SourcesFrom = req.ChartInfo.SourcesFrom
+	chartInfo.Instructions = req.ChartInfo.Instructions
+	chartInfo.MarkersLines = req.ChartInfo.MarkersLines
+	chartInfo.MarkersAreas = req.ChartInfo.MarkersAreas
+	chartInfo.Unit = req.ChartInfo.Unit
+	chartInfo.UnitEn = req.ChartInfo.UnitEn
+	err = chartInfo.Update([]string{})
+	if err != nil {
+		errMsg = `更新失败`
+		err = errors.New("更新失败,Err:" + err.Error())
+		return
+	}
+
+	// 处理映射,已存在的映射直接更新,不存在的映射再新增,同时删除旧的映射关系
+	oldChartEdbMappingList, err := models.GetChartEdbMappingListV2(chartInfo.ChartInfoId)
+
+	oldMappingIdMap := make(map[string]*models.ChartEdbMapping)
+	removeMapping := make(map[int]struct{})
+	for _, v := range oldChartEdbMappingList {
+		name := fmt.Sprintf("%s-%s", v.EdbCode, v.Source)
+		oldMappingIdMap[name] = v
+		removeMapping[v.ChartEdbMappingId] = struct{}{}
+	}
+	// todo 先整理好指标信息,生成指标ID,再将图表和指标ID进行绑定
+	chartEdbInfoList := req.ChartEdbMapping
+	addMappingList := make([]*models.ChartEdbMapping, 0)
+	for _, v := range chartEdbInfoList {
+		mapItem := new(models.ChartEdbMapping)
+		mapItem.ChartInfoId = chartInfo.ChartInfoId
+		edbCode, ok := originEdbInfoIdMap[v.EdbInfoId]
+		if !ok {
+			err = fmt.Errorf("指标代码不存在,EdbCode:%s", v.EdbCode)
+			errMsg = "指标代码不存在"
+			return
+		}
+		edbBase, ok := edbCodeMap[edbCode]
+		if !ok {
+			err = fmt.Errorf("指标代码不存在,EdbCode:%s", v.EdbCode)
+			errMsg = "指标代码不存在"
+			return
+		}
+		// 判断是否已经存在映射关系
+		existName := fmt.Sprintf("%s-%s", edbCode, v.Source)
+		if oldMapping, ok1 := oldMappingIdMap[existName]; ok1 {
+			//更新映射关系
+			mapItem = oldMapping
+			mapItem.ModifyTime = time.Now()
+			mapItem.MaxData = v.MaxData
+			mapItem.MinData = v.MinData
+			mapItem.IsOrder = v.IsOrder
+			mapItem.IsAxis = v.IsAxis
+			mapItem.EdbInfoType = v.EdbInfoType
+			mapItem.LeadValue = v.LeadValue
+			mapItem.LeadUnit = v.LeadUnit
+			mapItem.ChartStyle = v.ChartStyle
+			mapItem.ChartColor = v.ChartColor
+			mapItem.PredictChartColor = v.PredictChartColor
+			mapItem.ChartWidth = v.ChartWidth
+			mapItem.Source = v.Source
+			mapItem.EdbAliasName = v.EdbAliasName
+			mapItem.IsConvert = v.IsConvert
+			mapItem.ConvertType = v.ConvertType
+			mapItem.ConvertValue = v.ConvertValue
+			mapItem.ConvertUnit = v.ConvertUnit
+			mapItem.ConvertEnUnit = v.ConvertEnUnit
+			mapItem.EdbCode = edbCode
+
+			err = mapItem.Update([]string{})
+			if err != nil {
+				errMsg = `更新失败`
+				err = errors.New("更新图表指标映射关系失败,Err:" + err.Error())
+				return
+			}
+			delete(removeMapping, mapItem.ChartEdbMappingId)
+		} else {
+			mapItem.EdbInfoId = edbBase.EdbInfoId
+			mapItem.CreateTime = time.Now()
+			mapItem.ModifyTime = time.Now()
+			timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+			mapItem.UniqueCode = utils.MD5(fmt.Sprint(utils.CHART_PREFIX, "_", edbBase.EdbInfoId, "_", timestamp))
+			mapItem.MaxData = v.MaxData
+			mapItem.MinData = v.MinData
+			mapItem.IsOrder = v.IsOrder
+			mapItem.IsAxis = v.IsAxis
+			mapItem.EdbInfoType = v.EdbInfoType
+			mapItem.LeadValue = v.LeadValue
+			mapItem.LeadUnit = v.LeadUnit
+			mapItem.ChartStyle = v.ChartStyle
+			mapItem.ChartColor = v.ChartColor
+			mapItem.PredictChartColor = v.PredictChartColor
+			mapItem.ChartWidth = v.ChartWidth
+			mapItem.Source = v.Source
+			mapItem.EdbAliasName = v.EdbAliasName
+			mapItem.IsConvert = v.IsConvert
+			mapItem.ConvertType = v.ConvertType
+			mapItem.ConvertValue = v.ConvertValue
+			mapItem.ConvertUnit = v.ConvertUnit
+			mapItem.ConvertEnUnit = v.ConvertEnUnit
+			mapItem.EdbCode = edbCode
+			addMappingList = append(addMappingList, mapItem)
+		}
+
+	}
+	if len(addMappingList) > 0 {
+		err = models.AddChartEdbMapping(addMappingList)
+		if err != nil {
+			errMsg = `保存失败`
+			err = errors.New("保存失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	// 删除原先的绑定关系
+	if len(removeMapping) > 0 {
+		removeIdList := make([]string, 0) //需要移除的日期
+		for _, v := range removeMapping {
+			removeIdList = append(removeIdList, fmt.Sprint(v))
+		}
+		removeIdStr := strings.Join(removeIdList, `","`)
+		removeIdStr = `"` + removeIdStr + `"`
+		err = models.DeleteChartEdbMappingByChartEdbMappingId(removeIdStr)
+		if err != nil {
+			errMsg = `删除失败`
+			err = errors.New("删除失败,Err:" + err.Error())
+			return
+		}
+	}
+
+	return
+}
+
+// 删除图表信息
+func DeleteChart(chartInfoId int) (err error, errMsg string) {
+	//删除图表
+	chartInfo, err := models.GetChartInfoById(chartInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			errMsg = "图表已删除,请刷新页面"
+			err = fmt.Errorf("图表不存在,Err:" + err.Error())
+			return
+		} else {
+			errMsg = "删除失败"
+			err = fmt.Errorf("删除失败, 获取图表信息失败Err:" + err.Error())
+			return
+		}
+	}
+	if chartInfo == nil {
+		errMsg = "图表已删除,请刷新页面"
+		err = fmt.Errorf(errMsg)
+		return
+	}
+	// 处理映射,已存在的映射直接更新,不存在的映射再新增,同时删除旧的映射关系
+	oldChartEdbMappingList, err := models.GetChartEdbMappingListV2(chartInfo.ChartInfoId)
+
+	edbInfoIds := make([]int, 0)
+	for _, v := range oldChartEdbMappingList {
+		edbInfoIds = append(edbInfoIds, v.EdbInfoId)
+	}
+
+	//删除图表及关联指标
+	err = models.DeleteChartInfoAndData(chartInfo.ChartInfoId)
+	if err != nil {
+		errMsg = "删除失败"
+		err = fmt.Errorf("删除图表数据失败, Err:" + err.Error())
+		return
+	}
+
+	err, errMsg = BatchDeleteEdbInfo(edbInfoIds)
+	if err != nil {
+		errMsg = "删除失败"
+		err = fmt.Errorf("删除指标数据失败, Err:" + err.Error())
+		return
+	}
+	return
+}

+ 48 - 0
services/edb_data.go

@@ -0,0 +1,48 @@
+package services
+
+import (
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/mgodb"
+	"fmt"
+)
+
+func BatchAddOrUpdateEdbData(req []*models.AddEdbDataReq) (err error) {
+	for _, reqData := range req {
+		err = AddOrUpdateEdbData(reqData.EdbCode, reqData.DataList)
+		if err != nil {
+			return
+		}
+	}
+	return
+}
+
+func AddOrUpdateEdbData(edbCode string, dataList []*mgodb.EdbDataBase) (err error) {
+	addList := make([]interface{}, 0)
+	existList, err := mgodb.GetEdbDataBaseByEdbCode(edbCode)
+	if err != nil {
+		err = fmt.Errorf("查询指标数据出错 error, %v", err)
+		return
+	}
+	existMap := make(map[string]string, len(existList))
+	for _, exist := range existList {
+		existMap[exist.DataTime] = exist.Value
+	}
+	for _, v := range dataList {
+		if oldVal, ok := existMap[v.DataTime]; !ok {
+			v.EdbInfoId = 0
+			addList = append(addList, v)
+		} else {
+			if v.Value != oldVal {
+				err = mgodb.ModifyValueEdbInfoDataStatus(v.EdbCode, v.DataTime, v.Value)
+				if err != nil {
+					err = fmt.Errorf("更新指标数据出错 error, %v", err)
+					return
+				}
+			}
+		}
+	}
+	if len(addList) > 0 {
+		err = mgodb.InsertBatch(addList)
+	}
+	return
+}

+ 231 - 0
services/edb_info.go

@@ -0,0 +1,231 @@
+package services
+
+import (
+	"errors"
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/models/mgodb"
+	"eta/eta_forum_admin/utils"
+	"strconv"
+	"time"
+)
+
+// BatchAddOrUpdateEdbInfo 添加批量添加指标到指标库
+func BatchAddOrUpdateEdbInfo(edbList []*models.EdbInfo, edbMapping []*models.EdbInfoCalculateMapping, sysUserId int, sysUserRealName string) (newList []*models.EdbInfo, err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+	edbCodeMap := make(map[string]*models.EdbInfo)
+	for _, v := range edbList {
+		tmp, e, msg, _ := AddOrUpdateEdbInfo(v, sysUserId, sysUserRealName)
+		if e != nil {
+			err = e
+			errMsg = msg
+			return
+		}
+		newList = append(newList, tmp)
+		edbCodeMap[tmp.EdbCode] = tmp
+	}
+	//批量添加指标的mapping信息
+	calculateMappingItemList := make([]*models.EdbInfoCalculateMapping, 0)
+	for _, v := range edbMapping {
+		edbInfo, ok := edbCodeMap[v.EdbCode]
+		if !ok {
+			continue
+		}
+		fromEdbInfo, ok := edbCodeMap[v.FromEdbCode]
+		if !ok {
+			continue
+		}
+
+		calculateMappingItem := new(models.EdbInfoCalculateMapping)
+		calculateMappingItem.CreateTime = time.Now()
+		calculateMappingItem.ModifyTime = time.Now()
+		calculateMappingItem.Sort = v.Sort
+		calculateMappingItem.EdbCode = edbInfo.EdbCode
+		calculateMappingItem.EdbInfoId = edbInfo.EdbInfoId
+		calculateMappingItem.FromEdbInfoId = fromEdbInfo.EdbInfoId
+		calculateMappingItem.FromEdbCode = fromEdbInfo.EdbCode
+		calculateMappingItem.FromEdbName = fromEdbInfo.EdbName
+		calculateMappingItem.FromSource = fromEdbInfo.Source
+		calculateMappingItem.FromSourceName = fromEdbInfo.SourceName
+		calculateMappingItem.FromTag = v.FromTag
+		calculateMappingItem.Source = edbInfo.Source
+		calculateMappingItem.SourceName = edbInfo.SourceName
+		calculateMappingItem.FromSubSource = edbInfo.SubSource
+		calculateMappingItemList = append(calculateMappingItemList, calculateMappingItem)
+	}
+	err = models.AddEdbInfoCalculateMappingMulti(calculateMappingItemList)
+	if err != nil {
+		errMsg = "指标映射关系添加失败"
+		return
+	}
+	return
+}
+
+// AddOrUpdateEdbInfo 添加指标到指标库
+func AddOrUpdateEdbInfo(edbItem *models.EdbInfo, sysUserId int, sysUserRealName string) (edbInfo *models.EdbInfo, err error, errMsg string, isSendEmail bool) {
+	isSendEmail = true
+	//判断指标是否存在
+	edbInfo, err = models.GetEdbInfoByEdbCode(edbItem.Source, edbItem.EdbCode)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			err = nil
+		} else {
+			errMsg = "判断指标名称是否存在失败"
+			err = errors.New("判断指标名称是否存在失败,Err:" + err.Error())
+			return
+		}
+	}
+	var edbInfoId int64
+	// 指标已存在
+	if edbInfo != nil && edbInfo.EdbInfoId > 0 {
+		edbInfoId = int64(edbInfo.EdbInfoId)
+		// 更新指标信息
+		edbInfo.Source = edbItem.Source
+		edbInfo.SourceName = edbItem.SourceName
+		edbInfo.EdbType = edbItem.EdbType
+		edbInfo.EdbCode = edbItem.EdbCode
+		edbInfo.EdbName = edbItem.EdbName
+		edbInfo.EdbNameSource = edbItem.EdbNameSource
+		edbInfo.Frequency = edbItem.Frequency
+		edbInfo.Unit = edbItem.Unit
+		edbInfo.StartDate = edbItem.StartDate
+		edbInfo.EndDate = edbItem.EndDate
+		edbInfo.ClassifyId = edbItem.ClassifyId
+		edbInfo.SysUserId = sysUserId
+		edbInfo.SysUserRealName = sysUserRealName
+		edbInfo.ModifyTime = time.Now()
+		edbInfo.ServerUrl = edbItem.ServerUrl
+		edbInfo.Sort = edbItem.Sort
+		edbInfo.DataDateType = edbItem.DataDateType
+		edbInfo.TerminalCode = edbItem.TerminalCode
+		edbInfo.SourceIndexName = edbItem.SourceIndexName
+
+		edbInfo.MaxValue = edbItem.MaxValue
+		edbInfo.MinValue = edbItem.MinValue
+		edbInfo.EdbType = edbItem.EdbType
+		err = edbInfo.Update([]string{})
+		if err != nil {
+			errMsg = "保存失败"
+			err = errors.New("保存失败,Err:" + err.Error())
+			return
+		}
+		//同时删除指标的映射信息
+		err = models.DeleteEdbInfoMapping(edbInfoId)
+		if err != nil {
+			errMsg = "删除指标映射信息失败"
+			err = errors.New("删除指标映射信息失败,Err:" + err.Error())
+			return
+		}
+	} else {
+		edbInfo = new(models.EdbInfo)
+		edbInfo.Source = edbItem.Source
+		edbInfo.SourceName = edbItem.SourceName
+		edbInfo.EdbType = edbItem.EdbType
+		edbInfo.EdbCode = edbItem.EdbCode
+		edbInfo.EdbName = edbItem.EdbName
+		edbInfo.EdbNameSource = edbItem.EdbNameSource
+		edbInfo.Frequency = edbItem.Frequency
+		edbInfo.Unit = edbItem.Unit
+		edbInfo.StartDate = edbItem.StartDate
+		edbInfo.EndDate = edbItem.EndDate
+		edbInfo.ClassifyId = edbItem.ClassifyId
+		edbInfo.SysUserId = sysUserId
+		edbInfo.SysUserRealName = sysUserRealName
+		edbInfo.CreateTime = time.Now()
+		edbInfo.ModifyTime = time.Now()
+		edbInfo.ServerUrl = edbItem.ServerUrl
+		edbInfo.Sort = edbItem.Sort
+		edbInfo.DataDateType = edbItem.DataDateType
+		edbInfo.TerminalCode = edbItem.TerminalCode
+		edbInfo.SourceIndexName = edbItem.SourceIndexName
+		timestamp := strconv.FormatInt(time.Now().UnixNano(), 10)
+		edbInfo.UniqueCode = utils.MD5(utils.DATA_PREFIX + "_" + timestamp)
+
+		edbInfo.MaxValue = edbItem.MaxValue
+		edbInfo.MinValue = edbItem.MinValue
+		edbInfo.EdbType = edbItem.EdbType
+		edbInfoId, err = models.AddEdbInfo(edbInfo)
+		if err != nil {
+			errMsg = "保存失败"
+			err = errors.New("保存失败,Err:" + err.Error())
+			return
+		}
+		edbInfo.EdbInfoId = int(edbInfoId)
+	}
+
+	err = mgodb.ModifyEdbInfoDataStatus(edbInfoId, edbInfo.EdbCode)
+	if err != nil {
+		errMsg = "保存失败"
+		err = errors.New("更新指标数据失败,Err:" + err.Error())
+		return
+	}
+
+	return
+}
+
+// 批量删除指标,
+func BatchDeleteEdbInfo(edbInfoIds []int) (err error, errMsg string) {
+	for _, v := range edbInfoIds {
+		//查询单个指标是否允许删除
+		err, errMsg = DeleteEdbInfo(v)
+		if err != nil {
+			errMsg = "删除指标失败"
+			return
+		}
+	}
+	return
+}
+
+// 删除单个指标
+func DeleteEdbInfo(edbInfoId int) (err error, errMsg string) {
+	//判断指标是否存在
+	_, err = models.GetEdbInfoById(edbInfoId)
+	if err != nil {
+		if err.Error() == utils.ErrNoRow() {
+			err = nil
+			return
+		} else {
+			errMsg = "判断指标名称是否存在失败"
+			err = errors.New("判断指标名称是否存在失败,Err:" + err.Error())
+			return
+		}
+	}
+	//判断指标是否用于作图,如果用于作图,则不可删除
+	chartCount, tmpErr := models.GetChartEdbMappingCount(edbInfoId)
+	if tmpErr != nil && tmpErr.Error() != utils.ErrNoRow() {
+		errMsg = "删除失败"
+		err = errors.New("判断指标是否被用于作图失败,Err:" + tmpErr.Error())
+		return
+	}
+	if chartCount > 0 {
+		errMsg = "当前指标已用作画图,不可删除"
+		return
+	}
+
+	//判断指标是否用作其他指标的计算
+	calculateCount, tmpErr := models.GetEdbInfoCalculateMappingCount(edbInfoId)
+	if tmpErr != nil {
+		errMsg = "删除失败"
+		err = errors.New("判断指标是否被用于计算失败,GetEdbInfoCalculateCount Err:" + tmpErr.Error())
+		return
+	}
+	if calculateCount > 0 {
+		errMsg = "当前指标已用作,指标运算,不可删除"
+		return
+	}
+
+	// 删除指标,删除映射关系,删除指标数据
+	err = models.DeleteEdbInfo(edbInfoId)
+	if err != nil {
+		errMsg = "删除失败"
+		err = errors.New("删除指标失败,Err:" + err.Error())
+		return
+	}
+
+	err = mgodb.DeleteEdbInfoDataByEdbInfoId(edbInfoId)
+	if err != nil {
+		errMsg = "删除失败"
+		err = errors.New("删除指标数据失败,Err:" + err.Error())
+		return
+	}
+	return
+}

+ 114 - 0
services/login.go

@@ -0,0 +1,114 @@
+package services
+
+import (
+	"eta/eta_forum_admin/models"
+	"eta/eta_forum_admin/utils"
+	"fmt"
+	"time"
+)
+
+func ThirdLogin(adminName string) (login models.LoginResp, err error) {
+	// 获取用户信息
+	sysUser, e := models.GetSysUserByAdminName(adminName)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			err = fmt.Errorf("用户不存在")
+			return
+		}
+		err = fmt.Errorf("获取失败, 获取用户信息失败, err: " + e.Error())
+		return
+	}
+
+	var token string
+	account := utils.MD5(adminName)
+	// 获取用户未过期的session, 避免过于频繁生成token
+	expired := time.Now().AddDate(0, 0, 1).Format(utils.FormatDateTime)
+	session, _ := models.GetUserUnexpiredSysSession(adminName, expired)
+	if session != nil && session.AccessToken != "" {
+		token = session.AccessToken
+	} else {
+		token = utils.GenToken(account)
+		sysSession := new(models.SysSession)
+		sysSession.UserName = adminName
+		sysSession.SysUserId = sysUser.AdminId
+		sysSession.ExpiredTime = time.Now().AddDate(0, 0, 90)
+		sysSession.IsRemember = 1
+		sysSession.CreatedTime = time.Now()
+		sysSession.LastUpdatedTime = time.Now()
+		sysSession.AccessToken = token
+		if e = models.AddSysSession(sysSession); e != nil {
+			err = fmt.Errorf("获取失败,新增session失败, err: " + e.Error())
+			return
+		}
+	}
+
+	login.Authorization = token
+	login.Authorization = "authorization=" + token + "$account=" + account
+	login.RealName = sysUser.RealName
+	login.AdminId = sysUser.AdminId
+	login.AdminName = sysUser.AdminName
+	login.RoleName = sysUser.RoleName
+	login.SysRoleTypeCode = sysUser.RoleTypeCode //系统角色编码
+	login.RoleTypeCode = sysUser.RoleTypeCode
+	login.Authority = sysUser.Authority
+
+	// 判断实际的角色类型
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_GROUP {
+		login.RoleTypeCode = utils.ROLE_TYPE_CODE_FICC_SELLER
+	}
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_TEAM {
+		login.RoleTypeCode = utils.ROLE_TYPE_CODE_FICC_SELLER
+	}
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_DEPARTMENT {
+		login.RoleTypeCode = utils.ROLE_TYPE_CODE_FICC_SELLER
+	}
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_GROUP {
+		login.RoleTypeCode = utils.ROLE_TYPE_CODE_RAI_SELLER
+	}
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_DEPARTMENT {
+		login.RoleTypeCode = utils.ROLE_TYPE_CODE_RAI_SELLER
+	}
+	if sysUser.RoleName == utils.ROLE_NAME_FICC_DIRECTOR {
+		login.RoleTypeCode = utils.ROLE_TYPE_CODE_FICC_SELLER
+	}
+
+	// 角色产品ID
+	productId := GetProductId(sysUser.RoleTypeCode)
+	productIdName := map[int]string{
+		0:                             "admin",
+		utils.COMPANY_PRODUCT_FICC_ID: utils.COMPANY_PRODUCT_FICC_NAME,
+		utils.COMPANY_PRODUCT_RAI_ID:  utils.COMPANY_PRODUCT_RAI_NAME,
+	}
+	login.ProductName = productIdName[productId]
+
+	// 新增登录记录
+	{
+		record := new(models.SysUserLoginRecord)
+		record.Uid = sysUser.AdminId
+		record.UserName = adminName
+		record.Ip, _ = utils.GetLocalIP()
+		record.Stage = "login"
+		record.CreateTime = time.Now()
+		go models.AddSysUserLoginRecord(record)
+	}
+	return
+}
+
+// GetProductId 根据角色类型获取对应的产品ID
+func GetProductId(roleTypeCode string) (productId int) {
+	if roleTypeCode == utils.ROLE_TYPE_CODE_FICC_SELLER ||
+		roleTypeCode == utils.ROLE_TYPE_CODE_FICC_ADMIN ||
+		roleTypeCode == utils.ROLE_TYPE_CODE_FICC_TEAM ||
+		roleTypeCode == utils.ROLE_TYPE_CODE_FICC_GROUP ||
+		roleTypeCode == utils.ROLE_TYPE_CODE_FICC_DEPARTMENT {
+		productId = 1
+	} else if roleTypeCode == utils.ROLE_TYPE_CODE_RAI_SELLER ||
+		roleTypeCode == utils.ROLE_TYPE_CODE_RAI_ADMIN ||
+		roleTypeCode == utils.ROLE_TYPE_CODE_RAI_GROUP ||
+		roleTypeCode == utils.ROLE_TYPE_CODE_RAI_DEPARTMENT {
+		productId = 2
+	} else {
+		productId = 0
+	}
+	return
+}

+ 121 - 0
utils/calculate.go

@@ -0,0 +1,121 @@
+package utils
+
+import (
+	"github.com/gonum/stat"
+	"github.com/shopspring/decimal"
+	"math"
+)
+
+// Series is a container for a series of data
+type Series []Coordinate
+
+// Coordinate holds the data in a series
+type Coordinate struct {
+	X, Y float64
+}
+
+// GetLinearResult 生成线性方程式
+func GetLinearResult(s []Coordinate) (gradient, intercept float64) {
+	if len(s) <= 1 {
+		return
+	}
+
+	// Placeholder for the math to be done
+	var sum [5]float64
+
+	// Loop over data keeping index in place
+	i := 0
+	for ; i < len(s); i++ {
+		sum[0] += s[i].X
+		sum[1] += s[i].Y
+		sum[2] += s[i].X * s[i].X
+		sum[3] += s[i].X * s[i].Y
+		sum[4] += s[i].Y * s[i].Y
+	}
+
+	// Find gradient and intercept
+	f := float64(i)
+	gradient = (f*sum[3] - sum[0]*sum[1]) / (f*sum[2] - sum[0]*sum[0])
+	intercept = (sum[1] / f) - (gradient * sum[0] / f)
+
+	//fmt.Println("gradient:", gradient, ";intercept:", intercept)
+	// Create the new regression series
+	//for j := 0; j < len(s); j++ {
+	//	regressions = append(regressions, Coordinate{
+	//		X: s[j].X,
+	//		Y: s[j].X*gradient + intercept,
+	//	})
+	//}
+
+	return
+}
+
+// ComputeCorrelation 通过一组数据获取相关系数R
+// 计算步骤
+// 1.分别计算两个序列的平均值Mx和My
+// 2.分别计算两个序列的标准偏差SDx和SDy	=> √{1/(n-1)*SUM[(Xi-Mx)²]}
+// 3.计算相关系数	=> SUM[(Xi-Mx)*(Yi-My)]/[(N-1)(SDx*SDy)]
+func ComputeCorrelation(sList []Coordinate) (r float64) {
+	var xBar, yBar float64
+	lenSList := len(sList)
+	// 必须两组数据及两组以上的数据才能计算
+	if lenSList < 2 {
+		return
+	}
+	decimalX := decimal.NewFromFloat(0)
+	decimalY := decimal.NewFromFloat(0)
+
+	// 计算两组数据X、Y的平均值
+	for _, coordinate := range sList {
+		decimalX = decimalX.Add(decimal.NewFromFloat(coordinate.X))
+		decimalY = decimalY.Add(decimal.NewFromFloat(coordinate.Y))
+	}
+	xBar, _ = decimalX.Div(decimal.NewFromInt(int64(lenSList))).Round(4).Float64()
+	yBar, _ = decimalY.Div(decimal.NewFromInt(int64(lenSList))).Round(4).Float64()
+	//fmt.Println(xBar)
+	//fmt.Println(yBar)
+
+	varXDeci := decimal.NewFromFloat(0)
+	varYDeci := decimal.NewFromFloat(0)
+	ssrDeci := decimal.NewFromFloat(0)
+
+	for _, coordinate := range sList {
+		// 分别计算X、Y的实际数据与平均值的差值
+		diffXXbarDeci := decimal.NewFromFloat(coordinate.X).Sub(decimal.NewFromFloat(xBar))
+		diffYYbarDeci := decimal.NewFromFloat(coordinate.Y).Sub(decimal.NewFromFloat(yBar))
+		ssrDeci = ssrDeci.Add(diffXXbarDeci.Mul(diffYYbarDeci))
+		//fmt.Println("i:", i, ";diffXXbar:", diffXXbarDeci.String(), ";diffYYbar:", diffYYbarDeci.String(), ";ssr:", ssrDeci.String())
+		varXDeci = varXDeci.Add(diffXXbarDeci.Mul(diffXXbarDeci))
+		varYDeci = varYDeci.Add(diffYYbarDeci.Mul(diffYYbarDeci))
+		//varY += diffYYbar ** 2
+	}
+	//当输入的两个数组完全相同时,计算相关系数会导致除以零的操作,从而产生 NaN(Not a Number)的结果。为了避免这种情况,可以在计算相关系数之前先进行一个判断,如果两个数组的标准差为零,则相关系数应为1
+	if varXDeci.IsZero() && varYDeci.IsZero() {
+		r = 1
+		return
+	}
+	sqrtVal, _ := varXDeci.Mul(varYDeci).Round(4).Float64()
+	//fmt.Println("sqrtVal:", sqrtVal)
+	sst := math.Sqrt(sqrtVal) // 平方根
+	//fmt.Println("sst:", sst)
+	// 如果计算出来的平方根是0,那么就直接返回,因为0不能作为除数
+	if sst == 0 {
+		return
+	}
+	r, _ = ssrDeci.Div(decimal.NewFromFloat(sst)).Round(4).Float64()
+
+	return
+}
+
+// CalculationDecisive 通过一组数据获取决定系数R2
+func CalculationDecisive(sList []Coordinate) (r2 float64) {
+	r := ComputeCorrelation(sList)
+	r2, _ = decimal.NewFromFloat(r).Mul(decimal.NewFromFloat(r)).Round(4).Float64()
+
+	return
+}
+
+// CalculateStandardDeviation 计算标准差
+func CalculateStandardDeviation(data []float64) float64 {
+	return stat.StdDev(data, nil)
+}

+ 1202 - 0
utils/common.go

@@ -0,0 +1,1202 @@
+package utils
+
+import (
+	"bufio"
+	"crypto/md5"
+	"crypto/sha1"
+	"encoding/base64"
+	"encoding/hex"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"github.com/shopspring/decimal"
+	"image"
+	"image/png"
+	"io"
+	"math"
+	"math/rand"
+	"net"
+	"net/http"
+	"os"
+	"os/exec"
+	"path"
+	"regexp"
+	"runtime"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// 随机数种子
+var rnd = rand.New(rand.NewSource(time.Now().UnixNano()))
+
+func GetRandString(size int) string {
+	allLetterDigit := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "!", "@", "#", "$", "%", "^", "&", "*"}
+	randomSb := ""
+	digitSize := len(allLetterDigit)
+	for i := 0; i < size; i++ {
+		randomSb += allLetterDigit[rnd.Intn(digitSize)]
+	}
+	return randomSb
+}
+
+func GetRandStringNoSpecialChar(size int) string {
+	allLetterDigit := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
+	randomSb := ""
+	digitSize := len(allLetterDigit)
+	for i := 0; i < size; i++ {
+		randomSb += allLetterDigit[rnd.Intn(digitSize)]
+	}
+	return randomSb
+}
+
+func StringsToJSON(str string) string {
+	rs := []rune(str)
+	jsons := ""
+	for _, r := range rs {
+		rint := int(r)
+		if rint < 128 {
+			jsons += string(r)
+		} else {
+			jsons += "\\u" + strconv.FormatInt(int64(rint), 16) // json
+		}
+	}
+	return jsons
+}
+
+// 序列化
+func ToString(v interface{}) string {
+	data, _ := json.Marshal(v)
+	return string(data)
+}
+
+// md5加密
+func MD5(data string) string {
+	m := md5.Sum([]byte(data))
+	return hex.EncodeToString(m[:])
+}
+
+// 获取数字随机字符
+func GetRandDigit(n int) string {
+	return fmt.Sprintf("%0"+strconv.Itoa(n)+"d", rnd.Intn(int(math.Pow10(n))))
+}
+
+// 获取随机数
+func GetRandNumber(n int) int {
+	return rnd.Intn(n)
+}
+
+func GetRandInt(min, max int) int {
+	if min >= max || min == 0 || max == 0 {
+		return max
+	}
+	return rand.Intn(max-min) + min
+}
+
+func GetToday(format string) string {
+	today := time.Now().Format(format)
+	return today
+}
+
+// 获取今天剩余秒数
+func GetTodayLastSecond() time.Duration {
+	today := GetToday(FormatDate) + " 23:59:59"
+	end, _ := time.ParseInLocation(FormatDateTime, today, time.Local)
+	return time.Duration(end.Unix()-time.Now().Local().Unix()) * time.Second
+}
+
+// 处理出生日期函数
+func GetBrithDate(idcard string) string {
+	l := len(idcard)
+	var s string
+	if l == 15 {
+		s = "19" + idcard[6:8] + "-" + idcard[8:10] + "-" + idcard[10:12]
+		return s
+	}
+	if l == 18 {
+		s = idcard[6:10] + "-" + idcard[10:12] + "-" + idcard[12:14]
+		return s
+	}
+	return GetToday(FormatDate)
+}
+
+// 处理性别
+func WhichSexByIdcard(idcard string) string {
+	var sexs = [2]string{"女", "男"}
+	length := len(idcard)
+	if length == 18 {
+		sex, _ := strconv.Atoi(string(idcard[16]))
+		return sexs[sex%2]
+	} else if length == 15 {
+		sex, _ := strconv.Atoi(string(idcard[14]))
+		return sexs[sex%2]
+	}
+	return "男"
+}
+
+// 截取小数点后几位
+func SubFloatToString(f float64, m int) string {
+	n := strconv.FormatFloat(f, 'f', -1, 64)
+	if n == "" {
+		return ""
+	}
+	if m >= len(n) {
+		return n
+	}
+	newn := strings.Split(n, ".")
+	if m == 0 {
+		return newn[0]
+	}
+	if len(newn) < 2 || m >= len(newn[1]) {
+		return n
+	}
+	return newn[0] + "." + newn[1][:m]
+}
+
+// 截取小数点后几位
+func SubFloatToFloat(f float64, m int) float64 {
+	newn := SubFloatToString(f, m)
+	newf, _ := strconv.ParseFloat(newn, 64)
+	return newf
+}
+
+// 截取小数点后几位
+func SubFloatToFloatStr(f float64, m int) string {
+	newn := SubFloatToString(f, m)
+	return newn
+}
+
+// 获取相差时间-年
+func GetYearDiffer(start_time, end_time string) int {
+	t1, _ := time.ParseInLocation("2006-01-02", start_time, time.Local)
+	t2, _ := time.ParseInLocation("2006-01-02", end_time, time.Local)
+	age := t2.Year() - t1.Year()
+	if t2.Month() < t1.Month() || (t2.Month() == t1.Month() && t2.Day() < t1.Day()) {
+		age--
+	}
+	return age
+}
+
+// 获取相差时间-秒
+func GetSecondDifferByTime(start_time, end_time time.Time) int64 {
+	diff := end_time.Unix() - start_time.Unix()
+	return diff
+}
+
+func FixFloat(f float64, m int) float64 {
+	newn := SubFloatToString(f+0.00000001, m)
+	newf, _ := strconv.ParseFloat(newn, 64)
+	return newf
+}
+
+// 将字符串数组转化为逗号分割的字符串形式  ["str1","str2","str3"] >>> "str1,str2,str3"
+func StrListToString(strList []string) (str string) {
+	if len(strList) > 0 {
+		for k, v := range strList {
+			if k == 0 {
+				str = v
+			} else {
+				str = str + "," + v
+			}
+		}
+		return
+	}
+	return ""
+}
+
+// 数据没有记录
+func ErrNoRow() string {
+	return "<QuerySeter> no row found"
+}
+
+// 判断文件是否存在
+func FileIsExist(filePath string) bool {
+	_, err := os.Stat(filePath)
+	return err == nil || os.IsExist(err)
+}
+
+// 获取图片扩展名
+func GetImgExt(file string) (ext string, err error) {
+	var headerByte []byte
+	headerByte = make([]byte, 8)
+	fd, err := os.Open(file)
+	if err != nil {
+		return "", err
+	}
+	defer fd.Close()
+	_, err = fd.Read(headerByte)
+	if err != nil {
+		return "", err
+	}
+	xStr := fmt.Sprintf("%x", headerByte)
+	switch {
+	case xStr == "89504e470d0a1a0a":
+		ext = ".png"
+	case xStr == "0000010001002020":
+		ext = ".ico"
+	case xStr == "0000020001002020":
+		ext = ".cur"
+	case xStr[:12] == "474946383961" || xStr[:12] == "474946383761":
+		ext = ".gif"
+	case xStr[:10] == "0000020000" || xStr[:10] == "0000100000":
+		ext = ".tga"
+	case xStr[:8] == "464f524d":
+		ext = ".iff"
+	case xStr[:8] == "52494646":
+		ext = ".ani"
+	case xStr[:4] == "4d4d" || xStr[:4] == "4949":
+		ext = ".tiff"
+	case xStr[:4] == "424d":
+		ext = ".bmp"
+	case xStr[:4] == "ffd8":
+		ext = ".jpg"
+	case xStr[:2] == "0a":
+		ext = ".pcx"
+	default:
+		ext = ""
+	}
+	return ext, nil
+}
+
+// 保存图片
+func SaveImage(path string, img image.Image) (err error) {
+	//需要保持的文件
+	imgfile, err := os.Create(path)
+	defer imgfile.Close()
+	// 以PNG格式保存文件
+	err = png.Encode(imgfile, img)
+	return err
+}
+
+// 下载图片
+func DownloadImage(imgUrl string) (filePath string, err error) {
+	imgPath := "./static/imgs/"
+	fileName := path.Base(imgUrl)
+	res, err := http.Get(imgUrl)
+	if err != nil {
+		fmt.Println("A error occurred!")
+		return
+	}
+	defer res.Body.Close()
+	// 获得get请求响应的reader对象
+	reader := bufio.NewReaderSize(res.Body, 32*1024)
+
+	filePath = imgPath + fileName
+	file, err := os.Create(filePath)
+	if err != nil {
+		return
+	}
+	// 获得文件的writer对象
+	writer := bufio.NewWriter(file)
+
+	written, _ := io.Copy(writer, reader)
+	fmt.Printf("Total length: %d \n", written)
+	return
+}
+
+// 保存base64数据为文件
+func SaveBase64ToFile(content, path string) error {
+	data, err := base64.StdEncoding.DecodeString(content)
+	if err != nil {
+		return err
+	}
+	f, err := os.Create(path)
+	defer f.Close()
+	if err != nil {
+		return err
+	}
+	f.Write(data)
+	return nil
+}
+
+func SaveBase64ToFileBySeek(content, path string) (err error) {
+	data, err := base64.StdEncoding.DecodeString(content)
+	exist, err := PathExists(path)
+	if err != nil {
+		return
+	}
+	if !exist {
+		f, err := os.Create(path)
+		if err != nil {
+			return err
+		}
+		n, _ := f.Seek(0, 2)
+		// 从末尾的偏移量开始写入内容
+		_, err = f.WriteAt([]byte(data), n)
+		defer f.Close()
+	} else {
+		f, err := os.OpenFile(path, os.O_WRONLY, 0644)
+		if err != nil {
+			return err
+		}
+		n, _ := f.Seek(0, 2)
+		// 从末尾的偏移量开始写入内容
+		_, err = f.WriteAt([]byte(data), n)
+		defer f.Close()
+	}
+
+	return nil
+}
+
+func PathExists(path string) (bool, error) {
+	_, err := os.Stat(path)
+	if err == nil {
+		return true, nil
+	}
+	if os.IsNotExist(err) {
+		return false, nil
+	}
+	return false, err
+}
+
+func StartIndex(page, pagesize int) int {
+	if page > 1 {
+		return (page - 1) * pagesize
+	}
+	return 0
+}
+func PageCount(count, pagesize int) int {
+	if count%pagesize > 0 {
+		return count/pagesize + 1
+	} else {
+		return count / pagesize
+	}
+}
+
+func TrimHtml(src string) string {
+	//将HTML标签全转换成小写
+	re, _ := regexp.Compile("\\<[\\S\\s]+?\\>")
+	src = re.ReplaceAllStringFunc(src, strings.ToLower)
+
+	re, _ = regexp.Compile("\\<img[\\S\\s]+?\\>")
+	src = re.ReplaceAllString(src, "[图片]")
+
+	re, _ = regexp.Compile("class[\\S\\s]+?>")
+	src = re.ReplaceAllString(src, "")
+	re, _ = regexp.Compile("\\<[\\S\\s]+?\\>")
+	src = re.ReplaceAllString(src, "")
+	return strings.TrimSpace(src)
+}
+
+//1556164246  ->  2019-04-25 03:50:46 +0000
+//timestamp
+
+func TimeToTimestamp() {
+	fmt.Println(time.Unix(1556164246, 0).Format("2006-01-02 15:04:05"))
+}
+
+func ToUnicode(text string) string {
+	textQuoted := strconv.QuoteToASCII(text)
+	textUnquoted := textQuoted[1 : len(textQuoted)-1]
+	return textUnquoted
+}
+
+func VersionToInt(version string) int {
+	version = strings.Replace(version, ".", "", -1)
+	n, _ := strconv.Atoi(version)
+	return n
+}
+func IsCheckInList(list []int, s int) bool {
+	for _, v := range list {
+		if v == s {
+			return true
+		}
+	}
+	return false
+
+}
+
+func round(num float64) int {
+	return int(num + math.Copysign(0.5, num))
+}
+
+func toFixed(num float64, precision int) float64 {
+	output := math.Pow(10, float64(precision))
+	return float64(round(num*output)) / output
+}
+
+// GetWilsonScore returns Wilson Score
+func GetWilsonScore(p, n float64) float64 {
+	if p == 0 && n == 0 {
+		return 0
+	}
+
+	return toFixed(((p+1.9208)/(p+n)-1.96*math.Sqrt(p*n/(p+n)+0.9604)/(p+n))/(1+3.8416/(p+n)), 2)
+}
+
+// 将中文数字转化成数字,比如 第三百四十五章,返回第345章 不支持一亿及以上
+func ChangeWordsToNum(str string) (numStr string) {
+	words := ([]rune)(str)
+	num := 0
+	n := 0
+	for i := 0; i < len(words); i++ {
+		word := string(words[i : i+1])
+		switch word {
+		case "万":
+			if n == 0 {
+				n = 1
+			}
+			n = n * 10000
+			num = num*10000 + n
+			n = 0
+		case "千":
+			if n == 0 {
+				n = 1
+			}
+			n = n * 1000
+			num += n
+			n = 0
+		case "百":
+			if n == 0 {
+				n = 1
+			}
+			n = n * 100
+			num += n
+			n = 0
+		case "十":
+			if n == 0 {
+				n = 1
+			}
+			n = n * 10
+			num += n
+			n = 0
+		case "一":
+			n += 1
+		case "二":
+			n += 2
+		case "三":
+			n += 3
+		case "四":
+			n += 4
+		case "五":
+			n += 5
+		case "六":
+			n += 6
+		case "七":
+			n += 7
+		case "八":
+			n += 8
+		case "九":
+			n += 9
+		case "零":
+		default:
+			if n > 0 {
+				num += n
+				n = 0
+			}
+			if num == 0 {
+				numStr += word
+			} else {
+				numStr += strconv.Itoa(num) + word
+				num = 0
+			}
+		}
+	}
+	if n > 0 {
+		num += n
+		n = 0
+	}
+	if num != 0 {
+		numStr += strconv.Itoa(num)
+	}
+	return
+}
+
+func Sha1(data string) string {
+	sha1 := sha1.New()
+	sha1.Write([]byte(data))
+	return hex.EncodeToString(sha1.Sum([]byte("")))
+}
+
+func GetVideoPlaySeconds(videoPath string) (playSeconds float64, err error) {
+	cmd := `ffmpeg -i ` + videoPath + `  2>&1 | grep 'Duration' | cut -d ' ' -f 4 | sed s/,//`
+	out, err := exec.Command("bash", "-c", cmd).Output()
+	if err != nil {
+		return
+	}
+	outTimes := string(out)
+	fmt.Println("outTimes:", outTimes)
+	if outTimes != "" {
+		timeArr := strings.Split(outTimes, ":")
+		h := timeArr[0]
+		m := timeArr[1]
+		s := timeArr[2]
+		hInt, err := strconv.Atoi(h)
+		if err != nil {
+			return playSeconds, err
+		}
+
+		mInt, err := strconv.Atoi(m)
+		if err != nil {
+			return playSeconds, err
+		}
+		s = strings.Trim(s, " ")
+		s = strings.Trim(s, "\n")
+		sInt, err := strconv.ParseFloat(s, 64)
+		if err != nil {
+			return playSeconds, err
+		}
+		playSeconds = float64(hInt)*3600 + float64(mInt)*60 + float64(sInt)
+	}
+	return
+}
+
+func GetMaxTradeCode(tradeCode string) (maxTradeCode string, err error) {
+	tradeCode = strings.Replace(tradeCode, "W", "", -1)
+	tradeCode = strings.Trim(tradeCode, " ")
+	tradeCodeInt, err := strconv.Atoi(tradeCode)
+	if err != nil {
+		return
+	}
+	tradeCodeInt = tradeCodeInt + 1
+	maxTradeCode = fmt.Sprintf("W%06d", tradeCodeInt)
+	return
+}
+
+// excel日期字段格式化 yyyy-mm-dd
+func ConvertToFormatDay(excelDaysString string) string {
+	// 2006-01-02 距离 1900-01-01的天数
+	baseDiffDay := 38719 //在网上工具计算的天数需要加2天,什么原因没弄清楚
+	curDiffDay := excelDaysString
+	b, _ := strconv.Atoi(curDiffDay)
+	// 获取excel的日期距离2006-01-02的天数
+	realDiffDay := b - baseDiffDay
+	//fmt.Println("realDiffDay:",realDiffDay)
+	// 距离2006-01-02 秒数
+	realDiffSecond := realDiffDay * 24 * 3600
+	//fmt.Println("realDiffSecond:",realDiffSecond)
+	// 2006-01-02 15:04:05距离1970-01-01 08:00:00的秒数 网上工具可查出
+	baseOriginSecond := 1136185445
+	resultTime := time.Unix(int64(baseOriginSecond+realDiffSecond), 0).Format("2006-01-02")
+	return resultTime
+}
+
+func CheckPwd(pwd string) bool {
+	compile := `([0-9a-z]+){6,12}|(a-z0-9]+){6,12}`
+	reg := regexp.MustCompile(compile)
+	flag := reg.MatchString(pwd)
+	return flag
+}
+
+func GetMonthStartAndEnd(myYear string, myMonth string) (startDate, endDate string) {
+	// 数字月份必须前置补零
+	if len(myMonth) == 1 {
+		myMonth = "0" + myMonth
+	}
+	yInt, _ := strconv.Atoi(myYear)
+
+	timeLayout := "2006-01-02 15:04:05"
+	loc, _ := time.LoadLocation("Local")
+	theTime, _ := time.ParseInLocation(timeLayout, myYear+"-"+myMonth+"-01 00:00:00", loc)
+	newMonth := theTime.Month()
+
+	t1 := time.Date(yInt, newMonth, 1, 0, 0, 0, 0, time.Local).Format("2006-01-02")
+	t2 := time.Date(yInt, newMonth+1, 0, 0, 0, 0, 0, time.Local).Format("2006-01-02")
+	return t1, t2
+}
+
+// 移除字符串中的空格
+func TrimStr(str string) (str2 string) {
+	if str == "" {
+		return str
+	}
+	return strings.Replace(str, " ", "", -1)
+}
+
+// 字符串转换为time
+func StrTimeToTime(strTime string) time.Time {
+	timeLayout := "2006-01-02 15:04:05"  //转化所需模板
+	loc, _ := time.LoadLocation("Local") //重要:获取时区
+	resultTime, _ := time.ParseInLocation(timeLayout, strTime, loc)
+	return resultTime
+}
+
+// 字符串类型时间转周几
+func StrDateTimeToWeek(strTime string) string {
+	var WeekDayMap = map[string]string{
+		"Monday":    "周一",
+		"Tuesday":   "周二",
+		"Wednesday": "周三",
+		"Thursday":  "周四",
+		"Friday":    "周五",
+		"Saturday":  "周六",
+		"Sunday":    "周日",
+	}
+	var ctime = StrTimeToTime(strTime).Format("2006-01-02")
+	startday, _ := time.ParseInLocation("2006-01-02", ctime, time.Local)
+	staweek_int := startday.Weekday().String()
+	return WeekDayMap[staweek_int]
+}
+
+// 时间格式转年月日字符串
+func TimeToStrYmd(time2 time.Time) string {
+	var Ymd string
+	year := time2.Year()
+	month := time2.Format("1")
+	day1 := time.Now().Day()
+	Ymd = strconv.Itoa(year) + "年" + month + "月" + strconv.Itoa(day1) + "日"
+	return Ymd
+}
+
+// 时间格式去掉时分秒
+func TimeRemoveHms(strTime string) string {
+	var Ymd string
+	var resultTime = StrTimeToTime(strTime)
+	year := resultTime.Year()
+	month := resultTime.Format("01")
+	day1 := resultTime.Day()
+	Ymd = strconv.Itoa(year) + "." + month + "." + strconv.Itoa(day1)
+	return Ymd
+}
+
+// 时间格式去掉时分秒
+func TimeRemoveHms2(strTime string) string {
+	var Ymd string
+	var resultTime = StrTimeToTime(strTime)
+	year := resultTime.Year()
+	month := resultTime.Format("01")
+	day1 := resultTime.Day()
+	Ymd = strconv.Itoa(year) + "-" + month + "-" + strconv.Itoa(day1)
+	return Ymd
+}
+
+// 文章上一次编辑时间
+func ArticleLastTime(strTime string) string {
+	var newTime string
+	stamp, _ := time.ParseInLocation("2006-01-02 15:04:05", strTime, time.Local)
+	diffTime := time.Now().Unix() - stamp.Unix()
+	if diffTime <= 60 {
+		newTime = "当前"
+	} else if diffTime < 60*60 {
+		newTime = strconv.FormatInt(diffTime/60, 10) + "分钟前"
+	} else if diffTime < 24*60*60 {
+		newTime = strconv.FormatInt(diffTime/(60*60), 10) + "小时前"
+	} else if diffTime < 30*24*60*60 {
+		newTime = strconv.FormatInt(diffTime/(24*60*60), 10) + "天前"
+	} else if diffTime < 12*30*24*60*60 {
+		newTime = strconv.FormatInt(diffTime/(30*24*60*60), 10) + "月前"
+	} else {
+		newTime = "1年前"
+	}
+	return newTime
+}
+
+// 人民币小写转大写
+func ConvertNumToCny(num float64) (str string, err error) {
+	strNum := strconv.FormatFloat(num*100, 'f', 0, 64)
+	sliceUnit := []string{"仟", "佰", "拾", "亿", "仟", "佰", "拾", "万", "仟", "佰", "拾", "元", "角", "分"}
+	// log.Println(sliceUnit[:len(sliceUnit)-2])
+	s := sliceUnit[len(sliceUnit)-len(strNum):]
+	upperDigitUnit := map[string]string{"0": "零", "1": "壹", "2": "贰", "3": "叁", "4": "肆", "5": "伍", "6": "陆", "7": "柒", "8": "捌", "9": "玖"}
+	for k, v := range strNum[:] {
+		str = str + upperDigitUnit[string(v)] + s[k]
+	}
+	reg, err := regexp.Compile(`零角零分$`)
+	str = reg.ReplaceAllString(str, "整")
+
+	reg, err = regexp.Compile(`零角`)
+	str = reg.ReplaceAllString(str, "零")
+
+	reg, err = regexp.Compile(`零分$`)
+	str = reg.ReplaceAllString(str, "整")
+
+	reg, err = regexp.Compile(`零[仟佰拾]`)
+	str = reg.ReplaceAllString(str, "零")
+
+	reg, err = regexp.Compile(`零{2,}`)
+	str = reg.ReplaceAllString(str, "零")
+
+	reg, err = regexp.Compile(`零亿`)
+	str = reg.ReplaceAllString(str, "亿")
+
+	reg, err = regexp.Compile(`零万`)
+	str = reg.ReplaceAllString(str, "万")
+
+	reg, err = regexp.Compile(`零*元`)
+	str = reg.ReplaceAllString(str, "元")
+
+	reg, err = regexp.Compile(`亿零{0, 3}万`)
+	str = reg.ReplaceAllString(str, "^元")
+
+	reg, err = regexp.Compile(`零元`)
+	str = reg.ReplaceAllString(str, "零")
+	return
+}
+
+// GetNowWeekMonday 获取本周周一的时间
+func GetNowWeekMonday() time.Time {
+	offset := int(time.Monday - time.Now().Weekday())
+	if offset == 1 { //正好是周日,但是按照中国人的理解,周日是一周最后一天,而不是一周开始的第一天
+		offset = -6
+	}
+	mondayTime := time.Now().AddDate(0, 0, offset)
+	mondayTime = time.Date(mondayTime.Year(), mondayTime.Month(), mondayTime.Day(), 0, 0, 0, 0, mondayTime.Location())
+	return mondayTime
+}
+
+// GetNowWeekLastDay 获取本周最后一天的时间
+func GetNowWeekLastDay() time.Time {
+	offset := int(time.Monday - time.Now().Weekday())
+	if offset == 1 { //正好是周日,但是按照中国人的理解,周日是一周最后一天,而不是一周开始的第一天
+		offset = -6
+	}
+	firstDayTime := time.Now().AddDate(0, 0, offset)
+	firstDayTime = time.Date(firstDayTime.Year(), firstDayTime.Month(), firstDayTime.Day(), 0, 0, 0, 0, firstDayTime.Location()).AddDate(0, 0, 6)
+	lastDayTime := time.Date(firstDayTime.Year(), firstDayTime.Month(), firstDayTime.Day(), 23, 59, 59, 0, firstDayTime.Location())
+
+	return lastDayTime
+}
+
+// GetNowMonthFirstDay 获取本月第一天的时间
+func GetNowMonthFirstDay() time.Time {
+	nowMonthFirstDay := time.Date(time.Now().Year(), time.Now().Month(), 1, 0, 0, 0, 0, time.Now().Location())
+	return nowMonthFirstDay
+}
+
+// GetNowMonthLastDay 获取本月最后一天的时间
+func GetNowMonthLastDay() time.Time {
+	nowMonthLastDay := time.Date(time.Now().Year(), time.Now().Month(), 1, 0, 0, 0, 0, time.Now().Location()).AddDate(0, 1, -1)
+	nowMonthLastDay = time.Date(nowMonthLastDay.Year(), nowMonthLastDay.Month(), nowMonthLastDay.Day(), 23, 59, 59, 0, nowMonthLastDay.Location())
+	return nowMonthLastDay
+}
+
+// GetNowQuarterFirstDay 获取本季度第一天的时间
+func GetNowQuarterFirstDay() time.Time {
+	month := int(time.Now().Month())
+	var nowQuarterFirstDay time.Time
+	if month >= 1 && month <= 3 {
+		//1月1号
+		nowQuarterFirstDay = time.Date(time.Now().Year(), 1, 1, 0, 0, 0, 0, time.Now().Location())
+	} else if month >= 4 && month <= 6 {
+		//4月1号
+		nowQuarterFirstDay = time.Date(time.Now().Year(), 4, 1, 0, 0, 0, 0, time.Now().Location())
+	} else if month >= 7 && month <= 9 {
+		nowQuarterFirstDay = time.Date(time.Now().Year(), 7, 1, 0, 0, 0, 0, time.Now().Location())
+	} else {
+		nowQuarterFirstDay = time.Date(time.Now().Year(), 10, 1, 0, 0, 0, 0, time.Now().Location())
+	}
+	return nowQuarterFirstDay
+}
+
+// GetNowQuarterLastDay 获取本季度最后一天的时间
+func GetNowQuarterLastDay() time.Time {
+	month := int(time.Now().Month())
+	var nowQuarterLastDay time.Time
+	if month >= 1 && month <= 3 {
+		//03-31 23:59:59
+		nowQuarterLastDay = time.Date(time.Now().Year(), 3, 31, 23, 59, 59, 0, time.Now().Location())
+	} else if month >= 4 && month <= 6 {
+		//06-30 23:59:59
+		nowQuarterLastDay = time.Date(time.Now().Year(), 6, 30, 23, 59, 59, 0, time.Now().Location())
+	} else if month >= 7 && month <= 9 {
+		//09-30 23:59:59
+		nowQuarterLastDay = time.Date(time.Now().Year(), 9, 30, 23, 59, 59, 0, time.Now().Location())
+	} else {
+		//12-31 23:59:59
+		nowQuarterLastDay = time.Date(time.Now().Year(), 12, 31, 23, 59, 59, 0, time.Now().Location())
+	}
+	return nowQuarterLastDay
+}
+
+// GetNowHalfYearFirstDay 获取当前半年的第一天的时间
+func GetNowHalfYearFirstDay() time.Time {
+	month := int(time.Now().Month())
+	var nowHalfYearLastDay time.Time
+	if month >= 1 && month <= 6 {
+		//03-31 23:59:59
+		nowHalfYearLastDay = time.Date(time.Now().Year(), 1, 1, 0, 0, 0, 0, time.Now().Location())
+	} else {
+		//12-31 23:59:59
+		nowHalfYearLastDay = time.Date(time.Now().Year(), 7, 1, 0, 0, 0, 0, time.Now().Location())
+	}
+	return nowHalfYearLastDay
+}
+
+// GetNowHalfYearLastDay 获取当前半年的最后一天的时间
+func GetNowHalfYearLastDay() time.Time {
+	month := int(time.Now().Month())
+	var nowHalfYearLastDay time.Time
+	if month >= 1 && month <= 6 {
+		//03-31 23:59:59
+		nowHalfYearLastDay = time.Date(time.Now().Year(), 6, 30, 23, 59, 59, 0, time.Now().Location())
+	} else {
+		//12-31 23:59:59
+		nowHalfYearLastDay = time.Date(time.Now().Year(), 12, 31, 23, 59, 59, 0, time.Now().Location())
+	}
+	return nowHalfYearLastDay
+}
+
+// GetNowYearFirstDay 获取当前年的最后一天的时间
+func GetNowYearFirstDay() time.Time {
+	//12-31 23:59:59
+	nowYearFirstDay := time.Date(time.Now().Year(), 1, 1, 0, 0, 0, 0, time.Now().Location())
+	return nowYearFirstDay
+}
+
+// GetNowYearLastDay 获取当前年的最后一天的时间
+func GetNowYearLastDay() time.Time {
+	//12-31 23:59:59
+	nowYearLastDay := time.Date(time.Now().Year(), 12, 31, 23, 59, 59, 0, time.Now().Location())
+	return nowYearLastDay
+}
+
+// CalculationDate 计算两个日期之间相差n年m月y天
+func CalculationDate(startDate, endDate time.Time) (beetweenDay string, err error) {
+	//startDate := time.Date(2021, 3, 28, 0, 0, 0, 0, time.Now().Location())
+	//endDate := time.Date(2022, 3, 31, 0, 0, 0, 0, time.Now().Location())
+	numYear := endDate.Year() - startDate.Year()
+
+	numMonth := int(endDate.Month()) - int(startDate.Month())
+
+	numDay := 0
+	//获取截止月的总天数
+	endDateDays := getMonthDay(endDate.Year(), int(endDate.Month()))
+
+	//获取截止月的前一个月
+	endDatePrevMonthDate := endDate.AddDate(0, -1, 0)
+	//获取截止日期的上一个月的总天数
+	endDatePrevMonthDays := getMonthDay(endDatePrevMonthDate.Year(), int(endDatePrevMonthDate.Month()))
+	//获取开始日期的的月份总天数
+	startDateMonthDays := getMonthDay(startDate.Year(), int(startDate.Month()))
+
+	//判断,截止月是否完全被选中,如果相等,那么代表截止月份全部天数被选择
+	if endDate.Day() == endDateDays {
+		numDay = startDateMonthDays - startDate.Day() + 1
+
+		//如果剩余天数正好与开始日期的天数是一致的,那么月份加1
+		if numDay == startDateMonthDays {
+			numMonth++
+			numDay = 0
+			//超过月份了,那么年份加1
+			if numMonth == 12 {
+				numYear++
+				numMonth = 0
+			}
+		}
+	} else {
+		numDay = endDate.Day() - startDate.Day() + 1
+	}
+
+	//天数小于0,那么向月份借一位
+	if numDay < 0 {
+		//向上一个月借一个月的天数
+		numDay += endDatePrevMonthDays
+
+		//总月份减去一个月
+		numMonth = numMonth - 1
+	}
+
+	//月份小于0,那么向年份借一位
+	if numMonth < 0 {
+		//向上一个年借12个月
+		numMonth += 12
+
+		//总年份减去一年
+		numYear = numYear - 1
+	}
+	if numYear < 0 {
+		err = errors.New("日期异常")
+		return
+	}
+
+	if numYear > 0 {
+		beetweenDay += fmt.Sprint(numYear, "年")
+	}
+	if numMonth > 0 {
+		beetweenDay += fmt.Sprint(numMonth, "个月")
+	}
+	if numDay > 0 {
+		beetweenDay += fmt.Sprint(numDay, "天")
+	}
+	return
+}
+
+// FormatPrice 格式化展示金额数字(财务金额展示,小数点前,每三位用,隔开)    1,234,567,898.55
+func FormatPrice(price float64) (str string) {
+	str = decimal.NewFromFloat(price).String()
+
+	length := len(str)
+	if length < 4 {
+		return str
+	}
+	arr := strings.Split(str, ".") //用小数点符号分割字符串,为数组接收
+	length1 := len(arr[0])
+	if length1 < 4 {
+		return str
+	}
+	count := (length1 - 1) / 3
+	for i := 0; i < count; i++ {
+		arr[0] = arr[0][:length1-(i+1)*3] + "," + arr[0][length1-(i+1)*3:]
+	}
+	return strings.Join(arr, ".") //将一系列字符串连接为一个字符串,之间用sep来分隔。
+}
+
+// getMonthDay 获取某年某月有多少天
+func getMonthDay(year, month int) (days int) {
+	if month != 2 {
+		if month == 4 || month == 6 || month == 9 || month == 11 {
+			days = 30
+
+		} else {
+			days = 31
+		}
+	} else {
+		if ((year%4) == 0 && (year%100) != 0) || (year%400) == 0 {
+			days = 29
+		} else {
+			days = 28
+		}
+	}
+	return
+}
+
+func SaveToFile(content, path string) error {
+	f, err := os.Create(path)
+	defer f.Close()
+	if err != nil {
+		return err
+	}
+	f.Write([]byte(content))
+	return nil
+}
+
+// HideString 给字段加***(从字符串中间替换,少于需要替换的长度,那么就补全*的长度)
+// src 待*字符串
+// hideLen 需要加*的长度
+func HideString(src string, hideLen int) string {
+	if src == "" {
+		return src
+	}
+	str := []rune(src)
+	if hideLen == 0 {
+		hideLen = 4
+	}
+	hideStr := ""
+	for i := 0; i < hideLen; i++ {
+		hideStr += "*"
+	}
+	strLen := len(str)
+
+	// 字符长度是1
+	if strLen == 1 {
+		return string(str[:1]) + hideStr
+	}
+	//字符长度大于1,但是小于等于需要隐藏的字符长度,那么就隐藏中间,保留前后各一位字符
+	if strLen <= hideLen+2 {
+		return string(str[:1]) + hideStr + string(str[strLen-1:])
+	}
+
+	subLen := strLen - hideLen //剩余需要展示的字符长度
+	decimal.NewFromFloat(2)
+	frontLenDecimal := decimal.NewFromInt(int64(subLen)).Div(decimal.NewFromInt(2)) //前面需要展示的字符的长度
+	frontLen := frontLenDecimal.Floor().IntPart()
+	return string(str[:frontLen]) + hideStr + string(str[frontLen+int64(hideLen):])
+}
+
+// 用户参会时间转换
+func GetAttendanceDetailSeconds(secondNum int) string {
+	var timeStr string
+	if secondNum <= 60 {
+		if secondNum < 10 {
+			timeStr = "0" + strconv.Itoa(secondNum) + "''"
+		} else {
+			timeStr = strconv.Itoa(secondNum) + "''"
+		}
+	} else {
+		var remainderStr string
+		remainderNum := secondNum % 60
+		minuteNum := secondNum / 60
+		if remainderNum < 10 {
+			remainderStr = "0" + strconv.Itoa(remainderNum) + "''"
+		} else {
+			remainderStr = strconv.Itoa(remainderNum) + "''"
+		}
+		if minuteNum < 10 {
+			timeStr = "0" + strconv.Itoa(minuteNum) + "'" + remainderStr
+		} else {
+			timeStr = strconv.Itoa(minuteNum) + "'" + remainderStr
+		}
+	}
+	return timeStr
+}
+
+// SubStr 截取字符串(中文)
+func SubStr(str string, subLen int) string {
+	strRune := []rune(str)
+	bodyRuneLen := len(strRune)
+	if bodyRuneLen > subLen {
+		bodyRuneLen = subLen
+	}
+	str = string(strRune[:bodyRuneLen])
+	return str
+}
+
+func GetLocalIP() (ip string, err error) {
+	addrs, err := net.InterfaceAddrs()
+	if err != nil {
+		return
+	}
+	for _, addr := range addrs {
+		ipAddr, ok := addr.(*net.IPNet)
+		if !ok {
+			continue
+		}
+		if ipAddr.IP.IsLoopback() {
+			continue
+		}
+		if !ipAddr.IP.IsGlobalUnicast() {
+			continue
+		}
+		return ipAddr.IP.String(), nil
+	}
+	return
+}
+
+func PrintLog(params ...string) {
+	_, file, line, ok := runtime.Caller(1)
+	fmt.Println(file, line, ok, params)
+}
+
+// InArrayByStr php中的in_array(判断String类型的切片中是否存在该string值)
+func InArrayByStr(idStrList []string, searchId string) (has bool) {
+	for _, id := range idStrList {
+		if id == searchId {
+			has = true
+			return
+		}
+	}
+	return
+}
+
+// InArrayByInt php中的in_array(判断Int类型的切片中是否存在该Int值)
+func InArrayByInt(idStrList []int, searchId int) (has bool) {
+	for _, id := range idStrList {
+		if id == searchId {
+			has = true
+			return
+		}
+	}
+	return
+}
+
+// GetOrmInReplace 获取orm的in查询替换?的方法
+func GetOrmInReplace(num int) string {
+	template := make([]string, num)
+	for i := 0; i < num; i++ {
+		template[i] = "?"
+	}
+	return strings.Join(template, ",")
+}
+
+// GetTimeSubDay 计算两个时间的自然日期差
+func GetTimeSubDay(t1, t2 time.Time) int {
+	var day int
+	swap := false
+	if t1.Unix() > t2.Unix() {
+		t1, t2 = t2, t1
+		swap = true
+	}
+
+	t1_ := t1.Add(time.Duration(t2.Sub(t1).Milliseconds()%86400000) * time.Millisecond)
+	day = int(t2.Sub(t1).Hours() / 24)
+	// 计算在t1+两个时间的余数之后天数是否有变化
+	if t1_.Day() != t1.Day() {
+		day += 1
+	}
+
+	if swap {
+		day = -day
+	}
+
+	return day
+}
+
+// GetFrequencyEndDay 根据当前时间和频度,获取该频度下最后一天的日期
+func GetFrequencyEndDay(currDate time.Time, frequency string) (endDate time.Time) {
+	switch frequency {
+	case "周度":
+		// 如果当前就是最后一天,那么就直接返回本日期就好了
+		if currDate.Weekday() == 0 {
+			endDate = currDate
+		} else {
+			endDate = currDate.AddDate(0, 0, 7-int(currDate.Weekday()))
+		}
+	case "旬度":
+		nextDay := currDate.AddDate(0, 0, 1)
+		if nextDay.Day() == 1 || currDate.Day() == 10 || currDate.Day() == 20 {
+			//如果是每月10、20、最后一天,那么就直接返回本日期就好了
+			endDate = currDate
+		} else {
+			if currDate.Day() < 10 { // 每月10号
+				endDate = time.Date(currDate.Year(), currDate.Month(), 10, 0, 0, 0, 0, time.Local)
+			} else if currDate.Day() < 20 { // 每月10号
+				endDate = time.Date(currDate.Year(), currDate.Month(), 20, 0, 0, 0, 0, time.Local)
+			} else {
+				// 下旬,多种可能,最大天数可能存在8天,9天,10天,11天,
+				tmpNextMonth := currDate.AddDate(0, 0, 13)
+				endDate = time.Date(tmpNextMonth.Year(), tmpNextMonth.Month(), 1, 0, 0, 0, 0, time.Local).AddDate(0, 0, -1)
+			}
+		}
+	case "月度":
+		nextDay := currDate.AddDate(0, 0, 1)
+		if nextDay.Day() == 1 {
+			//如果是每月的最后一天,那么就直接返回本日期就好了
+			endDate = currDate
+		} else {
+			endDate = time.Date(nextDay.Year(), nextDay.Month()+1, 1, 0, 0, 0, 0, time.Local).AddDate(0, 0, -1)
+		}
+	case "季度":
+		nextDay := currDate.AddDate(0, 0, 1)
+		if (nextDay.Month() == 1 || nextDay.Month() == 4 || nextDay.Month() == 7 || nextDay.Month() == 10) && nextDay.Day() == 1 {
+			//如果是每季的最后一天,那么就直接返回本日期就好了
+			endDate = currDate
+		} else {
+			if currDate.Month() < 4 { // 1季度
+				endDate = time.Date(currDate.Year(), 3, 31, 0, 0, 0, 0, time.Local)
+			} else if currDate.Month() < 7 { // 2季度
+				endDate = time.Date(currDate.Year(), 6, 30, 0, 0, 0, 0, time.Local)
+			} else if currDate.Month() < 10 { // 3季度
+				endDate = time.Date(currDate.Year(), 9, 30, 0, 0, 0, 0, time.Local)
+			} else {
+				// 4季度
+				endDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, time.Local)
+			}
+		}
+	case "年度":
+		endDate = time.Date(currDate.Year(), 12, 31, 0, 0, 0, 0, time.Local)
+	default:
+		endDate = currDate
+		return
+	}
+
+	return
+}
+
+// CheckFrequency 获取两个频度之间是否相对低高频
+// 大于0,代表左侧是高频(例:左侧:日度,右侧:周度)
+// 等于0,代表同频
+// 小于0,代表右侧是高频(例:左侧:周度,右侧:日度)
+func CheckFrequency(leftFrequency, rightFrequency string) int {
+	frequencyMap := map[string]int{
+		"年度":  0,
+		"半年度": 1,
+		"季度":  2,
+		"月度":  3,
+		"旬度":  4,
+		"周度":  5,
+		"日度":  6,
+	}
+
+	return frequencyMap[leftFrequency] - frequencyMap[rightFrequency]
+}
+
+// GetLikeKeyword
+//
+//	@Description: 获取sql查询中的like查询字段
+//	@author: Roc
+//	@datetime2023-10-23 14:46:32
+//	@param keyword string
+//	@return string
+func GetLikeKeyword(keyword string) string {
+	return `%` + keyword + `%`
+}

+ 119 - 0
utils/config.go

@@ -0,0 +1,119 @@
+package utils
+
+import (
+	"fmt"
+	beeLogger "github.com/beego/bee/v2/logger"
+	"github.com/beego/beego/v2/server/web"
+	"strconv"
+)
+
+var (
+	RunMode            string //运行模式
+	APP_NAME_CN        string
+	MYSQL_URL          string //数据库连接
+	MONGODB_URL        string //mongodb连接
+	MONGODB_COMMUNITY  string //mongodb库名
+	MONGODB_CREDENTIAL string
+)
+
+// 公共api内部服务调用
+var (
+	// EDB_LIB_URL 公共指标库
+	EDB_LIB_URL         string
+	APP_EDB_LIB_NAME_EN string
+	EDB_LIB_Md5_KEY     string
+	OpUserId            int    // 操作人id
+	OpUserRealName      string // 操作人真实名称
+)
+
+// 弘则
+const (
+	APPID  = "SJCrpOPvagscPxEv"
+	SECRET = "gLLjT72uFHQZEFtaFCuoZegD1z2ezfyX"
+)
+
+// 日志配置
+var (
+	LogPath     string //调用过程中的日志存放地址
+	LogFile     string
+	LogDataPath string //调用过程中图表相关的日志存放地址
+	LogDataFile string
+	BinLogPath  string //数据库相关的日志存放地址
+	BinLogFile  string
+	ApiLogPath  string //接口请求地址和接口返回值日志存放地址
+	ApiLogFile  string
+	LogMaxDays  int //日志最大保留天数
+)
+
+var (
+	EtaBridgeUrl       string // 桥接服务地址
+	EtaBridgeAppNameEn string // 桥接服务英文名称-鉴权用
+	EtaBridgeMd5Key    string // 桥接服务Md5密钥-鉴权用
+	EtaBridgeDesKey    string // 桥接服务Des密钥-解密数据用
+	SyncUserPath       string // 桥接服务-用户同步服务地址
+	SyncIndexPath      string // 桥接服务-指标同步服务地址
+)
+
+// AlarmMsgUrl 报警服务地址
+var AlarmMsgUrl string
+
+func init() {
+	tmpRunMode, err := web.AppConfig.String("run_mode")
+	if err != nil {
+		panic("配置文件读取run_mode错误 " + err.Error())
+	}
+	RunMode = tmpRunMode
+	fmt.Println("RunMode:", RunMode)
+	config, err := web.AppConfig.GetSection(RunMode)
+	if err != nil {
+		panic("配置文件读取错误 " + err.Error())
+	}
+	beeLogger.Log.Info(RunMode + " 模式")
+	// 项目中文名称
+	appNameCn, err := web.AppConfig.String("app_name_cn")
+	if err != nil {
+		panic(any("配置文件读取app_name_cn错误 " + err.Error()))
+	}
+	APP_NAME_CN = appNameCn
+	MYSQL_URL = config["mysql_url"]
+	MONGODB_URL = config["mongodb_url"]
+	MONGODB_COMMUNITY = config["mongodb_community"]
+	MONGODB_CREDENTIAL = config["mongodb_credential"]
+	// 公共api内部服务调用
+	{
+		// 公共指标库相关
+		EDB_LIB_URL = config["edb_lib_url"]
+		APP_EDB_LIB_NAME_EN = config["app_edb_lib_name_en"]
+		EDB_LIB_Md5_KEY = config["edb_lib_md5_key"]
+		opUserIdStr := config["op_user_id"]
+		if opUserIdStr != `` {
+			OpUserId, err = strconv.Atoi(opUserIdStr)
+			if err != nil {
+				panic("配置文件中操作人配置错误 " + err.Error())
+			}
+		}
+		OpUserRealName = config["op_user_real_name"]
+	}
+	//日志配置
+	{
+		LogPath = config["log_path"]
+		LogFile = config["log_file"]
+		LogDataPath = config["log_data_path"]
+		LogDataFile = config["log_data_file"]
+		BinLogPath = config["binlog_path"]
+		BinLogFile = config["binlog_file"]
+		ApiLogPath = config["apilog_path"]
+		ApiLogFile = config["apilog_file"]
+		logMaxDaysStr := config["log_max_day"]
+		LogMaxDays, _ = strconv.Atoi(logMaxDaysStr)
+	}
+
+	{
+		// 报警服务地址
+		AlarmMsgUrl = config["alarm_msg_url"]
+
+	}
+}
+
+//修改接口文档
+//http://8.136.199.33:8300/swagger/

+ 280 - 0
utils/constants.go

@@ -0,0 +1,280 @@
+package utils
+
+import "time"
+
+// 常量定义
+const (
+	FormatTime                 = "15:04:05"                //时间格式
+	FormatDate                 = "2006-01-02"              //日期格式
+	FormatDateUnSpace          = "20060102"                //日期格式
+	FormatDateTime             = "2006-01-02 15:04:05"     //完整时间格式
+	HlbFormatDateTime          = "2006-01-02_15:04:05.999" //完整时间格式
+	FormatDateTimeUnSpace      = "20060102150405"          //完整时间格式
+	FormatShortDateTimeUnSpace = "060102150405"            //省去开头两位年份的时间格式
+	FormatYearMonthDate        = "2006-01"                 //日期格式
+	FormatYearMonthUnSpace     = "200601"                  //年月的日期格式
+	PageSize15                 = 15                        //列表页每页数据量
+	PageSize5                  = 5
+	PageSize10                 = 10
+	PageSize20                 = 20
+	PageSize30                 = 30
+)
+
+// 数据来源渠道
+const (
+	DATA_SOURCE_THS                                  = iota + 1 //同花顺
+	DATA_SOURCE_WIND                                            //wind->2
+	DATA_SOURCE_PB                                              //彭博->3
+	DATA_SOURCE_CALCULATE                                       //指标运算->4
+	DATA_SOURCE_CALCULATE_LJZZY                                 //累计值转月->5
+	DATA_SOURCE_CALCULATE_TBZ                                   //同比值->6
+	DATA_SOURCE_CALCULATE_TCZ                                   //同差值->7
+	DATA_SOURCE_CALCULATE_NSZYDPJJS                             //N数值移动平均计算->8
+	DATA_SOURCE_MANUAL                                          //手工指标->9
+	DATA_SOURCE_LZ                                              //隆众->10
+	DATA_SOURCE_YS                                              //有色->11
+	DATA_SOURCE_CALCULATE_HBZ                                   //环比值->12
+	DATA_SOURCE_CALCULATE_HCZ                                   //环差值->13
+	DATA_SOURCE_CALCULATE_BP                                    //变频->14
+	DATA_SOURCE_GL                                              //钢联->15
+	DATA_SOURCE_ZZ                                              //郑商所->16
+	DATA_SOURCE_DL                                              //大商所->17
+	DATA_SOURCE_SH                                              //上期所->18
+	DATA_SOURCE_CFFEX                                           //中金所->19
+	DATA_SOURCE_SHFE                                            //上期能源->20
+	DATA_SOURCE_GIE                                             //欧洲天然气->21
+	DATA_SOURCE_CALCULATE_TIME_SHIFT                            //时间移位->22
+	DATA_SOURCE_CALCULATE_ZJPJ                                  //直接拼接->23
+	DATA_SOURCE_CALCULATE_LJZTBPJ                               //累计值同比拼接->24
+	DATA_SOURCE_LT                                              //路透->25
+	DATA_SOURCE_COAL                                            //中国煤炭市场网->26
+	DATA_SOURCE_PYTHON                                          //python代码->27
+	DATA_SOURCE_PB_FINANCE                                      //彭博财务数据->28
+	DATA_SOURCE_GOOGLE_TRAVEL                                   //谷歌出行->29
+	DATA_SOURCE_PREDICT                                         //普通预测指标->30
+	DATA_SOURCE_PREDICT_CALCULATE                               //预测指标运算->31
+	DATA_SOURCE_PREDICT_CALCULATE_TBZ                           //预测同比值->32
+	DATA_SOURCE_PREDICT_CALCULATE_TCZ                           //预测同差值->33
+	DATA_SOURCE_MYSTEEL_CHEMICAL                                //钢联化工->34
+	DATA_SOURCE_CALCULATE_CJJX                                  //超季节性->35
+	DATA_SOURCE_EIA_STEO                                        //eia steo报告->36
+	DATA_SOURCE_CALCULATE_NHCC                                  //计算指标(拟合残差)->37
+	DATA_SOURCE_COM_TRADE                                       //联合国商品贸易数据->38
+	DATA_SOURCE_PREDICT_CALCULATE_NSZYDPJJS                     //预测指标 - N数值移动平均计算 -> 39
+	DATA_SOURCE_CALCULATE_ADJUST                                //数据调整->40
+	DATA_SOURCE_SCI                                             //卓创数据(红桃三)->41
+	DATA_SOURCE_PREDICT_CALCULATE_LJZZY                         //预测指标 - 累计值转月->42
+	DATA_SOURCE_PREDICT_CALCULATE_HBZ                           //预测指标 - 环比值->43
+	DATA_SOURCE_PREDICT_CALCULATE_HCZ                           //预测指标 - 环差值->44
+	DATA_SOURCE_PREDICT_CALCULATE_BP                            //预测指标 - 变频->45
+	DATA_SOURCE_PREDICT_CALCULATE_TIME_SHIFT                    //预测指标 - 时间移位->46
+	DATA_SOURCE_PREDICT_CALCULATE_ZJPJ                          //预测指标 - 直接拼接->47
+	DATA_SOURCE_PREDICT_CALCULATE_LJZTBPJ                       //预测指标 - 累计值同比拼接->48
+	DATA_SOURCE_PREDICT_CALCULATE_CJJX                          //预测指标 - 超季节性->49
+	DATA_SOURCE_PREDICT_CALCULATE_NHCC                          //预测指标 - 计算指标(拟合残差)->50
+	DATA_SOURCE_CALCULATE_JP                                    //变频->51
+	DATA_SOURCE_CALCULATE_NH                                    //年化->52
+	DATA_SOURCE_CALCULATE_KSZS                                  //扩散指数->53
+	DATA_SOURCE_PREDICT_CALCULATE_JP                            //预测指标 - 计算指标(降频)->54
+	DATA_SOURCE_PREDICT_CALCULATE_NH                            //预测指标 - 计算指标(年化)->55
+	DATA_SOURCE_PREDICT_CALCULATE_KSZS                          //预测指标 - 计算指标(扩散指数)->56
+	DATA_SOURCE_BAIINFO                                         //百川盈孚 ->57
+	DATA_SOURCE_STOCK_PLANT                                     //存量装置 ->58
+	DATA_SOURCE_CALCULATE_CORRELATION                           //滚动相关性->59
+	DATA_SOURCE_NATIONAL_STATISTICS                             //国家统计局->60
+	DATA_SOURCE_CALCULATE_LJZZJ                                 //累计值转季 -> 61
+	DATA_SOURCE_CALCULATE_LJZ                                   //累计值 -> 62
+	DATA_SOURCE_CALCULATE_LJZNCZJ                               //累计值(年初至今) -> 63
+	DATA_SOURCE_PREDICT_CALCULATE_LJZZJ                         //预测指标 - 累计值转季->64
+	DATA_SOURCE_PREDICT_CALCULATE_LJZ                           //预测指标 - 累计值 -> 65
+	DATA_SOURCE_PREDICT_CALCULATE_LJZNCZJ                       //预测指标 - 累计值(年初至今) -> 66
+	DATA_SOURCE_CALCULATE_STANDARD_DEVIATION                    //标准差->67
+	DATA_SOURCE_CALCULATE_PERCENTILE                            //百分位图表->68
+	DATA_SOURCE_PREDICT_CALCULATE_STANDARD_DEVIATION            //预测标准差->69
+	DATA_SOURCE_PREDICT_CALCULATE_PERCENTILE                    //预测百分位->70
+	DATA_SOURCE_FUBAO                                           //富宝->71
+	DATA_SOURCE_CALCULATE_ZSXY                                  // 指数修匀->72
+	DATA_SOURCE_PREDICT_CALCULATE_ZSXY                          // 预测指数修匀->73
+	DATA_SOURCE_CALCULATE_ZDYFX                                 // 自定义分析->74
+	DATA_SOURCE_CALCULATE_RJZ                                   // 日均值计算->75
+	DATA_SOURCE_GFEX                                 = 78       // 广州期货交易所->78
+	DATA_SOURCE_ICPI                                 = 79       // ICPI消费价格指数->79
+)
+
+// 子数据来源渠道
+const (
+	DATA_SUB_SOURCE_EDB  = iota //经济数据库
+	DATA_SUB_SOURCE_DATE        //日期序列
+)
+
+// 指标来源的中文展示
+const (
+	DATA_SOURCE_NAME_THS                                  = `同花顺`               //同花顺
+	DATA_SOURCE_NAME_WIND                                 = `wind`              //wind
+	DATA_SOURCE_NAME_PB                                   = `彭博`                //彭博
+	DATA_SOURCE_NAME_CALCULATE                            = `指标运算`              //指标运算
+	DATA_SOURCE_NAME_CALCULATE_LJZZY                      = `累计值转月值`            //累计值转月
+	DATA_SOURCE_NAME_CALCULATE_TBZ                        = `同比值`               //同比值
+	DATA_SOURCE_NAME_CALCULATE_TCZ                        = `同差值`               //同差值
+	DATA_SOURCE_NAME_CALCULATE_NSZYDPJJS                  = `N数值移动平均计算`         //N数值移动平均计算
+	DATA_SOURCE_NAME_MANUAL                               = `手工数据`              //手工指标
+	DATA_SOURCE_NAME_LZ                                   = `隆众`                //隆众
+	DATA_SOURCE_NAME_YS                                   = `SMM`               //有色
+	DATA_SOURCE_NAME_CALCULATE_HBZ                        = `环比值`               //环比值->12
+	DATA_SOURCE_NAME_CALCULATE_HCZ                        = `环差值`               //环差值->13
+	DATA_SOURCE_NAME_CALCULATE_BP                         = `升频`                //变频,2023-2-10 13:56:01调整为"升频"->14
+	DATA_SOURCE_NAME_GL                                   = `钢联`                //钢联->15
+	DATA_SOURCE_NAME_ZZ                                   = `郑商所`               //郑商所->16
+	DATA_SOURCE_NAME_DL                                   = `大商所`               //大商所->17
+	DATA_SOURCE_NAME_SH                                   = `上期所`               //上期所->18
+	DATA_SOURCE_NAME_CFFEX                                = `中金所`               //中金所->19
+	DATA_SOURCE_NAME_SHFE                                 = `上期能源`              //上期能源->20
+	DATA_SOURCE_NAME_GIE                                  = `欧洲天然气`             //欧洲天然气->21
+	DATA_SOURCE_NAME_CALCULATE_TIME_SHIFT                 = `时间移位`              //时间移位->22
+	DATA_SOURCE_NAME_CALCULATE_ZJPJ                       = `直接拼接`              //直接拼接->23
+	DATA_SOURCE_NAME_CALCULATE_LJZTBPJ                    = `累计值同比拼接`           //累计值同比拼接->24
+	DATA_SOURCE_NAME_LT                                   = `路透`                //路透->25
+	DATA_SOURCE_NAME_COAL                                 = `中国煤炭网`             //煤炭网->26
+	DATA_SOURCE_NAME_PYTHON                               = `代码运算`              //python代码->27
+	DATA_SOURCE_NAME_PB_FINANCE                           = `彭博财务`              //彭博财务数据->28
+	DATA_SOURCE_NAME_GOOGLE_TRAVEL                        = `our world in data` //谷歌出行数据->29
+	DATA_SOURCE_NAME_PREDICT                              = `预测指标`              //普通预测指标->30
+	DATA_SOURCE_NAME_PREDICT_CALCULATE                    = `预测指标运算`            //预测指标运算->31
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_TBZ                = `预测同比`              //预测指标 - 同比值->32
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_TCZ                = `预测同差`              //预测指标 - 同差值->33
+	DATA_SOURCE_NAME_MYSTEEL_CHEMICAL                     = `钢联化工`              //钢联化工->34
+	DATA_SOURCE_NAME_CALCULATE_CJJX                       = `超季节性`              //超季节性->35
+	DATA_SOURCE_NAME_EIA_STEO                             = `EIA STERO报告`       //eia stero报告->36
+	DATA_SOURCE_NAME_CALCULATE_NHCC                       = `拟合残差`              //计算指标(拟合残差)->37
+	DATA_SOURCE_NAME_COM_TRADE                            = `UN`                //联合国商品贸易数据->38
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_NSZYDPJJS          = `预测N数值移动平均计算`       //预测指标 - N数值移动平均计算 -> 39
+	DATA_SOURCE_NAME_CALCULATE_ADJUST                     = `数据调整`              //数据调整->40
+	DATA_SOURCE_NAME_SCI                                  = `SCI`               //卓创数据(红桃三)->41
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_LJZZY              = `预测累计值转月值`          //预测指标 - 累计值转月->42
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_HBZ                = `预测环比值`             //预测指标 - 环比值->43
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_HCZ                = `预测环差值`             //预测指标 - 环差值->44
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_BP                 = `预测升频`              //预测指标 - 升频->45
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_TIME_SHIFT         = `预测时间移位`            //预测指标 - 时间移位->46
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_ZJPJ               = `预测直接拼接`            //预测指标 - 直接拼接->47
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_LJZTBPJ            = `预测累计值同比拼接`         //预测指标 - 累计值同比拼接->48
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_CJJX               = `预测超季节性`            //预测指标 - 超季节性->49
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_NHCC               = `预测拟合残差`            //预测指标 - 计算指标(拟合残差)->50
+	DATA_SOURCE_NAME_CALCULATE_JP                         = `降频`                //降频->51
+	DATA_SOURCE_NAME_CALCULATE_NH                         = `年化`                //年化->52
+	DATA_SOURCE_NAME_CALCULATE_KSZS                       = `扩散指数`              //扩散指数->53
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_JP                 = `预测降频`              //预测指标 - 计算指标(降频)->54
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_NH                 = `预测年化`              //预测指标 - 计算指标(年化)->55
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_KSZS               = `预测扩散指数`            //预测指标 - 计算指标(扩散指数)->56
+	DATA_SOURCE_NAME_BAIINFO                              = `百川盈孚`              //百川盈孚 ->57
+	DATA_SOURCE_NAME_STOCK_PLANT                          = `存量装置`              //存量装置 ->58
+	DATA_SOURCE_NAME_CALCULATE_CORRELATION                = `相关性计算`             //相关性计算->59
+	DATA_SOURCE_NAME_NATIONAL_STATISTICS                  = `国家统计局`             //国家统计局->60
+	DATA_SOURCE_NAME_CALCULATE_LJZZJ                      = `累计值转季值`            //累计值转季 -> 61
+	DATA_SOURCE_NAME_CALCULATE_LJZ                        = `累计值`               //累计值 -> 62
+	DATA_SOURCE_NAME_CALCULATE_LJZNCZJ                    = `年初至今累计值`           //累计值(年初至今) -> 63
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_LJZZJ              = `预测累计值转季值`          //预测指标 - 累计值转季->64
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_LJZ                = `预测累计值`             //预测指标 - 累计值 -> 65
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_LJZNCZJ            = `预测年初至今累计值`         //预测指标 - 累计值(年初至今) -> 66
+	DATA_SOURCE_NAME_CALCULATE_STANDARD_DEVIATION         = `标准差`               //标准差->67
+	DATA_SOURCE_NAME_CALCULATE_PERCENTILE                 = `百分位`               //百分位->68
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_STANDARD_DEVIATION = `预测标准差`             //预测标准差->69
+	DATA_SOURCE_NAME_PREDICT_CALCULATE_PERCENTILE         = `预测百分位`             //预测百分位->70
+	DATA_SOURCE_NAME_FUBAO                                = `富宝数据`              //富宝数据->71
+)
+
+// 基础数据初始化日期
+var (
+	BASE_START_DATE         = time.Now().AddDate(-30, 0, 0).Format(FormatDate)        //基础数据开始日期
+	BASE_END_DATE           = time.Now().AddDate(4, 0, 0).Format(FormatDate)          //基础数据结束日期
+	BASE_START_DATE_UnSpace = time.Now().AddDate(-30, 0, 0).Format(FormatDateUnSpace) //基础数据开始日期
+	BASE_END_DATE_UnSpace   = time.Now().AddDate(4, 0, 0).Format(FormatDateUnSpace)   //基础数据结束日期
+)
+
+const (
+	DATA_PREFIX  = "hz_data"
+	CHART_PREFIX = "hz_chart"
+)
+
+// 数据刷新频率
+const (
+	DATA_REFRESH        = 7 //7个单位,日/周/月/季度/年
+	DATA_END_DATE_LIMIT = 4 //数据结束日期为,当前日期,加上4年时间
+)
+
+const (
+	CACHE_EDB_DATA_ADD          = "CACHE_EDB_DATA_ADD_"
+	CACHE_EDB_DATA_REFRESH      = "CACHE_EDB_DATA_REFRESH_"
+	CACHE_WIND_URL              = "CACHE_WIND_URL"
+	CACHE_CHART_INFO_DATA       = "chart:info:data:"             //图表数据
+	CACHE_STOCK_PLANT_CALCULATE = "CACHE_STOCK_PLANT_CALCULATE_" // 库存装置减产计算
+)
+
+// 图表类型
+const (
+	CHART_SOURCE_DEFAULT             = 1
+	CHART_SOURCE_FUTURE_GOOD         = 2
+	CHART_SOURCE_CORRELATION         = 3 // 相关性图表
+	CHART_SOURCE_ROLLING_CORRELATION = 4 // 滚动相关性图表
+	CHART_SOURCE_FUTURE_GOOD_PROFIT  = 5 // 商品利润曲线
+)
+
+// MonthQuarterMap 月份与季度的map
+var MonthQuarterMap = map[int]int{
+	1:  1,
+	2:  1,
+	3:  1,
+	4:  2,
+	5:  2,
+	6:  2,
+	7:  3,
+	8:  3,
+	9:  3,
+	10: 4,
+	11: 4,
+	12: 4,
+}
+
+// FrequencyDaysMap 频度日期的map关系
+var FrequencyDaysMap = map[string]int{
+	"天": 1, "周": 7, "月": 30, "季": 90, "年": 365,
+}
+
+// 角色类型/类型编码
+const (
+	ROLE_TYPE_ADMIN       = "管理员"
+	ROLE_TYPE_FICC_ADMIN  = "ficc管理员"
+	ROLE_TYPE_FICC_SELLER = "ficc销售"
+	ROLE_TYPE_RAI_ADMIN   = "权益管理员"
+	ROLE_TYPE_RAI_SELLER  = "权益销售"
+
+	ROLE_TYPE_FICC_GROUP      = "ficc销售组长"
+	ROLE_TYPE_FICC_MANAGER    = "ficc销售主管"
+	ROLE_TYPE_RAI_GROUP       = "权益组长"
+	ROLE_TYPE_FICC_DEPARTMENT = "ficc部门经理"
+	ROLE_TYPE_RAI_DEPARTMENT  = "权益部门经理"
+	ROLE_TYPE_FICC_RESEARCHR  = "ficc研究员"
+	ROLE_TYPE_RAI_RESEARCHR   = "权益研究员"
+	ROLE_NAME_FICC_DIRECTOR   = "ficc销售经理" // 实际角色类型为ficc销售主管
+
+	ROLE_TYPE_CODE_ADMIN           = "admin"           //管理员
+	ROLE_TYPE_CODE_FICC_ADMIN      = "ficc_admin"      //ficc管理员
+	ROLE_TYPE_CODE_FICC_SELLER     = "ficc_seller"     //ficc销售
+	ROLE_TYPE_CODE_RAI_ADMIN       = "rai_admin"       //权益管理员
+	ROLE_TYPE_CODE_RAI_SELLER      = "rai_seller"      //权益销售
+	ROLE_TYPE_CODE_FICC_GROUP      = "ficc_group"      //ficc销售主管
+	ROLE_TYPE_CODE_RAI_GROUP       = "rai_group"       //ficc组长
+	ROLE_TYPE_CODE_FICC_DEPARTMENT = "ficc_department" //ficc部门经理
+	ROLE_TYPE_CODE_RAI_DEPARTMENT  = "rai_department"  //权益部门经理
+	ROLE_TYPE_CODE_FICC_RESEARCHR  = "ficc_researcher" //ficc研究员
+	ROLE_TYPE_CODE_RESEARCHR       = "researcher"      //ficc研究员(最早定义的)
+	ROLE_TYPE_CODE_RAI_RESEARCHR   = "rai_researcher"  //权益研究员
+	ROLE_TYPE_CODE_COMPLIANCE      = "compliance"      //合规角色
+	ROLE_TYPE_CODE_FINANCE         = "finance"         //财务角色
+	ROLE_TYPE_CODE_FICC_TEAM       = "ficc_team"       //ficc销售组长
+)
+const (
+	COMPANY_PRODUCT_FICC_ID   = 1
+	COMPANY_PRODUCT_FICC_NAME = "ficc"
+	COMPANY_PRODUCT_RAI_ID    = 2
+	COMPANY_PRODUCT_RAI_NAME  = "权益"
+)

+ 187 - 0
utils/des3.go

@@ -0,0 +1,187 @@
+// 加密工具类,用了3des和base64
+package utils
+
+import (
+	"bytes"
+	"crypto/cipher"
+	"crypto/des"
+	"encoding/base64"
+	"encoding/hex"
+	"errors"
+	"strings"
+)
+
+// des3 + base64 encrypt
+func DesBase64Encrypt(origData []byte, desKey string) []byte {
+	result, err := TripleDesEncrypt(origData, []byte(desKey))
+	if err != nil {
+		panic(any(err))
+	}
+	return []byte(base64.StdEncoding.EncodeToString(result))
+}
+
+func DesBase64Decrypt(crypted []byte, desKey string) []byte {
+	result, _ := base64.StdEncoding.DecodeString(string(crypted))
+	remain := len(result) % 8
+	if remain > 0 {
+		mod := 8 - remain
+		for i := 0; i < mod; i++ {
+			result = append(result, 0)
+		}
+	}
+	origData, err := TripleDesDecrypt(result, []byte(desKey))
+	if err != nil {
+		panic(any(err))
+	}
+	return origData
+}
+
+// 3DES加密
+func TripleDesEncrypt(origData, key []byte) ([]byte, error) {
+	block, err := des.NewTripleDESCipher(key)
+	if err != nil {
+		return nil, err
+	}
+	origData = PKCS5Padding(origData, block.BlockSize())
+	// origData = ZeroPadding(origData, block.BlockSize())
+	blockMode := cipher.NewCBCEncrypter(block, key[:8])
+	crypted := make([]byte, len(origData))
+	blockMode.CryptBlocks(crypted, origData)
+	return crypted, nil
+}
+
+// 3DES解密
+func TripleDesDecrypt(crypted, key []byte) ([]byte, error) {
+	block, err := des.NewTripleDESCipher(key)
+	if err != nil {
+		return nil, err
+	}
+	blockMode := cipher.NewCBCDecrypter(block, key[:8])
+	origData := make([]byte, len(crypted))
+	// origData := crypted
+	blockMode.CryptBlocks(origData, crypted)
+	origData = PKCS5UnPadding(origData)
+	// origData = ZeroUnPadding(origData)
+	return origData, nil
+}
+
+func ZeroPadding(ciphertext []byte, blockSize int) []byte {
+	padding := blockSize - len(ciphertext)%blockSize
+	padtext := bytes.Repeat([]byte{0}, padding)
+	return append(ciphertext, padtext...)
+}
+
+func ZeroUnPadding(origData []byte) []byte {
+	length := len(origData)
+	unpadding := int(origData[length-1])
+	return origData[:(length - unpadding)]
+}
+
+func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
+	padding := blockSize - len(ciphertext)%blockSize
+	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+	return append(ciphertext, padtext...)
+}
+
+func PKCS5UnPadding(origData []byte) []byte {
+	length := len(origData)
+	// 去掉最后一个字节 unpadding 次
+	unpadding := int(origData[length-1])
+	return origData[:(length - unpadding)]
+}
+
+// DES加密
+func DesEncrypt(content string, key string) string {
+	contents := []byte(content)
+	keys := []byte(key)
+	block, err := des.NewCipher(keys)
+	if err != nil {
+		return ""
+	}
+	contents = PKCS5Padding(contents, block.BlockSize())
+	blockMode := cipher.NewCBCEncrypter(block, keys)
+	crypted := make([]byte, len(contents))
+	blockMode.CryptBlocks(crypted, contents)
+	return byteToHexString(crypted)
+}
+
+func byteToHexString(bytes []byte) string {
+	str := ""
+	for i := 0; i < len(bytes); i++ {
+		sTemp := hex.EncodeToString([]byte{bytes[i]})
+		if len(sTemp) < 2 {
+			str += string(0)
+		}
+		str += strings.ToUpper(sTemp)
+	}
+	return str
+}
+
+// DES解密
+func DesDecrypt(content string, key string) string {
+	contentBytes, err := hex.DecodeString(content)
+	if err != nil {
+		return "字符串转换16进制数组失败" + err.Error()
+	}
+	keys := []byte(key)
+	block, err := des.NewCipher(keys)
+	if err != nil {
+		return "解密失败" + err.Error()
+	}
+	blockMode := cipher.NewCBCDecrypter(block, keys)
+	origData := contentBytes
+	blockMode.CryptBlocks(origData, contentBytes)
+	origData = ZeroUnPadding(origData)
+	return string(origData)
+}
+
+// DES ECB PKCK5Padding
+func EntryptDesECB(data, key []byte) (string, error) {
+	if len(key) > 8 {
+		key = key[:8]
+	}
+	block, err := des.NewCipher(key)
+	if err != nil {
+		return "", errors.New("des.NewCipher " + err.Error())
+	}
+	bs := block.BlockSize()
+	data = PKCS5Padding(data, bs)
+	if len(data)%bs != 0 {
+		return "", errors.New("EntryptDesECB Need a multiple of the blocksize")
+	}
+	out := make([]byte, len(data))
+	dst := out
+	for len(data) > 0 {
+		block.Encrypt(dst, data[:bs])
+		data = data[bs:]
+		dst = dst[bs:]
+	}
+	return base64.StdEncoding.EncodeToString(out), nil
+}
+
+func DecryptDESECB(d string, key []byte) ([]byte, error) {
+	data, err := base64.StdEncoding.DecodeString(d)
+	if err != nil {
+		return nil, errors.New("decodebase64 " + err.Error())
+	}
+	if len(key) > 8 {
+		key = key[:8]
+	}
+	block, err := des.NewCipher(key)
+	if err != nil {
+		return nil, errors.New("des.NewCipher " + err.Error())
+	}
+	bs := block.BlockSize()
+	if len(data)%bs != 0 {
+		return nil, errors.New("DecryptDES crypto/cipher: input not full blocks")
+	}
+	out := make([]byte, len(data))
+	dst := out
+	for len(data) > 0 {
+		block.Decrypt(dst, data[:bs])
+		data = data[bs:]
+		dst = dst[bs:]
+	}
+	out = PKCS5UnPadding(out)
+	return out, nil
+}

+ 43 - 0
utils/email.go

@@ -0,0 +1,43 @@
+package utils
+
+import (
+	"fmt"
+	"gopkg.in/gomail.v2"
+	"runtime"
+	"strings"
+)
+
+//发送邮件
+func SendEmail(title, content string, touser string) bool {
+	emailSource := ``
+	pc, _, line, ok := runtime.Caller(1)
+	if ok{
+		emailSource = fmt.Sprint( runtime.FuncForPC(pc).Name(), ",第", line, "行:")
+	}
+	content = fmt.Sprint(emailSource,"\n",content)
+
+	if RunMode == "debug" {
+		FileLog.Info(fmt.Sprint(title, ";", content))
+		return false
+	}
+	var arr []string
+	sub := strings.Index(touser, ";")
+	if sub >= 0 {
+		spArr := strings.Split(touser, ";")
+		for _, v := range spArr {
+			arr = append(arr, v)
+		}
+	} else {
+		arr = append(arr, touser)
+	}
+	m := gomail.NewMessage()
+	m.SetHeader("From", "317699326@qq.com ")
+	m.SetHeader("To", arr...)
+	m.SetHeader("Subject", title+" "+GetRandString(16))
+	m.SetBody("text/html", content)
+	d := gomail.NewDialer("smtp.qq.com", 587, "317699326@qq.com", "oqdypwfcvruwcbea")
+	if err := d.DialAndSend(m); err != nil {
+		return false
+	}
+	return true
+}

+ 45 - 0
utils/jwt.go

@@ -0,0 +1,45 @@
+package utils
+
+import (
+	"fmt"
+	"time"
+
+	"github.com/beego/beego/v2/adapter/logs"
+	"github.com/dgrijalva/jwt-go"
+)
+
+var (
+	KEY = []byte("5Mb5Gdmb5y")
+)
+
+// 发放token
+func GenToken(account string) string {
+	token := jwt.New(jwt.SigningMethodHS256)
+	token.Claims = &jwt.StandardClaims{
+		NotBefore: int64(time.Now().Unix()),
+		ExpiresAt: int64(time.Now().Unix() + 90*24*60*60),
+		Issuer:    "hongze_admin",
+		Subject:   account,
+	}
+	ss, err := token.SignedString(KEY)
+	if err != nil {
+		logs.Error(err)
+		return ""
+	}
+	return ss
+}
+
+// 校验token
+func CheckToken(account, token string) bool {
+	t, err := jwt.Parse(token, func(*jwt.Token) (interface{}, error) {
+		return KEY, nil
+	})
+	if err != nil {
+		fmt.Println(err.Error())
+		return false
+	}
+	if account != t.Claims.(jwt.MapClaims)["sub"] {
+		return false
+	}
+	return t.Valid
+}

+ 142 - 0
utils/logs.go

@@ -0,0 +1,142 @@
+package utils
+
+import (
+	"encoding/json"
+	"github.com/beego/beego/v2/core/logs"
+	"os"
+	"path"
+)
+
+const (
+	DefaultLogPath    = "./etalogs/filelog"
+	DefaultBinlogPath = "./etalogs/binlog"
+	DefaultDataPath   = "./etalogs/datalog"
+	DefaultApiLogPath = "./etalogs/apilog"
+)
+
+var FileLog *logs.BeeLogger
+var ApiLog *logs.BeeLogger
+var FileLogData *logs.BeeLogger
+var Binlog *logs.BeeLogger
+
+func init() {
+	if LogMaxDays == 0 {
+		LogMaxDays = 30
+	}
+	logPath := LogPath
+	if logPath == "" {
+		logPath = DefaultLogPath
+	}
+	logFile := LogFile
+	if logFile == "" {
+		logFile = "filelog.log"
+	}
+	os.MkdirAll(logPath, os.ModePerm)
+
+	// 打开文件
+	logFileName := path.Join(logPath, logFile)
+	FileLog = logs.NewLogger(1000000)
+	logConf := getDefaultLogConfig()
+
+	logConf.FileName = logFileName
+	b, _ := json.Marshal(logConf)
+	FileLog.SetLogger(logs.AdapterFile, string(b))
+	FileLog.EnableFuncCallDepth(true)
+
+	initBinlog()
+	initApiLog()
+	initFileLogData()
+}
+
+type logConfig struct {
+	FileName string `json:"filename" description:"保存的文件名"`
+	MaxLines int    `json:"maxlines"  description:"每个文件保存的最大行数,默认值 1000000"`
+	MaxSize  int    `json:"maxsize" description:"每个文件保存的最大尺寸,默认值是 1 << 28, //256 MB"`
+	Daily    bool   `json:"daily" description:"是否按照每天 logrotate,默认是 true"`
+	MaxDays  int    `json:"maxdays" description:"文件最多保存多少天,默认保存 7 天"`
+	Rotate   bool   `json:"rotate" description:"是否开启 logrotate,默认是 true"`
+	Level    int    `json:"level" description:"日志保存的时候的级别,默认是 Trace 级别"`
+	Color    bool   `json:"color" description:"日志是否输出颜色"`
+	//Perm     string `json:"perm" description:"日志文件权限"`
+}
+
+func initBinlog() {
+	//binlog日志
+	//binlog日志
+	binlogPath := BinLogPath
+	if binlogPath == "" {
+		binlogPath = DefaultBinlogPath
+	}
+	binlogFile := BinLogFile
+	if binlogFile == "" {
+		binlogFile = "binlog.log"
+	}
+	os.MkdirAll(binlogPath, os.ModePerm)
+	logFileName := path.Join(binlogPath, binlogFile)
+	Binlog = logs.NewLogger(1000000)
+	logConf := getDefaultLogConfig()
+
+	logConf.FileName = logFileName
+	//logConf.MaxLines = 10000000
+	//logConf.Rotate = true
+	b, _ := json.Marshal(logConf)
+	Binlog.SetLogger(logs.AdapterFile, string(b))
+	Binlog.EnableFuncCallDepth(true)
+}
+
+func initApiLog() {
+	logPath := ApiLogPath
+	if logPath == "" {
+		logPath = DefaultApiLogPath
+	}
+	logFile := ApiLogFile
+	if logFile == "" {
+		logFile = "apilog.log"
+	}
+	os.MkdirAll(logPath, os.ModePerm)
+
+	// 打开文件
+	logFileName := path.Join(logPath, logFile)
+	ApiLog = logs.NewLogger(1000000)
+	logConf := getDefaultLogConfig()
+
+	logConf.FileName = logFileName
+	b, _ := json.Marshal(logConf)
+	ApiLog.SetLogger(logs.AdapterFile, string(b))
+	ApiLog.EnableFuncCallDepth(true)
+}
+
+func initFileLogData() {
+	logPath := LogDataPath
+	if logPath == "" {
+		logPath = DefaultDataPath
+	}
+	logFile := LogDataFile
+	if logFile == "" {
+		logFile = "datalog.log"
+	}
+	os.MkdirAll(logPath, os.ModePerm)
+
+	// 打开文件
+	logFileName := path.Join(logPath, logFile)
+	FileLogData = logs.NewLogger(1000000)
+	logConf := getDefaultLogConfig()
+
+	logConf.FileName = logFileName
+	b, _ := json.Marshal(logConf)
+	FileLogData.SetLogger(logs.AdapterFile, string(b))
+	FileLogData.EnableFuncCallDepth(true)
+}
+
+func getDefaultLogConfig() logConfig {
+	return logConfig{
+		FileName: "",
+		MaxLines: 10000000,
+		MaxSize:  1 << 28,
+		Daily:    true,
+		MaxDays:  LogMaxDays, //我就是喜欢31天,咋滴,不喜欢你就自己改-_-!
+		Rotate:   true,
+		Level:    logs.LevelTrace,
+		//Perm:     "",
+	}
+}