ソースを参照

新增逸诺数据初始化

tuoling805 1 年間 前
コミット
f9569e9a43

BIN
docs/逸诺初始化数据示例.xlsx


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

+ 18 - 0
models/edb_classify.go

@@ -0,0 +1,18 @@
+package models
+
+import "time"
+
+type EdbClassify struct {
+	ClassifyId      int       `orm:"column(classify_id);pk"`
+	ClassifyType    uint8     `description:"分类类型,0:普通指标分类,1:预测指标分类"`
+	ClassifyName    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"`
+}

+ 49 - 0
services/base.go

@@ -0,0 +1,49 @@
+package services
+
+import (
+	"encoding/json"
+	"fmt"
+	"hongze/hz_eta_data/models"
+	"hongze/hz_eta_data/utils"
+	"io/ioutil"
+	"net/http"
+	"strings"
+)
+
+// PostEdbLib 调用指标接口
+func PostEdbLib(param map[string]interface{}, method string) (resp *models.BaseResponse, 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
+	}
+	err = json.Unmarshal(result, &resp)
+	if err != nil {
+		return
+	}
+	return resp, nil
+}
+
+func HttpPost(url, postData string, params ...string) ([]byte, error) {
+	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)
+	defer resp.Body.Close()
+	b, err := ioutil.ReadAll(resp.Body)
+	fmt.Println("HttpPost:" + string(b))
+	return b, err
+}

+ 164 - 0
services/init_base_index.go

@@ -0,0 +1,164 @@
+package services
+
+import (
+	"fmt"
+	"github.com/xuri/excelize/v2"
+	"hongze/hz_eta_data/models"
+	"hongze/hz_eta_data/utils"
+	"os"
+	"path/filepath"
+)
+
+// 初始化基础指标数据
+func InitBaseIndexData() {
+	//读取excel
+	path, err := filepath.Abs(os.Args[0])
+	if err != nil {
+		fmt.Println(err)
+	}
+
+	dir := filepath.Dir(path)
+	fmt.Println(dir)
+	dataPath := dir + "/docs/逸诺初始化数据示例.xlsx"
+	f, err := excelize.OpenFile(dataPath)
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+	defer func() {
+		// Close the spreadsheet.
+		if err := f.Close(); err != nil {
+			fmt.Println(err)
+		}
+	}()
+	rows, err := f.GetRows("Sheet1")
+	if err != nil {
+		fmt.Println(err)
+		return
+	}
+	for rk, row := range rows {
+		if rk > 0 {
+			var classifyFirst, classifySecond, classifyThree, indexCode, indexName, unit, frequency, source string
+			for ck, colCell := range row {
+				switch ck {
+				case 0:
+					classifyFirst = colCell
+				case 1:
+					classifySecond = colCell
+				case 2:
+					classifySecond = colCell
+				case 3:
+					indexCode = colCell
+				case 4:
+					indexName = colCell
+				case 5:
+					frequency = colCell
+				case 6:
+					unit = colCell
+				case 7:
+					source = colCell
+				}
+				if classifyFirst != "" &&
+					classifySecond != "" &&
+					classifyThree != "" &&
+					indexCode != "" &&
+					indexName != "" &&
+					unit != "" &&
+					frequency != "" &&
+					source != "" {
+
+					method := "classify/get_or_add"
+					classifyFirstMap := make(map[string]interface{})
+					classifyFirstMap["ClassifyName"] = classifyFirst
+					classifyFirstMap["ParentId"] = 0
+					classifyFirstMap["Level"] = 0
+					classifyFirstMap["ClassifyType"] = 0
+					resp, err := PostEdbLib(classifyFirstMap, method)
+					if err != nil {
+						utils.FileLog.Info("初始化分类1失败:" + err.Error())
+						return
+					}
+					if resp.Ret != 200 {
+						utils.FileLog.Info("初始化分类1失败:" + resp.Msg + ";" + resp.ErrMsg)
+						return
+					}
+
+					classifySecondMap := make(map[string]interface{})
+					classifySecondMap["ClassifyName"] = classifySecond
+					classifySecondMap["ParentId"] = 0
+					classifySecondMap["Level"] = 0
+					classifySecondMap["ClassifyType"] = 0
+					resp, err = PostEdbLib(classifySecondMap, method)
+					if err != nil {
+						utils.FileLog.Info("初始化分类2失败:" + err.Error())
+						return
+					}
+					if resp.Ret != 200 {
+						utils.FileLog.Info("初始化分类2失败:" + resp.Msg + ";" + resp.ErrMsg)
+						return
+					}
+
+					classifyThreeMap := make(map[string]interface{})
+					classifyThreeMap["ClassifyName"] = classifyThree
+					classifyThreeMap["ParentId"] = 0
+					classifyThreeMap["Level"] = 0
+					classifyThreeMap["ClassifyType"] = 0
+					resp, err = PostEdbLib(classifyThreeMap, method)
+					if err != nil {
+						utils.FileLog.Info("初始化分类3失败:" + err.Error())
+						return
+					}
+					if resp.Ret != 200 {
+						utils.FileLog.Info("初始化分类3失败:" + resp.Msg + ";" + resp.ErrMsg)
+						return
+					}
+					classify := resp.Data.(*models.EdbClassify)
+					var classifyId int
+					if classify != nil {
+						classifyId = classify.ClassifyId
+					}
+					method = "edb_info/add"
+					indexMap := make(map[string]interface{})
+					indexMap["Source"] = IndexSourceMap[source]
+					indexMap["EdbCode"] = indexCode
+					indexMap["EdbName"] = indexName
+					indexMap["Frequency"] = frequency
+					indexMap["Unit"] = unit
+					indexMap["ClassifyId"] = classifyId
+					resp, err = PostEdbLib(indexMap, method)
+					if err != nil {
+						utils.FileLog.Info("初始化指标失败:" + err.Error())
+						return
+					}
+					if resp.Ret != 200 {
+						utils.FileLog.Info("初始化指标失败:" + resp.Msg + ";" + resp.ErrMsg)
+						return
+					}
+				}
+			}
+		}
+
+		fmt.Println()
+	}
+}
+
+/*
+// PostEdbLib 调用指标接口
+func PostEdbLib(param map[string]interface{}, method string) (resp *models.BaseResponse, 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
+	}
+	err = json.Unmarshal(result, &resp)
+	if err != nil {
+		return
+	}
+	return resp, nil
+}
+
+*/

+ 23 - 0
services/task.go

@@ -1 +1,24 @@
 package services
+
+import (
+	"fmt"
+	"hongze/hz_eta_data/utils"
+)
+
+var IndexSourceMap map[string]int
+
+func Task() {
+	fmt.Println("task start")
+	//path, err := filepath.Abs(os.Args[0])
+	//if err != nil {
+	//	fmt.Println(err)
+	//}
+	//
+	//dir := filepath.Dir(path)
+	//fmt.Println(dir)
+
+	IndexSourceMap = make(map[string]int)
+	IndexSourceMap["ths"] = utils.DATA_SOURCE_THS
+	IndexSourceMap["wind"] = utils.DATA_SOURCE_WIND
+	fmt.Println("task end")
+}

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

+ 1191 - 0
utils/common.go

@@ -0,0 +1,1191 @@
+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]
+}

+ 45 - 0
utils/config.go

@@ -0,0 +1,45 @@
+package utils
+
+import (
+	"fmt"
+	beeLogger "github.com/beego/bee/v2/logger"
+	"github.com/beego/beego/v2/server/web"
+)
+
+var (
+	RunMode string //运行模式
+)
+
+// 公共api内部服务调用
+var (
+	// EDB_LIB_URL 公共指标库
+	EDB_LIB_URL         string
+	APP_EDB_LIB_NAME_EN string
+	EDB_LIB_Md5_KEY     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 + " 模式")
+
+	// 公共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"]
+	}
+
+}
+
+//修改接口文档
+//http://8.136.199.33:8300/swagger/

+ 227 - 0
utils/constants.go

@@ -0,0 +1,227 @@
+package utils
+
+import "time"
+
+// 常量定义
+const (
+	FormatTime                 = "15:04:05"            //时间格式
+	FormatDate                 = "2006-01-02"          //日期格式
+	FormatDateUnSpace          = "20060102"            //日期格式
+	FormatDateTime             = "2006-01-02 15:04:05" //完整时间格式
+	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
+	DATA_SOURCE_PB                                              //彭博
+	DATA_SOURCE_CALCULATE                                       //指标运算
+	DATA_SOURCE_CALCULATE_LJZZY                                 //累计值转月
+	DATA_SOURCE_CALCULATE_TBZ                                   //同比值
+	DATA_SOURCE_CALCULATE_TCZ                                   //同差值
+	DATA_SOURCE_CALCULATE_NSZYDPJJS                             //N数值移动平均计算
+	DATA_SOURCE_MANUAL                                          //手工指标
+	DATA_SOURCE_LZ                                              //隆众
+	DATA_SOURCE_YS                                              //有色
+	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
+)
+
+// 指标来源的中文展示
+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,
+}

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

+ 57 - 0
utils/logs.go

@@ -0,0 +1,57 @@
+package utils
+
+import (
+	"encoding/json"
+	"github.com/beego/beego/v2/core/logs"
+	"os"
+)
+
+var FileLog *logs.BeeLogger
+var Binlog *logs.BeeLogger
+
+func init() {
+	FileLog = logs.NewLogger(1000000)
+	FileLog.SetLogger(logs.AdapterFile, `{"filename":"./rdlucklog/hz_eta_data.log"}`)
+	FileLog.EnableFuncCallDepth(true)
+
+	initBinlog()
+}
+
+func initBinlog() {
+	//binlog日志
+	binLogDir := `./binlog`
+	os.MkdirAll(binLogDir, os.ModePerm)
+	Binlog = logs.NewLogger(1000000)
+	logConfig := getDefaultLogConfig()
+	logConfig.FileName = "./binlog/binlog.log"
+	logConfig.MaxLines = 10000000
+	logConfig.Rotate = true
+	b, _ := json.Marshal(logConfig)
+	Binlog.SetLogger(logs.AdapterFile, string(b))
+	Binlog.EnableFuncCallDepth(true)
+}
+
+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 getDefaultLogConfig() logConfig {
+	return logConfig{
+		FileName: "",
+		MaxLines: 0,
+		MaxSize:  1 << 28,
+		Daily:    true,
+		MaxDays:  31, //我就是喜欢31天,咋滴,不喜欢你就自己改-_-!
+		Rotate:   true,
+		Level:    logs.LevelTrace,
+		//Perm:     "",
+	}
+}