Roc 3 lat temu
rodzic
commit
f788070438
13 zmienionych plików z 1521 dodań i 0 usunięć
  1. 6 0
      .gitignore
  2. 6 0
      conf/app.conf
  3. 7 0
      controllers/home.go
  4. 10 0
      go.mod
  5. BIN
      hz_sync_logs
  6. 51 0
      main.go
  7. 8 0
      routers/router.go
  8. 418 0
      services/sync_log.go
  9. 18 0
      services/task.go
  10. 932 0
      utils/common.go
  11. 27 0
      utils/config.go
  12. 25 0
      utils/constants.go
  13. 13 0
      utils/logs.go

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+/.DS_Store
+rdlucklog/
+/test
+/.idea
+go.sum
+lastupdate.tmp

+ 6 - 0
conf/app.conf

@@ -0,0 +1,6 @@
+appname = hz_sync_log
+httpport = 8080
+runmode = dev
+autorender = false
+copyrequestbody = true
+EnableDocs = true

+ 7 - 0
controllers/home.go

@@ -0,0 +1,7 @@
+package controllers
+
+import beego "github.com/beego/beego/v2/server/web"
+
+type HomeController struct {
+	beego.Controller
+}

+ 10 - 0
go.mod

@@ -0,0 +1,10 @@
+module hongze/hz_sync_logs
+
+go 1.16
+
+require (
+	github.com/beego/bee/v2 v2.0.2
+	github.com/beego/beego/v2 v2.0.1
+	github.com/pkg/sftp v1.13.4
+	golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa
+)

BIN
hz_sync_logs


+ 51 - 0
main.go

@@ -0,0 +1,51 @@
+package main
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/core/logs"
+	beego "github.com/beego/beego/v2/server/web"
+	"github.com/beego/beego/v2/server/web/context"
+	"hongze/hz_sync_logs/services"
+	"runtime"
+)
+
+func main() {
+	if beego.BConfig.RunMode == "dev" {
+		beego.BConfig.WebConfig.DirectoryIndex = true
+		beego.BConfig.WebConfig.StaticDir["/swagger"] = "swagger"
+	}
+	beego.BConfig.RecoverFunc = Recover
+	go services.Task()
+	beego.Run()
+}
+
+func Recover(ctx *context.Context, conf *beego.Config) {
+	if err := recover(); err != nil {
+		if err == beego.ErrAbort {
+			return
+		}
+		if !beego.BConfig.RecoverPanic {
+			panic(err)
+		}
+		stack := ""
+		msg := fmt.Sprintf("The request url is  %v", ctx.Input.URL())
+		stack += msg + "</br>"
+		logs.Critical(msg)
+		msg = fmt.Sprintf("The request data is %v", string(ctx.Input.RequestBody))
+		stack += msg + "</br>"
+		logs.Critical(msg)
+		msg = fmt.Sprintf("Handler crashed with error %v", err)
+		stack += msg + "</br>"
+		logs.Critical(msg)
+		for i := 1; ; i++ {
+			_, file, line, ok := runtime.Caller(i)
+			if !ok {
+				break
+			}
+			logs.Critical(fmt.Sprintf("%s:%d", file, line))
+			stack = stack + fmt.Sprintln(fmt.Sprintf("%s:%d</br>", file, line))
+		}
+		//go utils.SendEmail(utils.APPNAME+"崩了"+time.Now().Format("2006-01-02 15:04:05"), stack, utils.EmailSendToUsers)
+	}
+	return
+}

+ 8 - 0
routers/router.go

@@ -0,0 +1,8 @@
+// @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

+ 418 - 0
services/sync_log.go

@@ -0,0 +1,418 @@
+package services
+
+import (
+	"context"
+	"fmt"
+	"github.com/pkg/sftp"
+	"golang.org/x/crypto/ssh"
+	"hongze/hz_sync_logs/utils"
+	"io"
+	"log"
+	"os"
+	"time"
+)
+
+func SyncYesterdayLogs(cont context.Context) (err error){
+	defer func() {
+		if err != nil {
+
+			//fmt.Println(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "发送消息至同花顺失败 ErrMsg:"+err.Error(), utils.EmailSendToUsers)
+			log.Printf(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "同步昨天日志失败 ErrMsg:"+err.Error())
+		}
+	}()
+	projectNameArr := []string{
+		"equity_admin",
+		"hongze_admin",
+		"hongze_advisory",
+		"hongze_api",
+		"hongze_cygx",
+		"hongze_mobile_admin",
+		"hongze_open_api",
+	}
+	sshHost := ``	//ssh的地址和端口(测试)
+	pwd := ``	//ssh密码(测试)
+
+	sshConfig := &ssh.ClientConfig{
+		User: "root",
+		Auth: []ssh.AuthMethod{
+			ssh.Password(pwd),
+		},
+		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
+		ClientVersion:   "",
+		Timeout:         10 * time.Second,
+	}
+
+	//建立与SSH服务器的链接
+	sshClient, err := ssh.Dial("tcp", sshHost, sshConfig)
+	if err != nil {
+		fmt.Println("ssh.Dial Err:" + err.Error())
+		return
+	}
+	defer sshClient.Close()
+	sftpClient, err := sftp.NewClient(sshClient)
+	if err != nil {
+		fmt.Println("sftp.NewClient Err:" + err.Error())
+		return
+	}
+	defer sftpClient.Close()
+	//获取当前目录
+	cwd, err := sftpClient.Getwd()
+	if err != nil {
+		fmt.Println("Getwd Err:" + err.Error())
+		return
+	}
+	fmt.Println("当前目录:" + cwd)
+
+	//显示文件/目录详情
+	fi, err := sftpClient.Lstat(cwd)
+	if err != nil {
+		fmt.Println("sftpClient.Lstat Err:" + err.Error())
+		return
+	}
+	fmt.Println(fi)
+	prefixDir := `/data/rdlucklog/`
+	//preSaveDirPath := `/Users/roc/go/src/hongze/hz_sync_logs/test/`
+	//dirCut := `/` //linux的目录分割符
+	preSaveDirPath := `E:\\hongze_logs\\`
+	dirCut := `\\`	//windows的目录分割符
+
+	yesterdayTime := time.Now().AddDate(0,0,-1)
+
+	for _,projectName := range projectNameArr{
+		serverFilePath := prefixDir + projectName
+		localFilePath := preSaveDirPath + projectName
+		//下载文件
+		err = syncLog(sftpClient, yesterdayTime, serverFilePath, localFilePath, dirCut)
+		if err != nil {
+			fmt.Println(serverFilePath+"下载失败,ERR:", err)
+		}
+
+	}
+	return
+}
+
+func syncLog(sftpClient *sftp.Client, dateTime time.Time, dataDirPath, saveDirPath, dirCut string) (err error) {
+	fileExt := `.api.log`
+
+	date := dateTime.Format(utils.FormatDateUnSpace)
+	logPath := dateTime.Format(utils.FormatDateUnSpaceMonth)
+
+	//服务器的文件路径
+	remoteFileName := dataDirPath + "/" + logPath + `/` + date + fileExt
+	remoteFile, err := sftpClient.Open(remoteFileName)
+	if err != nil {
+		fmt.Println("sftpClient.Open err:" + err.Error() + " ;remoteDirName:" + remoteFileName)
+		if err.Error() == "file does not exist" {
+			fmt.Println(remoteFileName + " file does not exist")
+			err = nil
+		} else {
+			fmt.Println("sftpClient.Open err:" + err.Error() + " ;remoteFileName:" + remoteFileName)
+		}
+		return
+	}
+	defer remoteFile.Close()
+
+	//本地文件路径
+	saveFileName := saveDirPath + dirCut  + logPath + dirCut
+
+	localFileName := date + fileExt
+	if !utils.FileIsExist(saveFileName) {
+		err = os.MkdirAll(saveFileName, 0766)
+		if err != nil {
+			fmt.Println("创建目录失败,Err:" + err.Error())
+			return
+		}
+	}
+	localFilePath := saveFileName + localFileName
+	fmt.Println("remoteFileName:" + remoteFileName)
+	fmt.Println("localFilePath:" + localFilePath)
+
+	localFile, err := os.Create(localFilePath)
+	if err != nil {
+		fmt.Println("create file err:" + err.Error())
+		return
+	}
+	defer localFile.Close()
+	n, err := io.Copy(localFile, remoteFile)
+	if err != nil {
+		fmt.Println("copy err:" + err.Error())
+		return
+	}
+	//获取远程文件大小
+	remoteFileInfo, err := sftpClient.Stat(remoteFileName)
+	if err != nil {
+		fmt.Println("sftpClient.Stat Err:" + err.Error())
+		log.Fatalln(err.Error())
+	}
+	log.Printf("文件下载成功[%s->%s]远程文件大小:%s,下载文件大小:%s", remoteFileName, localFilePath, formatFileSize(remoteFileInfo.Size()), formatFileSize(n))
+	//删除远程目录文件
+	err = sftpClient.Remove(remoteFileName)
+	if err != nil {
+		fmt.Println("sftpClient.Remove Err:" + err.Error())
+		return
+	}
+	return
+}
+
+func SyncHistoryLogs() {
+	projectNameArr := []string{
+		"equity_admin",
+		"hongze_admin",
+		"hongze_advisory",
+		"hongze_api",
+		"hongze_cygx",
+		"hongze_mobile_admin",
+		"hongze_open_api",
+	}
+	sshHost := ``	//ssh的地址和端口(测试)
+	pwd := ``	//ssh密码(测试)
+
+	sshConfig := &ssh.ClientConfig{
+		User: "root",
+		Auth: []ssh.AuthMethod{
+			ssh.Password(pwd),
+		},
+		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
+		ClientVersion:   "",
+		Timeout:         10 * time.Second,
+	}
+
+	//建立与SSH服务器的链接
+	sshClient, err := ssh.Dial("tcp", sshHost, sshConfig)
+	if err != nil {
+		fmt.Println("ssh.Dial Err:" + err.Error())
+		return
+	}
+	defer sshClient.Close()
+	sftpClient, err := sftp.NewClient(sshClient)
+	if err != nil {
+		fmt.Println("sftp.NewClient Err:" + err.Error())
+		return
+	}
+	defer sftpClient.Close()
+	//获取当前目录
+	cwd, err := sftpClient.Getwd()
+	if err != nil {
+		fmt.Println("Getwd Err:" + err.Error())
+		return
+	}
+	fmt.Println("当前目录:" + cwd)
+
+	//显示文件/目录详情
+	fi, err := sftpClient.Lstat(cwd)
+	if err != nil {
+		fmt.Println("sftpClient.Lstat Err:" + err.Error())
+		return
+	}
+	fmt.Println(fi)
+	prefixDir := `/data/rdlucklog/`
+	//preSaveDirPath := `/Users/roc/go/src/hongze/hz_sync_logs/test/`
+	//dirCut := `/` //linux的目录分割符
+	preSaveDirPath := `E:\\hongze_logs\\`
+	dirCut := `\\`	//windows的目录分割符
+
+	yesterdayTime := time.Now().AddDate(0,0,-1)
+
+	for _,projectName := range projectNameArr{
+		serverFilePath := prefixDir + projectName
+		localFilePath := preSaveDirPath + projectName
+		//下载文件
+		for i := 0; i < 22; i++ {
+			dateTime := yesterdayTime.AddDate(0, 0, -i)
+			err = syncLog(sftpClient, dateTime, serverFilePath, localFilePath, dirCut)
+			if err != nil {
+				fmt.Println(serverFilePath+"下载失败,ERR:", err)
+			}
+		}
+
+	}
+
+
+	return
+	{
+		dataDirPath := `/data/rdlucklog`
+		saveDirPath := `E:\\log\\`
+		fileExt := `.api.log`
+
+		logPath := time.Now().AddDate(0, -1, 0).Format("200601")
+		dateObj, _ := time.Parse("2006-01-02", "2021-10-01")
+		for _, proName := range projectNameArr {
+			for i := 0; i < 31; i++ {
+				date := dateObj.AddDate(0, 0, i).Format("20060102")
+				remoteDirName := dataDirPath + `/` + proName + `/` + logPath + `/` + date + fileExt
+				remoteFile, err := sftpClient.Open(remoteDirName)
+				if err != nil {
+					fmt.Println("sftpClient.Open err:" + err.Error() + " ;remoteDirName:" + remoteDirName)
+					if err.Error() == "file does not exist" {
+						continue
+					} else {
+						fmt.Println("sftpClient.Open err:" + err.Error() + " ;remoteDirName:" + remoteDirName)
+						return
+					}
+				}
+				defer remoteFile.Close()
+				saveFileName := saveDirPath + proName + `\\202110\\`
+				localFileName := date + fileExt
+				fmt.Println("saveFileName:", saveFileName)
+				if !utils.FileIsExist(saveFileName) {
+					err = os.MkdirAll(saveFileName, 777)
+					if err != nil {
+						fmt.Println("创建目录失败,Err:" + err.Error())
+						return
+					}
+				}
+				localFilePath := saveFileName + localFileName
+				fmt.Println("localFilePath:" + localFilePath)
+				localFile, err := os.Create(localFilePath)
+				if err != nil {
+					fmt.Println("create file err:" + err.Error())
+					return
+				}
+				defer localFile.Close()
+				n, err := io.Copy(localFile, remoteFile)
+				if err != nil {
+					fmt.Println("copy err:" + err.Error())
+					return
+				}
+				//获取远程文件大小
+				remoteFileInfo, err := sftpClient.Stat(remoteDirName)
+				if err != nil {
+					fmt.Println("sftpClient.Stat Err:" + err.Error())
+					log.Fatalln(err.Error())
+				}
+				log.Printf("文件下载成功[%s->%s]远程文件大小:%s,下载文件大小:%s", remoteDirName, localFileName, formatFileSize(remoteFileInfo.Size()), formatFileSize(n))
+				//删除远程目录文件
+				//err = sftpClient.Remove(remoteDirName)
+				//if err != nil {
+				//	fmt.Println("sftpClient.Remove Err:" + err.Error())
+				//	return
+				//}
+			}
+		}
+	}
+}
+
+func SyncLogs2() {
+	projectNameArr := []string{
+		"equity_admin",
+		//"hongze_admin",
+		//"hongze_api",
+		//"hongze_cygx",
+	}
+	sshConfig := &ssh.ClientConfig{
+		User: "root",
+		Auth: []ssh.AuthMethod{
+			ssh.Password("pwd"),
+		},
+		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
+		ClientVersion:   "",
+		Timeout:         10 * time.Second,
+	}
+
+	//建立与SSH服务器的链接
+	sshClient, err := ssh.Dial("tcp", "ip:port", sshConfig)
+	if err != nil {
+		fmt.Println("ssh.Dial Err:" + err.Error())
+		return
+	}
+	defer sshClient.Close()
+	sftpClient, err := sftp.NewClient(sshClient)
+	if err != nil {
+		fmt.Println("sftp.NewClient Err:" + err.Error())
+		return
+	}
+	defer sftpClient.Close()
+	//获取当前目录
+	cwd, err := sftpClient.Getwd()
+	if err != nil {
+		fmt.Println("Getwd Err:" + err.Error())
+		return
+	}
+	fmt.Println("当前目录:" + cwd)
+	//显示文件/目录详情
+	fi, err := sftpClient.Lstat(cwd)
+	if err != nil {
+		fmt.Println("sftpClient.Lstat Err:" + err.Error())
+		return
+	}
+	fmt.Println(fi)
+	//下载文件
+	{
+		dataDirPath := `/data/rdlucklog`
+		saveDirPath := `E:\\log\\`
+		fileExt := `.api.log`
+
+		logPath := time.Now().AddDate(0, -1, 0).Format("200601")
+		dateObj, _ := time.Parse("2006-01-02", "2021-10-01")
+		for _, proName := range projectNameArr {
+			for i := 0; i < 31; i++ {
+				date := dateObj.AddDate(0, 0, i).Format("20060102")
+				remoteDirName := dataDirPath + `/` + proName + `/` + logPath + `/` + date + fileExt
+				remoteFile, err := sftpClient.Open(remoteDirName)
+				if err != nil {
+					fmt.Println("sftpClient.Open err:" + err.Error() + " ;remoteDirName:" + remoteDirName)
+					if err.Error() == "file does not exist" {
+						continue
+					} else {
+						fmt.Println("sftpClient.Open err:" + err.Error() + " ;remoteDirName:" + remoteDirName)
+						return
+					}
+				}
+				defer remoteFile.Close()
+				saveFileName := saveDirPath + proName + `\\202110\\`
+				localFileName := date + fileExt
+				fmt.Println("saveFileName:", saveFileName)
+				if !utils.FileIsExist(saveFileName) {
+					err = os.MkdirAll(saveFileName, 777)
+					if err != nil {
+						fmt.Println("创建目录失败,Err:" + err.Error())
+						return
+					}
+				}
+				localFilePath := saveFileName + localFileName
+				fmt.Println("localFilePath:" + localFilePath)
+				localFile, err := os.Create(localFilePath)
+				if err != nil {
+					fmt.Println("create file err:" + err.Error())
+					return
+				}
+				defer localFile.Close()
+				n, err := io.Copy(localFile, remoteFile)
+				if err != nil {
+					fmt.Println("copy err:" + err.Error())
+					return
+				}
+				//获取远程文件大小
+				remoteFileInfo, err := sftpClient.Stat(remoteDirName)
+				if err != nil {
+					fmt.Println("sftpClient.Stat Err:" + err.Error())
+					log.Fatalln(err.Error())
+				}
+				log.Printf("文件下载成功[%s->%s]远程文件大小:%s,下载文件大小:%s", remoteDirName, localFileName, formatFileSize(remoteFileInfo.Size()), formatFileSize(n))
+				//删除远程目录文件
+				//err = sftpClient.Remove(remoteDirName)
+				//if err != nil {
+				//	fmt.Println("sftpClient.Remove Err:" + err.Error())
+				//	return
+				//}
+			}
+		}
+	}
+}
+
+// 字节的单位转换 保留两位小数
+func formatFileSize(s int64) (size string) {
+	if s < 1024 {
+		return fmt.Sprintf("%.2fB", float64(s)/float64(1))
+	} else if s < (1024 * 1024) {
+		return fmt.Sprintf("%.2fKB", float64(s)/float64(1024))
+	} else if s < (1024 * 1024 * 1024) {
+		return fmt.Sprintf("%.2fMB", float64(s)/float64(1024*1024))
+	} else if s < (1024 * 1024 * 1024 * 1024) {
+		return fmt.Sprintf("%.2fGB", float64(s)/float64(1024*1024*1024))
+	} else if s < (1024 * 1024 * 1024 * 1024 * 1024) {
+		return fmt.Sprintf("%.2fTB", float64(s)/float64(1024*1024*1024*1024))
+	} else { //if s < (1024 * 1024 * 1024 * 1024 * 1024 * 1024)
+		return fmt.Sprintf("%.2fEB", float64(s)/float64(1024*1024*1024*1024*1024))
+	}
+}

+ 18 - 0
services/task.go

@@ -0,0 +1,18 @@
+package services
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/task"
+)
+
+func Task() {
+	fmt.Println("task start")
+	//SyncYesterdayLogs(nil)
+
+	// 每日同步接口日志
+	syncYesterdayLogs := task.NewTask("SyncYesterdayLogs", "0 0 4 * * * ", SyncYesterdayLogs)
+	task.AddTask("每日同步接口日志", syncYesterdayLogs)
+
+	task.StartTask()
+	fmt.Println("task end")
+}

+ 932 - 0
utils/common.go

@@ -0,0 +1,932 @@
+package utils
+
+import (
+	"bufio"
+	"crypto/md5"
+	"crypto/sha1"
+	"encoding/base64"
+	"encoding/hex"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"image"
+	"image/png"
+	"io"
+	"math"
+	"math/rand"
+	"net/http"
+	"os"
+	"os/exec"
+	"path"
+	"regexp"
+	"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 ""
+}
+
+//Token
+func GetToken() string {
+	randStr := GetRandString(64)
+	token := MD5(randStr + Md5Key)
+	tokenLen := 64 - len(token)
+	return strings.ToUpper(token + GetRandString(tokenLen))
+}
+
+//数据没有记录
+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) {
+	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.Parse("2006-01-02", ctime)
+	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 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
+}
+
+// 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
+}

+ 27 - 0
utils/config.go

@@ -0,0 +1,27 @@
+package utils
+
+import (
+	beeLogger "github.com/beego/bee/v2/logger"
+	"github.com/beego/beego/v2/server/web"
+)
+
+var (
+	RunMode        string //运行模式
+	MYSQL_URL      string //数据库连接
+)
+
+
+func init() {
+	tmpRunMode, err := web.AppConfig.String("run_mode")
+	if err != nil {
+		panic("配置文件读取run_mode错误 " + err.Error())
+	}
+	RunMode = tmpRunMode
+	beeLogger.Log.Info(RunMode + " 模式")
+
+	if RunMode == "release" {
+
+	} else {
+
+	}
+}

+ 25 - 0
utils/constants.go

@@ -0,0 +1,25 @@
+package utils
+
+const (
+	Md5Key = "Ks@h64WJ#tcVgG8$&WlNfqvLAtMgpxWN"
+)
+
+//常量定义
+const (
+	FormatTime            = "15:04:05"                //时间格式
+	FormatDate            = "2006-01-02"              //日期格式
+	FormatDateUnSpace     = "20060102"                //日期格式
+	FormatDateUnSpaceMonth     = "200601"                //日期格式(到月)
+	FormatDateTime        = "2006-01-02 15:04:05"     //完整时间格式
+	HlbFormatDateTime     = "2006-01-02_15:04:05.999" //完整时间格式
+	FormatDateTimeUnSpace = "20060102150405"          //完整时间格式
+	PageSize15            = 15                        //列表页每页数据量
+	PageSize5             = 5
+	PageSize10            = 10
+	PageSize20            = 20
+	PageSize30            = 30
+)
+
+const (
+	APPNAME          = "弘则-日志同步"
+)

+ 13 - 0
utils/logs.go

@@ -0,0 +1,13 @@
+package utils
+
+import (
+	"github.com/beego/beego/v2/core/logs"
+)
+
+var FileLog *logs.BeeLogger
+
+func init() {
+	FileLog = logs.NewLogger(1000000)
+	FileLog.SetLogger(logs.AdapterFile, `{"filename":"./rdlucklog/hz_sync_log.log"}`)
+	FileLog.EnableFuncCallDepth(true)
+}