Browse Source

Merge branch 'master' into feature/eta1.0.1_out_link

# Conflicts:
#	models/db.go
#	routers/router.go
xyxie 1 year ago
parent
commit
be7ff7e46c
64 changed files with 3985 additions and 663 deletions
  1. 5 4
      .gitignore
  2. 5 8
      controllers/business_conf.go
  3. 3 3
      controllers/data_manage/chart_classify.go
  4. 31 0
      controllers/data_manage/chart_info.go
  5. 5 4
      controllers/data_manage/edb_classify.go
  6. 96 0
      controllers/data_manage/edb_info.go
  7. 6 0
      controllers/data_manage/edb_info_calculate.go
  8. 31 0
      controllers/data_manage/future_good/future_good_chart_info.go
  9. 3 3
      controllers/data_manage/predict_edb_classify.go
  10. 42 1
      controllers/data_manage/predict_edb_info.go
  11. 103 6
      controllers/english_report/report.go
  12. 199 0
      controllers/eta_trial/questionnaire.go
  13. 140 0
      controllers/eta_trial/user.go
  14. 37 0
      controllers/ppt_english_group.go
  15. 37 0
      controllers/ppt_v2_group.go
  16. 99 3
      controllers/report.go
  17. 82 0
      controllers/semantic_analysis/sa_compare.go
  18. 134 47
      controllers/sys_admin.go
  19. 18 12
      controllers/sys_department.go
  20. 25 17
      controllers/sys_group.go
  21. 20 14
      controllers/sys_role.go
  22. 18 12
      controllers/sys_team.go
  23. 10 0
      controllers/sys_user.go
  24. 893 0
      controllers/user_login.go
  25. 3 1
      go.mod
  26. 5 159
      go.sum
  27. 12 0
      models/base.go
  28. 5 1
      models/company/company_config.go
  29. 13 0
      models/data_manage/edb_data_insert_config.go
  30. 5 168
      models/data_manage/edb_info_calculate.go
  31. 9 0
      models/data_manage/edb_source.go
  32. 10 0
      models/data_manage/predict_edb_conf.go
  33. 2 27
      models/data_manage/trade_analysis/trade_analysis.go
  34. 55 0
      models/data_manage/trade_analysis/trade_classify.go
  35. 14 2
      models/db.go
  36. 14 4
      models/english_report.go
  37. 41 0
      models/eta_trial/questionnaire_fill_record.go
  38. 80 0
      models/eta_trial/request.go
  39. 19 4
      models/report.go
  40. 18 0
      models/semantic_analysis/sa_compare.go
  41. 126 0
      models/system/admin_verify_code_record.go
  42. 5 0
      models/system/sys_admin.go
  43. 46 0
      models/system/sys_user.go
  44. 162 9
      routers/commentsRouter.go
  45. 13 0
      routers/router.go
  46. 48 0
      services/captcha_redis.go
  47. 10 2
      services/crm_eta.go
  48. 9 7
      services/data/chart_classify.go
  49. 16 8
      services/data/correlation/chart_info.go
  50. 13 13
      services/data/edb_classify.go
  51. 28 0
      services/data/predict_edb_info.go
  52. 61 44
      services/data/trade_analysis/trade_analysis.go
  53. 132 0
      services/eta_trial/questionnaire.go
  54. 573 0
      services/eta_trial/user.go
  55. 41 35
      services/excel/lucky_sheet.go
  56. 4 2
      services/oss.go
  57. 46 0
      services/ppt/ppt_english_group.go
  58. 47 0
      services/ppt/ppt_group.go
  59. 15 13
      services/report.go
  60. 98 26
      services/sms.go
  61. 123 0
      services/user_login.go
  62. 7 1
      utils/config.go
  63. 13 1
      utils/constants.go
  64. 2 2
      utils/des3.go

+ 5 - 4
.gitignore

@@ -6,12 +6,13 @@
 /conf/*.conf
 /binlog/*.log
 /*.pdf
-/hongze_admin.tar.gz
-/hongze_admin
+/eta_api.tar.gz
+/eta_api
 /static/searchKeywordCount.xlsx
 .DS_Store
 /doc/
 *.DS_Store
 /static/images/*.svg
-hz_eta_api.exe
-hz_eta_api.exe~
+eta_api.exe
+eta_api.exe~
+/static/tmpFile/*

+ 5 - 8
controllers/business_conf.go

@@ -16,6 +16,10 @@ type BusinessConfController struct {
 	BaseAuthController
 }
 
+type BusinessConfOpenController struct {
+	BaseCommonController
+}
+
 // Save
 // @Title 保存配置
 // @Description 保存配置
@@ -201,7 +205,7 @@ func (this *BusinessConfController) Fetch() {
 // @Description 商家编码加密
 // @Success 200 Ret=200 获取成功
 // @router /code_encrypt [get]
-func (this *BusinessConfController) CodeEncrypt() {
+func (this *BusinessConfOpenController) CodeEncrypt() {
 	br := new(models.BaseResponse).Init()
 	defer func() {
 		if br.ErrMsg == "" {
@@ -210,13 +214,6 @@ func (this *BusinessConfController) CodeEncrypt() {
 		this.Data["json"] = br
 		this.ServeJSON()
 	}()
-	sysUser := this.SysUser
-	if sysUser == nil {
-		br.Msg = "请登录"
-		br.ErrMsg = "请登录,SysUser Is Empty"
-		br.Ret = 408
-		return
-	}
 
 	res := ""
 	if utils.BusinessCode != "" {

+ 3 - 3
controllers/data_manage/chart_classify.go

@@ -345,9 +345,9 @@ func (this *ChartClassifyController) ChartClassifyItems() {
 	// 是否允许添加分类
 	canOpClassify := true
 	// 如果不是 超管 或者 ficc管理员 或者 ficc研究员,那么就没有权限
-	if !utils.InArrayByStr([]string{utils.ROLE_TYPE_CODE_ADMIN, utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_RESEARCHR, utils.ROLE_TYPE_CODE_FICC_RESEARCHR}, this.SysUser.RoleTypeCode) {
-		canOpClassify = false
-	}
+	//if !utils.InArrayByStr([]string{utils.ROLE_TYPE_CODE_ADMIN, utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_RESEARCHR, utils.ROLE_TYPE_CODE_FICC_RESEARCHR}, this.SysUser.RoleTypeCode) {
+	//	canOpClassify = false
+	//}
 	resp := data_manage.ChartClassifyListResp{
 		AllNodes:      nodeAll,
 		Language:      language,

+ 31 - 0
controllers/data_manage/chart_info.go

@@ -8,6 +8,7 @@ import (
 	"eta/eta_api/models/system"
 	"eta/eta_api/services"
 	"eta/eta_api/services/data"
+	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -241,6 +242,21 @@ func (this *ChartInfoController) ChartInfoAdd() {
 		go data_manage.AddChartInfoLog(chartLog)
 	}
 
+	// 试用平台更新用户累计新增图表数
+	adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取系统用户数据失败,Err:" + err.Error()
+		return
+	}
+	if utils.BusinessCode == utils.BusinessCodeSandbox && adminItem.DepartmentName == "ETA试用客户" {
+		go func() {
+			var r etaTrialService.EtaTrialUserReq
+			r.Mobile = adminItem.Mobile
+			_, _ = etaTrialService.UpdateUserChartNum(r)
+		}()
+	}
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"
@@ -2577,6 +2593,21 @@ func (this *ChartInfoController) CopyChartInfo() {
 		go data_manage.AddChartInfoLog(chartLog)
 	}
 
+	// 试用平台更新用户累计新增图表数
+	adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取系统用户数据失败,Err:" + err.Error()
+		return
+	}
+	if utils.BusinessCode == utils.BusinessCodeSandbox && adminItem.DepartmentName == "ETA试用客户" {
+		go func() {
+			var r etaTrialService.EtaTrialUserReq
+			r.Mobile = adminItem.Mobile
+			_, _ = etaTrialService.UpdateUserChartNum(r)
+		}()
+	}
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"

+ 5 - 4
controllers/data_manage/edb_classify.go

@@ -138,6 +138,7 @@ func (this *EdbClassifyController) ListV2() {
 	}
 	resp := new(data_manage.EdbClassifyListResp)
 	resp.AllNodes = nodeAll
+	resp.CanOpClassify = true
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"
@@ -714,10 +715,10 @@ func (this *EdbClassifyController) ItemsV2() {
 
 	// 是否允许添加一级分类
 	canOpClassify := true
-	button := data.GetEdbClassifyOpButton(this.SysUser, 0)
-	if !button.AddButton {
-		canOpClassify = false
-	}
+	//button := data.GetEdbClassifyOpButton(this.SysUser, 0)
+	//if !button.AddButton {
+	//	canOpClassify = false
+	//}
 
 	resp := data_manage.EdbClassifyListResp{
 		AllNodes:      nodeAll,

+ 96 - 0
controllers/data_manage/edb_info.go

@@ -10,6 +10,7 @@ import (
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/services/data"
 	"eta/eta_api/services/elastic"
+	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -1863,6 +1864,21 @@ func (this *EdbInfoController) EdbInfoAdd() {
 		return
 	}
 
+	// 试用平台更新用户累计新增指标数
+	adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取系统用户数据失败,Err:" + err.Error()
+		return
+	}
+	if utils.BusinessCode == utils.BusinessCodeSandbox && adminItem.DepartmentName == "ETA试用客户" {
+		go func() {
+			var r etaTrialService.EtaTrialUserReq
+			r.Mobile = adminItem.Mobile
+			_, _ = etaTrialService.UpdateUserIndexNum(r)
+		}()
+	}
+
 	//新增操作日志
 	{
 		edbLog := new(data_manage.EdbInfoLog)
@@ -2036,6 +2052,9 @@ func (this *EdbInfoController) EdbInfoEdit() {
 	//添加es
 	data.AddOrEditEdbInfoToEs(req.EdbInfoId)
 
+	// 修改关联的预测指标基础信息
+	go data.ModifyPredictEdbBaseInfoBySourceEdb(edbInfo)
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"
@@ -2142,6 +2161,11 @@ func (this *EdbInfoController) EdbEnInfoEdit() {
 	//添加es
 	data.AddOrEditEdbInfoToEs(req.EdbInfoId)
 
+	// 修改关联的预测指标基础信息
+	if edbInfo.EdbInfoType == 0 {
+		go data.ModifyPredictEdbEnBaseInfoBySourceEdb(edbInfo)
+	}
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"
@@ -4329,3 +4353,75 @@ func (this *EdbInfoController) EdbSourceList() {
 	br.Success = true
 	br.Msg = "获取成功"
 }
+
+// EdbSourceListByPython
+// @Title 指标来源列表
+// @Description 指标来源列表
+// @Param   IsBase   query   int  false	"是否为基础指标: 1-是"
+// @Success 200 {object} data_manage.EdbInfoListResp
+// @router /edb_source/list/python [get]
+func (this *EdbInfoController) EdbSourceListByPython() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	isBase, _ := this.GetInt("IsBase", 0)
+
+	cond := ``
+	if isBase > 0 {
+		cond = ` AND is_base = 1`
+	}
+	pars := make([]interface{}, 0)
+	list, e := data_manage.GetEdbSourceItemsByCondition(cond, pars, []string{}, "")
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取指标来源列表失败, Err: " + e.Error()
+		return
+	}
+
+	baseEdbList := make([]data_manage.EdbSourceChild, 0)
+	calEdbList := make([]data_manage.EdbSourceChild, 0)
+	for _, v := range list {
+		switch v.IsBase {
+		case 1:
+			baseEdbList = append(baseEdbList, data_manage.EdbSourceChild{
+				EdbSourceId: v.EdbSourceId,
+				SourceName:  v.SourceName,
+				IsBase:      v.IsBase,
+				TableName:   v.TableName,
+				Child:       []data_manage.EdbSourceChild{},
+			})
+		case 2:
+			calEdbList = append(calEdbList, data_manage.EdbSourceChild{
+				EdbSourceId: v.EdbSourceId,
+				SourceName:  v.SourceName,
+				IsBase:      v.IsBase,
+				TableName:   v.TableName,
+				Child:       []data_manage.EdbSourceChild{},
+			})
+		}
+	}
+	resp := []data_manage.EdbSourceChild{
+		{
+			EdbSourceId: 0,
+			SourceName:  "基础指标",
+			IsBase:      1,
+			Child:       baseEdbList,
+		},
+		{
+			EdbSourceId: 0,
+			SourceName:  "计算指标",
+			IsBase:      1,
+			Child:       calEdbList,
+		},
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 6 - 0
controllers/data_manage/edb_info_calculate.go

@@ -393,6 +393,9 @@ func (this *ChartInfoController) CalculateEdit() {
 	//添加es
 	data.AddOrEditEdbInfoToEs(req.EdbInfoId)
 
+	// 修改关联的预测指标基础信息
+	go data.ModifyPredictEdbBaseInfoBySourceEdb(edbInfoDetail)
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"
@@ -756,6 +759,9 @@ func (this *ChartInfoController) CalculateBatchEdit() {
 	//添加es
 	data.AddOrEditEdbInfoToEs(resp.EdbInfoId)
 
+	// 修改关联的预测指标基础信息
+	go data.ModifyPredictEdbBaseInfoBySourceEdb(edbInfo)
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"

+ 31 - 0
controllers/data_manage/future_good/future_good_chart_info.go

@@ -11,6 +11,7 @@ import (
 	"eta/eta_api/services"
 	"eta/eta_api/services/data"
 	future_goodServ "eta/eta_api/services/data/future_good"
+	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -561,6 +562,21 @@ func (this *FutureGoodChartInfoController) ChartInfoAdd() {
 		go data_manage.AddChartInfoLog(chartLog)
 	}
 
+	// 试用平台更新用户累计新增图表数
+	adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取系统用户数据失败,Err:" + err.Error()
+		return
+	}
+	if utils.BusinessCode == utils.BusinessCodeSandbox && adminItem.DepartmentName == "ETA试用客户" {
+		go func() {
+			var r etaTrialService.EtaTrialUserReq
+			r.Mobile = adminItem.Mobile
+			_, _ = etaTrialService.UpdateUserChartNum(r)
+		}()
+	}
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"
@@ -2423,6 +2439,21 @@ func (this *FutureGoodChartInfoController) CopyChartInfo() {
 		go data_manage.AddChartInfoLog(chartLog)
 	}
 
+	// 试用平台更新用户累计新增图表数
+	adminItem, e := system.GetSysAdminById(sysUser.AdminId)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取系统用户数据失败,Err:" + err.Error()
+		return
+	}
+	if utils.BusinessCode == utils.BusinessCodeSandbox && adminItem.DepartmentName == "ETA试用客户" {
+		go func() {
+			var r etaTrialService.EtaTrialUserReq
+			r.Mobile = adminItem.Mobile
+			_, _ = etaTrialService.UpdateUserChartNum(r)
+		}()
+	}
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "保存成功"

+ 3 - 3
controllers/data_manage/predict_edb_classify.go

@@ -96,9 +96,9 @@ func (this *PredictEdbClassifyController) List() {
 	canOpClassify := true
 
 	// 如果不是 超管 或者 ficc管理员 或者 ficc研究员,那么就没有权限
-	if !utils.InArrayByStr([]string{utils.ROLE_TYPE_CODE_ADMIN, utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_RESEARCHR, utils.ROLE_TYPE_CODE_FICC_RESEARCHR}, this.SysUser.RoleTypeCode) {
-		canOpClassify = false
-	}
+	//if !utils.InArrayByStr([]string{utils.ROLE_TYPE_CODE_ADMIN, utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_RESEARCHR, utils.ROLE_TYPE_CODE_FICC_RESEARCHR}, this.SysUser.RoleTypeCode) {
+	//	canOpClassify = false
+	//}
 
 	language := `CN`
 	// 指标显示的语言

+ 42 - 1
controllers/data_manage/predict_edb_info.go

@@ -2,7 +2,6 @@ package data_manage
 
 import (
 	"encoding/json"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
@@ -11,6 +10,8 @@ import (
 	"eta/eta_api/services/data"
 	"eta/eta_api/services/elastic"
 	"eta/eta_api/utils"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"github.com/shopspring/decimal"
 	"sort"
 	"strconv"
 	"strings"
@@ -931,6 +932,46 @@ func (this *PredictEdbInfoController) Detail() {
 		fixedValue = predictEdbConf.FixedValue
 	}
 
+	findEdbInfoIdList := make([]int, 0)
+	for k, v := range calculateList {
+		if v.LatestDate != v.EndDate && v.LatestDate != `` && v.EndDate != `` {
+			tmpLatestDate, _ := time.ParseInLocation(utils.FormatDate, v.LatestDate, time.Local)
+			tmpEndDate, _ := time.ParseInLocation(utils.FormatDate, v.EndDate, time.Local)
+			if tmpEndDate.After(tmpLatestDate) {
+				findEdbInfoIdList = append(findEdbInfoIdList, v.FromEdbInfoId)
+			}
+		}
+		v.EndValue = v.LatestValue
+		calculateList[k] = v
+	}
+	if len(findEdbInfoIdList) > 0 {
+		edbDataInsertConfigList, err := data_manage.GetEdbDataInsertConfigByEdbIdList(findEdbInfoIdList)
+		if err != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败插入值失败,Err:" + err.Error()
+			return
+		}
+		edbDataInsertConfigMap := make(map[int]*data_manage.EdbDataInsertConfig)
+		for _, v := range edbDataInsertConfigList {
+			edbDataInsertConfigMap[v.EdbInfoId] = v
+		}
+
+		for k, v := range calculateList {
+			edbDataInsertConfig, ok := edbDataInsertConfigMap[v.FromEdbInfoId]
+			if !ok {
+				continue
+			}
+			if v.EndDate != `` && v.EndDate == edbDataInsertConfig.Date.Format(utils.FormatDate) {
+				t, tmpErr := decimal.NewFromString(edbDataInsertConfig.Value)
+				if tmpErr != nil {
+					continue
+				}
+				v.EndValue, _ = t.Float64()
+			}
+			calculateList[k] = v
+		}
+	}
+
 	resp := response.PredictEdbInfo{
 		EdbInfo:       *edbInfo,
 		RuleType:      ruleType,

+ 103 - 6
controllers/english_report/report.go

@@ -2,8 +2,6 @@ package english_report
 
 import (
 	"encoding/json"
-	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"eta/eta_api/controllers"
 	"eta/eta_api/models"
 	"eta/eta_api/models/company"
@@ -11,6 +9,8 @@ import (
 	"eta/eta_api/services"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
 	"html"
 	"strconv"
 	"strings"
@@ -279,10 +279,12 @@ func (this *EnglishReportController) Detail() {
 	br.Data = item
 }
 
+// ListReport
 // @Title 获取报告列表接口
 // @Description 获取报告列表
 // @Param   PageSize   query   int  true       "每页数据条数"
 // @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   TimeType     query string true  "筛选的时间类别:publish_time(发布时间),modify_time(更新时间)"
 // @Param   StartDate   query   string  true       "开始时间"
 // @Param   EndDate   query   string  true       "结束时间"
 // @Param   Frequency   query   string  true       "频度"
@@ -311,6 +313,7 @@ func (this *EnglishReportController) ListReport() {
 	pageSize, _ := this.GetInt("PageSize")
 	currentIndex, _ := this.GetInt("CurrentIndex")
 
+	timeType := this.GetString("TimeType")
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
 	frequency := this.GetString("Frequency")
@@ -335,14 +338,24 @@ func (this *EnglishReportController) ListReport() {
 	var pars []interface{}
 
 	if keyWord != "" {
-		condition += ` AND (title LIKE '%` + keyWord + `%' OR author LIKE '%` + keyWord + `%' ) `
+		condition += ` AND (title LIKE '%` + keyWord + `%' OR admin_real_name LIKE '%` + keyWord + `%' ) `
+	}
+
+	if timeType == "" {
+		timeType = "publish_time"
+	}
+	if timeType != "publish_time" && timeType != "modify_time" {
+		br.Msg = "请选择正确的时间"
+		br.ErrMsg = "请选择正确的时间"
+		return
 	}
+
 	if startDate != "" {
-		condition += ` AND create_time >= ? `
+		condition += ` AND ` + timeType + ` >= ? `
 		pars = append(pars, startDate)
 	}
 	if endDate != "" {
-		condition += ` AND create_time <= ? `
+		condition += ` AND ` + timeType + ` <= ? `
 		pars = append(pars, endDate)
 	}
 	if frequency != "" {
@@ -603,7 +616,14 @@ func (this *EnglishReportController) PublishReport() {
 			br.ErrMsg = "报告内容为空,不需要生成,report_id:" + strconv.Itoa(report.Id)
 			return
 		}
-		if tmpErr = models.PublishEnglishReportById(report.Id); tmpErr != nil {
+		var publishTime string
+		if report.PublishTime != "" {
+			// 发布时间固定为首次发布时间
+			publishTime = report.PublishTime
+		} else {
+			publishTime = time.Now().Format(utils.FormatDateTime)
+		}
+		if tmpErr = models.PublishEnglishReportById(report.Id, publishTime); tmpErr != nil {
 			br.Msg = "报告发布失败"
 			br.ErrMsg = "报告发布失败, Err:" + tmpErr.Error() + ", report_id:" + strconv.Itoa(report.Id)
 			return
@@ -618,6 +638,83 @@ func (this *EnglishReportController) PublishReport() {
 	br.Msg = "发布成功"
 }
 
+// PrePublishReport
+// @Title 设置定时发布接口
+// @Description 设置定时发布接口
+// @Param	request	body models.PrePublishReq true "type json string"
+// @Success 200 Ret=200 发布成功
+// @router /pre_publish [post]
+func (this *EnglishReportController) PrePublishReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.PrePublishReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId == 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,报告id不可为空"
+		return
+	}
+	if req.PrePublishTime == "" {
+		br.Msg = "发布时间不能为空"
+		return
+	}
+	prePublishTime, err := time.ParseInLocation(utils.FormatDateTime, req.PrePublishTime, time.Local)
+	if err != nil {
+		br.Msg = "发布时间格式错误"
+		br.ErrMsg = "发布时间格式错误,Err:" + err.Error()
+		return
+	}
+	if prePublishTime.Before(time.Now()) {
+		br.Msg = "发布时间不允许选择过去时间"
+		return
+	}
+	if prePublishTime.Before(time.Now().Add(2 * time.Minute)) {
+		br.Msg = "发布时间距离当前时间太近了"
+		return
+	}
+	report, err := models.GetEnglishReportById(reportId)
+	if err != nil {
+		br.Msg = "获取报告信息失败"
+		br.ErrMsg = "获取报告信息失败,Err:" + err.Error()
+		return
+	}
+	if report == nil {
+		br.Msg = "报告不存在"
+		return
+	}
+
+	if report.Content == "" {
+		br.Msg = "报告内容为空,不可发布"
+		br.ErrMsg = "报告内容为空,不需要生成,report_id:" + strconv.Itoa(report.Id)
+		return
+	}
+
+	if report.State == 2 {
+		br.Msg = "报告已发布,不可设置定时发布"
+		return
+	}
+
+	var tmpErr error
+	if tmpErr = models.SetPrePublishEnglishReportById(report.Id, req.PrePublishTime); tmpErr != nil {
+		br.Msg = "设置定时发布失败"
+		br.ErrMsg = "设置定时发布失败, Err:" + tmpErr.Error() + ", report_id:" + strconv.Itoa(report.Id)
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "定时发布成功"
+}
+
 // @Title 取消发布报告接口
 // @Description 取消发布报告
 // @Param	request	body models.PublishCancelReq true "type json string"

+ 199 - 0
controllers/eta_trial/questionnaire.go

@@ -0,0 +1,199 @@
+package eta_trial
+
+import (
+	"encoding/json"
+	"eta/eta_api/models"
+	"eta/eta_api/models/eta_trial"
+	etaTrialService "eta/eta_api/services/eta_trial"
+	"eta/eta_api/utils"
+	"time"
+)
+
+// QuestionnairePopUp
+// @Title 获取问卷调查弹窗信息
+// @Description 获取问卷调查弹窗信息
+// @Success 200 {object} response.TryOutCompanyListResp
+// @router /questionnaire/popup [get]
+func (this *EtaTrialController) QuestionnairePopUp() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	// 仅试用平台用
+	if utils.BusinessCode != utils.BusinessCodeSandbox {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
+	var resp eta_trial.QuestionnairePopupResp
+
+	var userReq etaTrialService.GetEtaTrialUserReq
+	userReq.Mobile = sysUser.Mobile
+	item, e := etaTrialService.GetEtaTrialUser(userReq)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取ETA试用客户失败, GetEtaTrialUser Err:" + e.Error()
+		return
+	}
+	if item.EtaTrialId <= 0 {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "获取成功"
+		return
+	}
+
+	fillItem, e := eta_trial.GetQuestionnaireFillRecordCountByMobile(sysUser.Mobile)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取数据失败失败,GetQuestionnaireFillRecordCountByMobile Err:" + e.Error()
+		return
+	}
+	if fillItem == nil {
+		//没填过
+		if item.ModifyTime.AddDate(0, 0, 6).Before(time.Now()) {
+			//弹窗
+			resp.IsPopup = 1
+		}
+	} else {
+		//填过,判断是否超过了14天
+		if fillItem.IsFill == 2 && fillItem.CreateTime.AddDate(0, 0, 13).Before(time.Now()) {
+			//弹窗
+			resp.IsPopup = 1
+		} else if fillItem.IsFill == 1 {
+			//显示图标但不弹窗
+			resp.IsShow = 1
+		}
+	}
+
+	questionnaireList, e := etaTrialService.GetEtaTrialQuestionnaireList()
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取问卷调查列表失败, GetEtaTrialQuestionnaireList Err: " + e.Error()
+		return
+	}
+	resp.Question = questionnaireList
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// QuestionnaireCommit
+// @Title 提交问卷
+// @Description 提交问卷
+// @Success 200 {object} response.TryOutCompanyListResp
+// @router /questionnaire/commit [post]
+func (this *EtaTrialController) QuestionnaireCommit() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+
+	var req eta_trial.EtaTrialQuestionnaireReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	var userReq etaTrialService.GetEtaTrialUserReq
+	userReq.Mobile = sysUser.Mobile
+	admin, e := etaTrialService.GetEtaTrialUser(userReq)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "获取ETA试用客户失败, GetEtaTrialUser Err:" + e.Error()
+		return
+	}
+	if admin.EtaTrialId <= 0 {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+
+	if req.IsFill == 0 {
+		// 稍后再填
+		fillItem := &eta_trial.QuestionnaireFillRecord{
+			Mobile:     admin.Mobile,
+			IsFill:     1,
+			CreateTime: time.Now(),
+			ModifyTime: time.Now(),
+		}
+		err := eta_trial.AddQuestionnaireFillRecord(fillItem)
+		if err != nil {
+			br.Msg = "新增填写记录失败"
+			br.ErrMsg = "新增填写记录失败, Err:" + err.Error()
+			return
+		}
+	} else {
+		// 已填写
+		list := make([]eta_trial.EtaTrialQuestionnaireRecord, 0)
+		for _, q := range req.List {
+			v := eta_trial.EtaTrialQuestionnaireRecord{
+				UserName:        admin.UserName,
+				CompanyName:     admin.CompanyName,
+				Position:        admin.Position,
+				Options:         q.Options,
+				Mobile:          admin.Mobile,
+				Type:            q.Type,
+				QuestionnaireId: q.QuestionnaireId,
+				CreateTime:      time.Now(),
+			}
+			list = append(list, v)
+		}
+
+		var commitReq etaTrialService.EtaTrialQuestionnaireCommitReq
+		commitReq.List = list
+		commitRes, e := etaTrialService.EtaTrialQuestionnaireCommit(commitReq)
+		if e != nil {
+			br.Msg = "操作失败"
+			br.ErrMsg = "新增问卷记录失败, EtaTrialQuestionnaireCommit Err: " + e.Error()
+			return
+		}
+		if !commitRes {
+			br.Msg = "操作失败"
+			br.ErrMsg = "新增问卷记录失败" + e.Error()
+			return
+		}
+
+		fillItem := &eta_trial.QuestionnaireFillRecord{
+			Mobile:     admin.Mobile,
+			IsFill:     2,
+			CreateTime: time.Now(),
+			ModifyTime: time.Now(),
+		}
+		err := eta_trial.AddQuestionnaireFillRecord(fillItem)
+		if err != nil {
+			br.Msg = "新增填写记录失败"
+			br.ErrMsg = "新增填写记录失败, Err:" + err.Error()
+			return
+		}
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "提交成功"
+}

+ 140 - 0
controllers/eta_trial/user.go

@@ -0,0 +1,140 @@
+package eta_trial
+
+import (
+	"encoding/json"
+	"eta/eta_api/controllers"
+	"eta/eta_api/models"
+	"eta/eta_api/models/eta_trial"
+	etaTrialService "eta/eta_api/services/eta_trial"
+	"eta/eta_api/utils"
+)
+
+// EtaTrialController ETA试用
+type EtaTrialController struct {
+	controllers.BaseAuthController
+}
+
+// UpdateActiveTime
+// @Title 累计活跃时长
+// @Description 累计活跃时长
+// @Param   ActiveTime	query	int		true	"活跃时长"
+// @Param   Part		query	string	true	"活跃板块"
+// @Success 200 {object} models.ETATrialAddReq
+// @router /user/active [post]
+func (this *EtaTrialController) UpdateActiveTime() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	// 仅试用平台用
+	if utils.BusinessCode != utils.BusinessCodeSandbox {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+
+	var req eta_trial.UpdateUserActiveTimeReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.Part == "" {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+
+	params := etaTrialService.UpdateEtaTrialUserActiveTimeReq{
+		UserName:   sysUser.RealName,
+		Mobile:     sysUser.Mobile,
+		ActiveTime: req.ActiveTime,
+		Part:       req.Part,
+	}
+	res, e := etaTrialService.UpdateEtaTrialUserActiveTime(params)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新ETA试用用户活跃时长失败, Err: " + e.Error()
+		return
+	}
+	if !res {
+		br.Msg = "操作失败"
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "新增成功"
+}
+
+// UpdateLoginDuration
+// @Title 更新用户登录时长
+// @Description 更新用户登录时长
+// @Param   ActiveTime	query	int		true	"活跃时长"
+// @Success 200 {object} models.ETATrialAddReq
+// @router /user/login_duration [post]
+func (this *EtaTrialController) UpdateLoginDuration() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	// 仅试用平台用
+	if utils.BusinessCode != utils.BusinessCodeSandbox {
+		br.Ret = 200
+		br.Success = true
+		br.Msg = "操作成功"
+		return
+	}
+	var req eta_trial.UpdateUserLoginDurationReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+
+	params := etaTrialService.UpdateEtaTrialUserLoginDurationReq{
+		UserName:   sysUser.RealName,
+		Mobile:     sysUser.Mobile,
+		ActiveTime: req.ActiveTime,
+	}
+	res, e := etaTrialService.UpdateEtaTrialUserLoginDuration(params)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新ETA试用用户登录时长失败, Err: " + e.Error()
+		return
+	}
+	if !res {
+		br.Msg = "操作失败"
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "新增成功"
+}

+ 37 - 0
controllers/ppt_english_group.go

@@ -5,6 +5,7 @@ import (
 	"eta/eta_api/models"
 	"eta/eta_api/models/ppt_english"
 	"eta/eta_api/services/ppt"
+	"strings"
 )
 
 type PptEnglishGroupController struct {
@@ -506,3 +507,39 @@ func (this *PptEnglishGroupController) GrantPptList() {
 	br.Data = data
 	return
 }
+
+// ListSearch
+// @Title 我的/公开PPT搜索
+// @Description 我的/公开PPT搜索
+// @Param   Keyword		query  string	true	"关键字"
+// @Success 200 {object} ppt_english.RespGroupPptList
+// @router /ppt/search [get]
+func (this *PptEnglishGroupController) ListSearch() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	keyword := this.GetString("Keyword")
+	keyword = strings.TrimSpace(keyword)
+
+	data, err := ppt.SearchEnglishPptList(sysUser.AdminId, keyword)
+	if err != nil {
+		br.Msg = err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "查询成功"
+	br.Data = data
+}

+ 37 - 0
controllers/ppt_v2_group.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"eta/eta_api/models"
 	"eta/eta_api/services/ppt"
+	"strings"
 )
 
 type PptV2GroupController struct {
@@ -508,3 +509,39 @@ func (this *PptV2GroupController) GrantPptList() {
 	br.Data = data
 	return
 }
+
+// ListSearch
+// @Title 我的/公开PPT搜索
+// @Description 我的/公开PPT搜索
+// @Param   Keyword		query  string	true	"关键字"
+// @Success 200 {object} models.RespGroupPptList
+// @router /ppt/search [get]
+func (this *PptV2GroupController) ListSearch() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	keyword := this.GetString("Keyword")
+	keyword = strings.TrimSpace(keyword)
+
+	data, err := ppt.SearchPptList(sysUser.AdminId, keyword)
+	if err != nil {
+		br.Msg = err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "查询成功"
+	br.Data = data
+}

+ 99 - 3
controllers/report.go

@@ -39,6 +39,7 @@ type ReportUploadCommonController struct {
 // @Description 获取报告列表
 // @Param   PageSize   query   int  true       "每页数据条数"
 // @Param   CurrentIndex   query   int  true       "当前页页码,从1开始"
+// @Param   TimeType     query string true  "筛选的时间类别:publish_time(发布时间),modify_time(更新时间)"
 // @Param   StartDate   query   string  true       "开始时间"
 // @Param   EndDate   query   string  true       "结束时间"
 // @Param   Frequency   query   string  true       "频度"
@@ -59,6 +60,7 @@ func (this *ReportController) ListReport() {
 	pageSize, _ := this.GetInt("PageSize")
 	currentIndex, _ := this.GetInt("CurrentIndex")
 
+	timeType := this.GetString("TimeType")
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
 	frequency := this.GetString("Frequency")
@@ -78,18 +80,27 @@ func (this *ReportController) ListReport() {
 	}
 	startSize = utils.StartIndex(currentIndex, pageSize)
 
+	if timeType == "" {
+		timeType = "publish_time"
+	}
+	if timeType != "publish_time" && timeType != "modify_time" {
+		br.Msg = "请选择正确的时间"
+		br.ErrMsg = "请选择正确的时间"
+		return
+	}
+
 	var condition string
 	var pars []interface{}
 
 	if keyWord != "" {
-		condition += ` AND (title LIKE '%` + keyWord + `%' OR author LIKE '%` + keyWord + `%' ) `
+		condition += ` AND (title LIKE '%` + keyWord + `%' OR admin_real_name LIKE '%` + keyWord + `%' ) `
 	}
 	if startDate != "" {
-		condition += ` AND create_time >= ? `
+		condition += ` AND ` + timeType + ` >= ? `
 		pars = append(pars, startDate)
 	}
 	if endDate != "" {
-		condition += ` AND create_time <= ? `
+		condition += ` AND ` + timeType + ` <= ? `
 		pars = append(pars, endDate)
 	}
 	if frequency != "" {
@@ -3154,3 +3165,88 @@ func (this *ReportController) CheckDayWeekReportChapterVideo() {
 	br.Msg = "保存成功"
 	br.Data = typeNameArr
 }
+
+// PrePublishReport
+// @Title 设置定时发布接口
+// @Description 设置定时发布接口
+// @Param	request	body models.PrePublishReq true "type json string"
+// @Success 200 Ret=200 发布成功
+// @router /pre_publish [post]
+func (this *ReportController) PrePublishReport() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+	var req models.PrePublishReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	reportId := req.ReportId
+	if reportId == 0 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "参数错误,报告id不可为空"
+		return
+	}
+	if req.PrePublishTime == "" {
+		br.Msg = "发布时间不能为空"
+		return
+	}
+	if req.PreMsgSend != 0 && req.PreMsgSend != 1 {
+		br.Msg = "参数错误"
+		br.ErrMsg = "是否发送模版消息标识错误"
+		return
+	}
+	prePublishTime, err := time.ParseInLocation(utils.FormatDateTime, req.PrePublishTime, time.Local)
+	if err != nil {
+		br.Msg = "发布时间格式错误"
+		br.ErrMsg = "发布时间格式错误,Err:" + err.Error()
+		return
+	}
+	if prePublishTime.Before(time.Now()) {
+		br.Msg = "发布时间不允许选择过去时间"
+		return
+	}
+	if prePublishTime.Before(time.Now().Add(2 * time.Minute)) {
+		br.Msg = "发布时间距离当前时间太近了"
+		return
+	}
+	report, err := models.GetReportById(reportId)
+	if err != nil {
+		br.Msg = "获取报告信息失败"
+		br.ErrMsg = "获取报告信息失败,Err:" + err.Error()
+		return
+	}
+	if report == nil {
+		br.Msg = "报告不存在"
+		return
+	}
+	if report.HasChapter == 1 && (report.ChapterType == utils.REPORT_TYPE_DAY || report.ChapterType == utils.REPORT_TYPE_WEEK) {
+		br.Msg = "晨报周报不支持定时发布"
+		return
+	}
+	if report.Content == "" {
+		br.Msg = "报告内容为空,不可发布"
+		br.ErrMsg = "报告内容为空,不需要生成,report_id:" + strconv.Itoa(report.Id)
+		return
+	}
+
+	if report.State == 2 {
+		br.Msg = "报告已发布,不可设置定时发布"
+		return
+	}
+
+	var tmpErr error
+	if tmpErr = models.SetPrePublishReportById(report.Id, req.PrePublishTime, req.PreMsgSend); tmpErr != nil {
+		br.Msg = "设置定时发布失败"
+		br.ErrMsg = "设置定时发布失败, Err:" + tmpErr.Error() + ", report_id:" + strconv.Itoa(report.Id)
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "定时发布成功"
+}

+ 82 - 0
controllers/semantic_analysis/sa_compare.go

@@ -420,6 +420,10 @@ func (this *SaCompareController) SelectDocs() {
 		br.Msg = "请选择文档"
 		return
 	}
+	if len(docIdArr) > 10 {
+		br.Msg = "最多支持选择10个文档"
+		return
+	}
 	for i := range docIdArr {
 		d, e := strconv.Atoi(docIdArr[i])
 		if e != nil {
@@ -649,3 +653,81 @@ func (this *SaCompareController) Move() {
 	br.Success = true
 	br.Msg = "操作成功"
 }
+
+// Search
+// @Title 文档对比搜索(从es获取)
+// @Description  图表模糊搜索(从es获取)
+// @Param   Keyword   query   string  true       "文档对比标题"
+// @Success 200 {object} saModel.CompareListByEsResp
+// @router /compare/search [get]
+func (this *SaCompareController) Search() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	sysUser := this.SysUser
+	if sysUser == nil {
+		br.Msg = "请登录"
+		br.ErrMsg = "请登录,SysUser Is Empty"
+		br.Ret = 408
+		return
+	}
+	pageSize, _ := this.GetInt("PageSize")
+	currentIndex, _ := this.GetInt("CurrentIndex")
+
+	var startSize int
+	if pageSize <= 0 {
+		pageSize = utils.PageSize20
+	}
+	if currentIndex <= 0 {
+		currentIndex = 1
+	}
+	startSize = paging.StartIndex(currentIndex, pageSize)
+
+	keyword := this.GetString("Keyword")
+
+	var searchList []*saModel.SaCompareElastic
+	var total int
+	var err error
+
+	var list []*saModel.SaCompare
+	saCompare := new(saModel.SaCompare)
+	existCond := fmt.Sprintf(` AND result_img != ""`)
+	existPars := make([]interface{}, 0)
+	if keyword != "" {
+		existCond += ` AND  ( title LIKE ? )`
+		existPars = append(existPars, `%`+keyword+`%`)
+	}
+	total, list, err = saCompare.GetPageItemsByCondition(startSize, pageSize, existCond, existPars, []string{}, "")
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取图表信息失败,Err:" + err.Error()
+		return
+	}
+
+	for _, v := range list {
+		tmp := new(saModel.SaCompareElastic)
+		tmp.SaCompareId = v.SaCompareId
+		tmp.ResultImg = v.ResultImg
+		tmp.CreateTime = v.CreateTime.Format(utils.FormatDateTime)
+		tmp.ModifyTime = v.ModifyTime.Format(utils.FormatDateTime)
+		tmp.SysAdminId = v.SysAdminId
+		tmp.SysAdminName = v.SysAdminName
+		tmp.ClassifyId = v.ClassifyId
+		tmp.ClassifyName = v.ClassifyName
+		tmp.Title = v.Title
+		searchList = append(searchList, tmp)
+	}
+
+	page := paging.GetPaging(currentIndex, pageSize, total)
+	resp := saModel.CompareListByEsResp{
+		Paging: page,
+		List:   searchList,
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 134 - 47
controllers/sys_admin.go

@@ -8,6 +8,7 @@ import (
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services"
+	etaTrialService "eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
@@ -267,7 +268,7 @@ func (this *SysAdminController) ListSysuser() {
 		}
 	}
 	// 研究方向分组
-	if len(adminIdArr) > 0 && (utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox) {
+	if len(adminIdArr) > 0 && utils.BusinessCode == utils.BusinessCodeRelease {
 		//adminIds := strings.Join(adminIdArr, ",")
 		//researchGroupList, e := system.GetAdminResearchGroupListByAdminId(adminIds)
 		researchGroupList, e := models.GetAdminVarietyTagRelationListByAdminId(adminIdArr)
@@ -296,6 +297,7 @@ func (this *SysAdminController) ListSysuser() {
 	br.Data = resp
 }
 
+// Add
 // @Title 新增系统用户
 // @Description 新增系统用户接口
 // @Param	request	body system.SysuserAddReq true "type json string"
@@ -314,7 +316,6 @@ func (this *SysAdminController) Add() {
 		br.ErrMsg = "参数解析失败,Err:" + err.Error()
 		return
 	}
-
 	if req.RoleId <= 0 {
 		br.Msg = "请选择角色"
 		br.ErrMsg = "角色ID小于等于0"
@@ -332,7 +333,22 @@ func (this *SysAdminController) Add() {
 		br.IsSendEmail = false
 		return
 	}
+
+	// 手机号和邮箱必填一个
+	req.Mobile = strings.TrimSpace(req.Mobile)
+	req.Email = strings.TrimSpace(req.Email)
+	if req.Mobile == "" && req.Email == "" {
+		br.Msg = "至少输入一个手机号或邮箱"
+		return
+	}
 	if req.Mobile != "" {
+		if req.TelAreaCode == "86" {
+			if !utils.ValidateMobileFormatat(req.Mobile) {
+				br.Msg = "手机号格式有误, 请检查"
+				return
+			}
+		}
+
 		mobileCount, err := system.GetSysAdminCountByMobile(req.Mobile, 0)
 		if err != nil {
 			br.Msg = "判断手机号是否存在失败"
@@ -345,7 +361,17 @@ func (this *SysAdminController) Add() {
 			return
 		}
 	}
-	//req.Mobile
+	if req.Email != "" {
+		if !utils.ValidateEmailFormatat(req.Email) {
+			br.Msg = "邮箱格式有误, 请检查"
+			return
+		}
+		_, e := system.GetSysUserByEmail(req.Email)
+		if e.Error() != utils.ErrNoRow() {
+			br.Msg = "邮箱已存在, 请重新填写"
+			return
+		}
+	}
 
 	var roleName, departmentName, groupName, teamName string
 
@@ -417,7 +443,7 @@ func (this *SysAdminController) Add() {
 		return
 	}
 	pwdStr := string(pwdByte)
-	pwdStr = strings.ToLower(pwdStr)
+	//pwdStr = strings.ToLower(pwdStr)
 	if pwdStr == "" {
 		br.Msg = "请输入密码"
 		return
@@ -462,6 +488,7 @@ func (this *SysAdminController) Add() {
 		admin.Role = "admin"
 	}
 	admin.EmployeeId = req.EmployeeId
+	admin.Email = req.Email
 
 	var authority int
 	if roleItem.RoleTypeCode == utils.ROLE_TYPE_CODE_ADMIN {
@@ -485,6 +512,7 @@ func (this *SysAdminController) Add() {
 	admin.ProvinceCode = req.ProvinceCode
 	admin.City = req.City
 	admin.CityCode = req.CityCode
+	admin.TelAreaCode = req.TelAreaCode
 	err = system.AddAdmin(admin)
 	if err != nil {
 		br.Msg = "新增失败"
@@ -493,10 +521,12 @@ func (this *SysAdminController) Add() {
 	}
 
 	// 同步用户缓存
-	var syncData system.SyncAdminData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.AdminName = admin.AdminName
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncAdminData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.AdminName = admin.AdminName
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
+	}
 
 	err = services.UpdateResearcherTagGroup(admin.AdminId, req.ResearchGroupIds)
 	if err != nil {
@@ -584,26 +614,25 @@ func (this *SysAdminController) Edit() {
 		return
 	}
 
-	// 手机号
-	if item != nil {
-		if req.Mobile != "" && req.Mobile != item.Mobile {
-			mobileCount, err := system.GetSysAdminCountByMobile(req.Mobile, req.AdminId)
-			if err != nil {
-				br.Msg = "判断手机号是否存在失败"
-				br.ErrMsg = "判断手机号是否存在失败,Err:" + err.Error()
-				return
-			}
-			if mobileCount > 0 {
-				br.Msg = "手机号已存在,请重新填写"
-				br.IsSendEmail = false
+	// 手机号和邮箱必填一个
+	req.Mobile = strings.TrimSpace(req.Mobile)
+	req.Email = strings.TrimSpace(req.Email)
+	if req.Mobile == "" && req.Email == "" {
+		br.Msg = "至少输入一个手机号或邮箱"
+		return
+	}
+	if req.Mobile != "" {
+		if req.TelAreaCode == "86" {
+			if !utils.ValidateMobileFormatat(req.Mobile) {
+				br.Msg = "手机号格式有误, 请检查"
 				return
 			}
 		}
-	} else {
-		mobileCount, err := system.GetSysAdminCountByMobile(req.Mobile, req.AdminId)
-		if err != nil {
+
+		mobileCount, e := system.GetSysAdminCountByMobile(req.Mobile, adminInfo.AdminId)
+		if e != nil {
 			br.Msg = "判断手机号是否存在失败"
-			br.ErrMsg = "判断手机号是否存在失败,Err:" + err.Error()
+			br.ErrMsg = "判断手机号是否存在失败,Err:" + e.Error()
 			return
 		}
 		if mobileCount > 0 {
@@ -612,6 +641,22 @@ func (this *SysAdminController) Edit() {
 			return
 		}
 	}
+	if req.Email != "" {
+		if !utils.ValidateEmailFormatat(req.Email) {
+			br.Msg = "邮箱格式有误, 请检查"
+			return
+		}
+		emailUser, e := system.GetSysUserByEmail(req.Email)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			br.Msg = "操作失败"
+			br.ErrMsg = "邮箱获取用户信息失败, Err: " + e.Error()
+			return
+		}
+		if emailUser != nil && emailUser.AdminId != adminInfo.AdminId {
+			br.Msg = "邮箱已存在, 请检查"
+			return
+		}
+	}
 
 	// 角色
 	var roleName string
@@ -633,7 +678,7 @@ func (this *SysAdminController) Edit() {
 
 	// 员工工号
 	req.EmployeeId = strings.TrimSpace(req.EmployeeId)
-	if req.EmployeeId != "" {
+	if req.EmployeeId != "" && utils.BusinessCode == utils.BusinessCodeRelease {
 		// 去重
 		countOB := new(system.Admin)
 		countCond := ` AND employee_id = ? AND admin_id <> ?`
@@ -684,9 +729,11 @@ func (this *SysAdminController) Edit() {
 	adminInfo.City = req.City
 	adminInfo.CityCode = req.CityCode
 	adminInfo.EmployeeId = req.EmployeeId
+	adminInfo.Email = req.Email
+	adminInfo.TelAreaCode = req.TelAreaCode
 	cols := []string{
 		"AdminName", "RealName", "LastUpdatedTime", "Mobile", "RoleId", "RoleName", "Enabled", "Authority",
-		"Position", "RoleTypeCode", "Province", "ProvinceCode", "City", "CityCode", "EmployeeId",
+		"Position", "RoleTypeCode", "Province", "ProvinceCode", "City", "CityCode", "EmployeeId", "Email", "TelAreaCode",
 	}
 	if e := adminInfo.Update(cols); e != nil {
 		br.Msg = "编辑失败"
@@ -695,10 +742,12 @@ func (this *SysAdminController) Edit() {
 	}
 
 	// 同步用户缓存
-	var syncData system.SyncAdminData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.AdminName = adminInfo.AdminName
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncAdminData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.AdminName = adminInfo.AdminName
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
+	}
 
 	// 用户登出
 	logOutSystemUser(adminInfo.AdminId)
@@ -714,6 +763,18 @@ func (this *SysAdminController) Edit() {
 	utils.Rc.Delete(utils.CACHE_KEY_ADMIN)
 	utils.Rc.Delete(utils.CACHE_KEY_ADMIN_ID)
 
+	// 试用客户更新对应信息
+	if utils.BusinessCode == utils.BusinessCodeSandbox && adminInfo.DepartmentName == "ETA试用客户" {
+		go func() {
+			var r etaTrialService.EtaTrialUserEditReq
+			r.RealName = req.RealName
+			r.Position = req.Position
+			r.Mobile = adminInfo.Mobile
+			r.Enabled = req.Enabled
+			_, _ = etaTrialService.EditEtaTrialUser(r)
+		}()
+	}
+
 	br.Ret = 200
 	br.Success = true
 	br.IsAddLog = true
@@ -754,6 +815,15 @@ func (this *SysAdminController) EditEnabled() {
 			br.ErrMsg = "修改系统用户数据失败,Err:" + err.Error()
 			return
 		}
+
+		// 更新试用平台客户
+		if utils.BusinessCode == utils.BusinessCodeSandbox && adminItem.DepartmentName == "ETA试用客户" {
+			go func() {
+				var r etaTrialService.EtaTrialUserReq
+				r.Mobile = adminItem.Mobile
+				_, _ = etaTrialService.DisableEtaTrialUser(r)
+			}()
+		}
 	} else {
 		admin := new(system.Admin)
 		admin.Enabled = req.Enabled
@@ -767,10 +837,12 @@ func (this *SysAdminController) EditEnabled() {
 	}
 
 	// 同步用户缓存
-	var syncData system.SyncAdminData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.AdminName = adminItem.AdminName
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncAdminData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.AdminName = adminItem.AdminName
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
+	}
 
 	//用户被禁用的情况下,需要将他对应的token给过期
 	if adminItem.Enabled == 1 && req.Enabled == 0 {
@@ -829,10 +901,12 @@ func (this *SysAdminController) Delete() {
 	}
 
 	// 同步用户缓存
-	var syncData system.SyncAdminData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.AdminName = adminInfo.AdminName
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncAdminData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.AdminName = adminInfo.AdminName
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
+	}
 
 	// 删除手工数据关联用户
 	{
@@ -847,6 +921,15 @@ func (this *SysAdminController) Delete() {
 	utils.Rc.Delete(utils.CACHE_KEY_ADMIN)
 	utils.Rc.Delete(utils.CACHE_KEY_ADMIN_ID)
 
+	// 删除试用平台客户
+	if utils.BusinessCode == utils.BusinessCodeSandbox && adminInfo.DepartmentName == "ETA试用客户" {
+		go func() {
+			var r etaTrialService.EtaTrialUserReq
+			r.Mobile = mobile
+			_, _ = etaTrialService.RemoveEtaTrialUser(r)
+		}()
+	}
+
 	br.Ret = 200
 	br.Success = true
 	br.IsAddLog = true
@@ -1070,10 +1153,12 @@ func (this *SysAdminController) Move() {
 	}
 
 	// 同步用户缓存
-	var syncData system.SyncAdminData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.AdminName = adminInfo.AdminName
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncAdminData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.AdminName = adminInfo.AdminName
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
+	}
 
 	// 清除系统用户列表缓存key
 	_ = utils.Rc.Delete(utils.CACHE_KEY_ADMIN)
@@ -1142,7 +1227,7 @@ func (this *SysAdminController) ResetPass() {
 		return
 	}
 	pwd := string(b)
-	pwd = strings.ToLower(pwd)
+	//pwd = strings.ToLower(pwd)
 	pwd = utils.MD5(pwd)
 
 	adminInfo.Password = pwd
@@ -1155,10 +1240,12 @@ func (this *SysAdminController) ResetPass() {
 	}
 
 	// 同步用户缓存
-	var syncData system.SyncAdminData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.AdminName = adminInfo.AdminName
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncAdminData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.AdminName = adminInfo.AdminName
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
+	}
 
 	// 清除系统用户列表缓存key
 	_ = utils.Rc.Delete(utils.CACHE_KEY_ADMIN)

+ 18 - 12
controllers/sys_department.go

@@ -56,10 +56,12 @@ func (this *SysDepartmentController) Add() {
 	}
 
 	// 同步部门缓存
-	var syncData system.SyncDepartmentData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.DepartmentId = int(departmentId)
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_DEPARTMENT, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncDepartmentData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.DepartmentId = int(departmentId)
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_DEPARTMENT, syncData)
+	}
 
 	br.Ret = 200
 	br.Success = true
@@ -113,10 +115,12 @@ func (this *SysDepartmentController) Edit() {
 	}
 
 	// 同步部门缓存
-	var syncData system.SyncDepartmentData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.DepartmentId = req.DepartmentId
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_DEPARTMENT, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncDepartmentData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.DepartmentId = req.DepartmentId
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_DEPARTMENT, syncData)
+	}
 
 	br.Ret = 200
 	br.Success = true
@@ -154,10 +158,12 @@ func (this *SysDepartmentController) Delete() {
 	}
 
 	// 同步部门缓存
-	var syncData system.SyncDepartmentData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.DepartmentId = req.DepartmentId
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_DEPARTMENT, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncDepartmentData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.DepartmentId = req.DepartmentId
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_DEPARTMENT, syncData)
+	}
 
 	br.Ret = 200
 	br.Success = true

+ 25 - 17
controllers/sys_group.go

@@ -62,10 +62,12 @@ func (this *SysGroupController) Add() {
 			}
 
 			// 同步分组缓存
-			var syncData system.SyncGroupData
-			syncData.Source = utils.SOURCE_ETA_FLAG
-			syncData.GroupId = int(groupId)
-			_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+			if utils.BusinessCode == utils.BusinessCodeRelease {
+				var syncData system.SyncGroupData
+				syncData.Source = utils.SOURCE_ETA_FLAG
+				syncData.GroupId = int(groupId)
+				_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+			}
 		}
 	}
 	br.Ret = 200
@@ -120,10 +122,12 @@ func (this *SysGroupController) Edit() {
 	}
 
 	// 同步分组缓存
-	var syncData system.SyncGroupData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.GroupId = req.GroupId
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncGroupData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.GroupId = req.GroupId
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+	}
 
 	br.Ret = 200
 	br.Success = true
@@ -167,10 +171,12 @@ func (this *SysGroupController) Delete() {
 	}
 
 	// 同步分组缓存
-	var syncData system.SyncGroupData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.GroupId = req.GroupId
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncGroupData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.GroupId = req.GroupId
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+	}
 
 	br.Ret = 200
 	br.Success = true
@@ -244,11 +250,13 @@ func (this *SysGroupController) SetSort() {
 			}
 
 			// 同步分组缓存
-			for _, g := range updateArr {
-				var syncData system.SyncGroupData
-				syncData.Source = utils.SOURCE_ETA_FLAG
-				syncData.GroupId = g.GroupId
-				_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+			if utils.BusinessCode == utils.BusinessCodeRelease {
+				for _, g := range updateArr {
+					var syncData system.SyncGroupData
+					syncData.Source = utils.SOURCE_ETA_FLAG
+					syncData.GroupId = g.GroupId
+					_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+				}
 			}
 		}
 	}

+ 20 - 14
controllers/sys_role.go

@@ -2,12 +2,12 @@ package controllers
 
 import (
 	"encoding/json"
-	"fmt"
-	"github.com/rdlucklib/rdluck_tools/paging"
 	"eta/eta_api/models"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services"
 	"eta/eta_api/utils"
+	"fmt"
+	"github.com/rdlucklib/rdluck_tools/paging"
 	"strconv"
 	"strings"
 	"time"
@@ -63,10 +63,12 @@ func (this *SysRoleController) Add() {
 	}
 
 	// 同步角色缓存
-	var syncData system.SyncRoleData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.RoleId = int(roleId)
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_ROLE, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncRoleData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.RoleId = int(roleId)
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_ROLE, syncData)
+	}
 
 	br.Ret = 200
 	br.Success = true
@@ -121,10 +123,12 @@ func (this *SysRoleController) Edit() {
 	}
 
 	// 同步角色缓存
-	var syncData system.SyncRoleData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.RoleId = item.RoleId
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_ROLE, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncRoleData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.RoleId = item.RoleId
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_ROLE, syncData)
+	}
 
 	br.Ret = 200
 	br.Success = true
@@ -175,10 +179,12 @@ func (this *SysRoleController) Delete() {
 	}
 
 	// 同步角色缓存
-	var syncData system.SyncRoleData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.RoleId = role.RoleId
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_ROLE, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncRoleData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.RoleId = role.RoleId
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_ROLE, syncData)
+	}
 
 	br.Ret = 200
 	br.Success = true

+ 18 - 12
controllers/sys_team.go

@@ -63,10 +63,12 @@ func (this *SysTeamController) Add() {
 			}
 
 			// 同步分组缓存
-			var syncData system.SyncGroupData
-			syncData.Source = utils.SOURCE_ETA_FLAG
-			syncData.GroupId = int(groupId)
-			_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+			if utils.BusinessCode == utils.BusinessCodeRelease {
+				var syncData system.SyncGroupData
+				syncData.Source = utils.SOURCE_ETA_FLAG
+				syncData.GroupId = int(groupId)
+				_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+			}
 		}
 	}
 	br.Ret = 200
@@ -121,10 +123,12 @@ func (this *SysTeamController) Edit() {
 	}
 
 	// 同步分组缓存
-	var syncData system.SyncGroupData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.GroupId = item.GroupId
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncGroupData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.GroupId = item.GroupId
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+	}
 
 	br.Ret = 200
 	br.Success = true
@@ -168,10 +172,12 @@ func (this *SysTeamController) Delete() {
 	}
 
 	// 同步分组缓存
-	var syncData system.SyncGroupData
-	syncData.Source = utils.SOURCE_ETA_FLAG
-	syncData.GroupId = req.TeamId
-	_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+	if utils.BusinessCode == utils.BusinessCodeRelease {
+		var syncData system.SyncGroupData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.GroupId = req.TeamId
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_GROUP, syncData)
+	}
 
 	br.Ret = 200
 	br.Success = true

+ 10 - 0
controllers/sys_user.go

@@ -5,6 +5,7 @@ import (
 	"eta/eta_api/models"
 	"eta/eta_api/models/system"
 	"eta/eta_api/services"
+	"eta/eta_api/services/eta_trial"
 	"eta/eta_api/utils"
 	"fmt"
 	"time"
@@ -185,6 +186,15 @@ func (this *SysUserController) Login() {
 			utils.Rc.Put(noTrustLoginKey, sysSession.Id, 30*time.Minute)
 		}
 	}
+
+	// ETA试用平台-请求中间服务更新用户最后登录时间和次数
+	if utils.BusinessCode == utils.BusinessCodeSandbox {
+		go func() {
+			var r eta_trial.EtaTrialUserReq
+			r.Mobile = sysUser.Mobile
+			_, _ = eta_trial.UpdateEtaTrialUserLogin(r)
+		}()
+	}
 }
 
 type SysUserAuthController struct {

+ 893 - 0
controllers/user_login.go

@@ -0,0 +1,893 @@
+package controllers
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"eta/eta_api/models"
+	"eta/eta_api/models/company"
+	"eta/eta_api/models/system"
+	"eta/eta_api/services"
+	"eta/eta_api/utils"
+	"fmt"
+	"github.com/mojocn/base64Captcha"
+	"image/color"
+	"strings"
+	"time"
+)
+
+// UserLoginController 登录-无需Token
+type UserLoginController struct {
+	BaseCommonController
+}
+
+// UserLoginAuthController 登录-需Token
+type UserLoginAuthController struct {
+	BaseAuthController
+}
+
+// GenerateCaptcha
+// @Title 生成图形验证码
+// @Description 生成图形验证码
+// @Success 200 Ret=200 获取成功
+// @router /get_captcha [get]
+func (this *UserLoginController) GenerateCaptcha() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	//driver := base64Captcha.DefaultDriverDigit
+	// 自定义验证码样式
+	var driver base64Captcha.Driver
+	driverString := base64Captcha.DriverString{
+		Height:          60,    //高度
+		Width:           120,   //宽度
+		NoiseCount:      0,     //干扰数
+		ShowLineOptions: 2 | 4, //展示个数
+		Length:          4,     //长度
+		//Source:          "1234567890qwertyuioplkjhgfdsazxcvbnm", //验证码随机字符串来源
+		Source: "1234567890", //验证码随机字符串来源
+		BgColor: &color.RGBA{ // 背景颜色
+			R: 0,
+			G: 0,
+			B: 0,
+			A: 0,
+		},
+		Fonts: []string{"wqy-microhei.ttc"}, // 字体
+	}
+	driver = driverString.ConvertFonts()
+
+	// 生成验证码
+	store := services.CaptchaRedis{}
+	captcha := base64Captcha.NewCaptcha(driver, store)
+	id, b64s, err := captcha.Generate()
+	if err != nil {
+		br.Msg = "生成失败"
+		br.ErrMsg = "生成验证码失败, Err: " + err.Error()
+		return
+	}
+
+	type CaptchaResult struct {
+		Id         string
+		Base64Blob string
+	}
+	res := new(CaptchaResult)
+	res.Id = id
+	res.Base64Blob = b64s
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = res
+}
+
+// GetVerifyCode
+// @Title 获取短信/邮箱验证码
+// @Description 获取短信/邮箱验证码
+// @Param	request	body VerifyCodeReq true "type json string"
+// @Success 200 Ret=200 获取成功
+// @router /verify_code [post]
+func (this *UserLoginController) GetVerifyCode() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	type VerifyCodeReq struct {
+		VerifyType  int    `description:"验证方式: 1-手机号; 2-邮箱"`
+		CaptchaId   string `description:"验证码ID"`
+		CaptchaCode string `description:"图形验证码"`
+		Mobile      string `description:"手机号"`
+		TelAreaCode string `description:"手机区号"`
+		Email       string `description:"邮箱"`
+		Source      int    `description:"来源:1-登录;2-异常登录校验;3-忘记密码"`
+	}
+	var req VerifyCodeReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	if req.VerifyType != 1 && req.VerifyType != 2 {
+		br.Msg = "验证方式有误"
+		br.ErrMsg = "验证方式有误"
+		return
+	}
+	if req.Source != 1 && req.Source != 2 && req.Source != 3 {
+		br.Msg = "来源有误"
+		br.ErrMsg = "来源有误"
+		return
+	}
+	// 忘记密码图形校验在前一步, 这一步不再校验图形验证码
+	if req.Source != 3 {
+		if req.CaptchaId == "" || req.CaptchaCode == "" {
+			br.Msg = "请输入图形验证码"
+			return
+		}
+	}
+	if req.VerifyType == 1 {
+		if req.TelAreaCode == "" {
+			br.Msg = "请选择区号"
+			return
+		}
+		if req.Mobile == "" {
+			br.Msg = "请输入手机号"
+			return
+		}
+		if req.TelAreaCode == "86" && !utils.ValidateMobileFormatat(req.Mobile) {
+			br.Msg = "您的手机号输入有误, 请检查"
+			return
+		}
+		_, e := system.GetSysUserByMobile(req.Mobile)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "您的手机号未绑定账号, 请检查"
+				return
+			}
+			br.Msg = "您的手机号未绑定账号, 请检查"
+			br.ErrMsg = "手机号获取用户失败, Err:" + e.Error()
+			return
+		}
+	}
+	if req.VerifyType == 2 {
+		if req.Email == "" {
+			br.Msg = "请输入邮箱"
+			return
+		}
+		if !utils.ValidateEmailFormatat(req.Email) {
+			br.Msg = "您的邮箱输入有误, 请检查"
+			return
+		}
+		_, e := system.GetSysUserByEmail(req.Email)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "您的邮箱未绑定账号, 请检查"
+				return
+			}
+			br.Msg = "您的邮箱未绑定账号, 请检查"
+			br.ErrMsg = "邮箱获取用户失败, Err:" + e.Error()
+			return
+		}
+	}
+	if req.Source != 3 {
+		store := services.CaptchaRedis{}
+		ok := store.Verify(req.CaptchaId, req.CaptchaCode, true)
+		if !ok {
+			br.Msg = "图形验证码错误, 请重新输入"
+			return
+		}
+	}
+
+	// 限制最多60s获取一次
+	var lockKey string
+	if req.VerifyType == 1 {
+		lockKey = fmt.Sprint(utils.CaptchaCachePrefix, req.Mobile)
+	}
+	if req.VerifyType == 2 {
+		lockKey = fmt.Sprint(utils.CaptchaCachePrefix, req.Email)
+	}
+	locked := utils.Rc.SetNX(lockKey, 1, time.Minute)
+	if !locked {
+		br.Msg = "请勿频繁发送"
+		return
+	}
+
+	// 发送验证码
+	sendRes := false
+	if req.VerifyType == 1 {
+		r, e := services.SendAdminMobileVerifyCode(req.Source, req.Mobile, req.TelAreaCode)
+		if e != nil {
+			br.Msg = "发送失败"
+			br.ErrMsg = "发送短信验证码失败, Err: " + e.Error()
+			return
+		}
+		sendRes = r
+	}
+	if req.VerifyType == 2 {
+		r, e := services.SendAdminEmailVerifyCode(req.Source, req.Email)
+		if e != nil {
+			br.Msg = "发送失败"
+			br.ErrMsg = "发送邮箱验证码失败, Err: " + e.Error()
+			return
+		}
+		sendRes = r
+	}
+	if !sendRes {
+		br.Msg = "发送失败"
+		br.ErrMsg = "发送短信验证码失败"
+		return
+	}
+
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "发送成功"
+}
+
+// Login
+// @Title 用户登录
+// @Description 用户登录
+// @Param	request	body UserLoginReq true "type json string"
+// @Success 200 {object} models.LoginResp
+// @router /login [post]
+func (this *UserLoginController) Login() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg != "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	// 入参
+	type UserLoginReq struct {
+		LoginType  int    `description:"登录方式: 1-账号; 2-手机号; 3-邮箱"`
+		Username   string `description:"账号"`
+		Password   string `description:"密码"`
+		Mobile     string `description:"手机号"`
+		Email      string `description:"邮箱"`
+		VerifyCode string `description:"验证码"`
+	}
+	var req UserLoginReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	req.Username = strings.TrimSpace(req.Username)
+	req.Mobile = strings.TrimSpace(req.Mobile)
+	if req.Mobile != "" {
+		if !utils.ValidateMobileFormatat(req.Mobile) {
+			br.Msg = "您的手机号输入有误, 请检查"
+			return
+		}
+	}
+	req.Email = strings.TrimSpace(req.Email)
+	if req.Email != "" {
+		if !utils.ValidateEmailFormatat(req.Email) {
+			br.Msg = "您的邮箱输入有误, 请检查"
+			return
+		}
+	}
+	req.VerifyCode = strings.TrimSpace(req.VerifyCode)
+	if req.LoginType != 1 && req.LoginType != 2 && req.LoginType != 3 {
+		br.Msg = "登录方式有误"
+		br.ErrMsg = fmt.Sprintf("登录方式有误, Type: %d", req.LoginType)
+		return
+	}
+
+	sysUser := new(system.Admin)
+	// 账号密码登录
+	if req.LoginType == 1 {
+		if req.Username == "" {
+			br.Msg = "请输入账号"
+			return
+		}
+		if req.Password == "" {
+			br.Msg = "请输入密码"
+			return
+		}
+
+		// 判断账号是否为异常登录, 算作异常的话就需要换另外的方式去登录了
+		abnormalKey := fmt.Sprint(utils.CACHE_ABNORMAL_LOGIN, req.Username)
+		isAbnormal, _ := utils.Rc.RedisString(abnormalKey)
+		if isAbnormal != "" {
+			br.Ret = models.BaseRespCodeAbnormalLogin
+			br.Msg = "账号异常, 请进行手机号/邮箱校验"
+			return
+		}
+
+		// 账号密码校验
+		errPassKey := fmt.Sprint(utils.CACHE_LOGIN_ERR_PASS, req.Username)
+		accountUser, e := system.CheckSysUser(req.Username, req.Password)
+		if e != nil {
+			br.Ret = models.BaseRespCodeLoginErr
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "登录失败, 账号或密码错误"
+				if isAbnormal != "" {
+					return
+				}
+				// 错误密码计数, 超过6次标记异常
+				if !utils.Rc.IsExist(errPassKey) {
+					_ = utils.Rc.Put(errPassKey, 1, utils.GetTodayLastSecond())
+					return
+				}
+				errNum, _ := utils.Rc.RedisInt(errPassKey)
+				errNum += 1
+				if errNum >= 6 {
+					br.Ret = models.BaseRespCodeAbnormalLogin
+					br.Msg = "账号异常, 请进行手机号/邮箱校验"
+					// 标记异常登录, 重置计数
+					_ = utils.Rc.Put(abnormalKey, "true", utils.GetTodayLastSecond())
+					_ = utils.Rc.Delete(errPassKey)
+					return
+				}
+				_ = utils.Rc.Put(errPassKey, errNum, utils.GetTodayLastSecond())
+				return
+			}
+			br.Msg = "登录失败, 账号或密码错误"
+			br.ErrMsg = "登录失败, Err:" + e.Error()
+			return
+		}
+		if accountUser.Enabled == 0 {
+			br.Msg = "您的账号已被禁用, 如需登录, 请联系管理员"
+			br.ErrMsg = fmt.Sprintf("账号已被禁用, 登录账号: %s, 账户名称: %s", accountUser.AdminName, accountUser.RealName)
+			return
+		}
+
+		// 异常登录-是否登录间隔大于60天
+		if isAbnormal == "" {
+			abnormalTime := time.Now().AddDate(0, 0, -60)
+			lastLogin, _ := time.ParseInLocation(utils.FormatDateTime, accountUser.LastLoginTime, time.Local)
+			if !lastLogin.IsZero() && lastLogin.Before(abnormalTime) {
+				br.Msg = "请进行异常登录校验"
+				br.Ret = models.BaseRespCodeAbnormalLogin
+				// 标记异常登录
+				_ = utils.Rc.Put(abnormalKey, "true", utils.GetTodayLastSecond())
+				return
+			}
+		}
+
+		sysUser = accountUser
+	}
+
+	// 手机号
+	if req.LoginType == 2 {
+		if req.Mobile == "" {
+			br.Msg = "请输入手机号"
+			return
+		}
+		if !utils.ValidateMobileFormatat(req.Mobile) {
+			br.Msg = "您的手机号输入有误, 请检查"
+			return
+		}
+		if req.VerifyCode == "" {
+			br.Msg = "请输入验证码"
+			return
+		}
+		expiredTime := time.Now().Add(utils.VerifyCodeExpireMinute * time.Minute).Format(utils.FormatDateTime)
+		recordCond := ` AND source = ? AND mobile = ? AND code = ? AND expired_time <= ?`
+		recordPars := make([]interface{}, 0)
+		recordPars = append(recordPars, system.AdminVerifyCodeRecordSourceLogin, req.Mobile, req.VerifyCode, expiredTime)
+		recordOb := new(system.AdminVerifyCodeRecord)
+		_, e := recordOb.GetItemByCondition(recordCond, recordPars)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "验证码错误, 请重新输入"
+				return
+			}
+			br.Msg = "验证码错误, 请重新输入"
+			br.ErrMsg = "获取手机号登陆验证码失败, Err: " + e.Error()
+			return
+		}
+
+		mobileUser, e := system.GetSysUserByMobile(req.Mobile)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "您的手机号未绑定账号, 请检查"
+				return
+			}
+			br.Msg = "您的手机号未绑定账号, 请检查"
+			br.ErrMsg = "手机号获取用户失败, Err:" + e.Error()
+			return
+		}
+		if mobileUser.Enabled == 0 {
+			br.Msg = "您的账号已被禁用, 如需登录, 请联系管理员"
+			br.ErrMsg = fmt.Sprintf("账号已被禁用, 登录手机号: %s", req.Mobile)
+			return
+		}
+		sysUser = mobileUser
+	}
+
+	// 邮箱登录
+	if req.LoginType == 3 {
+		if req.Email == "" {
+			br.Msg = "请输入邮箱"
+			return
+		}
+		if !utils.ValidateEmailFormatat(req.Email) {
+			br.Msg = "您的邮箱输入有误, 请检查"
+			return
+		}
+		if req.VerifyCode == "" {
+			br.Msg = "请输入验证码"
+			return
+		}
+		expiredTime := time.Now().Add(utils.VerifyCodeExpireMinute * time.Minute).Format(utils.FormatDateTime)
+		recordCond := ` AND source = ? AND email = ? AND code = ? AND expired_time <= ?`
+		recordPars := make([]interface{}, 0)
+		recordPars = append(recordPars, system.AdminVerifyCodeRecordSourceLogin, req.Email, req.VerifyCode, expiredTime)
+		recordOb := new(system.AdminVerifyCodeRecord)
+		_, e := recordOb.GetItemByCondition(recordCond, recordPars)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "验证码错误, 请重新输入"
+				return
+			}
+			br.Msg = "验证码错误, 请重新输入"
+			br.ErrMsg = "获取手机号登陆验证码失败, Err: " + e.Error()
+			return
+		}
+
+		emailUser, e := system.GetSysUserByEmail(req.Email)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "您的邮箱未绑定账号, 请检查"
+				return
+			}
+			br.Msg = "您的邮箱未绑定账号, 请检查"
+			br.ErrMsg = "邮箱获取用户失败, Err:" + e.Error()
+			return
+		}
+		if emailUser.Enabled == 0 {
+			br.Msg = "您的账号已被禁用, 如需登录, 请联系管理员"
+			br.ErrMsg = fmt.Sprintf("账号已被禁用, 登录邮箱: %s", req.Email)
+			return
+		}
+		sysUser = emailUser
+	}
+
+	// 登录成功(无论哪种方式登录), 清除异常标记
+	abnormalCache := fmt.Sprint(utils.CACHE_ABNORMAL_LOGIN, sysUser.AdminName)
+	errPassCache := fmt.Sprint(utils.CACHE_LOGIN_ERR_PASS, sysUser.AdminName)
+	_ = utils.Rc.Delete(abnormalCache)
+	_ = utils.Rc.Delete(errPassCache)
+
+	account := utils.MD5(sysUser.AdminName)
+	token := utils.GenToken(account)
+	sysSession := new(system.SysSession)
+	sysSession.UserName = req.Username
+	sysSession.SysUserId = sysUser.AdminId
+	sysSession.ExpiredTime = time.Now().AddDate(0, 0, 90)
+	sysSession.IsRemember = 0 // 均需要做过期校验
+	sysSession.CreatedTime = time.Now()
+	sysSession.LastUpdatedTime = time.Now()
+	sysSession.AccessToken = token
+	if e := system.AddSysSession(sysSession); e != nil {
+		br.Msg = "登录失败"
+		br.ErrMsg = "新增session信息失败, Err:" + e.Error()
+		return
+	}
+
+	// 修改最后登录时间
+	{
+		sysUser.LastLoginTime = time.Now().Format(utils.FormatDateTime)
+		sysUser.LastUpdatedTime = time.Now().Format(utils.FormatDateTime)
+		_ = sysUser.Update([]string{"LastLoginTime", "LastUpdatedTime"})
+	}
+
+	resp := new(system.LoginResp)
+	resp.Authorization = token
+	resp.Authorization = "authorization=" + token + "$account=" + account
+	resp.RealName = sysUser.RealName
+	resp.AdminName = sysUser.AdminName
+	resp.RoleName = sysUser.RoleName
+	resp.SysRoleTypeCode = sysUser.RoleTypeCode //系统角色编码
+	resp.RoleTypeCode = sysUser.RoleTypeCode
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_GROUP {
+		resp.RoleTypeCode = utils.ROLE_TYPE_CODE_FICC_SELLER
+	}
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_TEAM {
+		resp.RoleTypeCode = utils.ROLE_TYPE_CODE_FICC_SELLER
+	}
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_FICC_DEPARTMENT {
+		resp.RoleTypeCode = utils.ROLE_TYPE_CODE_FICC_SELLER
+	}
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_GROUP {
+		resp.RoleTypeCode = utils.ROLE_TYPE_CODE_RAI_SELLER
+	}
+	if sysUser.RoleTypeCode == utils.ROLE_TYPE_CODE_RAI_DEPARTMENT {
+		resp.RoleTypeCode = utils.ROLE_TYPE_CODE_RAI_SELLER
+	}
+	if sysUser.RoleName == utils.ROLE_NAME_FICC_DIRECTOR {
+		resp.RoleTypeCode = utils.ROLE_TYPE_CODE_FICC_SELLER
+	}
+	resp.AdminId = sysUser.AdminId
+	var productName string
+	productId := services.GetProductId(sysUser.RoleTypeCode)
+	if productId == 1 {
+		productName = utils.COMPANY_PRODUCT_FICC_NAME
+	} else if productId == 2 {
+		productName = utils.COMPANY_PRODUCT_RAI_NAME
+	} else {
+		productName = "admin"
+	}
+	resp.ProductName = productName
+	resp.Authority = sysUser.Authority
+
+	// 设置redis缓存
+	{
+		// 获取不可信的登录态,并将该登录态重置掉,不允许多次登录
+		noTrustLoginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN_NO_TRUST, sysUser.AdminId)
+		noTrustLoginId, _ := utils.Rc.RedisString(noTrustLoginKey)
+		if noTrustLoginId != `` { // 如果存在不可信设备,那么将其下架
+			oldNoTrustLoginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN, noTrustLoginId)
+			_ = utils.Rc.Put(oldNoTrustLoginKey, "0", 30*time.Minute)
+		}
+
+		// 如果当前是不可信设备,那么将其加入到不可信名单
+		loginKey := fmt.Sprint(utils.CACHE_ACCESS_TOKEN_LOGIN, sysSession.Id)
+		_ = utils.Rc.Put(loginKey, "1", 30*time.Minute)
+		_ = utils.Rc.Put(noTrustLoginKey, sysSession.Id, 30*time.Minute)
+	}
+
+	// 新增登录记录
+	go func() {
+		record := new(system.SysUserLoginRecord)
+		record.Uid = sysUser.AdminId
+		record.UserName = req.Username
+		record.Ip = this.Ctx.Input.IP()
+		record.Stage = "login"
+		record.CreateTime = time.Now()
+		_ = system.AddSysUserLoginRecord(record)
+	}()
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "登录成功"
+}
+
+// ForgetAccountGet
+// @Title 忘记密码-账号校验
+// @Description 忘记密码-账号校验
+// @Param	request	body ForgetAccountGetReq true "type json string"
+// @Success 200 Ret=200 获取成功
+// @router /forget/account_get [post]
+func (this *UserLoginController) ForgetAccountGet() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	type ForgetAccountGetReq struct {
+		CaptchaId   string `description:"验证码ID"`
+		CaptchaCode string `description:"图形验证码"`
+		UserName    string `description:"用户名"`
+	}
+	var req ForgetAccountGetReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	req.UserName = strings.TrimSpace(req.UserName)
+	if req.UserName == "" {
+		br.Msg = "请输入账号"
+		return
+	}
+	if req.CaptchaId == "" || req.CaptchaCode == "" {
+		br.Msg = "请输入图形验证码"
+		return
+	}
+	store := services.CaptchaRedis{}
+	ok := store.Verify(req.CaptchaId, req.CaptchaCode, true)
+	if !ok {
+		br.Msg = "验证码错误, 请重新输入"
+		return
+	}
+
+	sysUser, e := system.GetSysUserByAdminName(req.UserName)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "用户不存在, 请检查"
+			return
+		}
+		br.Msg = "账号错误, 请重新输入"
+		br.ErrMsg = "用户名获取用户失败, Err: " + e.Error()
+		return
+	}
+
+	type ForgetAccountCheckResp struct {
+		Mobile      string `description:"手机号"`
+		Email       string `description:"邮箱"`
+		TelAreaCode string `description:"手机区号"`
+	}
+	resp := ForgetAccountCheckResp{
+		Mobile:      sysUser.Mobile,
+		Email:       sysUser.Email,
+		TelAreaCode: sysUser.TelAreaCode,
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}
+
+// ForgetCodeVerify
+// @Title 忘记密码-验证码校验
+// @Description 忘记密码-验证码校验
+// @Param	request	body ForgetCodeVerifyReq true "type json string"
+// @Success 200 Ret=200 获取成功
+// @router /forget/code_verify [post]
+func (this *UserLoginController) ForgetCodeVerify() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	type ForgetCodeVerifyReq struct {
+		FindType   int    `description:"密码找回方式: 1-手机号; 2-邮箱"`
+		VerifyCode string `description:"验证码"`
+		UserName   string `description:"用户名"`
+		Mobile     string `description:"手机号"`
+		Email      string `description:"邮箱"`
+	}
+	var req ForgetCodeVerifyReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	req.UserName = strings.TrimSpace(req.UserName)
+	if req.UserName == "" {
+		br.Msg = "请输入账号"
+		return
+	}
+	if req.VerifyCode == "" {
+		br.Msg = "请输入验证码"
+		return
+	}
+
+	// 手机号找回
+	var verifyRes bool
+	if req.FindType == 1 {
+		req.Mobile = strings.TrimSpace(req.Mobile)
+		if req.Mobile == "" {
+			br.Msg = "请输入手机号"
+			return
+		}
+		if !utils.ValidateMobileFormatat(req.Mobile) {
+			br.Msg = "您的手机号输入有误, 请检查"
+			return
+		}
+		expiredTime := time.Now().Add(utils.VerifyCodeExpireMinute * time.Minute).Format(utils.FormatDateTime)
+		recordCond := ` AND source = ? AND mobile = ? AND code = ? AND expired_time <= ?`
+		recordPars := make([]interface{}, 0)
+		recordPars = append(recordPars, system.AdminVerifyCodeRecordSourceForget, req.Mobile, req.VerifyCode, expiredTime)
+		recordOb := new(system.AdminVerifyCodeRecord)
+		_, e := recordOb.GetItemByCondition(recordCond, recordPars)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "验证码错误, 请重新输入"
+				return
+			}
+			br.Msg = "验证码错误, 请重新输入"
+			br.ErrMsg = "获取手机号登陆验证码失败, Err: " + e.Error()
+			return
+		}
+
+		verifyRes = true
+	}
+
+	// 邮箱找回
+	if req.FindType == 2 {
+		req.Email = strings.TrimSpace(req.Email)
+		if req.Email == "" {
+			br.Msg = "请输入邮箱"
+			return
+		}
+		if !utils.ValidateEmailFormatat(req.Email) {
+			br.Msg = "您的邮箱输入有误, 请检查"
+			return
+		}
+		expiredTime := time.Now().Add(utils.VerifyCodeExpireMinute * time.Minute).Format(utils.FormatDateTime)
+		recordCond := ` AND source = ? AND email = ? AND code = ? AND expired_time <= ?`
+		recordPars := make([]interface{}, 0)
+		recordPars = append(recordPars, system.AdminVerifyCodeRecordSourceForget, req.Email, req.VerifyCode, expiredTime)
+		recordOb := new(system.AdminVerifyCodeRecord)
+		_, e := recordOb.GetItemByCondition(recordCond, recordPars)
+		if e != nil {
+			if e.Error() == utils.ErrNoRow() {
+				br.Msg = "验证码错误, 请重新输入"
+				return
+			}
+			br.Msg = "验证码错误, 请重新输入"
+			br.ErrMsg = "获取手机号登陆验证码失败, Err: " + e.Error()
+			return
+		}
+
+		verifyRes = true
+	}
+
+	if verifyRes {
+		successKey := fmt.Sprint(utils.CACHE_FIND_PASS_VERIFY, req.UserName)
+		_ = utils.Rc.Put(successKey, "true", utils.GetTodayLastSecond())
+	}
+
+	br.Data = verifyRes
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// ForgetResetPass
+// @Title 忘记密码-重置密码
+// @Description 忘记密码-重置密码
+// @Param	request	body ForgetResetPassReq true "type json string"
+// @Success 200 Ret=200 获取成功
+// @router /forget/reset_pass [post]
+func (this *UserLoginController) ForgetResetPass() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	type ForgetResetPassReq struct {
+		UserName   string `description:"用户名"`
+		Password   string `description:"密码"`
+		RePassword string `description:"重复密码"`
+	}
+	var req ForgetResetPassReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	req.UserName = strings.TrimSpace(req.UserName)
+	if req.UserName == "" {
+		br.Msg = "请输入账号"
+		return
+	}
+	req.Password = strings.TrimSpace(req.Password)
+	if req.Password == "" {
+		br.Msg = "请输入新密码"
+		return
+	}
+	req.RePassword = strings.TrimSpace(req.RePassword)
+	if req.RePassword == "" {
+		br.Msg = "请确认新密码"
+		return
+	}
+	if req.Password != req.RePassword {
+		br.Msg = "两次密码输入不一致, 请检查"
+		return
+	}
+
+	// 接上一步-验证码校验成功后才允许修改
+	successKey := fmt.Sprint(utils.CACHE_FIND_PASS_VERIFY, req.UserName)
+	verifyOk, _ := utils.Rc.RedisString(successKey)
+	if verifyOk != "true" {
+		br.Msg = "验证码校验失效, 请重新验证"
+		br.ErrMsg = "重置密码异常, 验证码校验已失效"
+		return
+	}
+
+	sysUser, e := system.GetSysAdminByName(req.UserName)
+	if e != nil {
+		if e.Error() == utils.ErrNoRow() {
+			br.Msg = "用户不存在, 请检查"
+			return
+		}
+		br.Msg = "操作失败"
+		br.ErrMsg = "密码找回获取用户失败, Err:" + e.Error()
+		return
+	}
+
+	b, e := base64.StdEncoding.DecodeString(req.Password)
+	if e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "解析密码失败, Err:" + e.Error()
+		return
+	}
+	pwd := string(b)
+	pwd = utils.MD5(pwd)
+
+	sysUser.Password = pwd
+	sysUser.LastUpdatedPasswordTime = time.Now().Format(utils.FormatDateTime)
+	// 重置密码后更新一下登录时间, 不然账号密码登录如果超过60天还会被异常校验再校验一次, 影响体验
+	sysUser.LastLoginTime = time.Now().Format(utils.FormatDateTime)
+	sysUser.LastUpdatedTime = time.Now().Format(utils.FormatDateTime)
+	if e = sysUser.Update([]string{"Password", "LastUpdatedPasswordTime", "LastLoginTime", "LastUpdatedTime"}); e != nil {
+		br.Msg = "操作失败"
+		br.ErrMsg = "更新密码失败, Err: " + e.Error()
+		return
+	}
+
+	// 如果有异常标记也清除掉
+	abnormalKey := fmt.Sprint(utils.CACHE_ABNORMAL_LOGIN, req.UserName)
+	_ = utils.Rc.Delete(abnormalKey)
+	_ = utils.Rc.Delete(successKey)
+
+	// 同步ETA用户缓存
+	if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox {
+		var syncData system.SyncAdminData
+		syncData.Source = utils.SOURCE_ETA_FLAG
+		syncData.AdminName = sysUser.AdminName
+		_ = utils.Rc.LPush(utils.CACHE_SYNC_ADMIN, syncData)
+	}
+
+	br.Data = true
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "操作成功"
+}
+
+// AreaCodeList
+// @Title 手机号区号列表
+// @Description 手机号区号列表
+// @Success 200 Ret=200 获取成功
+// @router /area_code/list [get]
+func (this *UserLoginController) AreaCodeList() {
+	br := new(models.BaseResponse).Init()
+	defer func() {
+		if br.ErrMsg == "" {
+			br.IsSendEmail = false
+		}
+		this.Data["json"] = br
+		this.ServeJSON()
+	}()
+
+	type AreaCodeListResp struct {
+		Name  string `description:"地区"`
+		Value string `description:"区号"`
+	}
+	resp := make([]AreaCodeListResp, 0)
+	confAuth, e := company.GetConfigDetailByCode(company.ConfAreaCodeListKey)
+	if e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取手机号区号配置失败, Err: " + e.Error()
+		return
+	}
+	if confAuth.ConfigValue == "" {
+		br.Msg = "获取失败"
+		br.ErrMsg = "手机号区号配置为空"
+		return
+	}
+	if e := json.Unmarshal([]byte(confAuth.ConfigValue), &resp); e != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "手机号区号配置有误"
+		return
+	}
+
+	br.Data = resp
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+}

+ 3 - 1
go.mod

@@ -20,6 +20,7 @@ require (
 	github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b
 	github.com/gorilla/websocket v1.5.0
 	github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53
+	github.com/mojocn/base64Captcha v1.3.5
 	github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19
 	github.com/olivere/elastic/v7 v7.0.30
 	github.com/rdlucklib/rdluck_tools v1.0.3
@@ -36,7 +37,6 @@ require (
 require (
 	cloud.google.com/go v0.104.0 // indirect
 	github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
-	github.com/alibabacloud-go/darabonba-number v1.0.4 // indirect
 	github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 // indirect
 	github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
 	github.com/alibabacloud-go/openapi-util v0.1.0 // indirect
@@ -59,6 +59,7 @@ require (
 	github.com/fsnotify/fsnotify v1.6.0 // indirect
 	github.com/garyburd/redigo v1.6.3 // indirect
 	github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc // indirect
+	github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac // indirect
 	github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82 // indirect
@@ -96,6 +97,7 @@ require (
 	github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect
 	github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect
 	golang.org/x/crypto v0.5.0 // indirect
+	golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 // indirect
 	golang.org/x/net v0.5.0 // indirect
 	golang.org/x/sys v0.4.0 // indirect
 	golang.org/x/text v0.6.0 // indirect

+ 5 - 159
go.sum

@@ -1,37 +1,24 @@
-baliance.com/gooxml v1.0.1 h1:fG5lmxmjEVFfbKQ2NuyCuU3hMuuOb5avh5a38SZNO1o=
-baliance.com/gooxml v1.0.1/go.mod h1:+gpUgmkAF4zCtwOFPNRLDAvpVRWoKs5EeQTSv/HYFnw=
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
 cloud.google.com/go v0.104.0 h1:gSmWO7DY1vOm0MVU6DNXM11BWHHsTUmsC5cv1fuW5X8=
 cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
-cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk=
-cloud.google.com/go/iam v0.3.0 h1:exkAomrVUuzx9kWFI1wm3KI0uoDeUFPB4kKGzx6x+Gc=
-cloud.google.com/go/storage v1.23.0 h1:wWRIaDURQA8xxHguFCshYepGlrWIrbBnAmc7wfg07qY=
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
-github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
 github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
 github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
 github.com/SebastiaanKlippert/go-wkhtmltopdf v1.7.2 h1:LORAatv6KuKheYq8HXehiwx3f/VGuzJBNSydUDQ98EM=
 github.com/SebastiaanKlippert/go-wkhtmltopdf v1.7.2/go.mod h1:TY8r0gmwEL1c5Lbd66NgQCkL4ZjGDJCMVqvbbFvUx20=
-github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s=
 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
-github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc=
 github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
 github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
 github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo=
 github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
 github.com/alibabacloud-go/alimt-20181012/v2 v2.0.0 h1:RZF3WXYiPB/m1FiZS51udLbpAvg0urYi1wAfB18kiUQ=
 github.com/alibabacloud-go/alimt-20181012/v2 v2.0.0/go.mod h1:1J4N3YfuJjOdknqWFERjt82R3kTO6QXk/vuAbQtzc5I=
-github.com/alibabacloud-go/darabonba-number v1.0.4 h1:aTY1TanasI0A1AYT3Co+PLttFSW0qzUz9wFLIpG0tqI=
-github.com/alibabacloud-go/darabonba-number v1.0.4/go.mod h1:9NJbJwLCPxHzFwYqnr27G2X8pSTAz0uSQEJsrjr/kqw=
 github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.0/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
 github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2 h1:2kR1YkvQloHUstmPcG0Sjk24zTKbza7izzJfJNwBFSs=
 github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.2/go.mod h1:5JHVmnHvGzR2wNdgaW1zDLQG8kOC4Uec8ubkMogW7OQ=
@@ -41,8 +28,6 @@ github.com/alibabacloud-go/dm-20151123/v2 v2.0.1 h1:wEIEI4vvk7vtiJmXEUnom+ylEBfy
 github.com/alibabacloud-go/dm-20151123/v2 v2.0.1/go.mod h1:q9cd++SgWfT9U5kdBbRRlvzrr0HOKayLxfe9s0NVZhQ=
 github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q=
 github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
-github.com/alibabacloud-go/ocr-20191230/v3 v3.0.6 h1:fWBzB2Ef6ciNmh/CqMxxw1calsbHnVWdtlPEdecPQNQ=
-github.com/alibabacloud-go/ocr-20191230/v3 v3.0.6/go.mod h1:0KL/I9AMZMwecMjTcjh4/DK1RIBL+KCyBBcW8P2VZMA=
 github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
 github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY=
 github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
@@ -72,9 +57,7 @@ github.com/alibabacloud-go/tea-utils/v2 v2.0.1/go.mod h1:U5MTY10WwlquGPS34DOeomU
 github.com/alibabacloud-go/tea-xml v1.1.1/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
 github.com/alibabacloud-go/tea-xml v1.1.2 h1:oLxa7JUXm2EDFzMg+7oRsYc+kutgCVwm+bZlhhmvW5M=
 github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
-github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6 h1:45bxf7AZMwWcqkLzDAQugVEwedisr5nRJ1r+7LYnv0U=
 github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
-github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI=
 github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
 github.com/aliyun/alibaba-cloud-sdk-go v1.61.1656 h1:YTWW7mBjwviuRvqiEpqaAj0AyVPj9AoJmQGCb5lXYUc=
 github.com/aliyun/alibaba-cloud-sdk-go v1.61.1656/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
@@ -86,11 +69,8 @@ github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x0
 github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
 github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20211218165449-dd623ecc2f02 h1:o2oaBQGTzO+xNh12e7xWkphNe7H2DTiWv1ml9a2P9PQ=
 github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20211218165449-dd623ecc2f02/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY=
-github.com/apache/thrift v0.12.0 h1:pODnxUFNcjP9UTLZGTdeh+j16A8lJbRvD3rOtrk/7bs=
 github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
-github.com/astaxie/beego v1.12.3 h1:SAQkdD2ePye+v8Gn1r4X6IKZM1wd28EyUOVQ3PDSOOQ=
 github.com/astaxie/beego v1.12.3/go.mod h1:p3qIm0Ryx7zeBHLljmd7omloyca1s4yu1a8kM1FkpIA=
-github.com/aws/aws-sdk-go v1.42.23 h1:V0V5hqMEyVelgpu1e4gMPVCJ+KhmscdNxP/NWP1iCOA=
 github.com/aws/aws-sdk-go v1.42.23/go.mod h1:gyRszuZ/icHmHAVE4gc/r+cfCmhA1AD+vqfWbgI+eHs=
 github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA=
 github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
@@ -98,9 +78,7 @@ github.com/beego/bee/v2 v2.0.4 h1:nEjPwxJ8D+cr54eWChJGoGRH7bJ7OQwbhx8rU0OQf7E=
 github.com/beego/bee/v2 v2.0.4/go.mod h1:wq0YrEmPcdNfDNpaUgiTkaW9zso7M8n0HCCShEBOzM0=
 github.com/beego/beego/v2 v2.0.7 h1:9KNnUM40tn3pbCOFfe6SJ1oOL0oTi/oBS/C/wCEdAXA=
 github.com/beego/beego/v2 v2.0.7/go.mod h1:f0uOEkmJWgAuDTlTxUdgJzwG3PDSIf3UWF3NpMohbFE=
-github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd h1:jZtX5jh5IOMu0fpOTC3ayh6QGSPJ/KWOv1lgPvbRw1M=
 github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
-github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542 h1:nYXb+3jF6Oq/j8R/y90XrKpreCxIalBWfeyeKymgOPk=
 github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -110,39 +88,23 @@ github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6
 github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d h1:pVrfxiGfwelyab6n21ZBkbkmbevaf+WvMIiR7sr97hw=
 github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
 github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
-github.com/casbin/casbin v1.9.1 h1:ucjbS5zTrmSLtH4XogqOG920Poe6QatdXtz1FEbApeM=
-github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
 github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
-github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
-github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
 github.com/clbanning/mxj/v2 v2.5.5 h1:oT81vUeEiQQ/DcHbzSytRngP6Ky9O+L+0Bw0zSJag9E=
 github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
-github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg=
 github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
-github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f h1:WBZRG4aNOuI15bLRrCgN8fCq8E5Xuty6jGbmSNEvSsU=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
-github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
 github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
-github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
-github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
-github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg=
 github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
-github.com/couchbase/go-couchbase v0.1.0 h1:g4bCvDwRL+ZL6HLhYeRlXxEYP31Wpy0VFxnFw6efEp8=
 github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
-github.com/couchbase/gomemcached v0.1.3 h1:HIc5qMYNbuhB7zNaiEtj61DCYkquAwrQlf64q7JzdEY=
 github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
-github.com/couchbase/goutils v0.1.0 h1:0WLlKJilu7IBm98T8nS9+J36lBFVLRUSIUtyD/uWpAE=
-github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76 h1:Lgdd/Qp96Qj8jqLpq2cI1I1X7BJnu06efS+XkhRoLUQ=
 github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
@@ -153,27 +115,19 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
-github.com/eapache/go-resiliency v1.1.0 h1:1NtRmCAqadE2FN4ZcN6g90TP3uk8cg9rn9eNK2197aU=
 github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
-github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
 github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
-github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
 github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
-github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
 github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
-github.com/elastic/go-elasticsearch/v6 v6.8.10 h1:2lN0gJ93gMBXvkhwih5xquldszpm8FlUwqG5sPzr6a8=
 github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
 github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
 github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
-github.com/envoyproxy/go-control-plane v0.9.4 h1:rEvIZUSZ3fx39WIi3JkQqQBitGwpELBIYWeBVh6wn+E=
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
-github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
 github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
-github.com/flosch/pongo2 v0.0.0-20200529170236-5abacdfa4915 h1:rNVrewdFbSujcoKZifC6cHJfqCTbCIR7XTLHW5TqUWU=
 github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
 github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
@@ -183,21 +137,12 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4
 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
 github.com/garyburd/redigo v1.6.3 h1:HCeeRluvAgMusMomi1+6Y5dmFOdYV/JzoRrrbFlkGIc=
 github.com/garyburd/redigo v1.6.3/go.mod h1:rTb6epsqigu3kYKBnaF028A7Tf/Aw5s0cqA47doKKqw=
-github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c h1:iRTj5SRYwbvsygdwVp+y9kZT145Y1s6xOPpeOEIeGc4=
 github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
-github.com/go-delve/delve v1.5.0 h1:gQsRvFdR0BGk19NROQZsAv6iG4w5QIZoJlxJeEUBb0c=
 github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/kit v0.12.1-0.20220826005032-a7ba4fa4e289 h1:468Nv6YtYO38Z+pFL6fRSVILGfdTFPei9ksVneiUHUc=
-github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
-github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
-github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
-github.com/go-redis/redis v6.14.2+incompatible h1:UE9pLhzmWf+xHNmZsoccjXosPicuiNaInPgym8nzfg0=
 github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
-github.com/go-redis/redis/v7 v7.4.0 h1:7obg6wUoj05T0EpY0o8B59S9w5yeMWql7sw2kwNW1x4=
 github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc h1:jZY+lpZB92nvBo2f31oPC/ivGll6NcsnEOORm8Fkr4M=
 github.com/go-redis/redis/v8 v8.11.6-0.20220405070650-99c79f7041fc/go.mod h1:25mL1NKxbJhB63ihiK8MnNeTRd+xAizd6bOdydrTLUQ=
 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
@@ -205,28 +150,22 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
 github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
 github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
 github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
-github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I=
 github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
 github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
 github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
 github.com/go-xorm/xorm v0.7.9 h1:LZze6n1UvRmM5gpL9/U9Gucwqo6aWlFVlfcHKH10qA0=
 github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ=
-github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
 github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
-github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d h1:lBXNCxVENCipq4D1Is42JVOP4eQjlB8TQ6H69Yx5J9Q=
 github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
+github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
+github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -244,9 +183,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
 github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0=
 github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
 github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac h1:Q0Jsdxl5jbxouNs1TQYt0gxesYMU4VXRbsTlgDloZ50=
 github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc=
@@ -262,7 +199,6 @@ github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9 h1:V2IgdyerlBa/MxaEFR
 github.com/gonum/matrix v0.0.0-20181209220409-c518dec07be9/go.mod h1:0EXg4mc1CNP0HCqCz+K4ts155PXIlUywf0wqN+GfPZw=
 github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b h1:fbskpz/cPqWH8VqkQ7LJghFkl2KPAiIFUHrTJ2O3RGk=
 github.com/gonum/stat v0.0.0-20181125101827-41a0da705a5b/go.mod h1:Z4GIJBJO3Wa4gD4vbwQxXXZ+WHmW6E9ixmNrwvs0iZs=
-github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c h1:964Od4U6p2jUkFxvCydnIczKteheJEzHRToSGK3Bnlw=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -273,43 +209,26 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
-github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
-github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
-github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
-github.com/googleapis/enterprise-certificate-proxy v0.1.0 h1:zO8WHNx/MYiAKJ3d5spxZXZE6KHmIQGQcAzwUzV7qQw=
 github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
-github.com/googleapis/gax-go/v2 v2.4.0 h1:dS9eYAjhrE2RjmzYw2XAPvcXfmcQLtFEQWn0CR82awk=
-github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=
 github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
-github.com/gorilla/mux v1.6.2 h1:Pgr17XVTNXAk3q/r4CpKzC5xBM/qW1uVLV+IhRZpIIk=
 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
 github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
 github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc=
 github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
-github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI=
 github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
 github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
-github.com/jackc/pgx v3.6.0+incompatible h1:bJeo4JdVbDAW8KB2m8XkFeo8CPipREoG37BwEoKGz+Q=
 github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
@@ -318,43 +237,31 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw
 github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
 github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
 github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
-github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
 github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
 github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
-github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
-github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
 github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
 github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53 h1:+8X3HMX8A2QhvNg3dImiQTCiVUt6BQXz1mW+/DrWI+k=
 github.com/kgiannakakis/mp3duration v0.0.0-20191013070830-d834f8d5ed53/go.mod h1:E61jD6q4yJ6Cu9uDGRAfiENM1G5TVZhOog0Y3+GgTpQ=
-github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
-github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6 h1:wxyqOzKxsRJ6vVRL9sXQ64Z45wmBuQ+OTH9sLsC5rKc=
 github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ=
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
 github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
 github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=
-github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI=
 github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
 github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@@ -372,13 +279,10 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
 github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
 github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
-github.com/mozillazg/go-pinyin v0.19.0 h1:p+J8/kjJ558KPvVGYLvqBhxf8jbZA2exSLCs2uUVN8c=
-github.com/mozillazg/go-pinyin v0.19.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
+github.com/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0=
+github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
-github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
 github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
 github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19 h1:LhWT2dBuNkYexwRSsPpYh67e0ikmH1ebBDaVkGHoMts=
 github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19/go.mod h1:LjhyrWzOLJ9l1azMoNr9iCvfNrHEREqvJHzSLQcD0/o=
@@ -394,7 +298,6 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
 github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-github.com/onsi/ginkgo/v2 v2.1.3 h1:e/3Cwtogj0HA+25nMP1jCMDIf8RtRYbGwGGuBIFztkc=
 github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
 github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
@@ -402,16 +305,11 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
 github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
 github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
 github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
-github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
 github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
-github.com/openzipkin/zipkin-go v0.1.6 h1:yXiysv1CSK7Q5yjGy1710zZGnsbMUIjluWBxtLXHPBo=
 github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
 github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pelletier/go-toml v1.9.2 h1:7NiByeVF4jKSG1lDF3X8LTIkq2/bu+1uYbIm1eS5tzk=
-github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233 h1:jmJndGFBPjNWW+MAYarU/Nl8QrQVzbw4B/AYE0LzETo=
 github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
-github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I=
 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -443,7 +341,6 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
 github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
 github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
 github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
-github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a h1:9ZKAASQSHhDYGoxY8uLVpewe1GDZ2vu2Tr/vTdVAkFQ=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
 github.com/rdlucklib/rdluck_tools v1.0.3 h1:iOtK2QPlPQ6CL6c1htCk5VnFCHzyG6DCfJtunrMswK0=
 github.com/rdlucklib/rdluck_tools v1.0.3/go.mod h1:9Onw9o4w19C8KE5lxb8GyxgRBbZweRVkQSc79v38EaA=
@@ -461,11 +358,8 @@ github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18/go.mod h1:nkxAfR/
 github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
 github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
 github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
-github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0 h1:QIF48X1cihydXibm+4wfAc0r/qyPyuFiPFRNphdMpEE=
 github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
-github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400 h1:091wFNQB3PXcL5+me0joH7EiyqQaI0wGMpEjVCkK04U=
 github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s=
-github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d h1:NVwnfyR3rENtlz62bcrkXME3INVUa4lcdGt+opvxExs=
 github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
 github.com/silenceper/wechat/v2 v2.1.3 h1:vMHnU9PG6wROTqkQI+HnBiEzriEEE+sbmGHg9cdb4yc=
 github.com/silenceper/wechat/v2 v2.1.3/go.mod h1:FoU0YvegD+Z85TBGQhjkXjY8BMb0+cagbe9BqJ0fKhA=
@@ -474,31 +368,20 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
 github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
 github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/smartwalle/pongo2render v1.0.1 h1:rsPnDTu/+zIT5HEB5RbMjxKY5hisov26j0isZL/7YS0=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
-github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck=
 github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
-github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 h1:hp2CYQUINdZMHdvTdXtPOY2ainKl4IoMcpAXEf2xj3Q=
 github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9/go.mod h1:SnhjPscd9TpLiy1LpzGSKh3bXCfxxXuqd9xmQJy3slM=
-github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
 github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/smartystreets/gunit v1.4.2 h1:tyWYZffdPhQPfK5VsMQXfauwnJkqg7Tv5DLuQVYxq3Q=
 github.com/smartystreets/gunit v1.4.2/go.mod h1:ZjM1ozSIMJlAz/ay4SG8PeKF00ckUp+zMHZXV9/bvak=
-github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
 github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
 github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
-github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
-github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
-github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
-github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec h1:q6XVwXmKvCRHRqesF3cSv6lNqqHi0QWOvgDlSohg8UA=
 github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
 github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -510,9 +393,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
 github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
 github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
 github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
-github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c h1:3eGShk3EQf5gJCYW+WzA0TEJQd37HLOmlYF7N0YJwv0=
 github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
 github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
 github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
@@ -528,12 +409,8 @@ github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
 github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
 github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
 github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
-github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83 h1:9AUN7+NK4IV+A11igqjQM5i8obiOAQo4SXgjaxe+orI=
 github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
-github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b h1:0Ve0/CCjiAiyKddUMUn3RwIGlq2iTW4GuVzyoKBYO/8=
 github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
-github.com/wenzhenxi/gorsa v0.0.0-20210524035706-528c7050d703 h1:Tiqr9EWpYopXZf668mgTNWguzE6ssRIEviULO3gSWnU=
-github.com/wenzhenxi/gorsa v0.0.0-20210524035706-528c7050d703/go.mod h1:nfhBTKji6rC8lrjyikx8NJ85JHg6ZQam0a9Je+2RVOg=
 github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 h1:6932x8ltq1w4utjmfMPVj09jdMlkY0aiA6+Skbtl3/c=
 github.com/xuri/efp v0.0.0-20220603152613-6918739fd470/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
 github.com/xuri/excelize/v2 v2.6.1 h1:ICBdtw803rmhLN3zfvyEGH3cwSmZv+kde7LhTDT659k=
@@ -542,31 +419,15 @@ github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 h1:OAmKAfT06//esDdpi/DZ8Q
 github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
 github.com/yidane/formula v0.0.0-20210902154546-0782e1736717 h1:9CTJJpdISGxMAELfVlprj5kZEsJEaNAWiobv8ZAd72U=
 github.com/yidane/formula v0.0.0-20210902154546-0782e1736717/go.mod h1:9/dQiKiN04yPMdgsuFmKGuI2Hdp6OmFV9gSWS1col6g=
-github.com/ylywyn/jpush-api-go-client v0.0.0-20190906031852-8c4466c6e369 h1:g95WlXTqXFLM36fhvDKcZWHTUnBb2KCEn4tuSizk/d8=
 github.com/ylywyn/jpush-api-go-client v0.0.0-20190906031852-8c4466c6e369/go.mod h1:Nv7wKD2/bCdKUFNKcJRa99a+1+aSLlCRJFriFYdjz/I=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973 h1:iCnkJ/qjKZGdZnlcj1N55AxPDan814kpc3s1cDpQKd8=
 github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
 github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
 github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
-go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc=
-go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg=
-go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4=
 go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
-go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
 go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
-go.opentelemetry.io/otel v1.8.0 h1:zcvBFizPbpa1q7FehvFiHbQwGzmPILebO0tyqIR5Djg=
-go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.8.0 h1:FVy7BZCjoA2Nk+fHqIdoTmm554J9wTX+YcrDp+mc368=
-go.opentelemetry.io/otel/sdk v1.8.0 h1:xwu69/fNuwbSHWe/0PGS888RmjWY181OmcXDQKu7ZQk=
-go.opentelemetry.io/otel/trace v1.8.0 h1:cSy0DF9eGI5WIfNwZ1q2iUyGj00tGzP24dE1lOlHrfY=
-go.starlark.net v0.0.0-20190702223751-32f345186213 h1:lkYv5AKwvvduv5XWP6szk/bvvgO6aDeUujhZQXIFTes=
-go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
-go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
-go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
-golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4 h1:QlVATYS7JBoZMVaf+cNjb90WD/beKVHnIxFKT4QaHVI=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -578,18 +439,16 @@ golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0
 golang.org/x/crypto v0.0.0-20220817201139-bc19a97f63c8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
 golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4 h1:c2HOrn5iMezYjSlGPncknSEr/8x5LELb/ilJbXi9DEA=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9 h1:LRtI4W37N+KFebI/qV0OFiLUv4GLOWeEW5hn/KEJvxE=
 golang.org/x/image v0.0.0-20220413100746-70e8d0d3baa9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0=
 golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -616,7 +475,6 @@ golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
 golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
-golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -625,7 +483,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -656,7 +513,6 @@ golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
 golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -680,31 +536,25 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
 golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f h1:uF6paiQQebLeSXkrTqHqz0MXhXXS1KgF41eUdBNvxK0=
 google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
-google.golang.org/api v0.93.0 h1:T2xt9gi0gHdxdnRkVQhT8mIvPaXKNsDNWz+L696M66M=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
-google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
 google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
 google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
-google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc h1:Nf+EdcTLHR8qDNN/KfkQL0u0ssxt9OhbaWCl5C0ucEI=
 google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
 google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
 google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -718,7 +568,6 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
 google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
 gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
@@ -727,7 +576,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
@@ -737,7 +585,6 @@ gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
 gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
@@ -757,7 +604,6 @@ gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc h1:/hemPrYIhOhy8zYrNj+069zDB68us2sMGsfkFJO0iZs=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
 xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=

+ 12 - 0
models/base.go

@@ -1,5 +1,10 @@
 package models
 
+const (
+	BaseRespCodeAbnormalLogin = 4011 // 异常登录状态码
+	BaseRespCodeLoginErr      = 4012 // 账号或密码输入错误
+)
+
 type BaseResponse struct {
 	Ret         int
 	Msg         string
@@ -37,3 +42,10 @@ type BaseRequest struct {
 func (br *BaseRequest) Init() *BaseRequest {
 	return &BaseRequest{}
 }
+
+type ResultData struct {
+	Code   int         `json:"code" description:"状态码"`
+	Msg    string      `json:"msg" description:"提示信息"`
+	Data   interface{} `json:"data" description:"返回数据"`
+	ErrMsg string      `json:"-" description:"错误信息,不用返回给前端,只是做日志记录"`
+}

+ 5 - 1
models/company/company_config.go

@@ -4,6 +4,10 @@ import (
 	"github.com/beego/beego/v2/client/orm"
 )
 
+const (
+	ConfAreaCodeListKey = "area_code_list" // 手机号区号列表
+)
+
 type CrmConfig struct {
 	ConfigValue string `description:"详情"`
 }
@@ -15,7 +19,7 @@ func GetConfigValueByCode(configCode string) (total int, err error) {
 	return
 }
 
-//修改
+// 修改
 func CrmConfigUpdate(newValue, configCode string) (err error) {
 	o := orm.NewOrm()
 	sql := `UPDATE crm_config SET  config_value=?   WHERE config_code=  ?`

+ 13 - 0
models/data_manage/edb_data_insert_config.go

@@ -26,6 +26,19 @@ func GetEdbDataInsertConfigByEdbId(edbInfoId int) (item *EdbDataInsertConfig, er
 	return
 }
 
+// GetEdbDataInsertConfigByEdbIdList 根据指标id列表 获取数据插入配置详情
+func GetEdbDataInsertConfigByEdbIdList(edbInfoIdList []int) (items []*EdbDataInsertConfig, err error) {
+	num := len(edbInfoIdList)
+	if num <= 0 {
+		return
+	}
+
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT * FROM edb_data_insert_config WHERE edb_info_id in (` + utils.GetOrmInReplace(num) + `) `
+	_, err = o.Raw(sql, edbInfoIdList).QueryRows(&items)
+	return
+}
+
 // CreateEdbDataInsertConfigAndData 创建数据插入配置规则,及插入数据
 func CreateEdbDataInsertConfigAndData(edbInfo *EdbInfo, date time.Time, value float64) (err error, errMsg string, isSendEmail bool) {
 	isSendEmail = true

+ 5 - 168
models/data_manage/edb_info_calculate.go

@@ -2,10 +2,10 @@ package data_manage
 
 import (
 	"errors"
+	"eta/eta_api/utils"
 	"fmt"
 	"github.com/beego/beego/v2/client/orm"
 	"github.com/yidane/formula"
-	"eta/eta_api/utils"
 	"strconv"
 	"strings"
 	"time"
@@ -71,6 +71,9 @@ type EdbInfoCalculateDetail struct {
 	ModifyTime         time.Time `description:"修改时间"`
 	StartDate          string    `description:"开始日期"`
 	EndDate            string    `description:"结束日期"`
+	LatestDate         string    `description:"实际的结束日期"`
+	LatestValue        float64   `description:"最近实际数据的值"`
+	EndValue           float64   `description:"结束日期的值(可能是插入值)"`
 	EdbType            int       `description:"指标类型:1:基础指标,2:计算指标"`
 }
 
@@ -84,7 +87,7 @@ func GetEdbInfoCalculateDetail(edbInfoId, source int) (list []*EdbInfoCalculateD
 	//
 	//sql = fmt.Sprintf(sql, calculateTableName)
 
-	sql := ` SELECT a.edb_info_calculate_mapping_id,a.edb_info_id,a.source,a.source_name,a.edb_code,a.from_edb_info_id,a.from_edb_code,a.from_source,a.from_source_name,a.sort,a.create_time,a.modify_time,a.from_tag,a.move_value,b.edb_name_source as from_edb_name,b.start_date,b.end_date,b.edb_type FROM edb_info_calculate_mapping AS a
+	sql := ` SELECT a.edb_info_calculate_mapping_id,a.edb_info_id,a.source,a.source_name,a.edb_code,a.from_edb_info_id,a.from_edb_code,a.from_source,a.from_source_name,a.sort,a.create_time,a.modify_time,a.from_tag,a.move_value,b.edb_name_source as from_edb_name,b.start_date,b.end_date,b.latest_date,b.latest_value,b.edb_type FROM edb_info_calculate_mapping AS a
 			INNER JOIN edb_info AS b ON a.from_edb_info_id=b.edb_info_id
 			WHERE a.edb_info_id=? ORDER BY sort ASC `
 
@@ -232,172 +235,6 @@ func GetEdbInfoCalculateMap(edbInfoId, source int) (list []*EdbInfo, err error)
 	return
 }
 
-// 指标运算
-func AddCalculate(req *EdbInfoCalculateBatchSaveReq, fromEdbInfo []*EdbInfoCalculateDetail, edbCode, uniqueCode, formulaStr string, sysUserId int, sysUserRealName string) (edbInfoId int, err error) {
-	o := orm.NewOrmUsingDB("data")
-	to, err := o.Begin()
-	if err != nil {
-		return
-	}
-	defer func() {
-		if err != nil {
-			_ = to.Rollback()
-		} else {
-			_ = to.Commit()
-		}
-	}()
-	if req.EdbInfoId <= 0 {
-		edbInfo := new(EdbInfo)
-		edbInfo.Source = utils.DATA_SOURCE_CALCULATE
-		edbInfo.SourceName = "指标运算"
-		edbInfo.EdbCode = edbCode
-		edbInfo.EdbName = req.EdbName
-		edbInfo.EdbNameSource = req.EdbName
-		edbInfo.Frequency = req.Frequency
-		edbInfo.Unit = req.Unit
-		edbInfo.ClassifyId = req.ClassifyId
-		edbInfo.SysUserId = sysUserId
-		edbInfo.SysUserRealName = sysUserRealName
-		edbInfo.CreateTime = time.Now()
-		edbInfo.ModifyTime = time.Now()
-		edbInfo.UniqueCode = uniqueCode
-		edbInfo.CalculateFormula = req.CalculateFormula
-		edbInfo.EdbType = 2
-		newEdbInfoId, err := to.Insert(edbInfo)
-		if err != nil {
-			return int(newEdbInfoId), err
-		}
-		edbInfoId = int(newEdbInfoId)
-		//处理同名指标
-		{
-			edbNameList, err := GetEdbInfoByName(req.EdbName)
-			if err != nil {
-				return edbInfoId, err
-			}
-			if len(edbNameList) >= 2 {
-				for _, v := range edbNameList {
-					edbName := v.EdbName + "(" + v.SourceName + ")"
-					err = ModifyEdbInfoNameSource(edbName, v.EdbInfoId)
-					if err != nil {
-						return edbInfoId, err
-					}
-				}
-			}
-		}
-
-		//calculateList := make([]*EdbInfoCalculate, 0)
-		edbInfoList := make([]*EdbInfo, 0)
-
-		for _, v := range req.EdbInfoIdArr {
-			edbInfo, err := GetEdbInfoById(v.EdbInfoId)
-			if err != nil {
-				return edbInfoId, err
-			}
-			//calculateItem := new(EdbInfoCalculate)
-			//calculateItem.CreateTime = time.Now()
-			//calculateItem.ModifyTime = time.Now()
-			//calculateItem.Sort = k + 1
-			//calculateItem.EdbCode = edbCode
-			//calculateItem.EdbInfoId = int(edbInfoId)
-			//calculateItem.FromEdbInfoId = edbInfo.EdbInfoId
-			//calculateItem.FromEdbCode = edbInfo.EdbCode
-			//calculateItem.FromEdbName = edbInfo.EdbName
-			//calculateItem.FromSource = edbInfo.Source
-			//calculateItem.FromSourceName = edbInfo.SourceName
-			//calculateItem.FromTag = v.FromTag
-			//calculateList = append(calculateList, calculateItem)
-			edbInfoList = append(edbInfoList, edbInfo)
-		}
-		//if len(calculateList) > 0 {
-		//	_, err = to.InsertMulti(1, calculateList)
-		//	if err != nil {
-		//		return edbInfoId, err
-		//	}
-		//}
-	} else {
-		edbInfoId = req.EdbInfoId
-		dataTableName := GetEdbDataTableName(utils.DATA_SOURCE_CALCULATE)
-		deleteSql := ` DELETE FROM %s WHERE edb_info_id=? `
-		deleteSql = fmt.Sprintf(deleteSql, dataTableName)
-		_, err = to.Raw(deleteSql, req.EdbInfoId).Exec()
-	}
-	//计算数据
-	saveDataMap := make(map[string]map[int]float64)
-	for _, v := range fromEdbInfo {
-		var condition string
-		var pars []interface{}
-		condition += " AND edb_info_id=? "
-		pars = append(pars, v.FromEdbInfoId)
-		dataList, err := GetEdbDataListAll(condition, pars, v.FromSource, 1)
-		if err != nil {
-			return edbInfoId, err
-		}
-		dataMap := make(map[string]float64)
-		for _, dv := range dataList {
-			if val, ok := saveDataMap[dv.DataTime]; ok {
-				if _, ok := val[v.EdbInfoId]; !ok {
-					val[v.EdbInfoId] = dv.Value
-				}
-			} else {
-				temp := make(map[int]float64)
-				temp[v.EdbInfoId] = dv.Value
-				saveDataMap[dv.DataTime] = temp
-			}
-		}
-		item := new(CalculateItems)
-		item.EdbInfoId = v.EdbInfoId
-		item.DataMap = dataMap
-	}
-
-	//formulaMap := CheckFormula(formulaStr)
-	//addSql := ` INSERT INTO edb_data_calculate(edb_info_id,edb_code,data_time,value,create_time,modify_time,status,data_timestamp) values `
-	//nowStr := time.Now().Format(utils.FormatDateTime)
-	//var isAdd bool
-	//
-	//for sk, sv := range saveDataMap {
-	//	formulaStr = strings.ToUpper(formulaStr)
-	//	formulaFormStr := ReplaceFormula(edbInfoIdArr, sv, formulaMap, formulaStr, edbInfoIdBytes)
-	//	if formulaFormStr != "" {
-	//		utils.FileLog.Info("formulaFormStr:%s", formulaFormStr)
-	//		expression := formula.NewExpression(formulaFormStr)
-	//		calResult, err := expression.Evaluate()
-	//		if err != nil {
-	//			err = errors.New("计算失败:Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
-	//			fmt.Println(err)
-	//			return err
-	//		}
-	//		calVal, err := calResult.Float64()
-	//		if err != nil {
-	//			err = errors.New("计算失败:获取计算值失败 Err:" + err.Error() + ";formulaStr:" + formulaFormStr)
-	//			fmt.Println(err)
-	//			return err
-	//		}
-	//
-	//		//需要存入的数据
-	//		{
-	//			dataTime, _ := time.Parse(utils.FormatDate, sk)
-	//			timestamp := dataTime.UnixNano() / 1e6
-	//			timeStr := fmt.Sprintf("%d", timestamp)
-	//			addSql += "("
-	//			addSql += strconv.Itoa(edbInfoId) + "," + "'" + edbCode + "'" + "," + "'" + sk + "'" + "," + utils.SubFloatToString(calVal, 4) + "," + "'" + nowStr + "'" +
-	//				"," + "'" + nowStr + "'" + "," + "1"
-	//			addSql += "," + "'" + timeStr + "'"
-	//			addSql += "),"
-	//			isAdd = true
-	//		}
-	//	}
-	//}
-	//if isAdd {
-	//	addSql = strings.TrimRight(addSql, ",")
-	//	AddEdbDataCalculateBySql(addSql)
-	//	if err != nil {
-	//		fmt.Println("AddEdbDataCalculate Err:" + err.Error())
-	//		return
-	//	}
-	//}
-	return
-}
-
 type CalculateItems struct {
 	EdbInfoId int
 	DataMap   map[string]float64

+ 9 - 0
models/data_manage/edb_source.go

@@ -29,3 +29,12 @@ func GetEdbSourceItemsByCondition(condition string, pars []interface{}, fieldArr
 	_, err = o.Raw(sql, pars).QueryRows(&items)
 	return
 }
+
+// EdbSourceChild 指标来源表
+type EdbSourceChild struct {
+	EdbSourceId int    `orm:"column(edb_source_id);pk"`
+	SourceName  string `description:"指标来源名称"`
+	TableName   string `description:"数据表名"`
+	IsBase      int    `description:"是否为基础指标: 2-否; 1-是"`
+	Child       []EdbSourceChild
+}

+ 10 - 0
models/data_manage/predict_edb_conf.go

@@ -187,3 +187,13 @@ func GetPredictEdbConfListById(edbInfoId int) (items []*PredictEdbConf, err erro
 	_, err = o.Raw(sql, edbInfoId).QueryRows(&items)
 	return
 }
+
+// GetGroupPredictEdbBySourceEdbInfoId 根据来源指标id获取配置
+func GetGroupPredictEdbBySourceEdbInfoId(sourceEdbInfoId int) (items []*EdbInfo, err error) {
+	o := orm.NewOrmUsingDB("data")
+	sql := ` SELECT a.* FROM edb_info AS a 
+     JOIN predict_edb_conf AS b  on a.edb_info_id = b.predict_edb_info_id
+          WHERE b.source_edb_info_id=? group by a.edb_info_id`
+	_, err = o.Raw(sql, sourceEdbInfoId).QueryRows(&items)
+	return
+}

+ 2 - 27
models/data_manage/trade_analysis/trade_analysis.go

@@ -83,6 +83,7 @@ type TradeClassifyNameListItemItem struct {
 type TradeClassifyName struct {
 	ClassifyName string //分类名称
 	ClassifyType string //分类名称下的类型
+	LatestDate   string //分类下最晚日期
 }
 
 // GetExchangeClassify 获取交易所分类列表
@@ -128,6 +129,7 @@ type GetPositionTopResp struct {
 	CleanBuyList  GetPositionTopList `description:"净多单列表"`
 	CleanSoldList GetPositionTopList `description:"净空单列表"`
 	DataTime      string             `description:"最新日期或者请求日期"`
+	LastDataTime  string             `description:"最新日期"`
 }
 
 type GetPositionTopList struct {
@@ -156,30 +158,3 @@ func GetTradePositionTop(exchange string, classifyName, classifyType, dataTime s
 
 	return
 }
-
-func GetTradeTopLastDataTime(exchange string, classifyName, classifyType string) (item *TradePositionTop, err error) {
-	tableName := "trade_position_" + exchange + "_top"
-	sql := `SELECT * FROM ` + tableName + ` WHERE classify_name=? `
-	pars := make([]interface{}, 0)
-	if exchange == "zhengzhou" {
-		pars = append(pars, classifyType)
-	} else {
-		pars = append(pars, classifyName)
-		sql += ` AND classify_type=? `
-		pars = append(pars, classifyType)
-	}
-	sql += ` ORDER BY data_time desc`
-	o := orm.NewOrmUsingDB("data")
-	err = o.Raw(sql, pars...).QueryRow(&item)
-
-	return
-}
-
-func GetZhengzhouClassifyTypeByClassifyName(classifyName string) (item *TradeClassifyName, err error) {
-	tableName := "base_from_trade_zhengzhou_index"
-	sql := `SELECT classify_name, classify_type FROM ` + tableName + ` WHERE classify_name=? `
-	o := orm.NewOrmUsingDB("data")
-	err = o.Raw(sql, classifyName).QueryRow(&item)
-
-	return
-}

+ 55 - 0
models/data_manage/trade_analysis/trade_classify.go

@@ -0,0 +1,55 @@
+package trade_analysis
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// BaseFromTradeClassify 交易所分类表
+type BaseFromTradeClassify struct {
+	Id           uint64    `orm:"column(id);pk"`
+	ClassifyName string    //分类名称
+	ClassifyType string    //分类名称下的类型
+	Exchange     string    //交易所
+	LatestDate   time.Time //数据最近的日期
+	CreateTime   time.Time //插入时间
+	ModifyTime   time.Time //修改时间
+}
+
+// GetAllBaseFromTradeClassify 获取所有的交易所分类列表
+func GetAllBaseFromTradeClassify() (list []*BaseFromTradeClassify, err error) {
+	sql := `SELECT * FROM base_from_trade_classify   `
+
+	o := orm.NewOrmUsingDB("data")
+	_, err = o.Raw(sql).QueryRows(&list)
+
+	return
+}
+
+func GetTradeTopLastDataTime(exchange string, classifyName, classifyType string) (item *BaseFromTradeClassify, err error) {
+	sql := `SELECT * FROM base_from_trade_classify WHERE exchange = ? AND classify_name=? `
+	pars := make([]interface{}, 0)
+	pars = append(pars, exchange)
+
+	if exchange == "zhengzhou" {
+		pars = append(pars, classifyType)
+	} else {
+		pars = append(pars, classifyName)
+		sql += ` AND classify_type=? `
+		pars = append(pars, classifyType)
+	}
+	sql += ` ORDER BY latest_date desc`
+	o := orm.NewOrmUsingDB("data")
+	err = o.Raw(sql, pars...).QueryRow(&item)
+
+	return
+}
+
+// GetClassifyTypeByClassifyName 根据分类名称获取分类类型
+func GetClassifyTypeByClassifyName(exchange, classifyName string) (item *TradeClassifyName, err error) {
+	sql := `SELECT classify_name, classify_type FROM base_from_trade_classify WHERE exchange = ? AND classify_name=? `
+	o := orm.NewOrmUsingDB("data")
+	err = o.Raw(sql, exchange, classifyName).QueryRow(&item)
+
+	return
+}

+ 14 - 2
models/db.go

@@ -1,17 +1,18 @@
 package models
 
 import (
-	_ "github.com/go-sql-driver/mysql"
 	"eta/eta_api/models/company"
 	"eta/eta_api/models/data_manage"
 	future_good2 "eta/eta_api/models/data_manage/future_good"
 	"eta/eta_api/models/data_manage/supply_analysis"
+	"eta/eta_api/models/eta_trial"
 	"eta/eta_api/models/ppt_english"
 	"eta/eta_api/models/sandbox"
 	"eta/eta_api/models/semantic_analysis"
 	"eta/eta_api/models/system"
 	"eta/eta_api/models/yb"
 	"eta/eta_api/utils"
+	_ "github.com/go-sql-driver/mysql"
 	"time"
 
 	"github.com/beego/beego/v2/client/orm"
@@ -135,6 +136,10 @@ func init() {
 
 	// 外部链接
 	initOutLink()
+	// ETA试用相关表
+	if utils.BusinessCode == utils.BusinessCodeSandbox {
+		initEtaTrial()
+	}
 }
 
 // initSystem 系统表 数据表
@@ -154,6 +159,7 @@ func initSystem() {
 		new(system.SysRoleAdmin),          //管理员账号和角色映射表
 		new(system.AdminConfig),           //系统用户配置表
 		new(system.AdminOperateRecord),
+		new(system.AdminVerifyCodeRecord), // 用户短信邮箱验证码记录表
 	)
 }
 
@@ -398,6 +404,12 @@ func initBusinessConf() {
 
 func initOutLink() {
 	orm.RegisterModel(
-		new(OutLink), // 外部链接表
+		new(OutLink)) // 外部链接表
+}
+
+// initEtaTrial 试用平台相关表
+func initEtaTrial() {
+	orm.RegisterModel(
+		new(eta_trial.QuestionnaireFillRecord), // 问卷填写记录表
 	)
 }

+ 14 - 4
models/english_report.go

@@ -25,6 +25,7 @@ type EnglishReport struct {
 	ModifyTime         time.Time `description:"修改时间"`
 	State              int       `description:"1:未发布,2:已发布"`
 	PublishTime        time.Time `description:"发布时间"`
+	PrePublishTime     time.Time `description:"预发布时间"`
 	Stage              int       `description:"期数"`
 	Content            string    `description:"内容"`
 	VideoUrl           string    `description:"音频文件URL"`
@@ -238,6 +239,7 @@ type EnglishReportList struct {
 	ModifyTime         time.Time `description:"修改时间"`
 	State              int       `description:"1:未发布,2:已发布"`
 	PublishTime        string    `description:"发布时间"`
+	PrePublishTime     string    `description:"预发布时间"`
 	Stage              int       `description:"期数"`
 	Content            string    `description:"内容"`
 	VideoUrl           string    `description:"音频文件URL"`
@@ -317,21 +319,29 @@ func GetEnglishReportByCondition(condition string, pars []interface{}) (items []
 }
 
 // 发布报告
-func PublishEnglishReportById(reportId int) (err error) {
+func PublishEnglishReportById(reportId int, publishTime string) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `UPDATE english_report SET state=2,publish_time=now(),modify_time=NOW() WHERE id = ? `
-	_, err = o.Raw(sql, reportId).Exec()
+	sql := `UPDATE english_report SET state=2,publish_time=?,pre_publish_time=null,modify_time=NOW() WHERE id = ? `
+	_, err = o.Raw(sql, publishTime, reportId).Exec()
 	return
 }
 
 // 取消发布报告
 func PublishCancelEnglishReport(reportIds int) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := ` UPDATE english_report SET state=1,publish_time=null WHERE id =?  `
+	sql := ` UPDATE english_report SET state=1,pre_publish_time=null WHERE id =?  `
 	_, err = o.Raw(sql, reportIds).Exec()
 	return
 }
 
+// SetPrePublishEnglishReportById 设置定时发布
+func SetPrePublishEnglishReportById(reportId int, prePublishTime string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE english_report SET pre_publish_time=? WHERE id = ? and state = 1 `
+	_, err = o.Raw(sql, prePublishTime, reportId).Exec()
+	return
+}
+
 // DeleteEnglishReportAndChapter 删除报告及章节
 func DeleteEnglishReportAndChapter(reportInfo *EnglishReportDetail) (err error) {
 	reportId := reportInfo.Id

+ 41 - 0
models/eta_trial/questionnaire_fill_record.go

@@ -0,0 +1,41 @@
+package eta_trial
+
+import (
+	"eta/eta_api/utils"
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// QuestionnaireFillRecord 问卷填写记录表
+type QuestionnaireFillRecord struct {
+	RecordId   int    `orm:"column(fill_record_id);pk" description:"记录id"`
+	Mobile     string `description:"手机"`
+	IsFill     int    `description:"0未点过 1稍后再填 2已填写"`
+	CreateTime time.Time
+	ModifyTime time.Time
+}
+
+// GetQuestionnaireFillRecordCountByMobile 手机号获取问卷填写记录
+func GetQuestionnaireFillRecordCountByMobile(mobile string) (item *QuestionnaireFillRecord, err error) {
+	if utils.BusinessCode != utils.BusinessCodeSandbox {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `SELECT * FROM questionnaire_fill_record WHERE mobile=? ORDER BY create_time DESC LIMIT 1`
+	err = o.Raw(sql, mobile).QueryRow(&item)
+	return
+}
+
+// AddQuestionnaireFillRecord 新增问卷填写记录
+func AddQuestionnaireFillRecord(item *QuestionnaireFillRecord) (err error) {
+	if utils.BusinessCode != utils.BusinessCodeSandbox {
+		return
+	}
+	o := orm.NewOrm()
+	id, err := o.Insert(item)
+	if err != nil {
+		return
+	}
+	item.RecordId = int(id)
+	return
+}

+ 80 - 0
models/eta_trial/request.go

@@ -0,0 +1,80 @@
+package eta_trial
+
+import "time"
+
+type QuestionnairePopupResp struct {
+	IsPopup  int `description:"0 弹窗 1不弹"`
+	IsShow   int `description:"0 不显示 1显示"`
+	Question EtaTrialQuestionnaireResp
+}
+
+// EtaTrialQuestionnaireResp 问卷调查列表响应体
+type EtaTrialQuestionnaireResp struct {
+	List []EtaTrialQuestionnaireRespItem
+}
+
+// EtaTrialQuestionnaireRespItem 问卷调查信息
+type EtaTrialQuestionnaireRespItem struct {
+	QuestionnaireId int      `orm:"column(questionnaire_id);pk" description:"问卷题目id"`
+	Question        string   `description:"题目"`
+	Type            int      `description:"1单选 2多选 3简答题"`
+	Sort            int      `description:"排序"`
+	Option          []string `description:"选项"`
+	IsMust          int      `description:"是否必填"`
+	CreateTime      string
+}
+
+type EtaTrialQuestionnaireReq struct {
+	IsFill int `description:"0稍后再填 1已填写"`
+	List   []EtaTrialQuestionnaireReqItem
+}
+
+type EtaTrialQuestionnaireReqItem struct {
+	QuestionnaireId int    `description:"题目id"`
+	Options         string `description:"选项"`
+	Type            int    `description:"1单选 2多选 3简答题"`
+}
+
+// EtaTrialUserItem ETA试用客户
+type EtaTrialUserItem struct {
+	EtaTrialId    int    `description:"eta试用客户id"`
+	UserName      string `description:"客户名称"`
+	CompanyName   string `description:"客户公司姓名"`
+	Position      string `description:"职位"`
+	Password      string
+	Account       string
+	Mobile        string    `description:"手机号"`
+	Enabled       int       `description:"1:有效,0:禁用"`
+	ActiveTime    int       `description:"累计活跃时长"`
+	IndexNum      int       `description:"累计添加指标"`
+	ChartNum      int       `description:"累计添加图表"`
+	LoginNum      int       `description:"累计登录次数"`
+	LastLoginTime time.Time `description:"最后一次登陆时间"`
+	SellerId      int       `description:"销售id"`
+	Seller        string    `description:"销售员名称"`
+	CreateTime    time.Time
+	ModifyTime    time.Time
+}
+
+type EtaTrialQuestionnaireRecord struct {
+	RecordId        int       `description:"记录id" json:"record_id"`
+	UserName        string    `description:"用户名" json:"user_name"`
+	CompanyName     string    `description:"公司名" json:"company_name"`
+	Position        string    `description:"职位" json:"position"`
+	Options         string    `description:"选项" json:"options"`
+	Mobile          string    `description:"手机" json:"mobile"`
+	Type            int       `description:"1单选 2多选 3简答题" json:"type"`
+	QuestionnaireId int       `description:"题目id" json:"questionnaire_id"`
+	CreateTime      time.Time `json:"create_time"`
+}
+
+// UpdateUserActiveTimeReq 更新用户活跃时间请求体
+type UpdateUserActiveTimeReq struct {
+	ActiveTime int    `description:"活跃时长, 单位秒"`
+	Part       string `description:"活跃板块"`
+}
+
+// UpdateUserLoginDurationReq 更新用户登录时长请求体
+type UpdateUserLoginDurationReq struct {
+	ActiveTime int `description:"活跃时长, 单位秒"`
+}

+ 19 - 4
models/report.go

@@ -57,6 +57,7 @@ type ReportList struct {
 	ModifyTime         time.Time                 `description:"修改时间"`
 	State              int                       `description:"1:未发布,2:已发布"`
 	PublishTime        string                    `description:"发布时间"`
+	PrePublishTime     string                    `description:"预发布时间"`
 	Stage              int                       `description:"期数"`
 	MsgIsSend          int                       `description:"模板消息是否已发送,0:否,1:是"`
 	Content            string                    `description:"内容"`
@@ -137,14 +138,14 @@ func PublishReport(reportIds []int) (err error) {
 	return
 }
 
-// 取消发布报告
+// PublishCancleReport 取消发布报告
 func PublishCancleReport(reportIds int, publishTimeNullFlag bool) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
 	var sql string
 	if publishTimeNullFlag {
-		sql = ` UPDATE report SET state=1, publish_time=null WHERE id =?`
+		sql = ` UPDATE report SET state=1, publish_time=null, pre_publish_time=null, pre_msg_send=0 WHERE id =?`
 	} else {
-		sql = ` UPDATE report SET state=1 WHERE id =?`
+		sql = ` UPDATE report SET state=1, pre_publish_time=null, pre_msg_send=0 WHERE id =?`
 	}
 	_, err = o.Raw(sql, reportIds).Exec()
 	return
@@ -264,6 +265,12 @@ type AddReq struct {
 	ReportVersion      int    `description:"1:旧版,2:新版"`
 }
 
+type PrePublishReq struct {
+	ReportId       int    `description:"报告id"`
+	PrePublishTime string `description:"预发布时间"`
+	PreMsgSend     int    `description:"定时发布成功后是否立即推送模版消息:0否,1是"`
+}
+
 type AddResp struct {
 	ReportId   int64  `description:"报告id"`
 	ReportCode string `description:"报告code"`
@@ -648,7 +655,7 @@ SELECT DISTINCT report_id FROM report_chapter WHERE publish_state = 2 AND (video
 // 发布报告
 func PublishReportById(reportId int, publishTime time.Time) (err error) {
 	o := orm.NewOrmUsingDB("rddp")
-	sql := `UPDATE report SET state = 2, publish_time = ?, modify_time = NOW() WHERE id = ? `
+	sql := `UPDATE report SET state = 2, publish_time = ?, pre_publish_time=null, pre_msg_send=0, modify_time = NOW() WHERE id = ? `
 	_, err = o.Raw(sql, publishTime, reportId).Exec()
 	return
 }
@@ -1013,3 +1020,11 @@ func ModifyReportMsgIsSendV2(reportId int) (err error) {
 	_, err = o.Raw(sql, reportId).Exec()
 	return
 }
+
+// SetPrePublishReportById 设置定时发布
+func SetPrePublishReportById(reportId int, prePublishTime string, preMsgSend int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE report SET pre_publish_time=?, pre_msg_send=? WHERE id = ? and state = 1 `
+	_, err = o.Raw(sql, prePublishTime, preMsgSend, reportId).Exec()
+	return
+}

+ 18 - 0
models/semantic_analysis/sa_compare.go

@@ -193,6 +193,18 @@ type SaCompareItem struct {
 	CreateTime   string `description:"创建时间"`
 }
 
+type SaCompareElastic struct {
+	SaCompareId  int    `description:"比对ID"`
+	ClassifyId   int    `description:"比对分类ID"`
+	ClassifyName string `description:"比对分类名称"`
+	Title        string `description:"标题"`
+	ResultImg    string `description:"比对结果图片"`
+	SysAdminId   int    `description:"创建人ID"`
+	SysAdminName string `description:"创建人姓名"`
+	CreateTime   string `description:"创建时间"`
+	ModifyTime   string `description:"修改时间"`
+}
+
 // SaCompareUpdateResultImgReq 更新比对结果图片请求体
 type SaCompareUpdateResultImgReq struct {
 	SaCompareId int    `description:"比对ID"`
@@ -410,3 +422,9 @@ func GetFirstSortSaCompare(classifyId int) (item *SaCompare, err error) {
 	err = o.Raw(sql, classifyId).QueryRow(&item)
 	return
 }
+
+// CompareListByEsResp 文档对比Es搜索返回
+type CompareListByEsResp struct {
+	Paging *paging.PagingItem
+	List   []*SaCompareElastic
+}

+ 126 - 0
models/system/admin_verify_code_record.go

@@ -0,0 +1,126 @@
+package system
+
+import (
+	"fmt"
+	"github.com/beego/beego/v2/client/orm"
+	"strings"
+	"time"
+)
+
+const (
+	AdminVerifyCodeRecordTypeMobile = 1 // 验证方式-手机号
+	AdminVerifyCodeRecordTypeEmail  = 2 // 验证方式-邮箱
+
+	AdminVerifyCodeRecordSourceLogin    = 1 // 验证码来源-登录
+	AdminVerifyCodeRecordSourceAbnormal = 2 // 验证码来源-异常登录校验
+	AdminVerifyCodeRecordSourceForget   = 3 // 验证码来源-忘记密码
+
+	AdminVerifyCodeRecordStatusSuccess = 1 // 验证码发送状态-已发送
+	AdminVerifyCodeRecordStatusFail    = 2 // 验证码发送状态-发送失败
+)
+
+// AdminVerifyCodeRecord 短信邮箱验证码记录表
+type AdminVerifyCodeRecord struct {
+	Id          int       `orm:"column(id);pk"`
+	VerifyType  int       `description:"验证方式:1-手机号;2-邮箱"`
+	Source      int       `description:"来源:1-登录;2-异常登录校验;3-忘记密码"`
+	Mobile      string    `description:"手机号"`
+	Email       string    `description:"邮箱"`
+	Code        string    `description:"验证码"`
+	ExpiredTime time.Time `description:"验证码过期时间"`
+	SendResult  string    `description:"发送结果"`
+	SendStatus  int       `description:"发送状态:0-待发送;1-已发送;2-发送失败"`
+	CreateTime  time.Time `description:"创建时间"`
+	ModifyTime  time.Time `description:"更新时间"`
+}
+
+func (m *AdminVerifyCodeRecord) TableName() string {
+	return "admin_verify_code_record"
+}
+
+func (m *AdminVerifyCodeRecord) PrimaryId() string {
+	return "id"
+}
+
+func (m *AdminVerifyCodeRecord) Create() (err error) {
+	o := orm.NewOrm()
+	id, err := o.Insert(m)
+	if err != nil {
+		return
+	}
+	m.Id = int(id)
+	return
+}
+
+func (m *AdminVerifyCodeRecord) CreateMulti(items []*AdminVerifyCodeRecord) (err error) {
+	if len(items) == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+func (m *AdminVerifyCodeRecord) Update(cols []string) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Update(m, cols...)
+	return
+}
+
+func (m *AdminVerifyCodeRecord) Del() (err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`DELETE FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	_, err = o.Raw(sql, m.Id).Exec()
+	return
+}
+
+func (m *AdminVerifyCodeRecord) GetItemById(id int) (item *AdminVerifyCodeRecord, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE %s = ? LIMIT 1`, m.TableName(), m.PrimaryId())
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+func (m *AdminVerifyCodeRecord) GetItemByCondition(condition string, pars []interface{}) (item *AdminVerifyCodeRecord, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT * FROM %s WHERE 1=1 %s LIMIT 1`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+func (m *AdminVerifyCodeRecord) GetCountByCondition(condition string, pars []interface{}) (count int, err error) {
+	o := orm.NewOrm()
+	sql := fmt.Sprintf(`SELECT COUNT(1) FROM %s WHERE 1=1 %s`, m.TableName(), condition)
+	err = o.Raw(sql, pars).QueryRow(&count)
+	return
+}
+
+func (m *AdminVerifyCodeRecord) GetItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string) (items []*AdminVerifyCodeRecord, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+func (m *AdminVerifyCodeRecord) GetPageItemsByCondition(condition string, pars []interface{}, fieldArr []string, orderRule string, startSize, pageSize int) (items []*AdminVerifyCodeRecord, err error) {
+	o := orm.NewOrm()
+	fields := strings.Join(fieldArr, ",")
+	if len(fieldArr) == 0 {
+		fields = `*`
+	}
+	order := `ORDER BY create_time DESC`
+	if orderRule != "" {
+		order = ` ORDER BY ` + orderRule
+	}
+	sql := fmt.Sprintf(`SELECT %s FROM %s WHERE 1=1 %s %s LIMIT ?,?`, fields, m.TableName(), condition, order)
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}

+ 5 - 0
models/system/sys_admin.go

@@ -40,6 +40,7 @@ type AdminItem struct {
 	City                    string `description:"市"`
 	CityCode                string `description:"市编码"`
 	EmployeeId              string `description:"员工工号(钉钉/每刻报销)"`
+	TelAreaCode             string `description:"手机区号"`
 }
 
 func GetSysuserList(condition string, pars []interface{}, startSize, pageSize int) (items []*AdminItem, err error) {
@@ -114,6 +115,8 @@ type SysuserAddReq struct {
 	City             string `description:"市"`
 	CityCode         string `description:"市编码"`
 	EmployeeId       string `description:"员工工号(钉钉/每刻报销)"`
+	Email            string `description:"邮箱"`
+	TelAreaCode      string `description:"手机区号"`
 }
 
 func GetSysAdminCount(adminName string) (count int, err error) {
@@ -158,6 +161,8 @@ type SysuserEditReq struct {
 	City             string `description:"市"`
 	CityCode         string `description:"市编码"`
 	EmployeeId       string `description:"员工工号(钉钉/每刻报销)"`
+	Email            string `description:"邮箱"`
+	TelAreaCode      string `description:"手机区号"`
 }
 
 type SysUserMoveReq struct {

+ 46 - 0
models/system/sys_user.go

@@ -62,6 +62,7 @@ type Admin struct {
 	City                      string    `description:"市"`
 	CityCode                  string    `description:"市编码"`
 	EmployeeId                string    `description:"员工工号(钉钉/每刻报销)"`
+	TelAreaCode               string    `description:"手机区号"`
 }
 
 // Update 更新用户基础信息
@@ -151,3 +152,48 @@ func (item *Admin) GetItemsByCondition(condition string, pars []interface{}, fie
 	_, err = o.Raw(sql, pars).QueryRows(&items)
 	return
 }
+
+// GetSysUserByMobile 手机号获取用户
+func GetSysUserByMobile(mobile string) (item *Admin, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+				a.*, b.role_type_code
+			FROM
+				admin AS a
+			INNER JOIN sys_role AS b ON a.role_id = b.role_id
+			WHERE
+				a.mobile = ?
+			LIMIT 1`
+	err = o.Raw(sql, mobile).QueryRow(&item)
+	return
+}
+
+// GetSysUserByEmail 邮箱获取用户
+func GetSysUserByEmail(email string) (item *Admin, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+				a.*, b.role_type_code
+			FROM
+				admin AS a
+			INNER JOIN sys_role AS b ON a.role_id = b.role_id
+			WHERE
+				a.email = ?
+			LIMIT 1`
+	err = o.Raw(sql, email).QueryRow(&item)
+	return
+}
+
+// GetSysUserByAdminName 账号获取用户
+func GetSysUserByAdminName(adminName string) (item *Admin, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT
+				a.*, b.role_type_code
+			FROM
+				admin AS a
+			INNER JOIN sys_role AS b ON a.role_id = b.role_id
+			WHERE
+				a.admin_name = ?
+			LIMIT 1`
+	err = o.Raw(sql, adminName).QueryRow(&item)
+	return
+}

+ 162 - 9
routers/commentsRouter.go

@@ -2185,6 +2185,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
+        beego.ControllerComments{
+            Method: "EdbSourceListByPython",
+            Router: `/edb_source/list/python`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/data_manage:EdbInfoController"],
         beego.ControllerComments{
             Method: "EiaSteoClassify",
@@ -3751,6 +3760,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportController"],
+        beego.ControllerComments{
+            Method: "PrePublishReport",
+            Router: `/pre_publish`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/english_report:EnglishReportController"],
         beego.ControllerComments{
             Method: "PublishReport",
@@ -3985,6 +4003,42 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/eta_trial:EtaTrialController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/eta_trial:EtaTrialController"],
+        beego.ControllerComments{
+            Method: "QuestionnaireCommit",
+            Router: `/questionnaire/commit`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/eta_trial:EtaTrialController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/eta_trial:EtaTrialController"],
+        beego.ControllerComments{
+            Method: "QuestionnairePopUp",
+            Router: `/questionnaire/popup`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/eta_trial:EtaTrialController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/eta_trial:EtaTrialController"],
+        beego.ControllerComments{
+            Method: "UpdateActiveTime",
+            Router: `/user/active`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers/eta_trial:EtaTrialController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/eta_trial:EtaTrialController"],
+        beego.ControllerComments{
+            Method: "UpdateLoginDuration",
+            Router: `/user/login_duration`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/roadshow:CalendarController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/roadshow:CalendarController"],
         beego.ControllerComments{
             Method: "ResearcherList",
@@ -4192,6 +4246,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers/semantic_analysis:SaCompareController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/semantic_analysis:SaCompareController"],
+        beego.ControllerComments{
+            Method: "Search",
+            Router: `/compare/search`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers/semantic_analysis:SaCompareController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers/semantic_analysis:SaCompareController"],
         beego.ControllerComments{
             Method: "SelectDocs",
@@ -4372,15 +4435,6 @@ func init() {
             Filters: nil,
             Params: nil})
 
-    beego.GlobalControllerRouter["eta/eta_api/controllers:BusinessConfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:BusinessConfController"],
-        beego.ControllerComments{
-            Method: "CodeEncrypt",
-            Router: `/code_encrypt`,
-            AllowHTTPMethods: []string{"get"},
-            MethodParams: param.Make(),
-            Filters: nil,
-            Params: nil})
-
     beego.GlobalControllerRouter["eta/eta_api/controllers:BusinessConfController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:BusinessConfController"],
         beego.ControllerComments{
             Method: "Fetch",
@@ -4399,6 +4453,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:BusinessConfOpenController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:BusinessConfOpenController"],
+        beego.ControllerComments{
+            Method: "CodeEncrypt",
+            Router: `/code_encrypt`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:ClassifyController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ClassifyController"],
         beego.ControllerComments{
             Method: "Add",
@@ -4912,6 +4975,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:PptEnglishGroupController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:PptEnglishGroupController"],
+        beego.ControllerComments{
+            Method: "ListSearch",
+            Router: `/ppt/search`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:PptEnglishGroupController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:PptEnglishGroupController"],
         beego.ControllerComments{
             Method: "SharePpt",
@@ -5191,6 +5263,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:PptV2GroupController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:PptV2GroupController"],
+        beego.ControllerComments{
+            Method: "ListSearch",
+            Router: `/ppt/search`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:PptV2GroupController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:PptV2GroupController"],
         beego.ControllerComments{
             Method: "SharePpt",
@@ -5524,6 +5605,15 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"],
+        beego.ControllerComments{
+            Method: "PrePublishReport",
+            Router: `/pre_publish`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:ReportController"],
         beego.ControllerComments{
             Method: "PublishReport",
@@ -6595,6 +6685,69 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "AreaCodeList",
+            Router: `/area_code/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "ForgetAccountGet",
+            Router: `/forget/account_get`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "ForgetCodeVerify",
+            Router: `/forget/code_verify`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "ForgetResetPass",
+            Router: `/forget/reset_pass`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "GenerateCaptcha",
+            Router: `/get_captcha`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "Login",
+            Router: `/login`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "GetVerifyCode",
+            Router: `/verify_code`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["eta/eta_api/controllers:VarietyTagController"] = append(beego.GlobalControllerRouter["eta/eta_api/controllers:VarietyTagController"],
         beego.ControllerComments{
             Method: "TagTree",

+ 13 - 0
routers/router.go

@@ -16,6 +16,7 @@ import (
 	"eta/eta_api/controllers/data_manage/line_feature"
 	"eta/eta_api/controllers/data_manage/supply_analysis"
 	"eta/eta_api/controllers/english_report"
+	"eta/eta_api/controllers/eta_trial"
 	"eta/eta_api/controllers/roadshow"
 	"eta/eta_api/controllers/sandbox"
 	"eta/eta_api/controllers/semantic_analysis"
@@ -257,6 +258,7 @@ func init() {
 		web.NSNamespace("/business_conf",
 			web.NSInclude(
 				&controllers.BusinessConfController{},
+				&controllers.BusinessConfOpenController{},
 			),
 		),
 		web.NSNamespace("/trade_analysis",
@@ -269,6 +271,17 @@ func init() {
 				&controllers.OutLinkController{},
 			),
 		),
+		web.NSNamespace("/eta_trial",
+			web.NSInclude(
+				&eta_trial.EtaTrialController{},
+			),
+		),
+		web.NSNamespace("/user_login",
+			web.NSInclude(
+				&controllers.UserLoginController{},
+				&controllers.UserLoginAuthController{},
+			),
+		),
 	)
 	web.AddNamespace(ns)
 }

+ 48 - 0
services/captcha_redis.go

@@ -0,0 +1,48 @@
+package services
+
+import (
+	"eta/eta_api/utils"
+	"fmt"
+	"time"
+)
+
+// CaptchaRedis Redis验证器
+type CaptchaRedis struct{}
+
+// Set 实现验证器set方法
+func (r CaptchaRedis) Set(id string, value string) (err error) {
+	if utils.Rc == nil {
+		err = fmt.Errorf("redis config err")
+		return
+	}
+	key := utils.CaptchaCachePrefix + id
+	b := utils.Rc.SetNX(key, value, time.Minute*5)
+	if !b {
+		err = fmt.Errorf("redis setnx err")
+	}
+	return
+}
+
+// Get 实现原验证器get方法
+func (r CaptchaRedis) Get(id string, clear bool) (code string) {
+	if utils.Rc == nil {
+		return
+	}
+	key := utils.CaptchaCachePrefix + id
+	val, err := utils.Rc.RedisString(key)
+	if err != nil {
+		return
+	}
+	code = val
+	// clear为true时验证通过删除验证码
+	if clear {
+		_ = utils.Rc.Delete(key)
+	}
+	return
+}
+
+// Verify 实现原验证器verify方法
+func (r CaptchaRedis) Verify(id, answer string, clear bool) bool {
+	v := r.Get(id, clear)
+	return v == answer
+}

+ 10 - 2
services/crm_eta.go

@@ -29,6 +29,9 @@ type GetLoginAuthCodeReq struct {
 
 // GetAuthCodeFromMiddleServer CRM_ETA服务-获取登录编码
 func GetAuthCodeFromMiddleServer(adminName string) (authCode string, err error) {
+	if utils.BusinessCode != utils.BusinessCodeRelease && utils.BusinessCode != utils.BusinessCodeSandbox {
+		return
+	}
 	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/auth/auth_code")
 	param := GetLoginAuthCodeReq{
 		Source:    LoginAuthCodeSource,
@@ -50,6 +53,7 @@ func GetAuthCodeFromMiddleServer(adminName string) (authCode string, err error)
 
 	contentType := "application/json;charset=utf-8"
 	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.CrmEtaAuthorization)
 	resp, e := client.Do(req)
 	if e != nil {
 		err = fmt.Errorf("http client do err: %s", e.Error())
@@ -71,7 +75,7 @@ func GetAuthCodeFromMiddleServer(adminName string) (authCode string, err error)
 	if utils.RunMode == "release" {
 		str := string(b)
 		str = strings.Trim(str, `"`)
-		b = utils.DesBase64Decrypt([]byte(str))
+		b = utils.DesBase64Decrypt([]byte(str), utils.DesKey)
 	}
 
 	result := new(MiddleServerResultData)
@@ -120,6 +124,9 @@ type GetCrmTokenData struct {
 
 // CodeLoginFromMiddleServer 中间服务-编码登录
 func CodeLoginFromMiddleServer(authCode string) (tokenResp GetCrmTokenData, err error) {
+	if utils.BusinessCode != utils.BusinessCodeRelease && utils.BusinessCode != utils.BusinessCodeSandbox {
+		return
+	}
 	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/auth/eta_token")
 	param := GetCrmTokenReq{
 		AuthCode: authCode,
@@ -140,6 +147,7 @@ func CodeLoginFromMiddleServer(authCode string) (tokenResp GetCrmTokenData, err
 
 	contentType := "application/json;charset=utf-8"
 	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.CrmEtaAuthorization)
 	resp, e := client.Do(req)
 	if e != nil {
 		err = fmt.Errorf("http client do err: %s", e.Error())
@@ -161,7 +169,7 @@ func CodeLoginFromMiddleServer(authCode string) (tokenResp GetCrmTokenData, err
 	if utils.RunMode == "release" {
 		str := string(b)
 		str = strings.Trim(str, `"`)
-		b = utils.DesBase64Decrypt([]byte(str))
+		b = utils.DesBase64Decrypt([]byte(str), utils.DesKey)
 	}
 
 	result := new(GetCrmTokenDataResp)

+ 9 - 7
services/data/chart_classify.go

@@ -29,7 +29,9 @@ func ChartClassifyItemsMakeTree(sysUser *system.Admin, allNode []*data_manage.Ch
 	if len(childs) > 0 {
 		for _, child := range childs {
 			childButton := GetChartClassifyOpButton(sysUser, child.SysUserId)
-			childButton.AddButton = false //不管有没有权限,图表都是没有添加按钮的
+			if child.Level == 3 {
+				childButton.AddButton = false //第三级的话,默认图表都是没有添加按钮的
+			}
 			child.Button = childButton
 			//node.Children = append(node.Children, child)
 		}
@@ -107,12 +109,12 @@ func FixChartClassifySysUserId() {
 // GetChartClassifyOpButton 获取ETA图库分类的操作权限
 func GetChartClassifyOpButton(sysUser *system.Admin, belongUserId int) (button data_manage.ChartClassifyItemsButton) {
 	//ficc管理员和超管和ficc研究员有权限创建和管理分类,可以编辑分类名称(分类名称不允许重复),可以拖动分类,改变分类顺序,可以拖动分类下模型,改变顺序,可以删除分类,若分类下有预测指标,则不允许删除;
-	if utils.InArrayByStr([]string{utils.ROLE_TYPE_CODE_ADMIN, utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_RESEARCHR, utils.ROLE_TYPE_CODE_FICC_RESEARCHR}, sysUser.RoleTypeCode) {
-		button.AddButton = true
-		button.OpButton = true
-		button.DeleteButton = true
-		button.MoveButton = true
-	}
+	//if utils.InArrayByStr([]string{utils.ROLE_TYPE_CODE_ADMIN, utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_RESEARCHR, utils.ROLE_TYPE_CODE_FICC_RESEARCHR}, sysUser.RoleTypeCode) {
+	button.AddButton = true
+	button.OpButton = true
+	button.DeleteButton = true
+	button.MoveButton = true
+	//}
 
 	return
 }

+ 16 - 8
services/data/correlation/chart_info.go

@@ -275,27 +275,35 @@ func GetChartDataByEdbInfo(edbInfoMappingA, edbInfoMappingB *data_manage.ChartEd
 
 		// 如果A指标是高频,那么就需要对B指标进行升频
 		if frequencyIntMap[edbInfoMappingA.Frequency] < frequencyIntMap[edbInfoMappingB.Frequency] {
-			tmpNewChangeDataList, e := HandleDataByLinearRegression(aDataList, baseDataMap)
+			tmpNewChangeDataList, e := HandleDataByLinearRegression(bDataList, changeDataMap)
 			if e != nil {
 				err = fmt.Errorf("获取变频指标插值法Map失败, Err: %s", e.Error())
 				return
 			}
-			baseDataList = tmpNewChangeDataList
-		} else {
+			changeDataList = tmpNewChangeDataList
 			baseDataList = aDataList
 			for _, v := range baseDataList {
 				baseDataMap[v.DataTime] = v.Value
 			}
-		}
-		// 如果B指标是高频,那么就需要对A指标进行升频
-		if frequencyIntMap[edbInfoMappingA.Frequency] > frequencyIntMap[edbInfoMappingB.Frequency] {
-			tmpNewChangeDataList, e := HandleDataByLinearRegression(bDataList, changeDataMap)
+
+		} else if frequencyIntMap[edbInfoMappingA.Frequency] > frequencyIntMap[edbInfoMappingB.Frequency] {
+			// 如果B指标是高频,那么就需要对A指标进行升频
+			tmpNewChangeDataList, e := HandleDataByLinearRegression(aDataList, baseDataMap)
 			if e != nil {
 				err = fmt.Errorf("获取变频指标插值法Map失败, Err: %s", e.Error())
 				return
 			}
-			changeDataList = tmpNewChangeDataList
+			baseDataList = tmpNewChangeDataList
+
+			changeDataList = bDataList
+			for _, v := range changeDataList {
+				changeDataMap[v.DataTime] = v.Value
+			}
 		} else {
+			baseDataList = aDataList
+			for _, v := range baseDataList {
+				baseDataMap[v.DataTime] = v.Value
+			}
 			changeDataList = bDataList
 			for _, v := range changeDataList {
 				changeDataMap[v.DataTime] = v.Value

+ 13 - 13
services/data/edb_classify.go

@@ -2,11 +2,11 @@ package data
 
 import (
 	"errors"
-	"fmt"
 	"eta/eta_api/models"
 	"eta/eta_api/models/data_manage"
 	"eta/eta_api/models/system"
 	"eta/eta_api/utils"
+	"fmt"
 	"strconv"
 	"time"
 )
@@ -613,12 +613,12 @@ func GetEdbOpButton(sysUser *system.Admin, belongUserId, edbType, edbInfoType in
 // GetEdbClassifyOpButton 获取ETA指标分类的操作权限
 func GetEdbClassifyOpButton(sysUser *system.Admin, belongUserId int) (button data_manage.EdbClassifyItemsButton) {
 	//ficc管理员和超管和ficc研究员有权限创建和管理分类,可以编辑分类名称(分类名称不允许重复),可以拖动分类,改变分类顺序,可以拖动分类下模型,改变顺序,可以删除分类,若分类下有预测指标,则不允许删除;
-	if utils.InArrayByStr([]string{utils.ROLE_TYPE_CODE_ADMIN, utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_RESEARCHR, utils.ROLE_TYPE_CODE_FICC_RESEARCHR}, sysUser.RoleTypeCode) {
-		button.AddButton = true
-		button.OpButton = true
-		button.DeleteButton = true
-		button.MoveButton = true
-	}
+	//if utils.InArrayByStr([]string{utils.ROLE_TYPE_CODE_ADMIN, utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_RESEARCHR, utils.ROLE_TYPE_CODE_FICC_RESEARCHR}, sysUser.RoleTypeCode) {
+	button.AddButton = true
+	button.OpButton = true
+	button.DeleteButton = true
+	button.MoveButton = true
+	//}
 
 	return
 }
@@ -641,12 +641,12 @@ func GetPredictEdbOpButton(sysUser *system.Admin, belongUserId int) (button data
 // GetPredictEdbClassifyOpButton 获取ETA预测指标分类的操作权限
 func GetPredictEdbClassifyOpButton(sysUser *system.Admin, belongUserId int) (button data_manage.EdbClassifyItemsButton) {
 	//ficc管理员和超管和ficc研究员有权限创建和管理分类,可以编辑分类名称(分类名称不允许重复),可以拖动分类,改变分类顺序,可以拖动分类下模型,改变顺序,可以删除分类,若分类下有预测指标,则不允许删除;
-	if utils.InArrayByStr([]string{utils.ROLE_TYPE_CODE_ADMIN, utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_RESEARCHR, utils.ROLE_TYPE_CODE_FICC_RESEARCHR}, sysUser.RoleTypeCode) {
-		button.AddButton = true
-		button.OpButton = true
-		button.DeleteButton = true
-		button.MoveButton = true
-	}
+	//if utils.InArrayByStr([]string{utils.ROLE_TYPE_CODE_ADMIN, utils.ROLE_TYPE_CODE_FICC_ADMIN, utils.ROLE_TYPE_CODE_RESEARCHR, utils.ROLE_TYPE_CODE_FICC_RESEARCHR}, sysUser.RoleTypeCode) {
+	button.AddButton = true
+	button.OpButton = true
+	button.DeleteButton = true
+	button.MoveButton = true
+	//}
 
 	return
 }

+ 28 - 0
services/data/predict_edb_info.go

@@ -1281,3 +1281,31 @@ func CalculateByRuleByNine(formulaStr string, edbInfoList []*data_manage.EdbInfo
 	}
 	return
 }
+
+// ModifyPredictEdbBaseInfoBySourceEdb  根据来源ETA指标修改预测指标的基础信息
+func ModifyPredictEdbBaseInfoBySourceEdb(sourceEDdbInfo *data_manage.EdbInfo) {
+	list, err := data_manage.GetGroupPredictEdbBySourceEdbInfoId(sourceEDdbInfo.EdbInfoId)
+	if err != nil {
+		return
+	}
+	for _, v := range list {
+		v.Frequency = sourceEDdbInfo.Frequency
+		v.Unit = sourceEDdbInfo.Unit
+		v.Update([]string{"Frequency", "Unit"})
+		AddOrEditEdbInfoToEs(v.EdbInfoId)
+	}
+}
+
+// ModifyPredictEdbEnBaseInfoBySourceEdb  根据来源ETA指标修改预测指标的英文基础信息
+func ModifyPredictEdbEnBaseInfoBySourceEdb(sourceEDdbInfo *data_manage.EdbInfo) {
+	list, err := data_manage.GetGroupPredictEdbBySourceEdbInfoId(sourceEDdbInfo.EdbInfoId)
+	if err != nil {
+		return
+	}
+	for _, v := range list {
+		v.Frequency = sourceEDdbInfo.Frequency
+		v.UnitEn = sourceEDdbInfo.UnitEn
+		v.Update([]string{"Frequency", "UnitEn"})
+		AddOrEditEdbInfoToEs(v.EdbInfoId)
+	}
+}

+ 61 - 44
services/data/trade_analysis/trade_analysis.go

@@ -6,7 +6,6 @@ import (
 	"fmt"
 	"sort"
 	"strings"
-	"sync"
 	"time"
 )
 
@@ -26,36 +25,44 @@ func GetClassifyName() (list trade_analysis.TradeClassifyNameListSort, err error
 		"cffex":     4,
 		"ine":       5,
 	}
-	//查询每个交易所的最新更新时间
-	//查询每个交易所下的classifyNameList
+	//查询所有交易所下的分类
+	classifyExchangeList, tmpErr := trade_analysis.GetAllBaseFromTradeClassify()
+	if tmpErr != nil {
+		err = tmpErr
+		errMsg = "查询交易所最新更新时间失败"
+		return
+	}
+
+	// 每个交易所的分类信息
 	classifyExchangeMap := make(map[string][]trade_analysis.TradeClassifyName)
-	timeMap := make(map[string]string)
-	i := 0
-	var wg sync.WaitGroup
-	for k := range exchanges {
-		wg.Add(1)
-		go func(k string, classifyExchangeMap map[string][]trade_analysis.TradeClassifyName) {
-			defer wg.Done()
-			nameList, tmpErr := trade_analysis.GetExchangeClassify(k)
-			if tmpErr != nil {
-				err = tmpErr
-				return
-			}
-			for _, n := range nameList {
-				classifyExchangeMap[k] = append(classifyExchangeMap[k], n)
-			}
+	// 每个交易所的最新更新时间
+	timeLastMap := make(map[string]time.Time)
+	for _, v := range classifyExchangeList {
+		tmpExchange := v.Exchange
 
-			dataTimeItem, tmpErr := trade_analysis.GetExchangeLastTime(k)
-			if tmpErr != nil {
-				err = tmpErr
-				errMsg = "查询交易所最新更新时间失败"
-				return
-			}
-			timeMap[k] = dataTimeItem.CreateTime.Format(utils.FormatDateTime)
+		// 分类
+		tmpList, ok := classifyExchangeMap[tmpExchange]
+		if !ok {
+			tmpList = make([]trade_analysis.TradeClassifyName, 0)
+		}
+		tmpList = append(tmpList, trade_analysis.TradeClassifyName{
+			ClassifyName: v.ClassifyName,
+			ClassifyType: v.ClassifyType,
+			LatestDate:   v.LatestDate.Format(utils.FormatDate),
+		})
+		classifyExchangeMap[v.Exchange] = tmpList
 
-		}(k, classifyExchangeMap)
+		// 时间
+		if tmpLastTime, ok2 := timeLastMap[tmpExchange]; !ok2 {
+			timeLastMap[tmpExchange] = v.ModifyTime
+		} else {
+			if v.ModifyTime.After(tmpLastTime) {
+				timeLastMap[tmpExchange] = v.ModifyTime
+			}
+		}
 	}
-	wg.Wait()
+
+	i := 0
 	currDate := time.Now().Format(utils.FormatDate)
 	for k, v := range exchanges {
 		tmp := trade_analysis.TradeClassifyNameList{
@@ -70,12 +77,16 @@ func GetClassifyName() (list trade_analysis.TradeClassifyNameListSort, err error
 			errMsg = "查询交易所分类信息失败"
 			return
 		}
-		tmp.DataTime, ok = timeMap[k]
-		if !ok {
+
+		// 查询交易所最新更新时间失败
+		if timeLast, ok := timeLastMap[k]; ok {
+			tmp.DataTime = timeLast.Format(utils.FormatDateTime)
+		} else {
 			err = fmt.Errorf("查询交易所最新更新时间失败")
 			errMsg = "查询交易所最新更新时间失败"
 			return
 		}
+
 		classifyMap := make(map[string][]trade_analysis.TradeClassifyNameListItemItem)
 		if len(nameList) > 0 {
 			if k == "zhengzhou" {
@@ -217,10 +228,10 @@ func getZhengzhouClassifyName(code string) (name string) {
 func GetPositionTopDetail(req trade_analysis.GetPositionTopReq) (ret trade_analysis.GetPositionTopResp, err error, errMsg string) {
 	//定义交易所
 	exchanges := map[string]string{
-		"郑商所":  "zhengzhou",
-		"大商所":  "dalian",
-		"上期所":  "shanghai",
-		"中金所":  "cffex",
+		"郑商所":   "zhengzhou",
+		"大商所":   "dalian",
+		"上期所":   "shanghai",
+		"中金所":   "cffex",
 		"上期能源": "ine",
 	}
 	exchange, ok := exchanges[req.Exchange]
@@ -230,16 +241,20 @@ func GetPositionTopDetail(req trade_analysis.GetPositionTopReq) (ret trade_analy
 		return
 	}
 	dataTimeStr := req.DataTime
-	var dataTime time.Time
+	var lastDataTime, dataTime time.Time
+
 	//查询最新的时间
+	lastItem, tmpErr := trade_analysis.GetTradeTopLastDataTime(exchange, req.ClassifyName, req.ClassifyType)
+	if tmpErr != nil {
+		errMsg = "查询最新的榜单信息失败"
+		err = tmpErr
+		return
+	}
+	lastDataTime = lastItem.LatestDate
+
+	// 如果没有传入日期,那么就用最晚的的日期
 	if dataTimeStr == "" {
-		lastItem, tmpErr := trade_analysis.GetTradeTopLastDataTime(exchange, req.ClassifyName, req.ClassifyType)
-		if tmpErr != nil {
-			errMsg = "查询最新的榜单信息失败"
-			err = tmpErr
-			return
-		}
-		dataTime = lastItem.DataTime
+		dataTime = lastDataTime
 	} else {
 		dataTime, err = time.ParseInLocation(utils.FormatDate, dataTimeStr, time.Local)
 		if err != nil {
@@ -247,6 +262,10 @@ func GetPositionTopDetail(req trade_analysis.GetPositionTopReq) (ret trade_analy
 			return
 		}
 	}
+	dataTimeStr = dataTime.Format(utils.FormatDate)
+	
+	ret.DataTime = dataTimeStr
+	ret.LastDataTime = lastDataTime.Format(utils.FormatDate)
 
 	//遇到周末则跳过当天
 	weekStr := dataTime.Weekday().String()
@@ -255,13 +274,12 @@ func GetPositionTopDetail(req trade_analysis.GetPositionTopReq) (ret trade_analy
 		err = fmt.Errorf(errMsg)*/
 		return
 	}
-	dataTimeStr = dataTime.Format(utils.FormatDate)
 	classifyName := req.ClassifyName
 	classifyType := req.ClassifyType
 	if exchange == "zhengzhou" {
 		classifyName = classifyType
 		var typeItem *trade_analysis.TradeClassifyName
-		typeItem, err = trade_analysis.GetZhengzhouClassifyTypeByClassifyName(classifyName)
+		typeItem, err = trade_analysis.GetClassifyTypeByClassifyName(exchange, classifyName)
 		if err != nil {
 			if err.Error() == utils.ErrNoRow() {
 				errMsg = "该合约不存在"
@@ -360,6 +378,5 @@ func GetPositionTopDetail(req trade_analysis.GetPositionTopReq) (ret trade_analy
 	ret.CleanSoldList.TotalDealChange = totalChangeMap[4]
 	ret.CleanSoldList.List = detailList[4]
 
-	ret.DataTime = dataTimeStr
 	return
 }

+ 132 - 0
services/eta_trial/questionnaire.go

@@ -0,0 +1,132 @@
+package eta_trial
+
+import (
+	"encoding/json"
+	"eta/eta_api/models"
+	"eta/eta_api/models/eta_trial"
+	"eta/eta_api/utils"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strings"
+)
+
+type EtaTrialQuestionnaireListResp struct {
+	Code   int                                 `json:"code" description:"状态码"`
+	Msg    string                              `json:"msg" description:"提示信息"`
+	Data   eta_trial.EtaTrialQuestionnaireResp `json:"data" description:"返回数据"`
+	ErrMsg string                              `json:"-" description:"错误信息,不用返回给前端,只是做日志记录"`
+}
+
+// GetEtaTrialQuestionnaireList CRM_ETA服务-获取问卷列表
+func GetEtaTrialQuestionnaireList() (res eta_trial.EtaTrialQuestionnaireResp, err error) {
+	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/eta_trial/questionnaire/list")
+
+	body := ioutil.NopCloser(strings.NewReader(""))
+	client := &http.Client{}
+	req, e := http.NewRequest("POST", url, body)
+	if e != nil {
+		err = fmt.Errorf("http create request err: %s", e.Error())
+		return
+	}
+
+	contentType := "application/json;charset=utf-8"
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.CrmEtaAuthorization)
+	resp, e := client.Do(req)
+	if e != nil {
+		err = fmt.Errorf("http client do err: %s", e.Error())
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	b, e := ioutil.ReadAll(resp.Body)
+	if e != nil {
+		err = fmt.Errorf("resp body read err: %s", e.Error())
+		return
+	}
+	if len(b) == 0 {
+		err = fmt.Errorf("resp body is empty")
+		return
+	}
+	// 生产环境解密, 注意有个坑前后的双引号
+	if utils.RunMode == "release" {
+		str := string(b)
+		str = strings.Trim(str, `"`)
+		b = utils.DesBase64Decrypt([]byte(str), utils.CrmEtaServerDes3Key)
+	}
+
+	result := new(EtaTrialQuestionnaireListResp)
+	if e = json.Unmarshal(b, &result); e != nil {
+		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
+		return
+	}
+	if result.Code != 200 {
+		err = fmt.Errorf("result: %s", string(b))
+		return
+	}
+	res = result.Data
+	return
+}
+
+type EtaTrialQuestionnaireCommitReq struct {
+	List []eta_trial.EtaTrialQuestionnaireRecord
+}
+
+// EtaTrialQuestionnaireCommit CRM_ETA服务-提交问卷调查
+func EtaTrialQuestionnaireCommit(pars EtaTrialQuestionnaireCommitReq) (res bool, err error) {
+	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/eta_trial/questionnaire/commit")
+	params, e := json.Marshal(pars)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	body := ioutil.NopCloser(strings.NewReader(string(params)))
+	client := &http.Client{}
+	req, e := http.NewRequest("POST", url, body)
+	if e != nil {
+		err = fmt.Errorf("http create request err: %s", e.Error())
+		return
+	}
+
+	contentType := "application/json;charset=utf-8"
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.CrmEtaAuthorization)
+	resp, e := client.Do(req)
+	if e != nil {
+		err = fmt.Errorf("http client do err: %s", e.Error())
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	b, e := ioutil.ReadAll(resp.Body)
+	if e != nil {
+		err = fmt.Errorf("resp body read err: %s", e.Error())
+		return
+	}
+	if len(b) == 0 {
+		err = fmt.Errorf("resp body is empty")
+		return
+	}
+	// 生产环境解密, 注意有个坑前后的双引号
+	if utils.RunMode == "release" {
+		str := string(b)
+		str = strings.Trim(str, `"`)
+		b = utils.DesBase64Decrypt([]byte(str), utils.CrmEtaServerDes3Key)
+	}
+
+	result := new(models.ResultData)
+	if e = json.Unmarshal(b, &result); e != nil {
+		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
+		return
+	}
+	if result.Code != 200 {
+		err = fmt.Errorf("result: %s", string(b))
+		return
+	}
+	res = true
+	return
+}

+ 573 - 0
services/eta_trial/user.go

@@ -0,0 +1,573 @@
+package eta_trial
+
+import (
+	"encoding/json"
+	"eta/eta_api/models"
+	"eta/eta_api/models/eta_trial"
+	"eta/eta_api/utils"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"strings"
+)
+
+// EtaTrialUserReq 更新试用客户最后登录时间和次数请求体
+type EtaTrialUserReq struct {
+	Mobile string `description:"手机号"`
+}
+
+// UpdateEtaTrialUserLogin CRM_ETA服务-更新试用客户最后登录时间和次数
+func UpdateEtaTrialUserLogin(pars EtaTrialUserReq) (res bool, err error) {
+	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/eta_trial/user/update_login")
+	params, e := json.Marshal(pars)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	body := ioutil.NopCloser(strings.NewReader(string(params)))
+	client := &http.Client{}
+	req, e := http.NewRequest("POST", url, body)
+	if e != nil {
+		err = fmt.Errorf("http create request err: %s", e.Error())
+		return
+	}
+
+	contentType := "application/json;charset=utf-8"
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.CrmEtaAuthorization)
+	resp, e := client.Do(req)
+	if e != nil {
+		err = fmt.Errorf("http client do err: %s", e.Error())
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	b, e := ioutil.ReadAll(resp.Body)
+	if e != nil {
+		err = fmt.Errorf("resp body read err: %s", e.Error())
+		return
+	}
+	if len(b) == 0 {
+		err = fmt.Errorf("resp body is empty")
+		return
+	}
+	// 生产环境解密, 注意有个坑前后的双引号
+	if utils.RunMode == "release" {
+		str := string(b)
+		str = strings.Trim(str, `"`)
+		b = utils.DesBase64Decrypt([]byte(str), utils.CrmEtaServerDes3Key)
+	}
+
+	result := new(models.ResultData)
+	if e = json.Unmarshal(b, &result); e != nil {
+		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
+		return
+	}
+	if result.Code != 200 {
+		err = fmt.Errorf("result: %s", string(b))
+		return
+	}
+	res = true
+	return
+}
+
+// GetEtaTrialUserReq 获取试用客户请求体
+type GetEtaTrialUserReq struct {
+	Mobile string `description:"手机号"`
+}
+
+type EtaTrialUserResp struct {
+	Code   int                        `json:"code" description:"状态码"`
+	Msg    string                     `json:"msg" description:"提示信息"`
+	Data   eta_trial.EtaTrialUserItem `json:"data" description:"返回数据"`
+	ErrMsg string                     `json:"-" description:"错误信息,不用返回给前端,只是做日志记录"`
+}
+
+// GetEtaTrialUser CRM_ETA服务-获取试用客户信息
+func GetEtaTrialUser(pars GetEtaTrialUserReq) (res eta_trial.EtaTrialUserItem, err error) {
+	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/eta_trial/user/mobile_fetch")
+	params, e := json.Marshal(pars)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	body := ioutil.NopCloser(strings.NewReader(string(params)))
+	client := &http.Client{}
+	req, e := http.NewRequest("POST", url, body)
+	if e != nil {
+		err = fmt.Errorf("http create request err: %s", e.Error())
+		return
+	}
+
+	contentType := "application/json;charset=utf-8"
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.CrmEtaAuthorization)
+	resp, e := client.Do(req)
+	if e != nil {
+		err = fmt.Errorf("http client do err: %s", e.Error())
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	b, e := ioutil.ReadAll(resp.Body)
+	if e != nil {
+		err = fmt.Errorf("resp body read err: %s", e.Error())
+		return
+	}
+	if len(b) == 0 {
+		err = fmt.Errorf("resp body is empty")
+		return
+	}
+	// 生产环境解密, 注意有个坑前后的双引号
+	if utils.RunMode == "release" {
+		str := string(b)
+		str = strings.Trim(str, `"`)
+		b = utils.DesBase64Decrypt([]byte(str), utils.CrmEtaServerDes3Key)
+	}
+
+	//result := new(models.ResultData)
+	result := new(EtaTrialUserResp)
+	if e = json.Unmarshal(b, &result); e != nil {
+		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
+		return
+	}
+	utils.FileLog.Info("%s", string(b))
+	if result.Code != 200 {
+		err = fmt.Errorf("result: %s", string(b))
+		return
+	}
+	res = result.Data
+	//r, ok := result.Data.(eta_trial.EtaTrialUserResp)
+	//if !ok {
+	//	err = fmt.Errorf("result data err")
+	//	return
+	//}
+	//res = r
+	return
+}
+
+// DisableEtaTrialUser CRM_ETA服务-禁用试用客户
+func DisableEtaTrialUser(pars EtaTrialUserReq) (res bool, err error) {
+	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/eta_trial/user/disable")
+	params, e := json.Marshal(pars)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	body := ioutil.NopCloser(strings.NewReader(string(params)))
+	client := &http.Client{}
+	req, e := http.NewRequest("POST", url, body)
+	if e != nil {
+		err = fmt.Errorf("http create request err: %s", e.Error())
+		return
+	}
+
+	contentType := "application/json;charset=utf-8"
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.CrmEtaAuthorization)
+	resp, e := client.Do(req)
+	if e != nil {
+		err = fmt.Errorf("http client do err: %s", e.Error())
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	b, e := ioutil.ReadAll(resp.Body)
+	if e != nil {
+		err = fmt.Errorf("resp body read err: %s", e.Error())
+		return
+	}
+	if len(b) == 0 {
+		err = fmt.Errorf("resp body is empty")
+		return
+	}
+	// 生产环境解密, 注意有个坑前后的双引号
+	if utils.RunMode == "release" {
+		str := string(b)
+		str = strings.Trim(str, `"`)
+		b = utils.DesBase64Decrypt([]byte(str), utils.CrmEtaServerDes3Key)
+	}
+
+	result := new(models.ResultData)
+	if e = json.Unmarshal(b, &result); e != nil {
+		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
+		return
+	}
+	if result.Code != 200 {
+		err = fmt.Errorf("result: %s", string(b))
+		return
+	}
+	res = true
+	return
+}
+
+// RemoveEtaTrialUser CRM_ETA服务-删除试用客户
+func RemoveEtaTrialUser(pars EtaTrialUserReq) (res bool, err error) {
+	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/eta_trial/user/remove")
+	params, e := json.Marshal(pars)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	body := ioutil.NopCloser(strings.NewReader(string(params)))
+	client := &http.Client{}
+	req, e := http.NewRequest("POST", url, body)
+	if e != nil {
+		err = fmt.Errorf("http create request err: %s", e.Error())
+		return
+	}
+
+	contentType := "application/json;charset=utf-8"
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.CrmEtaAuthorization)
+	resp, e := client.Do(req)
+	if e != nil {
+		err = fmt.Errorf("http client do err: %s", e.Error())
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	b, e := ioutil.ReadAll(resp.Body)
+	if e != nil {
+		err = fmt.Errorf("resp body read err: %s", e.Error())
+		return
+	}
+	if len(b) == 0 {
+		err = fmt.Errorf("resp body is empty")
+		return
+	}
+	// 生产环境解密, 注意有个坑前后的双引号
+	if utils.RunMode == "release" {
+		str := string(b)
+		str = strings.Trim(str, `"`)
+		b = utils.DesBase64Decrypt([]byte(str), utils.CrmEtaServerDes3Key)
+	}
+
+	result := new(models.ResultData)
+	if e = json.Unmarshal(b, &result); e != nil {
+		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
+		return
+	}
+	if result.Code != 200 {
+		err = fmt.Errorf("result: %s", string(b))
+		return
+	}
+	res = true
+	return
+}
+
+// EtaTrialUserEditReq 更新用户信息请求体
+type EtaTrialUserEditReq struct {
+	RealName string `description:"姓名"`
+	Position string `description:"职务"`
+	Mobile   string `description:"手机号"`
+	Enabled  int    `description:"禁启用"`
+}
+
+// EditEtaTrialUser CRM_ETA服务-编辑试用客户
+func EditEtaTrialUser(pars EtaTrialUserEditReq) (res bool, err error) {
+	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/eta_trial/user/edit")
+	params, e := json.Marshal(pars)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	body := ioutil.NopCloser(strings.NewReader(string(params)))
+	client := &http.Client{}
+	req, e := http.NewRequest("POST", url, body)
+	if e != nil {
+		err = fmt.Errorf("http create request err: %s", e.Error())
+		return
+	}
+
+	contentType := "application/json;charset=utf-8"
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.CrmEtaAuthorization)
+	resp, e := client.Do(req)
+	if e != nil {
+		err = fmt.Errorf("http client do err: %s", e.Error())
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	b, e := ioutil.ReadAll(resp.Body)
+	if e != nil {
+		err = fmt.Errorf("resp body read err: %s", e.Error())
+		return
+	}
+	if len(b) == 0 {
+		err = fmt.Errorf("resp body is empty")
+		return
+	}
+	// 生产环境解密, 注意有个坑前后的双引号
+	if utils.RunMode == "release" {
+		str := string(b)
+		str = strings.Trim(str, `"`)
+		b = utils.DesBase64Decrypt([]byte(str), utils.CrmEtaServerDes3Key)
+	}
+
+	result := new(models.ResultData)
+	if e = json.Unmarshal(b, &result); e != nil {
+		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
+		return
+	}
+	if result.Code != 200 {
+		err = fmt.Errorf("result: %s", string(b))
+		return
+	}
+	res = true
+	return
+}
+
+// UpdateUserIndexNum CRM_ETA服务-更新用户累计新增指标数
+func UpdateUserIndexNum(pars EtaTrialUserReq) (res bool, err error) {
+	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/eta_trial/user/update_index_num")
+	params, e := json.Marshal(pars)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	body := ioutil.NopCloser(strings.NewReader(string(params)))
+	client := &http.Client{}
+	req, e := http.NewRequest("POST", url, body)
+	if e != nil {
+		err = fmt.Errorf("http create request err: %s", e.Error())
+		return
+	}
+
+	contentType := "application/json;charset=utf-8"
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.CrmEtaAuthorization)
+	resp, e := client.Do(req)
+	if e != nil {
+		err = fmt.Errorf("http client do err: %s", e.Error())
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	b, e := ioutil.ReadAll(resp.Body)
+	if e != nil {
+		err = fmt.Errorf("resp body read err: %s", e.Error())
+		return
+	}
+	if len(b) == 0 {
+		err = fmt.Errorf("resp body is empty")
+		return
+	}
+	// 生产环境解密, 注意有个坑前后的双引号
+	if utils.RunMode == "release" {
+		str := string(b)
+		str = strings.Trim(str, `"`)
+		b = utils.DesBase64Decrypt([]byte(str), utils.CrmEtaServerDes3Key)
+	}
+
+	result := new(models.ResultData)
+	if e = json.Unmarshal(b, &result); e != nil {
+		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
+		return
+	}
+	if result.Code != 200 {
+		err = fmt.Errorf("result: %s", string(b))
+		return
+	}
+	res = true
+	return
+}
+
+// UpdateUserChartNum CRM_ETA服务-更新用户累计新增图表数
+func UpdateUserChartNum(pars EtaTrialUserReq) (res bool, err error) {
+	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/eta_trial/user/update_chart_num")
+	params, e := json.Marshal(pars)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	body := ioutil.NopCloser(strings.NewReader(string(params)))
+	client := &http.Client{}
+	req, e := http.NewRequest("POST", url, body)
+	if e != nil {
+		err = fmt.Errorf("http create request err: %s", e.Error())
+		return
+	}
+
+	contentType := "application/json;charset=utf-8"
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.CrmEtaAuthorization)
+	resp, e := client.Do(req)
+	if e != nil {
+		err = fmt.Errorf("http client do err: %s", e.Error())
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	b, e := ioutil.ReadAll(resp.Body)
+	if e != nil {
+		err = fmt.Errorf("resp body read err: %s", e.Error())
+		return
+	}
+	if len(b) == 0 {
+		err = fmt.Errorf("resp body is empty")
+		return
+	}
+	// 生产环境解密, 注意有个坑前后的双引号
+	if utils.RunMode == "release" {
+		str := string(b)
+		str = strings.Trim(str, `"`)
+		b = utils.DesBase64Decrypt([]byte(str), utils.CrmEtaServerDes3Key)
+	}
+
+	result := new(models.ResultData)
+	if e = json.Unmarshal(b, &result); e != nil {
+		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
+		return
+	}
+	if result.Code != 200 {
+		err = fmt.Errorf("result: %s", string(b))
+		return
+	}
+	res = true
+	return
+}
+
+// UpdateEtaTrialUserActiveTimeReq 更新试用客户活跃时长请求体
+type UpdateEtaTrialUserActiveTimeReq struct {
+	Mobile     string `description:"手机号"`
+	UserName   string `description:"用户姓名"`
+	ActiveTime int    `description:"活跃时长, 单位秒"`
+	Part       string `description:"活跃板块"`
+}
+
+// UpdateEtaTrialUserActiveTime CRM_ETA服务-更新试用客户活跃时长
+func UpdateEtaTrialUserActiveTime(pars UpdateEtaTrialUserActiveTimeReq) (res bool, err error) {
+	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/eta_trial/user/update_active_time")
+	params, e := json.Marshal(pars)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	body := ioutil.NopCloser(strings.NewReader(string(params)))
+	client := &http.Client{}
+	req, e := http.NewRequest("POST", url, body)
+	if e != nil {
+		err = fmt.Errorf("http create request err: %s", e.Error())
+		return
+	}
+
+	contentType := "application/json;charset=utf-8"
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.CrmEtaAuthorization)
+	resp, e := client.Do(req)
+	if e != nil {
+		err = fmt.Errorf("http client do err: %s", e.Error())
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	b, e := ioutil.ReadAll(resp.Body)
+	if e != nil {
+		err = fmt.Errorf("resp body read err: %s", e.Error())
+		return
+	}
+	if len(b) == 0 {
+		err = fmt.Errorf("resp body is empty")
+		return
+	}
+	// 生产环境解密, 注意有个坑前后的双引号
+	if utils.RunMode == "release" {
+		str := string(b)
+		str = strings.Trim(str, `"`)
+		b = utils.DesBase64Decrypt([]byte(str), utils.CrmEtaServerDes3Key)
+	}
+
+	result := new(models.ResultData)
+	if e = json.Unmarshal(b, &result); e != nil {
+		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
+		return
+	}
+	if result.Code != 200 {
+		err = fmt.Errorf("result: %s", string(b))
+		return
+	}
+	res = true
+	return
+}
+
+// UpdateEtaTrialUserLoginDurationReq 更新试用客户登录时长请求体
+type UpdateEtaTrialUserLoginDurationReq struct {
+	Mobile     string `description:"手机号"`
+	UserName   string `description:"用户姓名"`
+	ActiveTime int    `description:"活跃时长, 单位秒"`
+}
+
+// UpdateEtaTrialUserLoginDuration CRM_ETA服务-更新试用客户登录时长
+func UpdateEtaTrialUserLoginDuration(pars UpdateEtaTrialUserLoginDurationReq) (res bool, err error) {
+	url := fmt.Sprint(utils.CrmEtaServerUrl, "/api/eta_trial/user/update_login_duration")
+	params, e := json.Marshal(pars)
+	if e != nil {
+		err = fmt.Errorf("data json marshal err: %s", e.Error())
+		return
+	}
+
+	body := ioutil.NopCloser(strings.NewReader(string(params)))
+	client := &http.Client{}
+	req, e := http.NewRequest("POST", url, body)
+	if e != nil {
+		err = fmt.Errorf("http create request err: %s", e.Error())
+		return
+	}
+
+	contentType := "application/json;charset=utf-8"
+	req.Header.Set("Content-Type", contentType)
+	req.Header.Set("Authorization", utils.CrmEtaAuthorization)
+	resp, e := client.Do(req)
+	if e != nil {
+		err = fmt.Errorf("http client do err: %s", e.Error())
+		return
+	}
+	defer func() {
+		_ = resp.Body.Close()
+	}()
+	b, e := ioutil.ReadAll(resp.Body)
+	if e != nil {
+		err = fmt.Errorf("resp body read err: %s", e.Error())
+		return
+	}
+	if len(b) == 0 {
+		err = fmt.Errorf("resp body is empty")
+		return
+	}
+	// 生产环境解密, 注意有个坑前后的双引号
+	if utils.RunMode == "release" {
+		str := string(b)
+		str = strings.Trim(str, `"`)
+		b = utils.DesBase64Decrypt([]byte(str), utils.CrmEtaServerDes3Key)
+	}
+
+	result := new(models.ResultData)
+	if e = json.Unmarshal(b, &result); e != nil {
+		err = fmt.Errorf("result unmarshal err: %s\nresult: %s", e.Error(), string(b))
+		return
+	}
+	if result.Code != 200 {
+		err = fmt.Errorf("result: %s", string(b))
+		return
+	}
+	res = true
+	return
+}

+ 41 - 35
services/excel/lucky_sheet.go

@@ -3,11 +3,11 @@ package excel
 import (
 	"encoding/json"
 	"errors"
+	"eta/eta_api/models/data_manage/request"
+	"eta/eta_api/utils"
 	"fmt"
 	"github.com/tealeg/xlsx"
 	"github.com/xuri/excelize/v2"
-	"eta/eta_api/models/data_manage/request"
-	"eta/eta_api/utils"
 	"os"
 	"reflect"
 	"strconv"
@@ -589,21 +589,24 @@ func handleTableDataList(tableDataList [][]LuckySheetDataValue, luckySheetDataCo
 		flag = false
 		//尾部
 		deleteBottomRowIndexList := make([]int, 0)
-		for rowIndex := lenRow - 1; rowIndex >= 0; rowIndex-- {
-			isDelete := true
-			for _, v := range tableDataList[rowIndex] {
-				if v.Monitor != `` {
-					isDelete = false
-					flag = true
+		// 数据要大于1行才会处理
+		if len(tableDataList) > 1 {
+			for rowIndex := lenRow - 1; rowIndex >= 0; rowIndex-- {
+				isDelete := true
+				for _, v := range tableDataList[rowIndex] {
+					if v.Monitor != `` {
+						isDelete = false
+						flag = true
+						break
+					}
+				}
+				if flag {
 					break
 				}
-			}
-			if flag {
-				break
-			}
-			if isDelete {
-				deleteBottomRowIndexList = append(deleteBottomRowIndexList, rowIndex)
-				removeBottomRow++
+				if isDelete {
+					deleteBottomRowIndexList = append(deleteBottomRowIndexList, rowIndex)
+					removeBottomRow++
+				}
 			}
 		}
 
@@ -655,25 +658,28 @@ func handleTableDataList(tableDataList [][]LuckySheetDataValue, luckySheetDataCo
 		flag = false
 		//右边
 		deleteTailColumnIndexList := make([]int, 0)
-		for columnIndex := lenColumn - 1; columnIndex >= 0; columnIndex-- {
-			isDelete := true
-			for _, v := range tableDataList {
-				//如果一列都没有,说明是上面几行是空行,没有数据
-				if len(v) <= 0 {
-					continue
+		// 数据要大于1列才会处理
+		if lenColumn > 1 {
+			for columnIndex := lenColumn - 1; columnIndex >= 0; columnIndex-- {
+				isDelete := true
+				for _, v := range tableDataList {
+					//如果一列都没有,说明是上面几行是空行,没有数据
+					if len(v) <= 0 {
+						continue
+					}
+					if v[columnIndex].Monitor != `` || (v[columnIndex].MergeCell.Column != columnIndex && v[columnIndex].MergeCell.Column != 0) {
+						isDelete = false
+						flag = true
+						break
+					}
 				}
-				if v[columnIndex].Monitor != `` || (v[columnIndex].MergeCell.Column != columnIndex && v[columnIndex].MergeCell.Column != 0) {
-					isDelete = false
-					flag = true
+				if flag {
 					break
 				}
-			}
-			if flag {
-				break
-			}
-			if isDelete {
-				deleteTailColumnIndexList = append(deleteTailColumnIndexList, columnIndex)
-				removeRightColumn++
+				if isDelete {
+					deleteTailColumnIndexList = append(deleteTailColumnIndexList, columnIndex)
+					removeRightColumn++
+				}
 			}
 		}
 
@@ -856,10 +862,10 @@ var LuckyFontFamilyMap = map[int]string{
 	2:  "Tahoma",
 	3:  "Verdana",
 	4:  "微软雅黑",
-	5:  "宋体",   //宋体(Song)、
-	6:  "黑体",   // 黑体(ST Heiti)
-	7:  "楷体",   //楷体(ST Kaiti),
-	8:  "仿宋",   //仿宋(ST FangSong),
+	5:  "宋体",  //宋体(Song)、
+	6:  "黑体",  // 黑体(ST Heiti)
+	7:  "楷体",  //楷体(ST Kaiti),
+	8:  "仿宋",  //仿宋(ST FangSong),
 	9:  "新宋体", //新宋体(ST Song),
 	10: "华文新魏",
 	11: "华文行楷",

+ 4 - 2
services/oss.go

@@ -3,13 +3,13 @@ package services
 import (
 	"encoding/json"
 	"errors"
-	"github.com/aliyun/aliyun-oss-go-sdk/oss"
 	"eta/eta_api/services/alarm_msg"
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
 	"os"
 	"time"
 
-	"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
 	"eta/eta_api/utils"
+	"github.com/aliyun/alibaba-cloud-sdk-go/services/sts"
 )
 
 // UploadAliyunV2 图片上传到阿里云
@@ -199,6 +199,8 @@ func NewSTSToken() (item *STSToken, err error) {
 	now := time.Now().Format(utils.FormatDateTimeUnSpace)
 	request.RoleSessionName = utils.RoleSessionName + now
 	request.DurationSeconds = "3600"
+	request.ConnectTimeout = 300 * time.Second
+	request.ReadTimeout = 300 * time.Second
 
 	response, e := client.AssumeRole(request)
 	if e != nil {

+ 46 - 0
services/ppt/ppt_english_group.go

@@ -1365,3 +1365,49 @@ func GetEnglishCopyUsableTitle(title string) (newTitle string, err error) {
 	}
 	return
 }
+
+// SearchEnglishPptList PPT搜索(我的/公开PPT)
+func SearchEnglishPptList(adminId int, keyword string) (ret ppt_english.RespGroupPptList, err error) {
+	list := make([]*ppt_english.RespGroupPptListItem, 0)
+	ret.List = list
+
+	var condition string
+	var pars []interface{}
+
+	// 公开的PPT或是我的非公开PPT
+	condition += ` AND (is_share = 1 OR (admin_id = ? AND is_share = 0)) `
+	pars = append(pars, adminId)
+
+	if keyword != `` {
+		kw := fmt.Sprint("%", keyword, "%")
+		condition += ` AND (title LIKE ? OR admin_real_name LIKE ? ) `
+		pars = append(pars, kw, kw)
+	}
+	pptList, err := ppt_english.GetAllPptEnglishList(condition, pars)
+
+	if len(pptList) <= 0 {
+		return
+	}
+
+	for _, v := range pptList {
+		tmpV := &ppt_english.RespGroupPptListItem{
+			GroupPptId:    int64(v.PptId),
+			PptId:         int64(v.PptId),
+			TemplateType:  v.TemplateType,
+			BackgroundImg: v.BackgroundImg,
+			Title:         v.Title,
+			PptCreateTime: v.CreateTime.Format(utils.FormatDateTime),
+			AdminId:       v.AdminId,
+			AdminRealName: v.AdminRealName,
+			IsSingleShare: v.IsShare,
+			PptxUrl:       v.PptxUrl,
+			ReportId:      v.ReportId,
+			ReportCode:    v.ReportCode,
+		}
+		list = append(list, tmpV)
+	}
+
+	ret.List = list
+	ret.Total = len(list)
+	return
+}

+ 47 - 0
services/ppt/ppt_group.go

@@ -1686,3 +1686,50 @@ func GetGrantPptList(adminId int, keyword, sourceType string) (ret models.RespGr
 	ret.Total = len(list)
 	return
 }
+
+// SearchPptList PPT搜索(我的/公开PPT)
+func SearchPptList(adminId int, keyword string) (ret models.RespGroupPptList, err error) {
+	list := make([]*models.RespGroupPptListItem, 0)
+	ret.List = list
+
+	var condition string
+	var pars []interface{}
+
+	// 公开的PPT或是我的非公开PPT
+	condition += ` AND (is_share = 1 OR (admin_id = ? AND is_share = 0)) `
+	pars = append(pars, adminId)
+
+	if keyword != `` {
+		kw := fmt.Sprint("%", keyword, "%")
+		condition += ` AND (title LIKE ? OR admin_real_name LIKE ? ) `
+		pars = append(pars, kw, kw)
+	}
+	pptList, err := models.GetAllPptV2List(condition, pars)
+
+	if len(pptList) <= 0 {
+		return
+	}
+
+	for _, v := range pptList {
+		tmpV := &models.RespGroupPptListItem{
+			GroupPptId:    int64(v.PptId),
+			PptId:         int64(v.PptId),
+			TemplateType:  v.TemplateType,
+			BackgroundImg: v.BackgroundImg,
+			Title:         v.Title,
+			PptCreateTime: v.CreateTime.Format(utils.FormatDateTime),
+			AdminId:       v.AdminId,
+			AdminRealName: v.AdminRealName,
+			PptVersion:    v.PptVersion,
+			IsSingleShare: v.IsShare,
+			PptxUrl:       v.PptxUrl,
+			ReportId:      v.ReportId,
+			ReportCode:    v.ReportCode,
+		}
+		list = append(list, tmpV)
+	}
+
+	ret.List = list
+	ret.Total = len(list)
+	return
+}

+ 15 - 13
services/report.go

@@ -941,21 +941,23 @@ func CreateNewReport(req models.AddReq, adminInfo *system.Admin) (newReportId in
 		err = errors.New("保存失败,Err:" + e.Error())
 		return
 	}
-	//处理权限
-	{
-		permissionItems, err := models.GetPermission(req.ClassifyNameSecond)
-		if err != nil {
-			go alarm_msg.SendAlarmMsg("获取权限失败,Err:"+err.Error(), 3)
-			//utils.SendEmail(utils.APPNAME+"失败提醒", "获取权限失败,Err:"+err.Error(), utils.EmailSendToUsers)
-		}
-		for _, v := range permissionItems {
-			err = models.AddChartPermissionChapterMapping(v.ChartPermissionId, newReportId)
-			if err != nil {
-				go alarm_msg.SendAlarmMsg("新增权限失败,Err:"+err.Error(), 3)
-				//utils.SendEmail(utils.APPNAME+"失败提醒", "新增权限失败,Err:"+err.Error()+strconv.FormatInt(newReportId, 10), utils.EmailSendToUsers)
+
+	// 处理权限
+	if utils.BusinessCode == utils.BusinessCodeRelease || utils.BusinessCode == utils.BusinessCodeSandbox {
+		go func() {
+			permissionItems, e := models.GetPermission(req.ClassifyNameSecond)
+			if e != nil {
+				alarm_msg.SendAlarmMsg("获取权限失败,Err:"+err.Error(), 3)
 			}
-		}
+			for _, v := range permissionItems {
+				e = models.AddChartPermissionChapterMapping(v.ChartPermissionId, newReportId)
+				if e != nil {
+					alarm_msg.SendAlarmMsg("新增权限失败,Err:"+err.Error(), 3)
+				}
+			}
+		}()
 	}
+
 	reportCode = utils.MD5(strconv.Itoa(int(newReportId)))
 	//修改唯一编码
 	{

+ 98 - 26
services/sms.go

@@ -2,25 +2,94 @@ package services
 
 import (
 	"encoding/json"
-	"errors"
-	"fmt"
 	"eta/eta_api/services/alarm_msg"
 	"eta/eta_api/utils"
+	"fmt"
 	"io/ioutil"
 	"net/http"
 	"net/url"
 )
 
-func SendSmsCode(mobile, ip string, codeType int, vcode string) bool {
-	flag := false
-	tplId := ""
-	switch codeType {
-	case utils.REGISTER_CODE:
-		tplId = "206722"
-	case utils.LOGIN_CODE:
-		tplId = "65692"
+// SendSmsCode 发送国内短信
+func SendSmsCode(mobile, vCode, tplId string) (flag bool) {
+	if mobile == "" || vCode == "" || tplId == "" {
+		return
 	}
-	result, err := sendSms(mobile, tplId, vcode)
+
+	var (
+		err error
+		res string
+	)
+	defer func() {
+		if err != nil {
+			tips := fmt.Sprintf("短信验证码发送失败, Err: %s; Result: %s", err.Error(), res)
+			utils.FileLog.Info("%s", tips)
+			go alarm_msg.SendAlarmMsg(tips, 2)
+		}
+	}()
+
+	result, e := sendSms(mobile, tplId, vCode)
+	if e != nil {
+		err = fmt.Errorf("send sms err: %s", e.Error())
+		return
+	}
+	res = string(result)
+
+	var netReturn map[string]interface{}
+	if e = json.Unmarshal(result, &netReturn); e != nil {
+		err = fmt.Errorf("json unmarshal err: %s", e.Error())
+		return
+	}
+	errCode, ok := netReturn["error_code"].(float64)
+	if !ok {
+		err = fmt.Errorf("result code err")
+		return
+	}
+	// 忽略错误的手机号码这种错误
+	if errCode != 0 && errCode != 205401 {
+		err = fmt.Errorf("err code %f", errCode)
+		return
+	}
+	// 发送成功
+	if errCode == 0 {
+		flag = true
+	}
+	return
+}
+
+// sendSms 发送国内短信
+func sendSms(mobile, tplId, code string) (rs []byte, err error) {
+	var Url *url.URL
+	apiURL := "http://v.juhe.cn/sms/send"
+	//初始化参数
+	param := url.Values{}
+	//配置请求参数,方法内部已处理urlencode问题,中文参数可以直接传参
+	param.Set("mobile", mobile) //接受短信的用户手机号码
+	param.Set("tpl_id", tplId)  //您申请的短信模板ID,根据实际情况修改
+	tplVal := fmt.Sprintf(`#code#=%s&#m#=%d`, code, utils.VerifyCodeExpireMinute)
+	param.Set("tpl_value", tplVal)     //您设置的模板变量,根据实际情况
+	param.Set("key", utils.JhGnAppKey) //应用APPKEY(应用详细页查询)
+
+	Url, err = url.Parse(apiURL)
+	if err != nil {
+		fmt.Printf("解析url错误:\r\n%v", err)
+		return nil, err
+	}
+	//如果参数中有中文参数,这个方法会进行URLEncode
+	Url.RawQuery = param.Encode()
+	resp, err := http.Get(Url.String())
+	if err != nil {
+		fmt.Println("err:", err)
+		return nil, err
+	}
+	defer resp.Body.Close()
+	return ioutil.ReadAll(resp.Body)
+}
+
+// SendSmsCodeGj 发送国际短信
+func SendSmsCodeGj(mobile, vCode, areaNum string) bool {
+	flag := false
+	result, err := sendSmsGj(mobile, vCode, areaNum)
 	if err != nil {
 		fmt.Println("发送短信失败")
 		return false
@@ -29,35 +98,35 @@ func SendSmsCode(mobile, ip string, codeType int, vcode string) bool {
 	var netReturn map[string]interface{}
 	err = json.Unmarshal(result, &netReturn)
 	if err != nil {
-		go alarm_msg.SendAlarmMsg("短信验证码发送失败 ErrMsg:"+err.Error()+" result"+string(result), 3)
-		//go utils.SendEmail("短信验证码发送失败","err:"+err.Error()+" result"+string(result), utils.EmailSendToUsers)
+		//go SendEmail("短信验证码发送失败", "err:"+err.Error()+" result"+string(result), utils.EmailSendToUsers)
+		go alarm_msg.SendAlarmMsg("短信验证码发送失败, Err:"+err.Error()+";Result:"+string(result), 2)
 		flag = false
 	}
 	if netReturn["error_code"].(float64) == 0 {
 		fmt.Printf("接口返回result字段是:\r\n%v", netReturn["result"])
 		flag = true
 	} else {
-		go alarm_msg.SendAlarmMsg("短信验证码发送失败 ErrMsg:"+err.Error()+" result"+string(result), 3)
-		//go utils.SendEmail("短信验证码发送失败"," result"+string(result), utils.EmailSendToUsers)
+		// 忽略错误的手机号码这种错误
+		if netReturn["error_code"].(float64) != 205401 {
+			go alarm_msg.SendAlarmMsg("短信验证码发送失败, Result:"+string(result), 2)
+		}
 		flag = false
 	}
 	return flag
 }
 
-func sendSms(mobile, tplId, code string) (rs []byte, err error) {
-	if utils.JhGnAppKey == `` {
-		err = errors.New("短信未配置")
-		return
-	}
+// sendSmsGj 发送国际短信
+func sendSmsGj(mobile, code, areaNum string) (rs []byte, err error) {
 	var Url *url.URL
-	apiURL := "http://v.juhe.cn/sms/send"
+	apiURL := "http://v.juhe.cn/smsInternational/send.php"
 	//初始化参数
 	param := url.Values{}
 	//配置请求参数,方法内部已处理urlencode问题,中文参数可以直接传参
-	param.Set("mobile", mobile)            //接受短信的用户手机号码
-	param.Set("tpl_id", tplId)             //您申请的短信模板ID,根据实际情况修改
-	param.Set("tpl_value", "#code#="+code) //您设置的模板变量,根据实际情况
-	param.Set("key", utils.JhGnAppKey)     //应用APPKEY(应用详细页查询)
+	param.Set("mobile", mobile)           //接受短信的用户手机号码
+	param.Set("tplId", "10054")           //您申请的短信模板ID,根据实际情况修改
+	param.Set("tplValue", "#code#="+code) //您设置的模板变量,根据实际情况
+	param.Set("key", utils.JhGjAppKey)    //应用APPKEY(应用详细页查询)
+	param.Set("areaNum", areaNum)         //应用APPKEY(应用详细页查询)
 
 	Url, err = url.Parse(apiURL)
 	if err != nil {
@@ -71,6 +140,9 @@ func sendSms(mobile, tplId, code string) (rs []byte, err error) {
 		fmt.Println("err:", err)
 		return nil, err
 	}
+	utils.FileLog.Info("sendSmsGj:param:" + Url.String())
 	defer resp.Body.Close()
-	return ioutil.ReadAll(resp.Body)
+	body, err := ioutil.ReadAll(resp.Body)
+	utils.FileLog.Info("sendSmsGj:result:" + string(body))
+	return body, err
 }

+ 123 - 0
services/user_login.go

@@ -0,0 +1,123 @@
+package services
+
+import (
+	"encoding/json"
+	"eta/eta_api/models"
+	"eta/eta_api/models/company"
+	"eta/eta_api/models/system"
+	"eta/eta_api/utils"
+	"fmt"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// SendAdminMobileVerifyCode 发送用户手机验证码
+func SendAdminMobileVerifyCode(source int, mobile, areaCode string) (ok bool, err error) {
+	verifyCode := utils.GetRandDigit(6)
+	record := new(system.AdminVerifyCodeRecord)
+	record.VerifyType = system.AdminVerifyCodeRecordTypeMobile
+	record.Mobile = mobile
+	record.Source = source
+	record.Code = verifyCode
+	record.ExpiredTime = time.Now().Add(utils.VerifyCodeExpireMinute * time.Minute)
+	record.CreateTime = time.Now().Local()
+	record.ModifyTime = time.Now().Local()
+	if e := record.Create(); e != nil {
+		err = fmt.Errorf("新增验证码记录失败, Err: %s", e.Error())
+		return
+	}
+
+	tplId := utils.SmsNewLoginTplId
+	if areaCode == "86" {
+		ok = SendSmsCode(mobile, verifyCode, tplId)
+	} else {
+		ok = SendSmsCodeGj(mobile, verifyCode, areaCode)
+	}
+	record.SendStatus = system.AdminVerifyCodeRecordStatusSuccess
+	if !ok {
+		record.SendStatus = system.AdminVerifyCodeRecordStatusFail
+	}
+	cols := []string{"SendStatus"}
+	if e := record.Update(cols); e != nil {
+		err = fmt.Errorf("更新验证码记录失败, Err: %s", e.Error())
+	}
+	return
+}
+
+// SendAdminEmailVerifyCode 发送用户邮箱验证码
+func SendAdminEmailVerifyCode(source int, email string) (ok bool, err error) {
+	verifyCode := utils.GetRandDigit(6)
+	record := new(system.AdminVerifyCodeRecord)
+	record.VerifyType = system.AdminVerifyCodeRecordTypeEmail
+	record.Email = email
+	record.Source = source
+	record.Code = verifyCode
+	record.ExpiredTime = time.Now().Add(utils.VerifyCodeExpireMinute * time.Minute)
+	record.CreateTime = time.Now().Local()
+	record.ModifyTime = time.Now().Local()
+	if e := record.Create(); e != nil {
+		err = fmt.Errorf("新增验证码记录失败, Err: %s", e.Error())
+		return
+	}
+
+	// 获取邮件配置
+	authKey := "english_report_email_conf"
+	emailConf, e := company.GetConfigDetailByCode(authKey)
+	if e != nil {
+		err = fmt.Errorf("获取群发邮件权限失败, Err: %s", e.Error())
+		return
+	}
+	if emailConf.ConfigValue == "" {
+		err = fmt.Errorf("邮件配置为空, 不可推送")
+		return
+	}
+	conf := new(models.EnglishReportEmailConf)
+	if e = json.Unmarshal([]byte(emailConf.ConfigValue), &conf); e != nil {
+		err = fmt.Errorf("邮件配置有误, 不可推送")
+		return
+	}
+
+	// 获取邮箱模板
+	confKey := "admin_verify_code_email_tmp"
+	confTmp, e := company.GetConfigDetailByCode(confKey)
+	if e != nil {
+		err = fmt.Errorf("获取邮件模板失败, Err: %s", e.Error())
+		return
+	}
+	if confTmp.ConfigValue == `` {
+		err = fmt.Errorf("邮件模板为空, 不可推送")
+		return
+	}
+
+	req := new(EnglishReportSendEmailRequest)
+	req.Subject = "弘则研究登录验证"
+	req.Email = email
+	req.FromAlias = conf.FromAlias
+	// 填充模板
+	t := time.Now().Format("2006年01月02日")
+	ct := confTmp.ConfigValue
+	ct = strings.Replace(ct, "{{VERIFY_CODE}}", verifyCode, 1)
+	ct = strings.Replace(ct, "{{EXPIRED_MINUTE}}", strconv.Itoa(utils.VerifyCodeExpireMinute), 1)
+	ct = strings.Replace(ct, "{{DATE_TIME}}", t, 1)
+	req.HtmlBody = ct
+
+	aliEmail := new(AliyunEmail)
+	o, result, e := aliEmail.SendEmail(req)
+	if e != nil {
+		err = fmt.Errorf("邮箱推送失败, Err: %s", e.Error())
+		return
+	}
+	ok = o
+
+	record.SendStatus = system.AdminVerifyCodeRecordStatusSuccess
+	if !ok {
+		record.SendStatus = system.AdminVerifyCodeRecordStatusFail
+	}
+	record.SendResult = result
+	cols := []string{"SendStatus", "SendResult"}
+	if e = record.Update(cols); e != nil {
+		err = fmt.Errorf("更新验证码记录失败, Err: %s", e.Error())
+	}
+	return
+}

+ 7 - 1
utils/config.go

@@ -30,6 +30,7 @@ var (
 	APPNAME          string //项目中文名称
 	EmailSendToUsers string // 邮件提醒人员
 	JhGnAppKey       string // 聚合短信,国内AppKey
+	JhGjAppKey       string // 聚合短信,国际AppKey
 )
 
 // ES配置
@@ -165,7 +166,10 @@ var (
 var LibreOfficePath string
 
 // CrmEtaServerUrl CRM-ETA服务地址
-var CrmEtaServerUrl string
+var (
+	CrmEtaServerUrl     string
+	CrmEtaServerDes3Key string
+)
 
 // BusinessCode 商家编码
 var BusinessCode string
@@ -236,6 +240,7 @@ func init() {
 
 	// 聚合短信,国内AppKey
 	JhGnAppKey = config["jh_gn_app_key"]
+	JhGjAppKey = config["jh_gj_app_key"]
 
 	// ppt 转图片服务地址
 	Ppt2ImageUrl = config["ppt2_image_url"]
@@ -379,6 +384,7 @@ func init() {
 	}
 
 	CrmEtaServerUrl = config["crm_eta_server_url"]
+	CrmEtaServerDes3Key = config["crm_eta_server_des3_key"]
 	LibreOfficePath = config["libre_office_path"]
 
 	// 商家编码

+ 13 - 1
utils/constants.go

@@ -195,6 +195,9 @@ const (
 	CACHE_IMPORT_MANUAL_DATA          = "import:manual:data"                  //手工数据导入后刷新
 	CACHE_ACCESS_TOKEN_LOGIN          = "pc_eta_admin:login:"                 //管理后台登录
 	CACHE_ACCESS_TOKEN_LOGIN_NO_TRUST = "pc_eta_admin:login:no_trust:"        //管理后台登录(不可信登录态)
+	CACHE_ABNORMAL_LOGIN              = "pc_eta_admin:login:abnormal:"        //管理后台登录-异常登录
+	CACHE_LOGIN_ERR_PASS              = "pc_eta_admin:login:errPass:"         //管理后台登录-输入错误密码次数
+	CACHE_FIND_PASS_VERIFY            = "pc_eta_admin:findPass:verify:"       //找回密码校验成功标记
 	CACHE_KEY_MYSTEEL_REFRESH         = "mysteel_chemical:refresh"            //钢联化工刷新
 	CACHE_KEY_DAYNEW_REFRESH          = "admin:day_new:refresh"               //每日资讯拉取企业微信聊天记录
 	CACHE_KEY_DAYNEW_TRANSLATE        = "admin:day_new:translate"             //每日资讯中翻英
@@ -289,7 +292,6 @@ const (
 
 // 系统来源
 const (
-	SOURCE_CRM_FLAG = 1
 	SOURCE_ETA_FLAG = 2
 )
 
@@ -301,3 +303,13 @@ const (
 	BusinessCodeSandbox = "E2023080700" // 试用平台
 	BusinessCodeRelease = "E2023080900" // 生产环境
 )
+
+// 验证码
+const (
+	CaptchaCachePrefix     = "captcha:lock:eta_" // 验证码缓存Key
+	VerifyCodeExpireMinute = 15                  // 短信/邮箱验证码过期时间-分钟
+	SmsLoginTplId          = "65692"             // 【弘则研究】您的验证码是XXX,如非本人操作,请忽略本短信
+	SmsNewLoginTplId       = "254663"            // 【弘则研究】您的验证码是XXX,有效期15分钟
+)
+
+const CrmEtaAuthorization = "NIi1RbEmH0C2rksXtPGDPBBgRgTZY87Q"

+ 2 - 2
utils/des3.go

@@ -20,7 +20,7 @@ func DesBase64Encrypt(origData []byte) []byte {
 	return []byte(base64.StdEncoding.EncodeToString(result))
 }
 
-func DesBase64Decrypt(crypted []byte) []byte {
+func DesBase64Decrypt(crypted []byte, desKey string) []byte {
 	result, _ := base64.StdEncoding.DecodeString(string(crypted))
 	remain := len(result) % 8
 	if remain > 0 {
@@ -29,7 +29,7 @@ func DesBase64Decrypt(crypted []byte) []byte {
 			result = append(result, 0)
 		}
 	}
-	origData, err := TripleDesDecrypt(result, []byte(DesKey))
+	origData, err := TripleDesDecrypt(result, []byte(desKey))
 	if err != nil {
 		panic(any(err))
 	}