Parcourir la source

Merge branch 'master' of http://8.136.199.33:3000/hongze/hz_crm_api into cygx/cygx_p2_864

xingzai il y a 1 an
Parent
commit
cea63f9f0b
55 fichiers modifiés avec 4338 ajouts et 412 suppressions
  1. 363 0
      controllers/company_renewal.go
  2. 8 0
      controllers/company_user.go
  3. 9 4
      controllers/cygx/activity.go
  4. 14 11
      controllers/cygx/activity_special.go
  5. 15 10
      controllers/cygx/cygx_activity_type.go
  6. 3 2
      controllers/cygx/minutes_summary.go
  7. 13 7
      controllers/cygx/morning_meeting_review.go
  8. 5 3
      controllers/cygx/product_interior.go
  9. 6 2
      controllers/cygx/report_article.go
  10. 3 2
      controllers/cygx/report_selection.go
  11. 3 2
      controllers/cygx/research_summary.go
  12. 24 20
      controllers/cygx/summary_manage.go
  13. 10 0
      controllers/eta_trial.go
  14. 279 15
      controllers/statistic_report.go
  15. 65 20
      controllers/sys_admin.go
  16. 891 0
      controllers/user_login.go
  17. 150 1
      controllers/yb/product_census.go
  18. 3 3
      go.mod
  19. 5 166
      go.sum
  20. 5 0
      models/base.go
  21. 74 0
      models/company/company_ascribe.go
  22. 5 1
      models/company/company_config.go
  23. 146 0
      models/company/company_no_renewed_ascribe.go
  24. 68 0
      models/company/company_no_renewed_note.go
  25. 1 1
      models/cygx/activity_type.go
  26. 1 0
      models/cygx/article.go
  27. 28 0
      models/cygx/cygx_morning_meeting_review_chapter.go
  28. 26 11
      models/cygx/cygx_report_mapping.go
  29. 5 5
      models/cygx/industrial_article_group_subject.go
  30. 1 1
      models/cygx/industrial_management.go
  31. 11 0
      models/cygx/report_article.go
  32. 65 0
      models/cygx/report_mapping_category_group.go
  33. 11 0
      models/cygx/report_mapping_cygx.go
  34. 47 3
      models/cygx/resource_data.go
  35. 102 0
      models/cygx/resource_data_industrial_group_management.go
  36. 8 0
      models/db.go
  37. 37 35
      models/eta_trial/eta_trial.go
  38. 33 30
      models/stack_company_statistic.go
  39. 29 26
      models/statistic_report.go
  40. 126 0
      models/system/admin_verify_code_record.go
  41. 5 0
      models/system/sys_admin.go
  42. 52 6
      models/system/sys_user.go
  43. 110 0
      models/yb/community_audio_listen_log.go
  44. 5 4
      models/yb/response/product_census.go
  45. 13 0
      models/yb/voice_section.go
  46. 117 0
      routers/commentsRouter.go
  47. 7 0
      routers/router.go
  48. 48 0
      services/captcha_redis.go
  49. 69 0
      services/company_ascribe.go
  50. 2 0
      services/crm_eta.go
  51. 70 0
      services/cygx/report_mapping_category_group.go
  52. 904 0
      services/cygx/resource_data.go
  53. 97 20
      services/sms.go
  54. 123 0
      services/user_login.go
  55. 18 1
      utils/constants.go

+ 363 - 0
controllers/company_renewal.go

@@ -0,0 +1,363 @@
+package controllers
+
+import (
+	"encoding/json"
+	"hongze/hz_crm_api/models"
+	"hongze/hz_crm_api/models/company"
+	"hongze/hz_crm_api/utils"
+	"time"
+)
+
+//公司未续约操作
+
+type CompanyRenewalController struct {
+	BaseAuthController
+}
+
+// @Title 新建归因
+// @Description 新建归因接口
+// @Param	request	body company.CompanyAscribeAddReq true "type json string"
+// @Success 200 {object} "保存成功"
+// @router /company_ascribe/add [post]
+func (this *CompanyRenewalController) CompanyAscribeAdd() {
+	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 company.CompanyAscribeAddReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	ascribeContent := req.AscribeContent
+	var condition string
+	var pars []interface{}
+	condition += " AND ascribe_content = ? "
+	pars = append(pars, ascribeContent)
+	total, err := company.GetCompanyAscribeCount(condition, pars)
+	if err != nil {
+		br.Msg = "新建失败"
+		br.ErrMsg = "获取失败,GetCompanyAscribeCount Err:" + err.Error()
+		return
+	}
+	if total > 0 {
+		br.Msg = "此归因已存在"
+		return
+	}
+	item := new(company.CompanyAscribe)
+	item.AscribeContent = ascribeContent
+	item.AdminId = sysUser.AdminId
+	item.CreateTime = time.Now()
+	item.ModifyTime = time.Now()
+	newId, err := company.AddCompanyAscribe(item)
+	if err != nil {
+		br.Msg = "新建失败"
+		br.ErrMsg = "新建失败,Err:" + err.Error()
+		return
+	}
+	detail := new(company.CompanyAscribeResp)
+	detail.CompanyAscribeId = int(newId)
+	detail.AscribeContent = ascribeContent
+	resp := new(company.CompanyAscribeDetailResp)
+	resp.Detail = detail
+	br.Ret = 200
+	br.Success = true
+	br.Data = resp
+	br.Msg = "新建成功"
+}
+
+// @Title 归因列表接口
+// @Description 归因列表接口
+// @Param   KeyWord   query   string  true       "搜索关键词"
+// @Success 200 {object} company.CompanyAscribeListResp
+// @router /company_ascribe/list [get]
+func (this *CompanyRenewalController) CompanyAscribeList() {
+	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"
+		return
+	}
+	keyWord := this.GetString("KeyWord")
+	var condition string
+	var pars []interface{}
+	if keyWord != "" {
+		condition += ` AND  ascribe_content LIKE '%` + keyWord + `%'  ORDER  BY  create_time DESC   `
+	}
+	list, err := company.GetCompanyAscribeList(condition, pars, 0, 0)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	resp := new(company.CompanyAscribeListResp)
+	if len(list) == 0 {
+		list = make([]*company.CompanyAscribeResp, 0)
+	}
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 添加未续约备注
+// @Description 添加未续约备注接口
+// @Param	request	body company.CompanyNoRenewedNoteReq true "type json string"
+// @Success 200 {object} "保存成功"
+// @router /company_no_renewed_note/add [post]
+func (this *CompanyRenewalController) CompanyNoRenewedNoteAdd() {
+	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 company.CompanyNoRenewedNoteReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	content := req.Content
+	companyId := req.CompanyId
+	productId := req.ProductId
+	if companyId < 1 {
+		br.Msg = "公司ID错误!"
+		return
+	}
+	if productId != 1 && productId != 2 {
+		br.Msg = "ProductId错误!"
+		return
+	}
+	item := new(company.CompanyNoRenewedNote)
+	item.Content = content
+	item.CompanyId = companyId
+	item.AdminId = sysUser.AdminId
+	item.ProductId = productId
+	item.CreateTime = time.Now()
+	item.ModifyTime = time.Now()
+	err = company.AddCompanyNoRenewedNote(item)
+	if err != nil {
+		br.Msg = "新建失败"
+		br.ErrMsg = "新建失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "新建成功"
+}
+
+// @Title 未续约备注列表
+// @Description 未续约备注列表接口
+// @Param   CompanyId   query   int  true       "公司ID"
+// @Param   ProductId   query   int  true       "产品ID 1:FICC ,2:权益"
+// @Success 200 {object} company.CompanyAscribeListResp
+// @router /company_no_renewed_note/list [get]
+func (this *CompanyRenewalController) CompanyNoRenewedNoteList() {
+	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"
+		return
+	}
+	companyId, _ := this.GetInt("CompanyId")
+	productId, _ := this.GetInt("ProductId")
+	if companyId < 1 {
+		br.Msg = "公司ID错误!"
+		return
+	}
+	if productId != 1 && productId != 2 {
+		br.Msg = "ProductId错误!"
+		return
+	}
+	var condition string
+	var pars []interface{}
+	condition += ` AND  company_id  = ?  AND product_id = ?  ORDER  BY  create_time DESC   `
+	pars = append(pars, companyId, productId)
+	list, err := company.GetCompanyNoRenewedNoteList(condition, pars, 0, 0)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	resp := new(company.CompanyNoRenewedNoteListResp)
+	if len(list) == 0 {
+		list = make([]*company.CompanyNoRenewedNoteResp, 0)
+	}
+	resp.List = list
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}
+
+// @Title 通过归因添加确认不续约
+// @Description 通过归因添加确认不续约接口
+// @Param	request	body company.CompanyNoRenewedNoteReq true "type json string"
+// @Success 200 {object} "保存成功"
+// @router /company_no_renewed_ascribe/add [post]
+func (this *CompanyRenewalController) CompanyNoRenewedAscribeAdd() {
+	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
+	}
+
+	//内容仅权益管理员账号可以修改
+	if sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_ADMIN && sysUser.RoleTypeCode != utils.ROLE_TYPE_CODE_RAI_ADMIN {
+		br.Msg = "仅管理员可修改!"
+		return
+	}
+	var req company.CompanyNoRenewedAscribeReq
+	err := json.Unmarshal(this.Ctx.Input.RequestBody, &req)
+	if err != nil {
+		br.Msg = "参数解析异常!"
+		br.ErrMsg = "参数解析失败,Err:" + err.Error()
+		return
+	}
+	content := req.Content
+	companyId := req.CompanyId
+	productId := req.ProductId
+	companyAscribeId := req.CompanyAscribeId
+
+	if content == "" {
+		br.Msg = "内容不能为空!"
+		return
+	}
+	if companyId < 1 {
+		br.Msg = "公司ID错误!"
+		return
+	}
+	if productId != 1 && productId != 2 {
+		if companyId < 1 {
+			br.Msg = "ProductId错误!"
+			return
+		}
+	}
+	detail, err := company.GetCompanyAscribeDetail(companyAscribeId)
+	if err != nil {
+		br.Msg = "新建失败"
+		br.ErrMsg = "新建失败,GetCompanyAscribeDetail Err:" + err.Error()
+		return
+	}
+	var condition string
+	var pars []interface{}
+	condition += ` AND  company_id  = ?  AND product_id = ?  ORDER  BY  create_time DESC   `
+	pars = append(pars, companyId, productId)
+
+	total, err := company.GetCompanyNoRenewedAscribeCount(condition, pars)
+	if err != nil {
+		br.Msg = "新建失败"
+		br.ErrMsg = "新建失败,GetCompanyNoRenewedAscribeCount Err:" + err.Error()
+		return
+	}
+	item := new(company.CompanyNoRenewedAscribe)
+	item.CompanyAscribeId = companyAscribeId
+	item.AscribeContent = detail.AscribeContent
+	item.Content = content
+	item.ProductId = productId
+	item.CompanyId = companyId
+	item.AdminId = sysUser.AdminId
+	item.CreateTime = time.Now()
+	item.ModifyTime = time.Now()
+
+	itemLog := new(company.CompanyNoRenewedAscribeLog)
+	itemLog.CompanyAscribeId = companyAscribeId
+	itemLog.AscribeContent = detail.AscribeContent
+	itemLog.Content = content
+	itemLog.ProductId = productId
+	itemLog.CompanyId = companyId
+	itemLog.AdminId = sysUser.AdminId
+	itemLog.CreateTime = time.Now()
+	itemLog.ModifyTime = time.Now()
+	if total == 0 {
+		err = company.AddCompanyNoRenewedAscribe(item, itemLog)
+	} else {
+		err = company.UpdateCompanyNoRenewedAscribe(item, itemLog)
+	}
+	if err != nil {
+		br.Msg = "新建失败"
+		br.ErrMsg = "新建失败,Err:" + err.Error()
+		return
+	}
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "新建成功"
+}
+
+// @Title 确认归因不续约详情接口
+// @Description 确认归因不续约详情接口
+// @Param   CompanyId   query   int  true       "公司ID"
+// @Param   ProductId   query   int  true       "产品ID 1:FICC ,2:权益"
+// @Success 200 {object} company.CompanyAscribeListResp
+// @router /company_no_renewed_ascribe/detail [get]
+func (this *CompanyRenewalController) CompanyNoRenewedAscribeAddDetail() {
+	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"
+		return
+	}
+	companyId, _ := this.GetInt("CompanyId")
+	productId, _ := this.GetInt("ProductId")
+	if companyId < 1 {
+		br.Msg = "公司ID错误!"
+		return
+	}
+	if productId != 1 && productId != 2 {
+		br.Msg = "ProductId错误!"
+		return
+	}
+	resp := new(company.CompanyNoRenewedAscribeDetailResp)
+	detail, err := company.GetCygxProductInteriorDetail(companyId, productId)
+	if err != nil {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,Err:" + err.Error()
+		return
+	}
+	resp.Detail = detail
+	br.Ret = 200
+	br.Success = true
+	br.Msg = "获取成功"
+	br.Data = resp
+}

+ 8 - 0
controllers/company_user.go

@@ -5310,6 +5310,14 @@ func (this *CompanyUserController) GetOtherProduct() {
 	if statisticFlag {
 		resp = append(resp, questionItem, roadVideoItem, videoItem)
 	}
+
+	// 全部
+	resp = append(resp, response.OtherProductTypeListResp{
+		ProductId:   0,
+		ProductType: 99,
+		ProductName: "全部",
+	})
+
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "获取成功"

+ 9 - 4
controllers/cygx/activity.go

@@ -804,7 +804,8 @@ func (this *ActivityCoAntroller) PreserveAndPublish() {
 				go func() {
 					//同时添加多个活动的时候,避免协程开的过多
 					cygxService.DoActivityOnenIdWxTemplateMsg(v)
-					cygxService.UpdateResourceData(v, "activity", "add", time.Now().Format(utils.FormatDateTime))
+					//cygxService.UpdateResourceData(v, "activity", "add", time.Now().Format(utils.FormatDateTime))
+					cygxService.UpdateActivityResourceData(v) //写入首页最新  cygx_resource_data 表
 					cygxService.YiDongSaveRoadshowDetail(v)
 					cygxService.YanXuanActivityPointsBillActivityPublishAndCancel(v, AdminUser.AdminId, 1) //活动发布以及取消发布处理研选扣
 					elastic.AddComprehensiveActivity(v)
@@ -816,7 +817,8 @@ func (this *ActivityCoAntroller) PreserveAndPublish() {
 	} else {
 		if req.DoType == 1 {
 			go cygxService.DoActivityOnenIdWxTemplateMsg(req.ActivityId)
-			go cygxService.UpdateResourceData(req.ActivityId, "activity", "add", time.Now().Format(utils.FormatDateTime))
+			//go cygxService.UpdateResourceData(req.ActivityId, "activity", "add", time.Now().Format(utils.FormatDateTime))
+			cygxService.UpdateActivityResourceData(req.ActivityId)                                             //写入首页最新  cygx_resource_data 表
 			go cygxService.YanXuanActivityPointsBillActivityPublishAndCancel(activityId, AdminUser.AdminId, 1) //活动发布以及取消发布处理研选扣点
 		}
 		//如果二次编辑的时候,取消了易董办会选项,那么就对易董发送取消发布到广场的通知
@@ -1276,7 +1278,9 @@ func (this *ActivityCoAntroller) PublishAndCancel() {
 		item.PublishStatus = 1
 		item.IsCancel = 0
 		go cygxService.DoActivityOnenIdWxTemplateMsg(activityId)
-		go cygxService.UpdateResourceData(activityId, "activity", "add", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(activityId, "activity", "add", time.Now().Format(utils.FormatDateTime))
+
+		cygxService.UpdateActivityResourceData(activityId) //写入首页最新  cygx_resource_data 表
 	} else {
 		item.PublishStatus = 0
 		item.IsCancel = 1
@@ -1284,7 +1288,8 @@ func (this *ActivityCoAntroller) PublishAndCancel() {
 	if activityInfo.PublishStatus == 1 {
 		go services.SendWxMsgWithCygxActivity(req.ActivityId)
 		//同步活动到最新数据表
-		go cygxService.UpdateResourceData(req.ActivityId, "activity", "delete", "")
+		//go cygxService.UpdateResourceData(req.ActivityId, "activity", "delete", "")
+		cygxService.UpdateActivityResourceData(activityId) //写入首页最新  cygx_resource_data 表
 	}
 	item.ActivityId = req.ActivityId
 	if item.PublishStatus == 1 {

+ 14 - 11
controllers/cygx/activity_special.go

@@ -275,7 +275,8 @@ func (this *ActivitySpecialCoAntroller) PreserveAndPublish() {
 	//模板消息推送
 	if req.DoType == 1 {
 		go services.SendWxMsgWithCygxActivitySpecial(activityId)
-		go cygxService.UpdateResourceData(activityId, "activityspecial", "add", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(activityId, "activityspecial", "add", time.Now().Format(utils.FormatDateTime))
+		go cygxService.UpdateActivitySpecialResourceData(activityId) //写入首页最新  cygx_resource_data 表
 	}
 	go elastic.AddComprehensiveActivitySpecial(activityId) // Es添加活动
 	br.Ret = 200
@@ -535,12 +536,13 @@ func (this *ActivitySpecialCoAntroller) PublishAndCancel() {
 	//模板消息推送
 	if item.PublishStatus == 1 {
 		go services.SendWxMsgWithCygxActivitySpecial(activityId)
-		go cygxService.UpdateResourceData(activityId, "activityspecial", "add", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(activityId, "activityspecial", "add", time.Now().Format(utils.FormatDateTime))
 	} else {
 		go cygxService.DetermineTripCancel(activityId)
-		go cygxService.UpdateResourceData(activityId, "activityspecial", "delete", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(activityId, "activityspecial", "delete", time.Now().Format(utils.FormatDateTime))
 	}
-	go elastic.AddComprehensiveActivitySpecial(activityId) // Es添加活动
+	go cygxService.UpdateActivitySpecialResourceData(activityId) //写入首页最新  cygx_resource_data 表
+	go elastic.AddComprehensiveActivitySpecial(activityId)       // Es添加活动
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"
@@ -1174,13 +1176,14 @@ func (this *ActivitySpecialCoAntroller) Offline() {
 		return
 	}
 	//模板消息推送
-	if item.PublishStatus == 1 {
-		go cygxService.UpdateResourceData(activityId, "activityspecial", "add", time.Now().Format(utils.FormatDateTime))
-		//go services.SendWxMsgWithCygxActivitySpecial(activityId)
-	} else {
-		go cygxService.UpdateResourceData(activityId, "activityspecial", "delete", time.Now().Format(utils.FormatDateTime))
-	}
-	go elastic.AddComprehensiveActivitySpecial(activityId) // Es添加活动
+	//if item.PublishStatus == 1 {
+	//	go cygxService.UpdateResourceData(activityId, "activityspecial", "add", time.Now().Format(utils.FormatDateTime))
+	//	//go services.SendWxMsgWithCygxActivitySpecial(activityId)
+	//} else {
+	//	go cygxService.UpdateResourceData(activityId, "activityspecial", "delete", time.Now().Format(utils.FormatDateTime))
+	//}
+	go cygxService.UpdateActivitySpecialResourceData(activityId) //写入首页最新  cygx_resource_data 表
+	go elastic.AddComprehensiveActivitySpecial(activityId)       // Es添加活动
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"

+ 15 - 10
controllers/cygx/cygx_activity_type.go

@@ -16,6 +16,7 @@ type ActivityTypeCoAntroller struct {
 // @Title 活动类型列表
 // @Description活动类型列表接口
 // @Param   IsResearch   query   bool  true       "是否为研选"
+// @Param   IsGetAll   query   bool  true       "是否获取所有"
 // @Success 200 {object} cygx.ActivityTypeListResp
 // @router /activityType/list [get]
 func (this *ActivityTypeCoAntroller) List() {
@@ -32,13 +33,17 @@ func (this *ActivityTypeCoAntroller) List() {
 		return
 	}
 	isResearch, _ := this.GetBool("IsResearch", false)
+	isGetAll, _ := this.GetBool("IsGetAll", false)
 	var condition string
 	resp := new(cygx.ActivityTypeListResp)
-	if isResearch {
-		condition = " source_type IN (0,2) "
-	} else {
-		condition = " source_type IN (0,1) "
+	if !isGetAll {
+		if isResearch {
+			condition = " AND source_type IN (0,2) "
+		} else {
+			condition = " AND source_type IN (0,1) "
+		}
 	}
+
 	list, err := cygx.GetActivityTypeSearchList(condition)
 	if err != nil {
 		br.Msg = "获取失败"
@@ -122,13 +127,13 @@ func (this *ActivityTypeCoAntroller) ListSearch() {
 	resp := new(cygx.ActivityTypeListResp)
 	var condition string
 	if searchType == 1 {
-		condition += ` activity_type_id  IN (1,2)`
+		condition += ` AND  activity_type_id  IN (1,2)`
 	} else if searchType == 2 {
-		condition += ` activity_type_id  IN (5,6)`
+		condition += ` AND activity_type_id  IN (5,6)`
 	} else if searchType == 3 {
-		condition += ` activity_type_id  IN (3,4)`
+		condition += ` AND activity_type_id  IN (3,4)`
 	} else {
-		condition += ` activity_type_id  IN (7)`
+		condition += ` AND activity_type_id  IN (7)`
 	}
 	list, err := cygx.GetActivityTypeSearchList(condition)
 	if err != nil {
@@ -167,9 +172,9 @@ func (this *ActivityTypeCoAntroller) MeetType() {
 	resp := new(cygx.ActivityTypeListResp)
 	var condition string
 	if meetType == 1 {
-		condition += ` activity_type =1 `
+		condition += ` AND activity_type =1 `
 	} else {
-		condition += ` activity_type  = 0 `
+		condition += ` AND activity_type  = 0 `
 	}
 	if isResearch {
 		condition += " AND  source_type IN (0,2) "

+ 3 - 2
controllers/cygx/minutes_summary.go

@@ -811,12 +811,12 @@ func (this *MinutesSummaryController) VisibleRange() {
 		if detail.IsSendWxMsg == 0 {
 			cygxService.DoThisWeekLastWeekWxTemplateMsg(detail.Title, detail.PublishDate, detail.Abstract, req.ArticleId, 3)
 		}
-		go cygxService.UpdateResourceData(articleId, "minutessummary", "add", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(articleId, "minutessummary", "add", time.Now().Format(utils.FormatDateTime))
 	} else {
 		item.AdminId = detail.AdminId
 		item.AdminName = detail.AdminName
 		item.VisibleRange = 0
-		go cygxService.UpdateResourceData(articleId, "minutessummary", "delete", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(articleId, "minutessummary", "delete", time.Now().Format(utils.FormatDateTime))
 	}
 	err = cygx.MinutesSummaryVisibleRange(item)
 	if err != nil {
@@ -824,6 +824,7 @@ func (this *MinutesSummaryController) VisibleRange() {
 		br.ErrMsg = "操作失败,Err:" + err.Error()
 		return
 	}
+	go cygxService.UpdateMinutesSummaryResourceData(articleId) //写入首页最新  cygx_resource_data 表
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"

+ 13 - 7
controllers/cygx/morning_meeting_review.go

@@ -3,6 +3,7 @@ package cygx
 import (
 	"encoding/json"
 	"errors"
+	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"hongze/hz_crm_api/controllers"
 	"hongze/hz_crm_api/models"
@@ -240,8 +241,9 @@ func (this *MorningMeetingController) PreserveAndPublish() {
 				return
 			}
 			for _, itemchapter := range listchapter {
-				go cygxService.UpdateResourceData(itemchapter.Id, "meetingreviewchapt", "add", time.Now().Format(utils.FormatDateTime))
-				go elastic.AddComprehensiveMeetingreviewchapt(itemchapter.Id) //Es添加晨会精华
+				//go cygxService.UpdateResourceData(itemchapter.Id, "meetingreviewchapt", "add", time.Now().Format(utils.FormatDateTime))
+				go cygxService.UpdateMeetingreviewchaptResourceData(itemchapter.Id) //写入首页最新  cygx_resource_data 表
+				go elastic.AddComprehensiveMeetingreviewchapt(itemchapter.Id)       //Es添加晨会精华
 			}
 			br.Msg = "发布成功"
 			br.Data = meetingId
@@ -400,7 +402,8 @@ func (this *MorningMeetingController) PreserveAndPublish() {
 				go services.SendWxMsgWithCygxMorningMeeting(reqList.MeetingId, item.IndustryId, item.IndustryName)
 			}
 			for _, itemchapter := range listchapter {
-				go cygxService.UpdateResourceData(itemchapter.Id, "meetingreviewchapt", "add", time.Now().Format(utils.FormatDateTime))
+				//go cygxService.UpdateResourceData(itemchapter.Id, "meetingreviewchapt", "add", time.Now().Format(utils.FormatDateTime))
+				go cygxService.UpdateMeetingreviewchaptResourceData(itemchapter.Id) //写入首页最新  cygx_resource_data 表
 			}
 			br.Msg = "发布成功"
 		}
@@ -552,9 +555,11 @@ func (this *MorningMeetingController) PublishReport() {
 			return
 		}
 		for _, item := range list {
+			fmt.Println(item.Id)
 			go services.SendWxMsgWithCygxMorningMeeting(vint, item.IndustryId, item.IndustryName)
-			go cygxService.UpdateResourceData(item.Id, "meetingreviewchapt", "add", time.Now().Format(utils.FormatDateTime))
-			go elastic.AddComprehensiveMeetingreviewchapt(item.Id) //Es添加晨会精华
+			//go cygxService.UpdateResourceData(item.Id, "meetingreviewchapt", "add", time.Now().Format(utils.FormatDateTime))
+			go cygxService.UpdateMeetingreviewchaptResourceData(item.Id) //写入首页最新  cygx_resource_data 表
+			go elastic.AddComprehensiveMeetingreviewchapt(item.Id)       //Es添加晨会精华
 		}
 	}
 
@@ -600,8 +605,9 @@ func (this *MorningMeetingController) PublishCancleReport() {
 		return
 	}
 	for _, item := range list {
-		go cygxService.UpdateResourceData(item.Id, "meetingreviewchapt", "delete", time.Now().Format(utils.FormatDateTime))
-		go elastic.DeleteComprehensiveMeetingreviewchapt(item.Id) //Es删除晨会精华
+		//go cygxService.UpdateResourceData(item.Id, "meetingreviewchapt", "delete", time.Now().Format(utils.FormatDateTime))
+		go cygxService.UpdateMeetingreviewchaptResourceData(item.Id) //写入首页最新  cygx_resource_data 表
+		go elastic.DeleteComprehensiveMeetingreviewchapt(item.Id)    //Es删除晨会精华
 	}
 	var condition string
 	var pars []interface{}

+ 5 - 3
controllers/cygx/product_interior.go

@@ -374,7 +374,8 @@ func (this *ProductInteriorController) PublishReport() {
 	} else {
 		status = 0
 		isCancel = 1
-		go cygxService.UpdateResourceData(productInteriorId, "productinterior", "delete", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(productInteriorId, "productinterior", "delete", time.Now().Format(utils.FormatDateTime))
+		go cygxService.UpdateProductInteriorResourceData(productInteriorId) //写入首页最新  cygx_resource_data 表
 	}
 	err = cygx.EditProductInteriorStatus(status, isCancel, productInteriorId)
 	if err != nil {
@@ -422,11 +423,11 @@ func (this *ProductInteriorController) VisibleRange() {
 	var visibleRange int
 	if detail.VisibleRange == 0 {
 		visibleRange = 1
-		go cygxService.UpdateResourceData(productInteriorId, "productinterior", "add", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(productInteriorId, "productinterior", "add", time.Now().Format(utils.FormatDateTime))
 		go cygxService.SendWxMsgWithCygxProductInterior(productInteriorId)
 	} else {
 		visibleRange = 0
-		go cygxService.UpdateResourceData(productInteriorId, "productinterior", "delete", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(productInteriorId, "productinterior", "delete", time.Now().Format(utils.FormatDateTime))
 	}
 
 	err = cygx.ProductInteriorVisibleRange(visibleRange, productInteriorId)
@@ -435,6 +436,7 @@ func (this *ProductInteriorController) VisibleRange() {
 		br.ErrMsg = "操作失败,Err:" + err.Error()
 		return
 	}
+	go cygxService.UpdateProductInteriorResourceData(productInteriorId) //写入首页最新  cygx_resource_data 表
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"

+ 6 - 2
controllers/cygx/report_article.go

@@ -750,6 +750,8 @@ func (this *IndustrialSubjectController) UpdateMatchTypeName() {
 		return
 	}
 	go cygxService.DoArticleOnenIdWxTemplateMsg(detailArt.ArticleId, 2)
+	go cygxService.AddCygxReportMappingCategoryGroupByArticleId(detailArt.ArticleId) // 手动修改报告归类
+	go cygxService.UpdateArticleResourceData(detailArt.ArticleId)                    //写入首页最新  cygx_resource_data 表
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "修改成功"
@@ -919,9 +921,10 @@ func (this *ReportArticleController) PreserveAndPublish() {
 	//cygx.UpdateSubjecIds(subjectIds+"—"+strconv.Itoa(industrialDetail.IndustrialManagementId)+"{|2|}", industrialDetail.IndustrialManagementId, articleId)
 	if item.PublishStatus == 1 {
 		services.EsAddOrEditData(item)
-		go cygxService.UpdateResourceData(item.ArticleId, "article", "add", time.Now().Format(utils.FormatDateTime))
-
+		//go cygxService.UpdateResourceData(item.ArticleId, "article", "add", time.Now().Format(utils.FormatDateTime))
+		go cygxService.UpdateArticleResourceData(item.ArticleId) //写入首页最新  cygx_resource_data 表
 	}
+
 	// 查研观向7.4-更新产业布局时间
 	if len(industryIds) > 0 {
 		go cygxService.UpdateIndustryLayoutTime(industryIds, false)
@@ -930,6 +933,7 @@ func (this *ReportArticleController) PreserveAndPublish() {
 		//查研观向8.2.1专项调研报告模板消息推送
 		cygxService.SendWxMsgWithCygxActivitySpecialArticle(item.ArticleId)
 	}
+	go cygxService.AddCygxReportMappingCategoryGroupByArticleId(item.ArticleId) // 手动修改报告归类
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"

+ 3 - 2
controllers/cygx/report_selection.go

@@ -200,8 +200,7 @@ func (this *ReportSelectionController) PreserveAndPublish() {
 	//	}
 	//	existMap[v.ChartPermissionId] = v.ChartPermissionId
 	//}
-	//go services.CreateVideoWhithContent(articleId, req.Title, content, "cygx_report_selection")
-
+	go cygxService.UpdateReportSelectionResourceData(req.ArticleId) //首页最新页面数据逻辑处理 V11.1.1
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"
@@ -552,6 +551,7 @@ func (this *ReportSelectionController) PublishAndCancel() {
 		br.ErrMsg = "操作失败,Err:" + err.Error()
 		return
 	}
+	go cygxService.UpdateReportSelectionResourceData(articleId) //首页最新页面数据逻辑处理 V11.1.1
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"
@@ -932,6 +932,7 @@ func (this *ReportSelectionController) VisibleRange() {
 		br.ErrMsg = "操作失败,Err:" + err.Error()
 		return
 	}
+	go cygxService.UpdateReportSelectionResourceData(articleId) //首页最新页面数据逻辑处理 V11.1.1
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"

+ 3 - 2
controllers/cygx/research_summary.go

@@ -1214,12 +1214,12 @@ func (this *ResearchSummaryController) VisibleRange() {
 		if detail.IsSendWxMsg == 0 {
 			cygxService.DoThisWeekLastWeekWxTemplateMsg(detail.Title, detail.PublishDate, detail.Abstract, req.ArticleId, 2)
 		}
-		go cygxService.UpdateResourceData(articleId, "researchsummary", "add", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(articleId, "researchsummary", "add", time.Now().Format(utils.FormatDateTime))
 	} else {
 		item.AdminId = detail.AdminId
 		item.AdminName = detail.AdminName
 		item.VisibleRange = 0
-		go cygxService.UpdateResourceData(articleId, "researchsummary", "delete", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(articleId, "researchsummary", "delete", time.Now().Format(utils.FormatDateTime))
 	}
 	err = cygx.ResearchSummaryVisibleRange(item)
 	if err != nil {
@@ -1227,6 +1227,7 @@ func (this *ResearchSummaryController) VisibleRange() {
 		br.ErrMsg = "操作失败,Err:" + err.Error()
 		return
 	}
+	go cygxService.UpdateResearchSummaryResourceData(articleId) //写入首页最新  cygx_resource_data 表
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"

+ 24 - 20
controllers/cygx/summary_manage.go

@@ -297,25 +297,26 @@ func (this *SummaryManage) PreserveAndPublish() {
 		services.EsAddOrEditData(item)
 	}
 
-	//同步数据到最新表
-	if req.ArticleId == 0 {
-		if item.PublishStatus == 1 {
-			go cygxService.UpdateResourceData(item.ArticleId, "article", "add", time.Now().Format(utils.FormatDateTime))
-		}
-	} else {
-		if item.PublishStatus == 1 {
-			go cygxService.UpdateResourceData(item.ArticleId, "article", "update", time.Now().Format(utils.FormatDateTime))
-		}
-		if req.DoType == 1 {
-			go cygxService.UpdateResourceData(item.ArticleId, "article", "add", time.Now().Format(utils.FormatDateTime))
-		}
-	}
+	////同步数据到最新表
+	//if req.ArticleId == 0 {
+	//	if item.PublishStatus == 1 {
+	//		go cygxService.UpdateResourceData(item.ArticleId, "article", "add", time.Now().Format(utils.FormatDateTime))
+	//	}
+	//} else {
+	//	if item.PublishStatus == 1 {
+	//		go cygxService.UpdateResourceData(item.ArticleId, "article", "update", time.Now().Format(utils.FormatDateTime))
+	//	}
+	//	if req.DoType == 1 {
+	//		go cygxService.UpdateResourceData(item.ArticleId, "article", "add", time.Now().Format(utils.FormatDateTime))
+	//	}
+	//}
 
 	if len(industryIds) > 0 {
 		go cygxService.UpdateIndustryLayoutTime(industryIds, false)       // 查研观向7.4-更新产业布局时间
 		go elastic.AddComprehensiveIndustrialSource("Yx", item.ArticleId) // 查研观向10.6 更新搜索引擎的产业资源包
 	}
-	go elastic.AddComprehensiveArticle(item.ArticleId) // ES添加文章:报告、纪要
+	go elastic.AddComprehensiveArticle(item.ArticleId)       // ES添加文章:报告、纪要
+	go cygxService.UpdateArticleResourceData(item.ArticleId) //写入首页最新  cygx_resource_data 表
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"
@@ -375,13 +376,13 @@ func (this *SummaryManage) PublishAndCancel() {
 		item.AdminId = AdminUser.AdminId
 		item.AdminName = AdminUser.RealName
 		services.EsAddOrEditData(item)
-		go cygxService.UpdateResourceData(item.ArticleId, "article", "add", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(item.ArticleId, "article", "add", time.Now().Format(utils.FormatDateTime))
 	} else {
 		item.AdminId = articleInfo.AdminId
 		item.AdminName = articleInfo.AdminName
 		item.PublishStatus = 0
 		services.EsDeleteData(indexName, strconv.Itoa(articleInfo.ArticleId))
-		go cygxService.UpdateResourceData(item.ArticleId, "article", "delete", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(item.ArticleId, "article", "delete", time.Now().Format(utils.FormatDateTime))
 	}
 	err = cygx.PublishAndCancel(item)
 	if err != nil {
@@ -403,7 +404,8 @@ func (this *SummaryManage) PublishAndCancel() {
 			go elastic.AddComprehensiveIndustrialSource("Yx", item.ArticleId) // 查研观向10.6 更新搜索引擎的产业资源包
 		}
 	}
-	go elastic.AddComprehensiveArticle(articleId) // ES添加文章:报告、纪要
+	go elastic.AddComprehensiveArticle(articleId)            // ES添加文章:报告、纪要
+	go cygxService.UpdateArticleResourceData(item.ArticleId) //写入首页最新  cygx_resource_data 表
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"
@@ -533,7 +535,8 @@ func (this *SummaryManage) Delete() {
 		br.ErrMsg = "删除信息失败,Err:" + err.Error()
 		return
 	}
-	go cygxService.UpdateResourceData(articleInfo.ArticleId, "article", "delete", time.Now().Format(utils.FormatDateTime))
+	//go cygxService.UpdateResourceData(articleInfo.ArticleId, "article", "delete", time.Now().Format(utils.FormatDateTime))
+	go cygxService.UpdateArticleResourceData(articleInfo.ArticleId) //写入首页最新  cygx_resource_data 表
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "已删除"
@@ -1604,13 +1607,13 @@ func (this *SummaryManage) VisibleRange() {
 		item.AdminId = AdminUser.AdminId
 		item.AdminName = AdminUser.RealName
 		services.EsAddOrEditData(item)
-		go cygxService.UpdateResourceData(item.ArticleId, "article", "add", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(item.ArticleId, "article", "add", time.Now().Format(utils.FormatDateTime))
 	} else {
 		item.AdminId = articleInfo.AdminId
 		item.AdminName = articleInfo.AdminName
 		item.PublishStatus = 0
 		services.EsDeleteData(indexName, strconv.Itoa(articleInfo.ArticleId))
-		go cygxService.UpdateResourceData(item.ArticleId, "article", "delete", time.Now().Format(utils.FormatDateTime))
+		//go cygxService.UpdateResourceData(item.ArticleId, "article", "delete", time.Now().Format(utils.FormatDateTime))
 	}
 	err = cygx.ArticleVisibleRange(item)
 	if err != nil {
@@ -1631,6 +1634,7 @@ func (this *SummaryManage) VisibleRange() {
 			}
 		}
 	}
+	go cygxService.UpdateArticleResourceData(item.ArticleId) //写入首页最新  cygx_resource_data 表
 	br.Ret = 200
 	br.Success = true
 	br.Msg = "操作成功"

+ 10 - 0
controllers/eta_trial.go

@@ -129,6 +129,12 @@ func (this *ETATrialController) List() {
 			} else {
 				sortStr = "ORDER  BY login_num DESC"
 			}
+		} else if sortParam == "LastLoginDuration" {
+			if sortType == "asc" {
+				sortStr = "ORDER BY last_login_duration ASC"
+			} else {
+				sortStr = "ORDER  BY last_login_duration DESC"
+			}
 		}
 	} else {
 		sortStr = "ORDER BY create_time DESC "
@@ -146,6 +152,10 @@ func (this *ETATrialController) List() {
 		activeTime, _ := strconv.Atoi(item.ActiveTime)
 		item.ActiveTime = utils.GetDurationFormatBySecond(activeTime)
 
+		// 最近一次登录时长
+		loginDuration, _ := strconv.Atoi(item.LastLoginDuration)
+		item.LastLoginDuration = utils.GetDurationFormatBySecond(loginDuration)
+
 		//到期时间
 		modifyTime, err := time.Parse(utils.FormatDateTime, item.ModifyTime)
 		if err != nil {

+ 279 - 15
controllers/statistic_report.go

@@ -2087,6 +2087,8 @@ func IncomeListExport(this *StatisticReportController, resp response.IncomeListR
 // @Param   DataType   query   string  false       "报表类型,枚举值:`新签客户`,`续约客户`,`未续约客户`"
 // @Param   TryOutType   query   string  false       " '试用', '非试用' 非试用即为冻结/流失"
 // @Param   IsExport   query   bool  false       "是否导出excel,默认是false"
+// @Param   IsConfirm   query   int  false       "是否确认续约: -1-默认全部; 0-待确认; 1-已确认"
+// @Param   CompanyAscribeId   query   int  false       "归因ID"
 // @Success 200 {object} response.StackCompanyListResp
 // @router /stack_company_list [get]
 func (this *StatisticReportController) StackCompanyList() {
@@ -2122,6 +2124,10 @@ func (this *StatisticReportController) StackCompanyList() {
 	keyword := this.GetString("Keyword")
 
 	date := this.GetString("Date")
+
+	isConfirm, _ := this.GetInt("IsConfirm", -1)               // CRM 13.9
+	companyAscribeId, _ := this.GetInt("CompanyAscribeId", -1) // CRM 13.9
+
 	if date == "" {
 		br.Msg = "获取失败,请选择日期"
 		br.ErrMsg = "获取失败,请选择日期"
@@ -2144,7 +2150,7 @@ func (this *StatisticReportController) StackCompanyList() {
 	var resp response.StackCompanyListResp
 	//历史统计数据
 	if dateTime < todayTimeNumber {
-		tmpResp, err := getHistoryStackCompanyList(sysUser, currentIndex, pageSize, adminId, regionType, companyType, dataType, tryOutType, date, keyword)
+		tmpResp, err := getHistoryStackCompanyList(sysUser, currentIndex, pageSize, isConfirm, companyAscribeId, adminId, regionType, companyType, dataType, tryOutType, date, keyword)
 		if err != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取失败,Err:" + err.Error()
@@ -2153,7 +2159,7 @@ func (this *StatisticReportController) StackCompanyList() {
 		resp = tmpResp
 	} else {
 		//获取实时统计数据(今天数据)
-		tmpResp, err := getTodayStackCompanyListV2(sysUser, currentIndex, pageSize, adminId, regionType, companyType, dataType, tryOutType, keyword)
+		tmpResp, err := getTodayStackCompanyListV2(sysUser, currentIndex, pageSize, isConfirm, companyAscribeId, adminId, regionType, companyType, dataType, tryOutType, keyword)
 		if err != nil {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取失败,Err:" + err.Error()
@@ -2249,6 +2255,14 @@ func StackCompanyListExport(this *StatisticReportController, dataType string, re
 		cellDay := titleRow.AddCell()
 		cellDay.SetStyle(style)
 		cellDay.SetValue("超出过期天数")
+
+		cellAscribeContent := titleRow.AddCell()
+		cellAscribeContent.SetStyle(style)
+		cellAscribeContent.SetValue("不续约归因")
+
+		cellContent := titleRow.AddCell()
+		cellContent.SetStyle(style)
+		cellContent.SetValue("详细原因")
 	default:
 		cellTime := titleRow.AddCell()
 		cellTime.SetStyle(style)
@@ -2294,6 +2308,18 @@ func StackCompanyListExport(this *StatisticReportController, dataType string, re
 		cellDay := dataRow.AddCell()
 		cellDay.SetStyle(style)
 		cellDay.SetValue(v.ExpireDay)
+
+		switch dataType {
+		case "未续约客户":
+			cellAscribeContent := dataRow.AddCell()
+			cellAscribeContent.SetStyle(style)
+			cellAscribeContent.SetValue(v.AscribeContent)
+
+			cellContent := dataRow.AddCell()
+			cellContent.SetStyle(style)
+			cellContent.SetValue(v.Content)
+		}
+
 	}
 	err = xlsxFile.Save(downLoadnFilePath)
 	if err != nil {
@@ -2313,7 +2339,7 @@ func StackCompanyListExport(this *StatisticReportController, dataType string, re
 }
 
 // 获取历史的数据
-func getHistoryStackCompanyList(sysUser *system.Admin, currentIndex, pageSize int, adminId, regionType, companyType, dataType, tryOutType, date, keyword string) (returnData response.StackCompanyListResp, err error) {
+func getHistoryStackCompanyList(sysUser *system.Admin, currentIndex, pageSize, isConfirm, companyAscribeId int, adminId, regionType, companyType, dataType, tryOutType, date, keyword string) (returnData response.StackCompanyListResp, err error) {
 	if date == "" {
 		err = errors.New("请选择日期")
 		return
@@ -2335,6 +2361,57 @@ func getHistoryStackCompanyList(sysUser *system.Admin, currentIndex, pageSize in
 	condition += ` AND date = ? `
 	pars = append(pars, date)
 
+	var conditionAscribRai string // 处理权益未续约客户检索列表SQL查询条件
+	var parsAscribeRai []interface{}
+	//是否确认续约 CRM 13.9
+	if isConfirm != -1 {
+		var conditionConfirm string
+		var parsConfirm []interface{}
+
+		companyConfirmList, e := company.GetCompanyNoRenewedAscribeList(conditionConfirm, parsConfirm, 0, 0)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = errors.New("GetCompanyNoRenewedAscribeList" + e.Error())
+			return
+		}
+		var companyIds []int
+		if len(companyConfirmList) == 0 {
+			companyIds = append(companyIds, 0) // 给一个不存在的ID
+		} else {
+			for _, v := range companyConfirmList {
+				companyIds = append(companyIds, v.CompanyId)
+			}
+		}
+		if isConfirm == 0 {
+			conditionAscribRai += ` AND c.company_id NOT IN (` + utils.GetOrmInReplace(len(companyIds)) + `)` // 待确认
+		} else {
+			conditionAscribRai += ` AND c.company_id IN (` + utils.GetOrmInReplace(len(companyIds)) + `)` // 已确认
+		}
+		parsAscribeRai = append(parsAscribeRai, companyIds)
+	}
+
+	//归因ID CRM 13.9
+	if companyAscribeId > 0 {
+		var conditionAscribe string
+		var parsAscribe []interface{}
+		conditionAscribe = "  AND  company_ascribe_id = ? "
+		parsAscribe = append(parsAscribe, companyAscribeId)
+		companyNoRenewedAscribeList, e := company.GetCompanyNoRenewedAscribeList(conditionAscribe, parsAscribe, 0, 0)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = errors.New("GetCompanyNoRenewedAscribeList" + e.Error())
+			return
+		}
+		var companyIds []int
+		if len(companyNoRenewedAscribeList) == 0 {
+			companyIds = append(companyIds, 0) // 给一个不存在的ID
+		} else {
+			for _, v := range companyNoRenewedAscribeList {
+				companyIds = append(companyIds, v.CompanyId)
+			}
+		}
+		conditionAscribRai += ` AND c.company_id IN (` + utils.GetOrmInReplace(len(companyIds)) + `)`
+		parsAscribeRai = append(parsAscribeRai, companyIds)
+	}
+
 	if adminId != "" {
 		condition += ` AND a.seller_id in  (` + adminId + `) `
 		//pars = append(pars, adminId)
@@ -2428,7 +2505,8 @@ func getHistoryStackCompanyList(sysUser *system.Admin, currentIndex, pageSize in
 			return
 		}
 		notRenewalCompanyTotal = total
-
+		condition1 += conditionAscribRai
+		pars1 = append(pars1, parsAscribeRai)
 		if dataType == "未续约客户" {
 			//列表页数据总和
 			//统计数据
@@ -2493,12 +2571,17 @@ func getHistoryStackCompanyList(sysUser *system.Admin, currentIndex, pageSize in
 	//moreListMap := make(map[int][]*models.StackCompanyStatisticList)
 	if dataType == "续约客户" {
 		var ids []string
+		var ascribecompanyIds []int
 		oldCompanyMap := make(map[int]*models.IncrementalList)
 		oldMoneyMap := make(map[int]float64)
 		countMap := make(map[int]int)
 		for _, item := range list {
 			ids = append(ids, strconv.Itoa(item.CompanyId))
+			ascribecompanyIds = append(ascribecompanyIds, item.CompanyId)
 		}
+		//归因标签
+		mapGetCompanyAscribeContent, mapContent := services.GetCompanyAscribeContentMap(ascribecompanyIds)
+		mapNoRenewedNote := services.GetCompanyNoRenewedNoteMap(ascribecompanyIds)
 		if len(ids) > 0 {
 			idStr := strings.Join(ids, ",")
 			lists, contractErr := models.GetLastContractMoney(idStr)
@@ -2531,6 +2614,9 @@ func getHistoryStackCompanyList(sysUser *system.Admin, currentIndex, pageSize in
 						item.PackageDifference = "维持套餐"
 					}
 				}
+				item.AscribeContent = mapGetCompanyAscribeContent[fmt.Sprint("CID_", item.CompanyId, "PID_", item.ProductId)]
+				item.Content = mapContent[fmt.Sprint("CID_", item.CompanyId, "PID_", item.ProductId)]
+				item.IsShowNoRenewedNote = mapNoRenewedNote[fmt.Sprint("CID_", item.CompanyId, "PID_", item.ProductId)]
 			}
 		}
 	}
@@ -2847,7 +2933,7 @@ func getTodayStackCompanyList(sysUser *system.Admin, currentIndex, pageSize int,
 	return resp, err
 }
 
-func getTodayStackCompanyListV2(sysUser *system.Admin, currentIndex, pageSize int, adminId, regionType, companyType, dataType, tryOutType, keyword string) (returnData response.StackCompanyListResp, err error) {
+func getTodayStackCompanyListV2(sysUser *system.Admin, currentIndex, pageSize, isConfirm, companyAscribeId int, adminId, regionType, companyType, dataType, tryOutType, keyword string) (returnData response.StackCompanyListResp, err error) {
 	var startSize int
 	if pageSize <= 0 {
 		pageSize = utils.PageSize20
@@ -2862,6 +2948,56 @@ func getTodayStackCompanyListV2(sysUser *system.Admin, currentIndex, pageSize in
 
 	today := utils.GetToday(utils.FormatDate)
 	//条件
+	var conditionAscribRai string // 处理权益未续约客户检索列表SQL查询条件
+	var parsAscribeRai []interface{}
+	//是否确认续约 CRM 13.9
+	if isConfirm != -1 {
+		var conditionConfirm string
+		var parsConfirm []interface{}
+
+		companyConfirmList, e := company.GetCompanyNoRenewedAscribeList(conditionConfirm, parsConfirm, 0, 0)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = errors.New("GetCompanyNoRenewedAscribeList" + e.Error())
+			return
+		}
+		var companyIds []int
+		if len(companyConfirmList) == 0 {
+			companyIds = append(companyIds, 0) // 给一个不存在的ID
+		} else {
+			for _, v := range companyConfirmList {
+				companyIds = append(companyIds, v.CompanyId)
+			}
+		}
+		if isConfirm == 0 {
+			conditionAscribRai += ` AND ( c.company_id NOT IN (` + utils.GetOrmInReplace(len(companyIds)) + `) AND  c.product_id = 2 ) ` // 待确认
+		} else {
+			conditionAscribRai += ` AND ( c.company_id IN (` + utils.GetOrmInReplace(len(companyIds)) + `)  OR  c.product_id = 2 )` // 已确认
+		}
+		parsAscribeRai = append(parsAscribeRai, companyIds)
+	}
+
+	//归因ID CRM 13.9
+	if companyAscribeId > 0 {
+		var conditionAscribe string
+		var parsAscribe []interface{}
+		conditionAscribe = "  AND  company_ascribe_id = ? "
+		parsAscribe = append(parsAscribe, companyAscribeId)
+		companyNoRenewedAscribeList, e := company.GetCompanyNoRenewedAscribeList(conditionAscribe, parsAscribe, 0, 0)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = errors.New("GetCompanyNoRenewedAscribeList" + e.Error())
+			return
+		}
+		var companyIds []int
+		if len(companyNoRenewedAscribeList) == 0 {
+			companyIds = append(companyIds, 0) // 给一个不存在的ID
+		} else {
+			for _, v := range companyNoRenewedAscribeList {
+				companyIds = append(companyIds, v.CompanyId)
+			}
+		}
+		conditionAscribRai += ` AND c.company_id IN (` + utils.GetOrmInReplace(len(companyIds)) + `)`
+		parsAscribeRai = append(parsAscribeRai, companyIds)
+	}
 
 	if adminId != "" {
 		condition += ` AND c.seller_id in  (` + adminId + `) `
@@ -2879,6 +3015,7 @@ func getTodayStackCompanyListV2(sysUser *system.Admin, currentIndex, pageSize in
 	if keyword != "" {
 		condition += ` and b.company_name like "%` + keyword + `%" `
 	}
+
 	switch companyType {
 	case "ficc":
 		condition += ` AND a.product_id = ? `
@@ -2971,7 +3108,8 @@ func getTodayStackCompanyListV2(sysUser *system.Admin, currentIndex, pageSize in
 			return
 		}
 		notRenewalCompanyTotal = total
-
+		condition1 += conditionAscribRai
+		pars1 = append(pars1, parsAscribeRai)
 		if dataType == "未续约客户" {
 			//页表页数据总和
 			//统计数据
@@ -3081,6 +3219,7 @@ func getTodayStackCompanyListV2(sysUser *system.Admin, currentIndex, pageSize in
 					oldCompanyMap[item.CompanyId] = item
 				}
 			}
+
 			//给list赋值
 			for _, item := range list {
 				if item.ProductName == "权益" {
@@ -3097,6 +3236,20 @@ func getTodayStackCompanyListV2(sysUser *system.Admin, currentIndex, pageSize in
 			}
 		}
 	}
+
+	var ascribecompanyIds []int
+	for _, item := range list {
+		ascribecompanyIds = append(ascribecompanyIds, item.CompanyId)
+	}
+	//归因标签
+	mapGetCompanyAscribeContent, mapContent := services.GetCompanyAscribeContentMap(ascribecompanyIds)
+	mapNoRenewedNote := services.GetCompanyNoRenewedNoteMap(ascribecompanyIds)
+	for _, item := range list {
+		item.AscribeContent = mapGetCompanyAscribeContent[fmt.Sprint("CID_", item.CompanyId, "PID_", item.ProductId)]
+		item.Content = mapContent[fmt.Sprint("CID_", item.CompanyId, "PID_", item.ProductId)]
+		item.IsShowNoRenewedNote = mapNoRenewedNote[fmt.Sprint("CID_", item.CompanyId, "PID_", item.ProductId)]
+	}
+
 	listLen := len(list)
 	for i := 0; i < listLen; i++ {
 		item := list[i]
@@ -3134,12 +3287,15 @@ func getTodayStackCompanyListV2(sysUser *system.Admin, currentIndex, pageSize in
 			EndDate:     v.EndDate,
 			RegionType:  v.RegionType,
 			//CreateTime   :v.CreateTime,
-			CreateTimeStr:     v.CreateTime,
-			ExpireDay:         v.ExpireDay,
-			RenewalReason:     v.RenewalReason,
-			RenewalTodo:       v.RenewalTodo,
-			Status:            v.Status,
-			PackageDifference: v.PackageDifference,
+			CreateTimeStr:       v.CreateTime,
+			ExpireDay:           v.ExpireDay,
+			RenewalReason:       v.RenewalReason,
+			RenewalTodo:         v.RenewalTodo,
+			Status:              v.Status,
+			PackageDifference:   v.PackageDifference,
+			AscribeContent:      v.AscribeContent,
+			IsShowNoRenewedNote: v.IsShowNoRenewedNote,
+			Content:             v.Content,
 		}
 		stackCompanyStatisticList = append(stackCompanyStatisticList, &stackCompanyStatistic)
 	}
@@ -3170,6 +3326,8 @@ func getTodayStackCompanyListV2(sysUser *system.Admin, currentIndex, pageSize in
 // @Param   DataType   query   string  false       "报表类型,枚举值:`新增试用`,`新签客户`,`续约客户`,`未续约客户`"
 // @Param   TryOutType   query   string  false       " '试用', '非试用' 非试用即为冻结/流失"
 // @Param   IsExport   query   bool  false       "是否导出excel,默认是false"
+// @Param   IsConfirm   query   int  false       "是否确认续约: -1-默认全部; 0-待确认; 1-已确认"
+// @Param   CompanyAscribeId   query   int  false       "归因ID"
 // @Success 200 {object} response.IncrementalCompanyListResp
 // @router /incremental_company_list [get]
 func (this *StatisticReportController) IncrementalCompanyList() {
@@ -3199,6 +3357,9 @@ func (this *StatisticReportController) IncrementalCompanyList() {
 
 	startDate := this.GetString("StartDate")
 	endDate := this.GetString("EndDate")
+
+	isConfirm, _ := this.GetInt("IsConfirm", -1)               // CRM 13.9
+	companyAscribeId, _ := this.GetInt("CompanyAscribeId", -1) // CRM 13.9
 	//if startDate == "" || endDate == "" {
 	//	br.Msg = "获取失败,开始日期或结束日期未传"
 	//	br.ErrMsg = "获取失败,开始日期或结束日期未传"
@@ -3248,6 +3409,64 @@ func (this *StatisticReportController) IncrementalCompanyList() {
 	if keyword != "" {
 		condition += ` and b.company_name like "%` + keyword + `%" `
 	}
+
+	var conditionAscribRai string      // 处理权益未续约客户检索列表SQL查询条件
+	var conditionAscribRaiTotal string // 处理权益未续约客户总量查询条件
+	var parsAscribeRai []interface{}
+	var parsAscribeRaiTotal []interface{} // 处理权益未续约客户总量查询条件
+	var conditionConfirm string
+	var parsConfirm []interface{}
+
+	companyConfirmList, err := company.GetCompanyNoRenewedAscribeList(conditionConfirm, parsConfirm, 0, 0)
+	if err != nil && err.Error() != utils.ErrNoRow() {
+		br.Msg = "获取失败"
+		br.ErrMsg = "获取失败,GetCompanyNoRenewedAscribeList Err:" + err.Error()
+		return
+	}
+	var noRenewedcompanyIds []int //已经确定未续约的公司ID
+	if len(companyConfirmList) == 0 {
+		noRenewedcompanyIds = append(noRenewedcompanyIds, 0) // 给一个不存在的ID
+	} else {
+		for _, v := range companyConfirmList {
+			noRenewedcompanyIds = append(noRenewedcompanyIds, v.CompanyId)
+		}
+	}
+	//是否确认续约 CRM 13.9
+	conditionAscribRaiTotal += ` AND ( c.company_id IN (` + utils.GetOrmInReplace(len(noRenewedcompanyIds)) + `)   OR c.product_id = 1  OR  a.create_time <  '2023-01-01'  )   ` // 已确认
+	parsAscribeRaiTotal = append(parsAscribeRaiTotal, noRenewedcompanyIds)
+	if isConfirm != -1 {
+		if isConfirm == 0 {
+			conditionAscribRai += ` AND ( c.company_id NOT IN (` + utils.GetOrmInReplace(len(noRenewedcompanyIds)) + `)  AND  c.product_id = 2  AND  a.create_time >=  '2023-01-01' )  ` // 待确认
+		} else {
+			conditionAscribRai += ` AND ( c.company_id IN (` + utils.GetOrmInReplace(len(noRenewedcompanyIds)) + `)   OR c.product_id = 1  OR   a.create_time <  '2023-01-01' )    ` // 已确认
+		}
+		parsAscribeRai = append(parsAscribeRai, noRenewedcompanyIds)
+	}
+
+	//归因ID CRM 13.9
+	if companyAscribeId > 0 {
+		var conditionAscribe string
+		var parsAscribe []interface{}
+		conditionAscribe = "  AND  company_ascribe_id = ? "
+		parsAscribe = append(parsAscribe, companyAscribeId)
+		companyNoRenewedAscribeList, err := company.GetCompanyNoRenewedAscribeList(conditionAscribe, parsAscribe, 0, 0)
+		if err != nil && err.Error() != utils.ErrNoRow() {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取失败,GetCompanyNoRenewedAscribeList Err:" + err.Error()
+			return
+		}
+		var companyIds []int
+		if len(companyNoRenewedAscribeList) == 0 {
+			companyIds = append(companyIds, 0) // 给一个不存在的ID
+		} else {
+			for _, v := range companyNoRenewedAscribeList {
+				companyIds = append(companyIds, v.CompanyId)
+			}
+		}
+		conditionAscribRai += ` AND c.company_id IN (` + utils.GetOrmInReplace(len(companyIds)) + `)`
+		parsAscribeRai = append(parsAscribeRai, companyIds)
+	}
+
 	switch companyType {
 	case "ficc":
 		condition += ` AND c.product_id = ? `
@@ -3493,9 +3712,14 @@ func (this *StatisticReportController) IncrementalCompanyList() {
 		pars1 = append(pars1, startDate, tryOutEndDate)
 		condition1 += ` AND a.operation = ? `
 		pars1 = append(pars1, "try_out")
-		condition1 += ` AND c.status not in ("永续","正式")  `
-
-		total, err := models.GetIncrementalCompanyCountByOperationRecord(condition1, pars1)
+		condition1 += ` AND c.status not in ("永续","正式","关闭")  `
+
+		//未续约这里只统计已经确定的
+		notRenewalCondition := condition1
+		notRenewalPars := pars1
+		notRenewalCondition += conditionAscribRaiTotal
+		notRenewalPars = append(notRenewalPars, parsAscribeRaiTotal)
+		total, err := models.GetIncrementalCompanyCountByOperationRecord(notRenewalCondition, notRenewalPars)
 		if err != nil && err.Error() != utils.ErrNoRow() {
 			br.Msg = "获取失败"
 			br.ErrMsg = "获取失败,Err:" + err.Error()
@@ -3503,6 +3727,8 @@ func (this *StatisticReportController) IncrementalCompanyList() {
 		}
 		notRenewalCompanyTotal = total
 
+		condition1 += conditionAscribRai
+		pars1 = append(pars1, parsAscribeRai)
 		if dataType == "未续约客户" {
 			//统计数据
 			for _, v := range []string{"试用", "非试用"} {
@@ -3546,6 +3772,13 @@ func (this *StatisticReportController) IncrementalCompanyList() {
 			//	return
 			//}
 
+			//分页total单独计算
+			total, err = models.GetIncrementalCompanyProductCountByOperationRecord(condition1, pars1)
+			if err != nil && err.Error() != utils.ErrNoRow() {
+				br.Msg = "获取失败"
+				br.ErrMsg = "获取失败,Err:" + err.Error()
+				return
+			}
 			//列表页数据
 			tmpList, err := models.GetIncrementalCompanyListByOperationRecord(condition1, pars1, startSize, pageSize)
 			if err != nil {
@@ -3553,9 +3786,21 @@ func (this *StatisticReportController) IncrementalCompanyList() {
 				br.ErrMsg = "获取失败,Err:" + err.Error()
 				return
 			}
+
+			var ascribecompanyIds []int
 			for _, item := range tmpList {
 				endDateTime, _ := time.Parse(utils.FormatDateTime, item.CreateTime)
 				item.EndDate = endDateTime.Format(utils.FormatDate)
+				ascribecompanyIds = append(ascribecompanyIds, item.CompanyId)
+			}
+			//归因标签
+			mapGetCompanyAscribeContent, mapContent := services.GetCompanyAscribeContentMap(ascribecompanyIds)
+			mapNoRenewedNote := services.GetCompanyNoRenewedNoteMap(ascribecompanyIds)
+
+			for _, item := range tmpList {
+				item.AscribeContent = mapGetCompanyAscribeContent[fmt.Sprint("CID_", item.CompanyId, "PID_", item.ProductId)]
+				item.Content = mapContent[fmt.Sprint("CID_", item.CompanyId, "PID_", item.ProductId)]
+				item.IsShowNoRenewedNote = mapNoRenewedNote[fmt.Sprint("CID_", item.CompanyId, "PID_", item.ProductId)]
 			}
 			list = tmpList
 			dataTotal = total
@@ -3588,6 +3833,9 @@ func (this *StatisticReportController) IncrementalCompanyList() {
 
 	listLen := len(list)
 
+	if listLen == 0 {
+		list = make([]*models.IncrementalList, 0)
+	}
 	for i := 0; i < listLen; i++ {
 		item := list[i]
 
@@ -3780,6 +4028,14 @@ func IncrementalCompanyListExport(this *StatisticReportController, dataType stri
 		cellTime.SetValue("续约时间")
 	case "未续约客户":
 		cellTime.SetValue("最近合同到期时间")
+
+		cellAscribeContent := titleRow.AddCell()
+		cellAscribeContent.SetStyle(style)
+		cellAscribeContent.SetValue("不续约归因")
+
+		cellContent := titleRow.AddCell()
+		cellContent.SetStyle(style)
+		cellContent.SetValue("详细原因")
 	}
 
 	for _, v := range resp.List {
@@ -3820,6 +4076,14 @@ func IncrementalCompanyListExport(this *StatisticReportController, dataType stri
 			cellDataTime.SetValue(v.StartDate)
 		case "未续约客户":
 			cellDataTime.SetValue(v.EndDate)
+
+			cellAscribeContent := dataRow.AddCell()
+			cellAscribeContent.SetStyle(style)
+			cellAscribeContent.SetValue(v.AscribeContent)
+
+			cellContent := dataRow.AddCell()
+			cellContent.SetStyle(style)
+			cellContent.SetValue(v.Content)
 		}
 
 	}

+ 65 - 20
controllers/sys_admin.go

@@ -296,6 +296,7 @@ func (this *SysAdminController) ListSysuser() {
 	br.Data = resp
 }
 
+// Add
 // @Title 新增系统用户
 // @Description 新增系统用户接口
 // @Param	request	body system.SysuserAddReq true "type json string"
@@ -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
 
@@ -431,7 +457,7 @@ func (this *SysAdminController) Add() {
 		return
 	}
 	pwdStr := string(pwdByte)
-	pwdStr = strings.ToLower(pwdStr)
+	//pwdStr = strings.ToLower(pwdStr)
 	if pwdStr == "" {
 		br.Msg = "请输入密码"
 		return
@@ -507,6 +533,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 {
@@ -530,6 +557,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 = "新增失败"
@@ -630,26 +658,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 {
@@ -658,6 +685,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
@@ -758,9 +801,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 = "编辑失败"
@@ -1230,7 +1275,7 @@ func (this *SysAdminController) ResetPass() {
 		return
 	}
 	pwd := string(b)
-	pwd = strings.ToLower(pwd)
+	//pwd = strings.ToLower(pwd)
 	pwd = utils.MD5(pwd)
 
 	adminInfo.Password = pwd

+ 891 - 0
controllers/user_login.go

@@ -0,0 +1,891 @@
+package controllers
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"github.com/mojocn/base64Captcha"
+	"hongze/hz_crm_api/models"
+	"hongze/hz_crm_api/models/company"
+	"hongze/hz_crm_api/models/system"
+	"hongze/hz_crm_api/services"
+	"hongze/hz_crm_api/utils"
+	"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用户缓存
+	var syncData system.SyncAdminData
+	syncData.Source = utils.SOURCE_CRM_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 = "获取成功"
+}

+ 150 - 1
controllers/yb/product_census.go

@@ -1,12 +1,14 @@
 package yb
 
 import (
+	"fmt"
 	"github.com/rdlucklib/rdluck_tools/paging"
 	"hongze/hz_crm_api/controllers"
 	"hongze/hz_crm_api/models"
 	"hongze/hz_crm_api/models/yb"
 	"hongze/hz_crm_api/models/yb/response"
 	"hongze/hz_crm_api/utils"
+	"sync"
 )
 
 type ProductCensusController struct {
@@ -83,7 +85,7 @@ func (this *ProductCensusController) UserVisitCount() {
 // @Title 用户阅读统计详情
 // @Description 用户阅读统计详情
 // @Param   UserId			query  int  true	"用户ID"
-// @Param   ProductType		query  int  true	"产品类型:1-语音播报 2-视频社区 3-问答社区"
+// @Param   ProductType		query  int  true	"产品类型:1-语音播报 2-视频社区 3-问答社区 99-全部"
 // @Param   ProductId		query  int  false	"产品ID:仅语音播报传该参数"
 // @Param   ClickSort		query  int  false	"点击量排序:1-升序 2-降序"
 // @Param   PageSize		query  int  false	"页码"
@@ -205,6 +207,7 @@ func (this *ProductCensusController) UserVisitCountDetail() {
 			})
 		}
 	}
+
 	// 问答社区
 	if productType == 3 {
 		t, list, e := yb.GetCommunityQuestionVisitCountByUserId(userId, startSize, pageSize, orderRule)
@@ -240,6 +243,152 @@ func (this *ProductCensusController) UserVisitCountDetail() {
 		}
 	}
 
+	// 全部
+	if productType == 99 {
+		listTotal, list, e := yb.GetUserAllYbProductVisitCountPageList(userId, startSize, pageSize, orderRule)
+		if e != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = "获取用户所有产品点击量失败, Err: " + e.Error()
+			return
+		}
+		total = listTotal
+
+		sectionIds := make([]int, 0)
+		videoIds := make([]int, 0)
+		rsVideoIds := make([]int, 0)
+		questionIds := make([]int, 0)
+
+		for _, v := range list {
+			// 语音播报-取出板块ID
+			if v.ProductType == 1 && v.SectionId > 0 && !utils.InArrayByInt(sectionIds, v.SectionId) {
+				sectionIds = append(sectionIds, v.SectionId)
+			}
+			// 视频社区-取出视频ID
+			if v.ProductType == 2 && v.TitleId > 0 {
+				if v.VideoType == 1 && !utils.InArrayByInt(videoIds, v.TitleId) {
+					videoIds = append(videoIds, v.TitleId)
+				}
+				if v.VideoType == 2 && !utils.InArrayByInt(rsVideoIds, v.TitleId) {
+					rsVideoIds = append(rsVideoIds, v.TitleId)
+				}
+			}
+			// 问答社区-取出问题ID
+			if v.ProductType == 3 && v.TitleId > 0 && !utils.InArrayByInt(questionIds, v.TitleId) {
+				questionIds = append(questionIds, v.TitleId)
+			}
+		}
+
+		var sectionErr, videoErr, questionErr error
+		wg := sync.WaitGroup{}
+		wg.Add(3)
+
+		// 板块名称map
+		sectionNameMap := make(map[int]string, 0)
+		go func() {
+			defer func() {
+				wg.Done()
+			}()
+
+			sectionList, e := yb.GetVoiceSectionByIds(sectionIds)
+			if e != nil {
+				sectionErr = fmt.Errorf("获取语音播报板块列表失败, Err: %s", e.Error())
+				return
+			}
+			for _, v := range sectionList {
+				sectionNameMap[v.SectionId] = v.SectionName
+			}
+		}()
+
+		// 视频社区标题map
+		videoTitleMap := make(map[int]string, 0)
+		rsVideoTitleMap := make(map[int]string, 0)
+		go func() {
+			defer func() {
+				wg.Done()
+			}()
+
+			videoList, e := yb.GetCommunityVideoListByIds(videoIds)
+			if e != nil {
+				videoErr = fmt.Errorf("获取视频社区列表失败, Err: %s", e.Error())
+				return
+			}
+			for _, v := range videoList {
+				videoTitleMap[v.CommunityVideoId] = v.Title
+			}
+
+			rsVideoList, e := yb.GetRoadVideoListByIds(rsVideoIds)
+			if e != nil {
+				videoErr = fmt.Errorf("获取路演视频列表失败, Err: %s", e.Error())
+				return
+			}
+			for _, v := range rsVideoList {
+				rsVideoTitleMap[v.RoadVideoId] = v.Title
+			}
+		}()
+
+		// 问答社区标题map
+		questionTitleMap := make(map[int]string, 0)
+		go func() {
+			defer func() {
+				wg.Done()
+			}()
+
+			questionList, e := yb.GetQuestionListByIds(questionIds)
+			if e != nil {
+				questionErr = fmt.Errorf("获取问答列表失败, Err: %s", e.Error())
+				return
+			}
+			for _, v := range questionList {
+				questionTitleMap[v.CommunityQuestionId] = v.QuestionContent
+			}
+		}()
+
+		wg.Wait()
+		if sectionErr != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = sectionErr.Error()
+			return
+		}
+		if videoErr != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = videoErr.Error()
+			return
+		}
+		if questionErr != nil {
+			br.Msg = "获取失败"
+			br.ErrMsg = questionErr.Error()
+			return
+		}
+
+		// 填充产品名/标题
+		for _, v := range list {
+			d := new(response.ProductCensusUserVisitCountDetail)
+			d.VisitCount = v.VisitCount
+			d.Source = v.NewSource
+			d.RecentTime = v.RecentTime
+			if v.ProductType == 1 {
+				d.ProductName = sectionNameMap[v.SectionId]
+				d.Title = v.Title
+			}
+			if v.ProductType == 2 {
+				if v.VideoType == 1 {
+					d.ProductName = "5分钟小视频"
+					d.Title = videoTitleMap[v.TitleId]
+				}
+				if v.VideoType == 2 {
+					d.ProductName = "线上路演"
+					d.Title = rsVideoTitleMap[v.TitleId]
+				}
+			}
+			if v.ProductType == 3 {
+				d.ProductName = "问答社区"
+				d.Title = questionTitleMap[v.TitleId]
+			}
+
+			respList = append(respList, d)
+		}
+	}
+
 	page := paging.GetPaging(currentIndex, pageSize, total)
 	resp := response.ProductCensusUserVisitCountDetailResp{
 		List:   respList,

+ 3 - 3
go.mod

@@ -22,6 +22,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/mozillazg/go-pinyin v0.19.0
 	github.com/nosixtools/solarlunar v0.0.0-20211112060703-1b6dea7b4a19
 	github.com/olivere/elastic/v7 v7.0.30
@@ -31,7 +32,6 @@ require (
 	github.com/tealeg/xlsx v1.0.5
 	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.541
 	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ses v1.0.541
-	github.com/wenzhenxi/gorsa v0.0.0-20210524035706-528c7050d703
 	github.com/xuri/excelize/v2 v2.6.1
 	github.com/yidane/formula v0.0.0-20210902154546-0782e1736717
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
@@ -53,7 +53,6 @@ require (
 	github.com/aliyun/credentials-go v1.1.2 // indirect
 	github.com/andybalholm/cascadia v1.3.2 // indirect
 	github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20211218165449-dd623ecc2f02 // indirect
-	github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect
 	github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/bradfitz/gomemcache v0.0.0-20220106215444-fb4bf637b56d // indirect
@@ -64,6 +63,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
@@ -93,7 +93,6 @@ require (
 	github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
 	github.com/sirupsen/logrus v1.9.0 // indirect
 	github.com/spf13/cast v1.5.0 // indirect
-	github.com/stevenyao/go-opencc v0.0.0-20161014062826-cc376a51b65e // indirect
 	github.com/stretchr/testify v1.8.1 // indirect
 	github.com/tidwall/gjson v1.14.1 // indirect
 	github.com/tidwall/match v1.1.1 // indirect
@@ -102,6 +101,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.9.0 // indirect
 	golang.org/x/sys v0.7.0 // indirect
 	golang.org/x/text v0.9.0 // indirect

+ 5 - 166
go.sum

@@ -5,27 +5,18 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
 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=
@@ -72,9 +63,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=
@@ -82,29 +71,21 @@ github.com/aliyun/aliyun-oss-go-sdk v2.2.0+incompatible h1:ht2+VfbXtNLGhCsnTMc6/
 github.com/aliyun/aliyun-oss-go-sdk v2.2.0+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
 github.com/aliyun/credentials-go v1.1.2 h1:qU1vwGIBb3UJ8BwunHDRFtAhS6jnQLnde/yk0+Ih2GY=
 github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
-github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
 github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
 github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
 github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
 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/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=
-github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg=
 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=
 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=
@@ -114,39 +95,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=
@@ -157,27 +122,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=
@@ -187,21 +144,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=
@@ -209,28 +157,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=
@@ -248,9 +190,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=
@@ -266,7 +206,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=
@@ -277,43 +216,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=
@@ -322,43 +244,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=
@@ -376,13 +286,12 @@ 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/mojocn/base64Captcha v1.3.5 h1:Qeilr7Ta6eDtG4S+tQuZ5+hO+QHbiGAJdi4PfoagaA0=
+github.com/mojocn/base64Captcha v1.3.5/go.mod h1:/tTTXn4WTpX9CfrmipqRytCpJ27Uw3G6I7NcP2WwcmY=
 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/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=
@@ -398,7 +307,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=
@@ -406,16 +314,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=
@@ -447,7 +350,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=
@@ -465,11 +367,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=
@@ -478,33 +377,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/stevenyao/go-opencc v0.0.0-20161014062826-cc376a51b65e h1:EScGwVsN5G1/xRUdOKtUGugKseKjvHrvA71486Q39zg=
-github.com/stevenyao/go-opencc v0.0.0-20161014062826-cc376a51b65e/go.mod h1:lHc78mZexRHu9CQrKCEO0CLFl7GN0Xpj3PH3jBJ5EpY=
 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=
@@ -516,9 +402,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=
@@ -534,12 +418,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=
@@ -548,33 +428,16 @@ 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/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-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=
@@ -587,20 +450,17 @@ 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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
 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=
@@ -626,14 +486,11 @@ golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-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/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
 golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
 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=
@@ -643,7 +500,6 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
 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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
 golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -673,16 +529,12 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
-golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
 golang.org/x/sys v0.7.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/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
 golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
 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=
@@ -690,8 +542,6 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
-golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
@@ -710,34 +560,27 @@ 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/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
 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=
@@ -751,7 +594,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=
@@ -760,7 +602,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=
@@ -770,7 +611,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=
@@ -790,7 +630,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=

+ 5 - 0
models/base.go

@@ -1,5 +1,10 @@
 package models
 
+const (
+	BaseRespCodeAbnormalLogin = 4011 // 异常登录状态码
+	BaseRespCodeLoginErr      = 4012 // 账号或密码输入错误
+)
+
 type BaseResponse struct {
 	Ret         int
 	Msg         string

+ 74 - 0
models/company/company_ascribe.go

@@ -0,0 +1,74 @@
+package company
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type CompanyAscribe struct {
+	CompanyAscribeId int       `orm:"column(company_ascribe_id);pk" description:"归因ID"`
+	AscribeContent   string    `description:"归因说明"`
+	AdminId          int       `description:"管理员ID"`
+	CreateTime       time.Time `description:"创建时间"`
+	ModifyTime       time.Time `description:"更新时间"`
+}
+
+type CompanyAscribeResp struct {
+	CompanyAscribeId int    `orm:"column(company_ascribe_id);pk" description:"归因ID"`
+	AscribeContent   string `description:"归因说明"`
+	AdminId          int    `description:"管理员ID"`
+	CreateTime       string `description:"创建时间"`
+}
+
+type CompanyAscribeDetailResp struct {
+	Detail *CompanyAscribeResp
+}
+
+type CompanyAscribeListResp struct {
+	List []*CompanyAscribeResp
+}
+
+type CompanyAscribeAddReq struct {
+	AscribeContent string `description:"归因说明"`
+}
+
+// 添加
+func AddCompanyAscribe(item *CompanyAscribe) (newId int64, err error) {
+	o := orm.NewOrm()
+	newId, err = o.Insert(item)
+	return
+}
+
+// 获取数量
+func GetCompanyAscribeCount(condition string, pars []interface{}) (count int, err error) {
+	sqlCount := ` SELECT COUNT(1) AS count  FROM company_ascribe as a WHERE 1= 1  `
+	if condition != "" {
+		sqlCount += condition
+	}
+	o := orm.NewOrm()
+	err = o.Raw(sqlCount, pars).QueryRow(&count)
+	return
+}
+
+// 列表
+func GetCompanyAscribeList(condition string, pars []interface{}, startSize, pageSize int) (items []*CompanyAscribeResp, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM company_ascribe as a  WHERE 1= 1 `
+	if condition != "" {
+		sql += condition
+	}
+	if startSize+pageSize > 0 {
+		sql += ` LIMIT ?,?  `
+		_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
+// 通过ID获取详情
+func GetCompanyAscribeDetail(companyAscribeId int) (item *CompanyAscribeResp, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM company_ascribe  WHERE company_ascribe_id=? `
+	err = o.Raw(sql, companyAscribeId).QueryRow(&item)
+	return
+}

+ 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=  ?`

+ 146 - 0
models/company/company_no_renewed_ascribe.go

@@ -0,0 +1,146 @@
+package company
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type CompanyNoRenewedAscribe struct {
+	NoRenewedAscribeId int       `orm:"column(no_renewed_ascribe_id);pk" description:"主键ID"`
+	CompanyAscribeId   int       `description:"归因ID"`
+	AscribeContent     string    `description:"归因说明"`
+	Content            string    `description:"内容说明"`
+	AdminId            int       `description:"管理员ID"`
+	CompanyId          int       `description:"公司ID"`
+	ProductId          int       `description:"产品id"`
+	CreateTime         time.Time `description:"创建时间"`
+	ModifyTime         time.Time `description:"更新时间"`
+}
+
+type CompanyNoRenewedAscribeLog struct {
+	NoRenewedAscribeId int       `orm:"column(no_renewed_ascribe_id);pk" description:"主键ID"`
+	CompanyAscribeId   int       `description:"归因ID"`
+	AscribeContent     string    `description:"归因说明"`
+	Content            string    `description:"内容说明"`
+	AdminId            int       `description:"管理员ID"`
+	CompanyId          int       `description:"公司ID"`
+	ProductId          int       `description:"产品id"`
+	CreateTime         time.Time `description:"创建时间"`
+	ModifyTime         time.Time `description:"更新时间"`
+}
+
+type CompanyNoRenewedAscribeResp struct {
+	NoRenewedAscribeId int    `description:"主键ID"`
+	CompanyAscribeId   int    `description:"归因ID"`
+	AscribeContent     string `description:"归因说明"`
+	ProductId          int    `description:"产品id"`
+	Content            string `description:"内容说明"`
+	AdminId            int    `description:"管理员ID"`
+	CompanyId          int    `description:"公司ID"`
+	CreateTime         string `description:"创建时间"`
+	ModifyTime         string `description:"更新时间"`
+}
+type CompanyNoRenewedAscribeListResp struct {
+	List []*CompanyNoRenewedAscribeResp
+}
+
+type CompanyNoRenewedAscribeDetailResp struct {
+	Detail *CompanyNoRenewedAscribeResp
+}
+
+type CompanyNoRenewedAscribeReq struct {
+	CompanyAscribeId int    `description:"归因ID"`
+	ProductId        int    `description:"产品id"`
+	Content          string `description:"内容说明"`
+	CompanyId        int    `description:"公司ID"`
+}
+
+// 添加
+func AddCompanyNoRenewedAscribe(item *CompanyNoRenewedAscribe, itemLog *CompanyNoRenewedAscribeLog) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	_, err = to.Insert(item)
+	if err != nil {
+		return
+	}
+
+	_, err = to.Insert(itemLog)
+	return
+}
+
+// 修改
+func UpdateCompanyNoRenewedAscribe(item *CompanyNoRenewedAscribe, itemLog *CompanyNoRenewedAscribeLog) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	updateParams := make(map[string]interface{})
+	updateParams["CompanyAscribeId"] = item.CompanyAscribeId
+	updateParams["ModifyTime"] = item.ModifyTime
+	updateParams["Content"] = item.Content
+	updateParams["AscribeContent"] = item.AscribeContent
+	ptrStructOrTableName := "company_no_renewed_ascribe"
+	whereParam := map[string]interface{}{"company_id": item.CompanyId, "product_id": item.ProductId}
+	qs := to.QueryTable(ptrStructOrTableName)
+	for expr, exprV := range whereParam {
+		qs = qs.Filter(expr, exprV)
+	}
+	_, err = qs.Update(updateParams)
+	if err != nil {
+		return
+	}
+	_, err = to.Insert(itemLog)
+	return
+}
+
+// 获取数量
+func GetCompanyNoRenewedAscribeCount(condition string, pars []interface{}) (count int, err error) {
+	sqlCount := ` SELECT COUNT(1) AS count  FROM company_no_renewed_ascribe as a WHERE 1= 1  `
+	if condition != "" {
+		sqlCount += condition
+	}
+	o := orm.NewOrm()
+	err = o.Raw(sqlCount, pars).QueryRow(&count)
+	return
+}
+
+// 通过ID获取详情
+func GetCygxProductInteriorDetail(companyId, productId int) (item *CompanyNoRenewedAscribeResp, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM company_no_renewed_ascribe   WHERE company_id=?  AND product_id = ? `
+	err = o.Raw(sql, companyId, productId).QueryRow(&item)
+	return
+}
+
+// 列表
+func GetCompanyNoRenewedAscribeList(condition string, pars []interface{}, startSize, pageSize int) (items []*CompanyNoRenewedAscribeResp, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM company_no_renewed_ascribe as a  WHERE 1= 1 `
+	if condition != "" {
+		sql += condition
+	}
+	if startSize+pageSize > 0 {
+		sql += ` LIMIT ?,?  `
+		_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}

+ 68 - 0
models/company/company_no_renewed_note.go

@@ -0,0 +1,68 @@
+package company
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type CompanyNoRenewedNote struct {
+	NoRenewedNoteId int       `orm:"column(no_renewed_note_id);pk" description:"主键ID"`
+	Content         string    `description:"内容说明"`
+	AdminId         int       `description:"管理员ID"`
+	CompanyId       int       `description:"公司ID"`
+	ProductId       int       `description:"产品id"`
+	CreateTime      time.Time `description:"创建时间"`
+	ModifyTime      time.Time `description:"更新时间"`
+}
+
+type CompanyNoRenewedNoteResp struct {
+	NoRenewedNoteId int    `orm:"column(no_renewed_note_id);pk" description:"主键ID"`
+	CompanyId       int    `description:"公司ID"`
+	Content         string `description:"内容说明"`
+	AdminId         int    `description:"管理员ID"`
+	ProductId       int    `description:"产品id"`
+	CreateTime      string `description:"创建时间"`
+}
+
+type CompanyNoRenewedNoteListResp struct {
+	List []*CompanyNoRenewedNoteResp
+}
+
+type CompanyNoRenewedNoteReq struct {
+	Content   string `description:"内容说明"`
+	CompanyId int    `description:"公司ID"`
+	ProductId int    `description:"产品id"`
+}
+
+// 添加
+func AddCompanyNoRenewedNote(item *CompanyNoRenewedNote) (err error) {
+	o := orm.NewOrm()
+	_, err = o.Insert(item)
+	return
+}
+
+// 获取数量
+func GetCompanyNoRenewedNoteCount(condition string, pars []interface{}) (count int, err error) {
+	sqlCount := ` SELECT COUNT(1) AS count  FROM company_no_renewed_note as a WHERE 1= 1  `
+	if condition != "" {
+		sqlCount += condition
+	}
+	o := orm.NewOrm()
+	err = o.Raw(sqlCount, pars).QueryRow(&count)
+	return
+}
+
+// 列表
+func GetCompanyNoRenewedNoteList(condition string, pars []interface{}, startSize, pageSize int) (items []*CompanyNoRenewedNoteResp, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM company_no_renewed_note as a  WHERE 1= 1 `
+	if condition != "" {
+		sql += condition
+	}
+	if startSize+pageSize > 0 {
+		sql += ` LIMIT ?,?  `
+		_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}

+ 1 - 1
models/cygx/activity_type.go

@@ -37,7 +37,7 @@ func GetActivityTypeDetailById(activityTypeId int) (item *ActivityType, err erro
 // 列表
 func GetActivityTypeSearchList(condition string) (items []*ActivityType, err error) {
 	o := orm.NewOrm()
-	sql := `SELECT * FROM cygx_activity_type WHERE  ` + condition + `  ORDER BY sort DESC`
+	sql := `SELECT * FROM cygx_activity_type WHERE 1= 1  ` + condition + `  ORDER BY sort DESC`
 	_, err = o.Raw(sql).QueryRows(&items)
 	return
 }

+ 1 - 0
models/cygx/article.go

@@ -0,0 +1 @@
+package cygx

+ 28 - 0
models/cygx/cygx_morning_meeting_review_chapter.go

@@ -53,6 +53,18 @@ func GetCygxMorningMeetingReviewsListById(meetingId int) (items []*CygxMorningMe
 	return
 }
 
+// 列表
+func GetCygxMorningMeetingReviewChapterList(condition string, pars []interface{}, startSize, pageSize int) (items []*CygxMorningMeetingReviewChapter, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM cygx_morning_meeting_review_chapter WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	sql += ` LIMIT ?,? `
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&items)
+	return
+}
+
 type IndustrialSubjectItem struct {
 	IndustrialSubjectId int    `orm:"column(industrial_subject_id);pk" description:"标的id"`
 	SubjectName         string `description:"标的名称"`
@@ -158,3 +170,19 @@ func GetCygxMorningMeetingReviewChapterDetail(meetingGatherId int) (item *CygxMo
 	err = o.Raw(sql, meetingGatherId).QueryRow(&item)
 	return
 }
+
+// 获取数量
+func GetCygxMorningMeetingReviewChapterCount(cid int) (count int, err error) {
+	o := orm.NewOrm()
+	sqlCount := ` SELECT
+				COUNT( 1 ) AS count 
+			FROM
+				cygx_morning_meeting_review_chapter AS c
+				INNER JOIN cygx_morning_meeting_reviews AS a ON a.id = c.meeting_id 
+			WHERE
+				1 = 1 
+				AND c.id = ?
+				AND a.STATUS = 1`
+	err = o.Raw(sqlCount, cid).QueryRow(&count)
+	return
+}

+ 26 - 11
models/cygx/cygx_report_mapping.go

@@ -91,15 +91,30 @@ func GetMatchTypeNameByPermissionId(permissionId int) (item *ReportMappingRep, e
 
 func GetMatchTypeNameByKeyword(keyWord string) (items []*CygxReportMapping, err error) {
 	o := orm.NewOrm()
-	sql := ` SELECT
-	* 
-FROM
-( SELECT sub_category_name FROM cygx_report_mapping UNION 
-SELECT "晨会精华" AS sub_category_name UNION 
-SELECT "路演精华" AS sub_category_name UNION 
-SELECT article_type_name AS sub_category_name FROM cygx_article_type ) AS a
-WHERE
-	a.sub_category_name LIKE '%` + keyWord + `%' `
-	_,err = o.Raw(sql).QueryRows(&items)
+	sql := `SELECT
+			* 
+		FROM
+			(
+			SELECT
+				match_type_name AS sub_category_name 
+			FROM
+				cygx_report_mapping_cygx UNION
+			SELECT
+				"晨会精华" AS sub_category_name UNION
+			SELECT
+				"路演精华" AS sub_category_name UNION
+			SELECT
+				"重点公司" AS sub_category_name UNION
+			SELECT
+				"本周研究汇总" AS sub_category_name UNION
+			SELECT
+				"上周纪要汇总" AS sub_category_name UNION
+			SELECT
+				article_type_name AS sub_category_name 
+			FROM
+			cygx_article_type 
+			) AS a
+		WHERE a.sub_category_name LIKE '%` + keyWord + `%' `
+	_, err = o.Raw(sql).QueryRows(&items)
 	return
-}
+}

+ 5 - 5
models/cygx/industrial_article_group_subject.go

@@ -13,7 +13,7 @@ type CygxIndustrialArticleGroupSubject struct {
 	CreateTime          time.Time `description:"创建时间"`
 }
 
-//新增
+// 新增
 func AddCygxIndustrialArticleGroupSubject(item *CygxIndustrialArticleGroupSubject) (newId int64, err error) {
 	o := orm.NewOrm()
 	_, err = o.Insert(item)
@@ -43,8 +43,8 @@ func GetArticleIdsBySubjectId(industrialSubjectId string) (articleId string, err
 	return
 }
 
-//列表
-func GetSubjectArticleGroupManagementList(activityId int) (items []*SubjectActivityGroupManagementRep, err error) {
+// 列表
+func GetSubjectArticleGroupManagementList(articleId int) (items []*SubjectActivityGroupManagementRep, err error) {
 	o := orm.NewOrm()
 	sql := `SELECT
 			s.subject_name,
@@ -54,7 +54,7 @@ func GetSubjectArticleGroupManagementList(activityId int) (items []*SubjectActiv
 			INNER JOIN cygx_industrial_subject AS s ON s.industrial_subject_id = ag.industrial_subject_id 
 			WHERE
 			ag.article_id = ?`
-	_, err = o.Raw(sql, activityId).QueryRows(&items)
+	_, err = o.Raw(sql, articleId).QueryRows(&items)
 	return
 }
 
@@ -65,7 +65,7 @@ type SubjectlArticleGroupResp struct {
 	SubjectName            string `description:"标的名称"`
 }
 
-//列表
+// 列表
 func GetSubjectArticleGroupListByarticleIds(articleIds string) (items []*SubjectlArticleGroupResp, err error) {
 	o := orm.NewOrm()
 	sql := `SELECT

+ 1 - 1
models/cygx/industrial_management.go

@@ -874,7 +874,7 @@ func UpdateIndustrialManagementArticleNewPublishData(items map[int]string) (err
 	return
 }
 
-// GetTopOneMonthArtReadNumIndustry 获取近一个月报告阅读数量最多的产业信息 根据行业分组
+// GetTopOneMonthArtReadNumIndustry 获取列表
 func GetTopOneMonthArtReadNumIndustryAll(condition string, pars []interface{}) (items []*IndustrialManagement, err error) {
 	sql := `SELECT * FROM cygx_industrial_management WHERE 1 = 1 `
 	if condition != `` {

+ 11 - 0
models/cygx/report_article.go

@@ -468,6 +468,7 @@ type ArticleDetail struct {
 	Abstract             string `description:"摘要"`
 	CategoryName         string `description:"一级分类"`
 	SubCategoryName      string `description:"二级分类"`
+	MatchTypeName        string `description:"匹配类型"`
 	IsCollect            bool   `description:"是否收藏:true,已收藏,false:未收藏"`
 	IsInterviewApply     bool   `description:"是否申请访谈:true,已申请,false:未申请"`
 	BodyText             string `description:"内容"`
@@ -482,6 +483,9 @@ type ArticleDetail struct {
 	CategoryId           int    `description:"分类ID"`
 	FileLink             string `description:"下载预览链接"`
 	ReportType           int    `description:"报告类型,2产业报告,1行业报告"`
+	TypeName             string `description:"策略平台类型字段名称"`
+	ArticleTypeId        int    `description:"文章类型ID"`
+	ArticleTypeName      string `description:"文章类型名称"`
 }
 
 func GetArticleDetailById(articleId int) (item *ArticleDetail, err error) {
@@ -491,6 +495,13 @@ func GetArticleDetailById(articleId int) (item *ArticleDetail, err error) {
 	return
 }
 
+func GetArticleDetailByArticleId(articleId int) (item *ArticleDetail, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM cygx_article WHERE article_id = ? `
+	err = o.Raw(sql, articleId).QueryRow(&item)
+	return
+}
+
 // 修改报告匹配类型
 type UpdateReportMatchTypeNameRep struct {
 	MatchID  int `orm:"column(id);"description:"匹配ID"`

+ 65 - 0
models/cygx/report_mapping_category_group.go

@@ -0,0 +1,65 @@
+package cygx
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+type CygxReportMappingCategoryGroup struct {
+	Id                int       `orm:"column(id);pk" description:"id"`
+	ChartPermissionId int       `description:"行业ID"`
+	IdCygx            int       `description:"表cygx_report_mapping_cygx 主键ID"`
+	ArticleId         int       `description:"报告Id"`
+	CreateTime        time.Time `description:"创建时间"`
+	ModifyTime        time.Time `description:"更新时间"`
+	PermissionType    int       `description:"1主观,2客观 ,0不限制"`
+	IsByHand          int       `description:"是否手动修改过,1是,0否"`
+}
+
+type CygxReportMappingCategoryGroupResp struct {
+	Id                int       `orm:"column(id);pk" description:"id"`
+	ChartPermissionId int       `description:"行业ID"`
+	IdCygx            int       `description:"表cygx_report_mapping_cygx 主键ID"`
+	ArticleId         int       `description:"报告Id"`
+	CreateTime        time.Time `description:"创建时间"`
+	ModifyTime        time.Time `description:"更新时间"`
+	PermissionType    int       `description:"1主观,2客观 ,0不限制"`
+}
+
+// AddCygxReportMappingCategoryGroupMulti 批量添加
+func AddCygxReportMappingCategoryGroupMulti(items []*CygxReportMappingCategoryGroup, articleId int) (err error) {
+	o, err := orm.NewOrm().Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err == nil {
+			o.Commit()
+		} else {
+			o.Rollback()
+		}
+	}()
+	//删除原有数据
+	sql := ` DELETE FROM cygx_report_mapping_category_group WHERE article_id = ?   `
+	_, err = o.Raw(sql, articleId).Exec()
+	if err != nil {
+		return
+	}
+	//批量插入新的关联数据
+	if len(items) > 0 {
+		//批量添加流水信息
+		_, err = o.InsertMulti(len(items), items)
+	}
+	return
+}
+
+// 列表
+func GetCygxReportMappingCategoryGroupList(condition string, pars []interface{}) (items []*CygxReportMappingCategoryGroupResp, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM cygx_report_mapping_category_group as art WHERE 1= 1 `
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}

+ 11 - 0
models/cygx/report_mapping_cygx.go

@@ -49,6 +49,17 @@ func GetCygxReportMappingCygxList(condition string, pars []interface{}, startSiz
 	return
 }
 
+// 列表
+func GetCygxReportMappingCygxByCon(condition string, pars []interface{}) (items []*CygxReportMappingCygx, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM cygx_report_mapping_cygx as art WHERE 1= 1 `
+	if condition != "" {
+		sql += condition
+	}
+	_, err = o.Raw(sql, pars).QueryRows(&items)
+	return
+}
+
 // 获取数量
 func GetCygxReportMappingCygxCount(condition string, pars []interface{}) (count int, err error) {
 	sqlCount := ` SELECT COUNT(1) AS count  FROM cygx_report_mapping_cygx as art WHERE 1= 1  `

+ 47 - 3
models/cygx/resource_data.go

@@ -14,16 +14,25 @@ type CygxResourceData struct {
 	CreateTime  time.Time `description:"创建时间"`
 	PublishDate string    `description:"发布时间"`
 	Abstract    string    `description:"摘要"`
+	SearchTag   string    `description:"搜索标签"`
 }
 
-//添加
+// 根据资源类型获取列表
+func GetCygxResourceDataListBysource(source string) (items []*CygxResourceData, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM cygx_resource_data as art WHERE 1= 1  AND  source = ? `
+	_, err = o.Raw(sql, source).QueryRows(&items)
+	return
+}
+
+// 添加
 func AddCygxResourceData(item *CygxResourceData) (lastId int64, err error) {
 	o := orm.NewOrm()
 	lastId, err = o.Insert(item)
 	return
 }
 
-//删除数据
+// 删除数据
 func DeleteResourceData(sourceId int, source string) (err error) {
 	o := orm.NewOrm()
 	sql := ` DELETE FROM cygx_resource_data WHERE source_id = ? AND source =?  `
@@ -31,10 +40,45 @@ func DeleteResourceData(sourceId int, source string) (err error) {
 	return
 }
 
-//修改数据
+// 修改数据
 func UpdateResourceData(sourceId int, source, publishDate string) (err error) {
 	o := orm.NewOrm()
 	sql := `UPDATE cygx_resource_data SET publish_date=?  WHERE source_id=?  AND source =? `
 	_, err = o.Raw(sql, publishDate, sourceId, source).Exec()
 	return
 }
+
+// 修改
+func UpdateResourceDataByItem(item *CygxResourceData) (err error) {
+	o := orm.NewOrm()
+	updateParams := make(map[string]interface{})
+	updateParams["PublishDate"] = item.PublishDate
+	updateParams["SearchTag"] = item.SearchTag
+	ptrStructOrTableName := "cygx_resource_data"
+	whereParam := map[string]interface{}{"source_id": item.SourceId, "source": item.Source}
+	qs := o.QueryTable(ptrStructOrTableName)
+	for expr, exprV := range whereParam {
+		qs = qs.Filter(expr, exprV)
+	}
+	_, err = qs.Update(updateParams)
+	if err != nil {
+		return
+	}
+	return
+}
+
+// 获取数量
+func GetCygxReportSelectionBySourceAndId(sourceId int, source string) (count int, err error) {
+	o := orm.NewOrm()
+	sqlCount := ` SELECT COUNT(1) AS count  FROM cygx_resource_data  WHERE  source_id = ? AND source =?  `
+	err = o.Raw(sqlCount, sourceId, source).QueryRow(&count)
+	return
+}
+
+// 通过ID跟资源获取详情
+func GetCygxResourceDataByIdAndSource(sourceId int, source string) (item *CygxResourceData, err error) {
+	o := orm.NewOrm()
+	sql := `SELECT * FROM cygx_resource_data WHERE  source_id = ? AND source =?  `
+	err = o.Raw(sql, sourceId, source).QueryRow(&item)
+	return
+}

+ 102 - 0
models/cygx/resource_data_industrial_group_management.go

@@ -0,0 +1,102 @@
+package cygx
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"time"
+)
+
+// 首页资源表与产业的关系
+type CygxResourceDataIndustrialGroupManagement struct {
+	Id                     int       `orm:"column(id);pk" description:"主键ID"`
+	ResourceDataId         int       `description:"cygx_resource_data 主键ID"`
+	SourceId               int       `description:"资源ID"`
+	Source                 string    `description:"资源类型 报告 :article 、图表 :newchart、微路演 :roadshow、活动 :activity、活动视频:activityvideo、活动音频:activityvoice、专项调研活动:activityspecial"`
+	IndustrialManagementId int       `description:"cygx_industrial_management表的主键ID"`
+	CreateTime             time.Time `description:"创建时间"`
+}
+
+// 首页资源表与产业的关系
+type CygxResourceDataIndustrialGroupSubject struct {
+	Id                  int       `orm:"column(id);pk" description:"主键ID"`
+	ResourceDataId      int       `description:"cygx_resource_data 主键ID"`
+	SourceId            int       `description:"资源ID"`
+	Source              string    `description:"资源类型 报告 :article 、图表 :newchart、微路演 :roadshow、活动 :activity、活动视频:activityvideo、活动音频:activityvoice、专项调研活动:activityspecial"`
+	IndustrialSubjectId int       `description:"cygx_industrial_subject表的主键ID"`
+	CreateTime          time.Time `description:"创建时间"`
+}
+
+// 添加
+func AddCygxResourceDataGroup(sourceId int, source string, industrialItems []*CygxResourceDataIndustrialGroupManagement, subjectItems []*CygxResourceDataIndustrialGroupSubject) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	//删除关联产业
+	sql := `	DELETE FROM cygx_resource_data_industrial_group_management WHERE source_id = ? AND source =? `
+	_, err = to.Raw(sql, sourceId, source).Exec()
+	if err != nil {
+		return
+	}
+	//删除关联标的
+	sql = `	DELETE FROM cygx_resource_data_industrial_group_subject WHERE source_id = ? AND source =? `
+	_, err = to.Raw(sql, sourceId, source).Exec()
+	if err != nil {
+		return
+	}
+
+	//批量插入关联产业
+	if len(industrialItems) > 0 {
+		_, err = to.InsertMulti(len(industrialItems), industrialItems)
+		if err != nil {
+			return
+		}
+	}
+
+	//批量插入关联标的
+	if len(subjectItems) > 0 {
+		_, err = to.InsertMulti(len(subjectItems), subjectItems)
+		if err != nil {
+			return
+		}
+	}
+
+	return
+}
+
+// 删除
+func DeleteCygxResourceDataGroup(sourceId int, source string) (err error) {
+	o := orm.NewOrm()
+	to, err := o.Begin()
+	if err != nil {
+		return
+	}
+	defer func() {
+		if err != nil {
+			_ = to.Rollback()
+		} else {
+			_ = to.Commit()
+		}
+	}()
+	//删除关联产业
+	sql := `	DELETE FROM cygx_resource_data_industrial_group_management WHERE source_id = ? AND source =? `
+	_, err = to.Raw(sql, sourceId, source).Exec()
+	if err != nil {
+		return
+	}
+	//删除关联标的
+	sql = `	DELETE FROM cygx_resource_data_industrial_group_subject WHERE source_id = ? AND source =? `
+	_, err = to.Raw(sql, sourceId, source).Exec()
+	if err != nil {
+		return
+	}
+
+	return
+}

+ 8 - 0
models/db.go

@@ -222,6 +222,7 @@ func initSystem() {
 		new(system.SysRoleAdmin),          //管理员账号和角色映射表
 		new(system.AdminConfig),           //系统用户配置表
 		new(system.AdminOperateRecord),
+		new(system.AdminVerifyCodeRecord), // 用户短信邮箱验证码记录表
 	)
 }
 
@@ -251,6 +252,10 @@ func initCompany() {
 		new(company_todo.CompanyTodo),                        //客户任务列表
 		new(company.SellerCompanyPermission),                 //销售客户权限("添加客户"权限)
 		new(company.CompanyServiceRecord),                    // 客户服务记录表
+		new(company.CompanyAscribe),                          // 客户归因
+		new(company.CompanyNoRenewedNote),                    // 客户未续约记录
+		new(company.CompanyNoRenewedAscribe),                 // 确认不续约记录
+		new(company.CompanyNoRenewedAscribeLog),              // 确认不续约记录日志
 	)
 }
 
@@ -438,6 +443,9 @@ func initCygx() {
 		new(cygx.CygxActivityPointsCompany),
 		new(cygx.CygxTag),
 		new(cygx.AliyunOcrLog),
+		new(cygx.CygxReportMappingCategoryGroup),
+		new(cygx.CygxResourceDataIndustrialGroupManagement),
+		new(cygx.CygxResourceDataIndustrialGroupSubject),
 	)
 }
 

+ 37 - 35
models/eta_trial/eta_trial.go

@@ -7,20 +7,21 @@ import (
 )
 
 type EtaTrial struct {
-	EtaTrialId    int       `orm:"column(eta_trial_id);pk" description:"eta试用客户id"`
-	UserName      string    `description:"客户名称"`
-	CompanyName   string    `description:"客户公司姓名"`
-	Position      string    `description:"职位"`
-	Password      string    `json:"-"`
-	Account       string    `json:"-"`
-	Mobile        string    `description:"手机号"`
-	Enabled       int       `description:"1:有效,0:禁用"`
-	ActiveTime    int       `description:"累计活跃时长"`
-	LastLoginTime time.Time `description:"最后一次登陆时间"`
-	SellerId      int       `description:"销售id"`
-	Seller        string    `description:"销售员名称"`
-	CreateTime    time.Time
-	ModifyTime    time.Time
+	EtaTrialId        int       `orm:"column(eta_trial_id);pk" description:"eta试用客户id"`
+	UserName          string    `description:"客户名称"`
+	CompanyName       string    `description:"客户公司姓名"`
+	Position          string    `description:"职位"`
+	Password          string    `json:"-"`
+	Account           string    `json:"-"`
+	Mobile            string    `description:"手机号"`
+	Enabled           int       `description:"1:有效,0:禁用"`
+	ActiveTime        int       `description:"累计活跃时长"`
+	LastLoginTime     time.Time `description:"最后一次登陆时间"`
+	SellerId          int       `description:"销售id"`
+	Seller            string    `description:"销售员名称"`
+	CreateTime        time.Time
+	ModifyTime        time.Time
+	LastLoginDuration int `description:"最后一次登录时长"`
 }
 
 // Update 更新用户基础信息
@@ -56,25 +57,26 @@ func GetETATrialListCount(condition string, pars []interface{}) (count int, err
 }
 
 type ETATrialListRespItem struct {
-	EtaTrialId     int    `orm:"column(eta_trial_id);pk" description:"eta试用客户id"`
-	UserName       string `description:"客户名称"`
-	CompanyName    string `description:"客户公司姓名"`
-	Position       string `description:"职位"`
-	Mobile         string `description:"手机号"`
-	ActiveTime     string `description:"累计活跃时长"`
-	LastLoginTime  string `description:"最后一次登陆时间"`
-	SellerId       int    `description:"销售id"`
-	Seller         string `description:"销售员名称"`
-	InterestModule string `description:"感兴趣模块"`
-	Expiration     int    `description:"账号到期时长"`
-	Enabled        int    `description:"1:有效,0:禁用"`
-	IndexNum       int    `description:"累计添加指标"`
-	ChartNum       int    `description:"累计添加图表"`
-	LoginNum       int    `description:"累计登录次数"`
-	Password       string
-	Account        string
-	CreateTime     string
-	ModifyTime     string
+	EtaTrialId        int    `orm:"column(eta_trial_id);pk" description:"eta试用客户id"`
+	UserName          string `description:"客户名称"`
+	CompanyName       string `description:"客户公司姓名"`
+	Position          string `description:"职位"`
+	Mobile            string `description:"手机号"`
+	ActiveTime        string `description:"累计活跃时长"`
+	LastLoginTime     string `description:"最后一次登陆时间"`
+	SellerId          int    `description:"销售id"`
+	Seller            string `description:"销售员名称"`
+	InterestModule    string `description:"感兴趣模块"`
+	Expiration        int    `description:"账号到期时长"`
+	Enabled           int    `description:"1:有效,0:禁用"`
+	IndexNum          int    `description:"累计添加指标"`
+	ChartNum          int    `description:"累计添加图表"`
+	LoginNum          int    `description:"累计登录次数"`
+	Password          string
+	Account           string
+	CreateTime        string
+	ModifyTime        string
+	LastLoginDuration string `description:"最后一次登录时长"`
 }
 
 type ETATrialListRespList struct {
@@ -102,7 +104,7 @@ func GetETATrialByMobile(mobile string) (item *EtaTrial, err error) {
 	return
 }
 
-//新增客户
+// 新增客户
 func AddETATrial(item *EtaTrial) (lastId int64, err error) {
 	o := orm.NewOrm()
 	lastId, err = o.Insert(item)
@@ -123,4 +125,4 @@ func UpdateETATrialEnable(mobile string) (err error) {
 WHERE mobile=? `
 	_, err = o.Raw(sql, mobile).Exec()
 	return
-}
+}

+ 33 - 30
models/stack_company_statistic.go

@@ -5,7 +5,7 @@ import (
 	"time"
 )
 
-//存量客户数据表
+// 存量客户数据表
 type StackCompanyStatistic struct {
 	StatisticId  int       `orm:"column(statistic_id);pk"`
 	Type         string    `description:"数据类型,取值范围:新签客户,续约客户,未续约客户"`
@@ -25,7 +25,7 @@ type StackCompanyStatistic struct {
 	CreateTime   time.Time `description:"记录添加时间"`
 }
 
-//获取客户数量
+// 获取客户数量
 func GetStackCompanyCount(condition string, pars []interface{}) (count int, err error) {
 	o := orm.NewOrm()
 	//o.Using("rddp")
@@ -43,7 +43,7 @@ join company c on a.company_id=c.company_id WHERE 1 = 1 `
 	return
 }
 
-//获取客户产品数量
+// 获取客户产品数量
 func GetStackCompanyProductCount(condition string, pars []interface{}) (count int, err error) {
 	o := orm.NewOrm()
 	//o.Using("rddp")
@@ -60,7 +60,7 @@ join company c on a.company_id=c.company_id WHERE 1 = 1 `
 	return
 }
 
-//GetNotRenewalStackCompanyProductCount 获取未续约客户产品数量
+// GetNotRenewalStackCompanyProductCount 获取未续约客户产品数量
 func GetNotRenewalStackCompanyProductCount(condition string, pars []interface{}) (count int, err error) {
 	o := orm.NewOrm()
 	//o.Using("rddp")
@@ -78,31 +78,34 @@ join company c on a.company_id=c.company_id WHERE 1 = 1 `
 	return
 }
 
-//获取收入统计报表列表数据(根据合同来展示)
+// 获取收入统计报表列表数据(根据合同来展示)
 type StackCompanyStatisticList struct {
-	StatisticId       int       `orm:"column(statistic_id);pk"`
-	Type              string    `description:"数据类型,取值范围:新签客户,续约客户,未续约客户"`
-	Status            string    `description:"当前状态"`
-	CompanyId         int       `description:"企业客户id"`
-	CompanyName       string    `description:"企业客户名称"`
-	ProductId         int       `description:"客户产品id"`
-	ProductName       string    `description:"客户产品名称"`
-	ContractNum       int       `description:"第几份合同,默认是:1"`
-	SellerId          int       `description:"所属销售id"`
-	SellerName        string    `description:"所属销售名称"`
-	GroupId           int       `description:"所属销售分组id"`
-	DepartmentId      int       `description:"所属销售部门id"`
-	Date              string    `description:"记录日期"`
-	StartDate         string    `description:"服务起始时间"`
-	EndDate           string    `description:"服务截止时间"`
-	RegionType        string    `description:"所属区域,国内,海外"`
-	CreateTime        time.Time `description:"记录添加时间"`
-	CreateTimeStr     string    `description:"记录添加时间,字符串形式"`
-	ExpireDay         string    `description:"剩余可用天数"`
-	Count             int       `json:"-"`
-	RenewalReason     string    `description:"未续约说明"`
-	RenewalTodo       string    `description:"未续约说明中的待办事项说明"`
-	PackageDifference string    `description:"和上一份合同的区别"`
+	StatisticId         int       `orm:"column(statistic_id);pk"`
+	Type                string    `description:"数据类型,取值范围:新签客户,续约客户,未续约客户"`
+	Status              string    `description:"当前状态"`
+	CompanyId           int       `description:"企业客户id"`
+	CompanyName         string    `description:"企业客户名称"`
+	ProductId           int       `description:"客户产品id"`
+	ProductName         string    `description:"客户产品名称"`
+	ContractNum         int       `description:"第几份合同,默认是:1"`
+	SellerId            int       `description:"所属销售id"`
+	SellerName          string    `description:"所属销售名称"`
+	GroupId             int       `description:"所属销售分组id"`
+	DepartmentId        int       `description:"所属销售部门id"`
+	Date                string    `description:"记录日期"`
+	StartDate           string    `description:"服务起始时间"`
+	EndDate             string    `description:"服务截止时间"`
+	RegionType          string    `description:"所属区域,国内,海外"`
+	CreateTime          time.Time `description:"记录添加时间"`
+	CreateTimeStr       string    `description:"记录添加时间,字符串形式"`
+	ExpireDay           string    `description:"剩余可用天数"`
+	Count               int       `json:"-"`
+	RenewalReason       string    `description:"未续约说明"`
+	RenewalTodo         string    `description:"未续约说明中的待办事项说明"`
+	PackageDifference   string    `description:"和上一份合同的区别"`
+	AscribeContent      string    `description:"归因标签说明"`
+	IsShowNoRenewedNote bool      `description:"是否展示未续约备注按钮"`
+	Content             string    `description:"归因内容说明"`
 }
 
 func GetStackCompanyList(condition, orderBy string, pars []interface{}, startSize, pageSize int) (items []*StackCompanyStatisticList, err error) {
@@ -132,7 +135,7 @@ func GetStackCompanyList(condition, orderBy string, pars []interface{}, startSiz
 	return
 }
 
-//GetStackCompanyListFromReportRecord 查找存量客户数据FromReportRecord
+// GetStackCompanyListFromReportRecord 查找存量客户数据FromReportRecord
 func GetStackCompanyListFromReportRecord(condition, orderBy string, pars []interface{}, startSize, pageSize int) (items []*StackCompanyStatisticList, err error) {
 	o := orm.NewOrm()
 
@@ -150,7 +153,7 @@ func GetStackCompanyListFromReportRecord(condition, orderBy string, pars []inter
 	return
 }
 
-//根据企业客户编号数据集、记录编号数据集获取不在里面的数据列表
+// 根据企业客户编号数据集、记录编号数据集获取不在里面的数据列表
 func GetStackCompanyListByCompanyIds(companyIds, statisticIds, condition string, pars []interface{}) (items []*StackCompanyStatisticList, err error) {
 	o := orm.NewOrm()
 	sql := `SELECT * from stack_company_statistic WHERE 1 = 1 AND company_id in (` + companyIds + `) 

+ 29 - 26
models/statistic_report.go

@@ -371,32 +371,35 @@ func GetIncomeList(condition string, pars []interface{}, startSize, pageSize int
 
 // 增量客户统计报表列表数据结构
 type IncrementalList struct {
-	CompanyContractId int                                `description:"合同id"`
-	ContractType      string                             `description:"合同类型"`
-	CompanyId         int                                `description:"企业客户id"`
-	CompanyName       string                             `description:"企业客户名称"`
-	ProductId         int                                `description:"产品id"`
-	ProductName       string                             `description:"产品名称"`
-	CompanyProductId  int                                `description:"客户购买产品授权id"`
-	ContractCode      string                             `description:"合同编码"`
-	StartDate         string                             `description:"合同开始日期"`
-	EndDate           string                             `description:"合同结束日期"`
-	Money             float64                            `description:"合同金额"`
-	PayMethod         string                             `description:"付款方式"`
-	PayChannel        string                             `description:"付款渠道"`
-	ImgUrl            string                             `description:"合同图片"`
-	CreateTime        string                             `description:"合同创建时间"`
-	ModifyTime        string                             `description:"合同修改时间"`
-	Status            string                             `description:"合同审批状态,0:待审批,1:已审批;默认:1"`
-	RegionType        string                             `description:"企业客户所属区域;可选范围:国内,海外"`
-	SellerId          int                                `description:"归属销售id"`
-	SellerName        string                             `description:"归属销售名称"`
-	ExpireDay         string                             `description:"剩余可用天数"`
-	PermissionList    []*company.CompanyReportPermission `description:"产品权限"`
-	Count             int                                `json:"-" description:"合同数"`
-	RenewalReason     string                             `description:"未续约说明"`
-	RenewalTodo       string                             `description:"未续约说明中的待办事项说明"`
-	PackageDifference string                             `description:"和上一份合同的区别"`
+	CompanyContractId   int                                `description:"合同id"`
+	ContractType        string                             `description:"合同类型"`
+	CompanyId           int                                `description:"企业客户id"`
+	CompanyName         string                             `description:"企业客户名称"`
+	ProductId           int                                `description:"产品id"`
+	ProductName         string                             `description:"产品名称"`
+	CompanyProductId    int                                `description:"客户购买产品授权id"`
+	ContractCode        string                             `description:"合同编码"`
+	StartDate           string                             `description:"合同开始日期"`
+	EndDate             string                             `description:"合同结束日期"`
+	Money               float64                            `description:"合同金额"`
+	PayMethod           string                             `description:"付款方式"`
+	PayChannel          string                             `description:"付款渠道"`
+	ImgUrl              string                             `description:"合同图片"`
+	CreateTime          string                             `description:"合同创建时间"`
+	ModifyTime          string                             `description:"合同修改时间"`
+	Status              string                             `description:"合同审批状态,0:待审批,1:已审批;默认:1"`
+	RegionType          string                             `description:"企业客户所属区域;可选范围:国内,海外"`
+	SellerId            int                                `description:"归属销售id"`
+	SellerName          string                             `description:"归属销售名称"`
+	ExpireDay           string                             `description:"剩余可用天数"`
+	PermissionList      []*company.CompanyReportPermission `description:"产品权限"`
+	Count               int                                `json:"-" description:"合同数"`
+	RenewalReason       string                             `description:"未续约说明"`
+	RenewalTodo         string                             `description:"未续约说明中的待办事项说明"`
+	PackageDifference   string                             `description:"和上一份合同的区别"`
+	AscribeContent      string                             `description:"归因标签说明"`
+	IsShowNoRenewedNote bool                               `description:"是否展示未续约备注按钮"`
+	Content             string                             `description:"归因内容说明"`
 }
 
 // GetIncrementalNewCompanyCount 获取增量客户报表列表统计数据(根据合同来展示)

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

+ 52 - 6
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 更新用户基础信息
@@ -130,12 +131,12 @@ func GetAdminList() (items []*Admin, err error) {
 }
 
 // GetSysUserByMobile 根据手机号获取管理信息
-func GetSysUserByMobile(mobile string) (item *Admin, err error) {
-	sql := `SELECT * FROM admin WHERE mobile = ? LIMIT 1`
-	o := orm.NewOrm()
-	err = o.Raw(sql, mobile).QueryRow(&item)
-	return
-}
+//func GetSysUserByMobile(mobile string) (item *Admin, err error) {
+//	sql := `SELECT * FROM admin WHERE mobile = ? LIMIT 1`
+//	o := orm.NewOrm()
+//	err = o.Raw(sql, mobile).QueryRow(&item)
+//	return
+//}
 
 // 通过用户姓名跟身份获取管理员信息
 func CheckSysUserByName(userName, roleTypeCode string) (item *Admin, err error) {
@@ -225,3 +226,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
+}

+ 110 - 0
models/yb/community_audio_listen_log.go

@@ -1,6 +1,7 @@
 package yb
 
 import (
+	"fmt"
 	"github.com/beego/beego/v2/client/orm"
 	"hongze/hz_crm_api/utils"
 	"time"
@@ -135,6 +136,17 @@ func GetCompanyUserYbProductVisitCountByProductType(userIds []int, productType,
 		pars = append(pars, productId)
 	case 3: // 问答社区
 		sql = `SELECT COUNT(1) AS visit_count, user_id FROM yb_community_audio_listen_log WHERE user_id IN (` + inReplace + `) GROUP BY user_id `
+	case 99: // 全部
+		sql = `SELECT COUNT(1) AS visit_count, t.user_id FROM (
+					SELECT user_id, 1 AS product_id FROM yb_voice_broadcast_statistics WHERE user_id IN (` + inReplace + `)
+					UNION ALL
+					SELECT user_id, 2 FROM yb_community_video_play_log WHERE user_id IN (` + inReplace + `)
+					UNION ALL
+					SELECT user_id, 3 FROM yb_community_audio_listen_log WHERE user_id IN (` + inReplace + `)
+				) AS t
+				GROUP BY t.user_id`
+		_, err = o.Raw(sql, pars, pars, pars).QueryRows(&list)
+		return
 	}
 
 	_, err = o.Raw(sql, pars).QueryRows(&list)
@@ -259,3 +271,101 @@ func GetSellerYbProductUserCountByProductType(productId, productType int, compan
 	_, err = o.Raw(sql, pars).QueryRows(&list)
 	return
 }
+
+// UserYbProductVisitCount 用户研报产品阅读量信息
+type UserYbProductVisitCount struct {
+	ProductType int    `description:"类型名称: 问答社区/线上路演/语音播报板块名称等"`
+	VisitCount  int    `description:"点击量"`
+	TitleId     int    `description:"对应的不同产品的ID"`
+	Title       string `description:"标题"`
+	SectionId   int    `description:"语音播报板块ID"`
+	NewSource   int    `description:"来源:1-小程序 2-PC端"`
+	RecentTime  string `description:"最近点击时间"`
+	VideoType   int    `description:"视频社区类型: 1-视频社区; 2-路演视频"`
+}
+
+// GetUserAllYbProductVisitCountPageList 获取用户所有研报产品阅读量-分页
+func GetUserAllYbProductVisitCountPageList(userId, startSize, pageSize int, orderRule string) (total int, items []*UserYbProductVisitCount, err error) {
+	o := orm.NewOrm()
+	sub := `(
+				SELECT
+					1 AS product_type,
+					COUNT(1) AS visit_count,
+					broadcast_id AS title_id,
+					broadcast_name AS title,
+					section_id,
+					IF (source = 4, 2, source) AS new_source,
+					MAX(create_time) AS recent_time,
+					0 AS video_type
+			FROM
+				yb_voice_broadcast_statistics
+			WHERE
+				user_id = ?
+			GROUP BY
+				broadcast_id,
+				new_source
+			)
+
+			UNION ALL
+			(
+				SELECT
+					2 AS product_type,
+					COUNT(1) AS visit_count,
+					community_video_id,
+					"",
+					0,
+					IF (
+						source_agent = 4,
+						2,
+						source_agent
+					) AS new_source,
+					MAX(create_time) AS recent_time,
+					type AS video_type
+			FROM
+				yb_community_video_play_log
+			WHERE
+				user_id = ?
+			GROUP BY
+				community_video_id,
+				new_source
+			)
+
+			UNION ALL
+			(
+				SELECT
+					3 AS product_type,
+					COUNT(1) AS visit_count,
+					community_question_id,
+					"",
+					0,
+					IF (
+						source_agent = 4,
+						2,
+						source_agent
+					) AS new_source,
+					MAX(create_time) AS recent_time,
+					0 AS video_type
+			FROM
+				yb_community_audio_listen_log
+			WHERE
+				user_id = ?
+			GROUP BY
+				community_question_id,
+				new_source
+			)`
+
+	totalSql := fmt.Sprintf(`SELECT COUNT(1) FROM (%s) AS t`, sub)
+	err = o.Raw(totalSql, userId, userId, userId).QueryRow(&total)
+	if err != nil {
+		return
+	}
+
+	order := `ORDER BY recent_time DESC`
+	if orderRule != "" {
+		order = `ORDER BY ` + orderRule
+	}
+
+	sql := fmt.Sprintf(`SELECT * FROM (%s) AS t %s LIMIT ?, ?`, sub, order)
+	_, err = o.Raw(sql, userId, userId, userId, startSize, pageSize).QueryRows(&items)
+	return
+}

+ 5 - 4
models/yb/response/product_census.go

@@ -19,8 +19,9 @@ type ProductCensusUserVisitCountDetailResp struct {
 
 // ProductCensusUserVisitCountDetail 用户阅读统计详情
 type ProductCensusUserVisitCountDetail struct {
-	VisitCount int    `description:"点击量"`
-	Title      string `description:"标题"`
-	Source     int    `description:"来源:1-小程序 2-PC端"`
-	RecentTime string `description:"最近点击时间"`
+	ProductName string `description:"产品名称: 问答社区/线上路演/语音播报板块名称等"`
+	VisitCount  int    `description:"点击量"`
+	Title       string `description:"标题"`
+	Source      int    `description:"来源:1-小程序 2-PC端"`
+	RecentTime  string `description:"最近点击时间"`
 }

+ 13 - 0
models/yb/voice_section.go

@@ -2,6 +2,7 @@ package yb
 
 import (
 	"github.com/beego/beego/v2/client/orm"
+	"hongze/hz_crm_api/utils"
 )
 
 type VoiceSection struct {
@@ -82,3 +83,15 @@ func GetVoiceSectionById(sectionId int) (item *VoiceSection, err error) {
 	err = o.Raw(sql, sectionId).QueryRow(&item)
 	return
 }
+
+// GetVoiceSectionByIds 根据IDs获取板块
+func GetVoiceSectionByIds(ids []int) (list []*VoiceSection, err error) {
+	arrLen := len(ids)
+	if arrLen == 0 {
+		return
+	}
+	o := orm.NewOrm()
+	sql := `SELECT * FROM yb_voice_section WHERE section_id IN (` + utils.GetOrmInReplace(arrLen) + `)`
+	_, err = o.Raw(sql, ids).QueryRows(&list)
+	return
+}

+ 117 - 0
routers/commentsRouter.go

@@ -8953,6 +8953,60 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:CompanyRenewalController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:CompanyRenewalController"],
+        beego.ControllerComments{
+            Method: "CompanyAscribeAdd",
+            Router: `/company_ascribe/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:CompanyRenewalController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:CompanyRenewalController"],
+        beego.ControllerComments{
+            Method: "CompanyAscribeList",
+            Router: `/company_ascribe/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:CompanyRenewalController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:CompanyRenewalController"],
+        beego.ControllerComments{
+            Method: "CompanyNoRenewedAscribeAdd",
+            Router: `/company_no_renewed_ascribe/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:CompanyRenewalController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:CompanyRenewalController"],
+        beego.ControllerComments{
+            Method: "CompanyNoRenewedAscribeAddDetail",
+            Router: `/company_no_renewed_ascribe/detail`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:CompanyRenewalController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:CompanyRenewalController"],
+        beego.ControllerComments{
+            Method: "CompanyNoRenewedNoteAdd",
+            Router: `/company_no_renewed_note/add`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:CompanyRenewalController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:CompanyRenewalController"],
+        beego.ControllerComments{
+            Method: "CompanyNoRenewedNoteList",
+            Router: `/company_no_renewed_note/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:CompanySellerController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:CompanySellerController"],
         beego.ControllerComments{
             Method: "CheckList",
@@ -10393,6 +10447,69 @@ func init() {
             Filters: nil,
             Params: nil})
 
+    beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "AreaCodeList",
+            Router: `/area_code/list`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "ForgetAccountGet",
+            Router: `/forget/account_get`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "ForgetCodeVerify",
+            Router: `/forget/code_verify`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "ForgetResetPass",
+            Router: `/forget/reset_pass`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "GenerateCaptcha",
+            Router: `/get_captcha`,
+            AllowHTTPMethods: []string{"get"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "Login",
+            Router: `/login`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
+    beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:UserLoginController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:UserLoginController"],
+        beego.ControllerComments{
+            Method: "GetVerifyCode",
+            Router: `/verify_code`,
+            AllowHTTPMethods: []string{"post"},
+            MethodParams: param.Make(),
+            Filters: nil,
+            Params: nil})
+
     beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:VarietyTagController"] = append(beego.GlobalControllerRouter["hongze/hz_crm_api/controllers:VarietyTagController"],
         beego.ControllerComments{
             Method: "ClassifyList",

+ 7 - 0
routers/router.go

@@ -107,6 +107,7 @@ func init() {
 				&controllers.CompanyReceiveController{},
 				&controllers.CompanyServiceRecordController{},
 				&controllers.FullCompanyController{},
+				&controllers.CompanyRenewalController{},
 			),
 		),
 		web.NSNamespace("/resource",
@@ -318,6 +319,12 @@ func init() {
 				&help_doc.HelpDocController{},
 			),
 		),
+		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 (
+	"fmt"
+	"hongze/hz_crm_api/utils"
+	"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
+}

+ 69 - 0
services/company_ascribe.go

@@ -0,0 +1,69 @@
+package services
+
+import (
+	"errors"
+	"fmt"
+	"hongze/hz_crm_api/models/company"
+	"hongze/hz_crm_api/services/alarm_msg"
+	"hongze/hz_crm_api/utils"
+)
+
+// 处理公司归因展示
+func GetCompanyAscribeContentMap(companyIds []int) (mapResp map[string]string, mapCountResp map[string]string) {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println("err:", err)
+			go alarm_msg.SendAlarmMsg(" 处理公司归因展示失败 GetCompanyAscribeContent,Err:"+err.Error(), 3)
+		}
+	}()
+	lenArr := len(companyIds)
+	if lenArr == 0 {
+		return
+	}
+	var condition string
+	var pars []interface{}
+	condition = ` AND company_id IN (` + utils.GetOrmInReplace(len(companyIds)) + `)`
+	pars = append(pars, companyIds)
+	list, e := company.GetCompanyNoRenewedAscribeList(condition, pars, 0, 0)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		err = errors.New("GetCompanyNoRenewedAscribeList, Err: " + e.Error())
+		return
+	}
+	mapResp = make(map[string]string, 0)
+	mapCountResp = make(map[string]string, 0)
+	for _, v := range list {
+		mapResp[fmt.Sprint("CID_", v.CompanyId, "PID_", v.ProductId)] = v.AscribeContent
+		mapCountResp[fmt.Sprint("CID_", v.CompanyId, "PID_", v.ProductId)] = v.Content
+	}
+	return
+}
+
+// 处理公司是否添加过未续约说明展示
+func GetCompanyNoRenewedNoteMap(companyIds []int) (mapResp map[string]bool) {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println("err:", err)
+			go alarm_msg.SendAlarmMsg(" 处理公司是否添加过未续约说明展示失败 GetCompanyNoRenewedNoteMap,Err:"+err.Error(), 3)
+		}
+	}()
+	lenArr := len(companyIds)
+	if lenArr == 0 {
+		return
+	}
+	var condition string
+	var pars []interface{}
+	condition = ` AND company_id IN (` + utils.GetOrmInReplace(len(companyIds)) + `)`
+	pars = append(pars, companyIds)
+	list, e := company.GetCompanyNoRenewedNoteList(condition, pars, 0, 0)
+	if e != nil && e.Error() != utils.ErrNoRow() {
+		err = errors.New("GetCompanyNoRenewedNoteList, Err: " + e.Error())
+		return
+	}
+	mapResp = make(map[string]bool, 0)
+	for _, v := range list {
+		mapResp[fmt.Sprint("CID_", v.CompanyId, "PID_", v.ProductId)] = true
+	}
+	return
+}

+ 2 - 0
services/crm_eta.go

@@ -50,6 +50,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())
@@ -140,6 +141,7 @@ func CodeLoginFromMiddleServer(authCode string) (tokenResp GetEtaTokenData, 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())

+ 70 - 0
services/cygx/report_mapping_category_group.go

@@ -0,0 +1,70 @@
+package cygx
+
+import (
+	"errors"
+	"fmt"
+	"hongze/hz_crm_api/models/cygx"
+	"hongze/hz_crm_api/services/alarm_msg"
+	"hongze/hz_crm_api/utils"
+	"time"
+)
+
+// AddCygxReportMappingCategoryGroupByArticleId  根据文章ID建立分类分组,主客观权限分组
+func AddCygxReportMappingCategoryGroupByArticleId(articleId int) {
+	time.Sleep(5 * time.Second)
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println(err)
+			go alarm_msg.SendAlarmMsg("根据文章ID建立分类分组,主客观权限分组,失败"+err.Error()+fmt.Sprint("articleId", articleId), 2)
+		}
+	}()
+	detail, e := cygx.GetArticleDetailByArticleId(articleId)
+	if e != nil {
+		err = errors.New("GetArticleDetailById, Err: " + e.Error())
+		return
+	}
+	categoryId := detail.CategoryId
+
+	var condition string
+	var pars []interface{}
+	condition += ` AND category_id_celue = ? `
+	pars = append(pars, categoryId)
+	list, e := cygx.GetCygxReportMappingGroupList(condition, pars, 0, 999)
+	if e != nil {
+		err = errors.New("GetCygxReportMappingGroupList, Err: " + e.Error())
+		return
+	}
+	pars = make([]interface{}, 0)
+	var idCygxs []int
+	for _, v := range list {
+		idCygxs = append(idCygxs, v.IdCygx)
+	}
+	condition = ` AND id  IN (` + utils.GetOrmInReplace(len(idCygxs)) + `)`
+	pars = append(pars, idCygxs)
+	cygxlist, e := cygx.GetCygxReportMappingCygxByCon(condition, pars)
+	if e != nil {
+		err = errors.New("GetCygxReportMappingCygxByCon, Err: " + e.Error())
+		return
+	}
+	var items []*cygx.CygxReportMappingCategoryGroup
+	for _, v := range cygxlist {
+		item := new(cygx.CygxReportMappingCategoryGroup)
+		item.IdCygx = v.Id
+		item.ChartPermissionId = v.ChartPermissionId
+		//如果类型是报告就是主观,类型是纪要就是客观
+		if detail.TypeName == "报告" {
+			item.PermissionType = 1
+		}
+		if detail.TypeName == "纪要" {
+			item.PermissionType = 2
+		}
+		item.ArticleId = articleId
+		item.IsByHand = 1
+		item.CreateTime = time.Now()
+		item.ModifyTime = time.Now()
+		items = append(items, item)
+	}
+	e = cygx.AddCygxReportMappingCategoryGroupMulti(items, articleId)
+	return
+}

+ 904 - 0
services/cygx/resource_data.go

@@ -0,0 +1,904 @@
+package cygx
+
+import (
+	"errors"
+	"fmt"
+	"hongze/hz_crm_api/models/cygx"
+	"hongze/hz_crm_api/services/alarm_msg"
+	"hongze/hz_crm_api/utils"
+	"strconv"
+	"time"
+)
+
+//首页最新页面数据逻辑处理
+
+//activity
+//activityspecial
+//activityvideo
+//activityvoice
+//article
+//meetingreviewchapt
+//minutessummary
+//newchart
+//productinterior
+//reportselection
+//researchsummary
+//roadshow
+
+//func init() {
+//	UpdateMeetingreviewchaptResourceData(141)
+//}
+
+// 更新活动
+func UpdateActivityResourceData(sourceId int) {
+	var err error
+	//time.Sleep(3*time.Second) // 有时候同时添加多个活动,延迟三秒
+	defer func() {
+		if err != nil {
+			fmt.Println("err:", err)
+			go alarm_msg.SendAlarmMsg("更新活动 失败,UpdateActivityResourceData Err:"+err.Error()+"资源ID"+strconv.Itoa(sourceId), 3)
+		}
+	}()
+	var source = utils.CYGX_OBJ_ACTIVITY
+	var condition string
+	var pars []interface{}
+	condition = ` AND publish_status = 1  AND  activity_id = ?  `
+	pars = append(pars, sourceId)
+	total, e := cygx.GetActivityCount(condition, pars)
+	if e != nil {
+		err = errors.New("GetCygxReportSelection, Err: " + e.Error())
+		return
+	}
+	//如果取消发布了就做删除处理
+	if total == 0 {
+		e = cygx.DeleteResourceData(sourceId, source)
+		if e != nil {
+			err = errors.New("DeleteResourceData, Err: " + e.Error())
+			return
+		}
+		//删除 cygx_resource_data 表关联的产业ID,标的ID
+		e = cygx.DeleteCygxResourceDataGroup(sourceId, source)
+		if e != nil {
+			err = errors.New("DeleteCygxResourceDataGroup, Err: " + e.Error())
+			return
+		}
+	} else {
+		//判断是否存在,如果不存在就新增,存在就更新
+		totalData, e := cygx.GetCygxReportSelectionBySourceAndId(sourceId, source)
+		if e != nil {
+			err = errors.New("GetCygxReportSelectionBySourceAndId, Err: " + e.Error())
+			return
+		}
+		detail, e := cygx.GetAddActivityInfoById(sourceId)
+		if e != nil {
+			err = errors.New("GetCygxReportSelectionInfoById, Err: " + e.Error())
+			return
+		}
+		var resourceDataId int
+		publishDate := time.Now().Format(utils.FormatDateTime)
+		item := new(cygx.CygxResourceData)
+		item.SourceId = sourceId
+		item.Source = source
+		//分析师电话会(C类)
+		item.SearchTag = detail.ActivityTypeName
+		item.PublishDate = publishDate
+		item.CreateTime = time.Now()
+		if totalData == 0 {
+			newId, e := cygx.AddCygxResourceData(item)
+			if e != nil {
+				err = errors.New("AddCygxResourceData, Err: " + e.Error())
+				return
+			}
+			resourceDataId = int(newId)
+		} else {
+			e = cygx.UpdateResourceDataByItem(item)
+			if e != nil {
+				err = errors.New("UpdateResourceData, Err: " + e.Error())
+				return
+			}
+			sourceDetail, e := cygx.GetCygxResourceDataByIdAndSource(sourceId, source)
+			if e != nil {
+				err = errors.New("UpdateResourceData, Err: " + e.Error())
+				return
+			}
+			resourceDataId = sourceDetail.Id
+		}
+
+		//建立首页资源表,与产业的关系
+		industrialList, e := cygx.GetIndustrialActivityGroupManagementList(sourceId, 1)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = errors.New("GetIndustrialActivityGroupManagementList, Err: " + e.Error() + "activityId:" + strconv.Itoa(sourceId))
+			return
+		}
+		var industrialItems []*cygx.CygxResourceDataIndustrialGroupManagement
+		for _, v := range industrialList {
+			var industrialItem = new(cygx.CygxResourceDataIndustrialGroupManagement)
+			industrialItem.SourceId = sourceId
+			industrialItem.Source = source
+			industrialItem.IndustrialManagementId = v.IndustrialManagementId
+			industrialItem.ResourceDataId = resourceDataId
+			industrialItem.CreateTime = time.Now()
+			industrialItems = append(industrialItems, industrialItem)
+		}
+
+		//建立首页资源表,与标的 的关系
+		subjectList, e := cygx.GetSubjectActivityGroupManagementList(sourceId, 1)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = errors.New("GetIndustrialActivityGroupManagementList, Err: " + e.Error() + "activityId:" + strconv.Itoa(sourceId))
+			return
+		}
+		var subjectItems []*cygx.CygxResourceDataIndustrialGroupSubject
+		for _, v := range subjectList {
+			var subjectItem = new(cygx.CygxResourceDataIndustrialGroupSubject)
+			subjectItem.SourceId = sourceId
+			subjectItem.Source = source
+			subjectItem.IndustrialSubjectId = v.IndustrialSubjectId
+			subjectItem.ResourceDataId = resourceDataId
+			subjectItem.CreateTime = time.Now()
+			subjectItems = append(subjectItems, subjectItem)
+		}
+
+		//插入关联信息
+		e = cygx.AddCygxResourceDataGroup(sourceId, source, industrialItems, subjectItems)
+		if e != nil {
+			err = errors.New("AddCygxResourceDataGroup, Err: " + e.Error())
+			return
+		}
+	}
+	return
+}
+
+// 更新专项调研活动
+func UpdateActivitySpecialResourceData(sourceId int) {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println("err:", err)
+			go alarm_msg.SendAlarmMsg("更新活动 失败,UpdateActivitySpecialResourceData Err:"+err.Error()+"资源ID"+strconv.Itoa(sourceId), 3)
+		}
+	}()
+	var source = utils.CYGX_OBJ_ACTIVITYSPECIAL
+	var condition string
+	var pars []interface{}
+	condition = ` AND publish_status = 1  AND  activity_id = ?  `
+	pars = append(pars, sourceId)
+	total, e := cygx.GetActivityCount(condition, pars)
+	if e != nil {
+		err = errors.New("GetCygxReportSelection, Err: " + e.Error())
+		return
+	}
+	//如果取消发布了就做删除处理
+	if total == 0 {
+		e = cygx.DeleteResourceData(sourceId, source)
+		if e != nil {
+			err = errors.New("DeleteResourceData, Err: " + e.Error())
+			return
+		}
+		//删除 cygx_resource_data 表关联的产业ID,标的ID
+		e = cygx.DeleteCygxResourceDataGroup(sourceId, source)
+		if e != nil {
+			err = errors.New("DeleteCygxResourceDataGroup, Err: " + e.Error())
+			return
+		}
+	} else {
+		//判断是否存在,如果不存在就新增,存在就更新
+		totalData, e := cygx.GetCygxReportSelectionBySourceAndId(sourceId, source)
+		if e != nil {
+			err = errors.New("GetCygxReportSelectionBySourceAndId, Err: " + e.Error())
+			return
+		}
+		var resourceDataId int
+		publishDate := time.Now().Format(utils.FormatDateTime)
+		item := new(cygx.CygxResourceData)
+		item.SourceId = sourceId
+		item.Source = source
+		//分析师电话会(C类)
+		item.SearchTag = ""
+		item.PublishDate = publishDate
+		item.CreateTime = time.Now()
+		if totalData == 0 {
+			newId, e := cygx.AddCygxResourceData(item)
+			if e != nil {
+				err = errors.New("AddCygxResourceData, Err: " + e.Error())
+				return
+			}
+			resourceDataId = int(newId)
+		} else {
+			e = cygx.UpdateResourceDataByItem(item)
+			if e != nil {
+				err = errors.New("UpdateResourceData, Err: " + e.Error())
+				return
+			}
+			sourceDetail, e := cygx.GetCygxResourceDataByIdAndSource(sourceId, source)
+			if e != nil {
+				err = errors.New("UpdateResourceData, Err: " + e.Error())
+				return
+			}
+			resourceDataId = sourceDetail.Id
+		}
+
+		//建立首页资源表,与产业的关系
+		industrialList, e := cygx.GetIndustrialActivityGroupManagementList(sourceId, 2)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = errors.New("GetIndustrialActivityGroupManagementList, Err: " + e.Error() + "activityId:" + strconv.Itoa(sourceId))
+			return
+		}
+		var industrialItems []*cygx.CygxResourceDataIndustrialGroupManagement
+		for _, v := range industrialList {
+			var industrialItem = new(cygx.CygxResourceDataIndustrialGroupManagement)
+			industrialItem.SourceId = sourceId
+			industrialItem.Source = source
+			industrialItem.IndustrialManagementId = v.IndustrialManagementId
+			industrialItem.ResourceDataId = resourceDataId
+			industrialItem.CreateTime = time.Now()
+			industrialItems = append(industrialItems, industrialItem)
+		}
+
+		//建立首页资源表,与标的 的关系
+		subjectList, e := cygx.GetSubjectArticleGroupManagementList(sourceId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = errors.New("GetSubjectArticleGroupManagementList, Err: " + e.Error() + "sourceId:" + strconv.Itoa(sourceId))
+			return
+		}
+		var subjectItems []*cygx.CygxResourceDataIndustrialGroupSubject
+		for _, v := range subjectList {
+			var subjectItem = new(cygx.CygxResourceDataIndustrialGroupSubject)
+			subjectItem.SourceId = sourceId
+			subjectItem.Source = source
+			subjectItem.IndustrialSubjectId = v.IndustrialSubjectId
+			subjectItem.ResourceDataId = resourceDataId
+			subjectItem.CreateTime = time.Now()
+			subjectItems = append(subjectItems, subjectItem)
+		}
+		//插入关联信息
+		e = cygx.AddCygxResourceDataGroup(sourceId, source, industrialItems, subjectItems)
+		if e != nil {
+			err = errors.New("AddCygxResourceDataGroup, Err: " + e.Error())
+			return
+		}
+	}
+	return
+}
+
+// 更新文章
+func UpdateArticleResourceData(sourceId int) {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println("err:", err)
+			go alarm_msg.SendAlarmMsg("更新文章 失败,UpdateArticleResourceData Err:"+err.Error()+"资源ID"+strconv.Itoa(sourceId), 3)
+		}
+	}()
+	var source = utils.CYGX_OBJ_ARTICLE
+	var condition string
+	var pars []interface{}
+	condition = ` AND publish_status = 1  AND  article_id = ?  `
+	pars = append(pars, sourceId)
+	total, e := cygx.GetCygxArticleCount(condition, pars)
+	if e != nil {
+		err = errors.New("GetCygxReportSelection, Err: " + err.Error())
+		return
+	}
+	//如果取消发布了就做删除处理
+	if total == 0 {
+		e = cygx.DeleteResourceData(sourceId, source)
+		if e != nil {
+			err = errors.New("DeleteResourceData, Err: " + e.Error())
+			return
+		}
+		//删除 cygx_resource_data 表关联的产业ID,标的ID
+		e = cygx.DeleteCygxResourceDataGroup(sourceId, source)
+		if e != nil {
+			err = errors.New("DeleteCygxResourceDataGroup, Err: " + e.Error())
+			return
+		}
+	} else {
+		//判断是否存在,如果不存在就新增,存在就更新
+		totalData, e := cygx.GetCygxReportSelectionBySourceAndId(sourceId, source)
+		if e != nil {
+			err = errors.New("GetCygxReportSelectionBySourceAndId, Err: " + e.Error())
+			return
+		}
+		detail, e := cygx.GetArticleDetailByArticleId(sourceId)
+		if e != nil {
+			err = errors.New("GetCygxReportSelectionInfoById, Err: " + e.Error())
+			return
+		}
+		var resourceDataId int
+		publishDate := time.Now().Format(utils.FormatDateTime)
+		item := new(cygx.CygxResourceData)
+		if detail.ArticleTypeId > 0 {
+			item.SearchTag = detail.ArticleTypeName // 研选类型名称
+		} else {
+			item.SearchTag = detail.MatchTypeName
+		}
+		item.SourceId = sourceId
+		item.Source = source
+		item.PublishDate = publishDate
+		item.CreateTime = time.Now()
+		if totalData == 0 {
+			newId, e := cygx.AddCygxResourceData(item)
+			if e != nil {
+				err = errors.New("AddCygxResourceData, Err: " + e.Error())
+				return
+			}
+			resourceDataId = int(newId)
+		} else {
+			e = cygx.UpdateResourceDataByItem(item)
+			if e != nil {
+				err = errors.New("UpdateResourceData, Err: " + e.Error())
+				return
+			}
+			sourceDetail, e := cygx.GetCygxResourceDataByIdAndSource(sourceId, source)
+			if e != nil {
+				err = errors.New("UpdateResourceData, Err: " + e.Error())
+				return
+			}
+			resourceDataId = sourceDetail.Id
+		}
+
+		//建立首页资源表,与产业的关系
+		industrialList, e := cygx.GetIndustrialArticleGroupManagementList(sourceId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = errors.New("GetIndustrialArticleGroupManagementList, Err: " + e.Error() + "sourceId:" + strconv.Itoa(sourceId))
+			return
+		}
+		var industrialItems []*cygx.CygxResourceDataIndustrialGroupManagement
+		for _, v := range industrialList {
+			var industrialItem = new(cygx.CygxResourceDataIndustrialGroupManagement)
+			industrialItem.SourceId = sourceId
+			industrialItem.Source = source
+			industrialItem.IndustrialManagementId = v.IndustrialManagementId
+			industrialItem.ResourceDataId = resourceDataId
+			industrialItem.CreateTime = time.Now()
+			industrialItems = append(industrialItems, industrialItem)
+		}
+
+		//建立首页资源表,与标的 的关系
+		subjectList, e := cygx.GetSubjectArticleGroupManagementList(sourceId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = errors.New("GetSubjectArticleGroupManagementList, Err: " + e.Error() + "sourceId:" + strconv.Itoa(sourceId))
+			return
+		}
+		var subjectItems []*cygx.CygxResourceDataIndustrialGroupSubject
+		for _, v := range subjectList {
+			var subjectItem = new(cygx.CygxResourceDataIndustrialGroupSubject)
+			subjectItem.SourceId = sourceId
+			subjectItem.Source = source
+			subjectItem.IndustrialSubjectId = v.IndustrialSubjectId
+			subjectItem.ResourceDataId = resourceDataId
+			subjectItem.CreateTime = time.Now()
+			subjectItems = append(subjectItems, subjectItem)
+		}
+
+		//插入关联信息
+		e = cygx.AddCygxResourceDataGroup(sourceId, source, industrialItems, subjectItems)
+		if e != nil {
+			err = errors.New("AddCygxResourceDataGroup, Err: " + e.Error())
+			return
+		}
+	}
+	return
+}
+
+// 更新产品内测
+func UpdateProductInteriorResourceData(sourceId int) {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println("err:", err)
+			go alarm_msg.SendAlarmMsg("更新产品内测 失败,UpdateProductInteriorResourceData Err:"+err.Error()+"资源ID"+strconv.Itoa(sourceId), 3)
+		}
+	}()
+	var source = utils.CYGX_OBJ_PRODUCTINTERIOR
+	var condition string
+	var pars []interface{}
+	condition = ` AND visible_range = 1  AND  product_interior_id = ?  `
+	pars = append(pars, sourceId)
+	total, e := cygx.GetCygxProductInteriorCount(condition, pars)
+	if e != nil {
+		err = errors.New("GetCygxReportSelection, Err: " + e.Error())
+		return
+	}
+
+	//如果取消发布了就做删除处理
+	if total == 0 {
+		e = cygx.DeleteResourceData(sourceId, source)
+		if e != nil {
+			err = errors.New("DeleteResourceData, Err: " + e.Error())
+			return
+		}
+		//删除 cygx_resource_data 表关联的产业ID,标的ID
+		e = cygx.DeleteCygxResourceDataGroup(sourceId, source)
+		if e != nil {
+			err = errors.New("DeleteCygxResourceDataGroup, Err: " + e.Error())
+			return
+		}
+	} else {
+		//判断是否存在,如果不存在就新增,存在就更新
+		totalData, e := cygx.GetCygxReportSelectionBySourceAndId(sourceId, source)
+		if e != nil {
+			err = errors.New("GetCygxReportSelectionBySourceAndId, Err: " + e.Error())
+			return
+		}
+		detail, e := cygx.GetCygxProductInteriorDetail(sourceId)
+		if e != nil {
+			err = errors.New("GetCygxReportSelectionInfoById, Err: " + err.Error())
+			return
+		}
+		mapMatchTypeName := GetCygxReportMappingCygxListMap() //报告匹配类型
+		var resourceDataId int
+		publishDate := time.Now().Format(utils.FormatDateTime)
+		item := new(cygx.CygxResourceData)
+		item.SourceId = sourceId
+		item.Source = source
+		item.SearchTag = mapMatchTypeName[detail.MatchTypeId]
+		item.PublishDate = publishDate
+		item.CreateTime = time.Now()
+		if totalData == 0 {
+			newId, e := cygx.AddCygxResourceData(item)
+			if e != nil {
+				err = errors.New("AddCygxResourceData, Err: " + e.Error())
+				return
+			}
+			resourceDataId = int(newId)
+		} else {
+			e = cygx.UpdateResourceDataByItem(item)
+			if e != nil {
+				err = errors.New("UpdateResourceData, Err: " + e.Error())
+				return
+			}
+			sourceDetail, e := cygx.GetCygxResourceDataByIdAndSource(sourceId, source)
+			if e != nil {
+				err = errors.New("UpdateResourceData, Err: " + e.Error())
+				return
+			}
+			resourceDataId = sourceDetail.Id
+		}
+
+		//建立首页资源表,与产业的关系
+		industrialList, e := cygx.GetProductInteriorIndustrialGroupManagementList(sourceId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = errors.New("GetIndustrialArticleGroupManagementList, Err: " + e.Error() + "sourceId:" + strconv.Itoa(sourceId))
+			return
+		}
+		var industrialItems []*cygx.CygxResourceDataIndustrialGroupManagement
+		for _, v := range industrialList {
+			var industrialItem = new(cygx.CygxResourceDataIndustrialGroupManagement)
+			industrialItem.SourceId = sourceId
+			industrialItem.Source = source
+			industrialItem.IndustrialManagementId = v.IndustrialManagementId
+			industrialItem.ResourceDataId = resourceDataId
+			industrialItem.CreateTime = time.Now()
+			industrialItems = append(industrialItems, industrialItem)
+		}
+
+		//建立首页资源表,与标的 的关系
+		subjectList, e := cygx.GetProductInteriorIndustrialGroupSubjecttList(sourceId)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = errors.New("GetSubjectArticleGroupManagementList, Err: " + e.Error() + "sourceId:" + strconv.Itoa(sourceId))
+			return
+		}
+		var subjectItems []*cygx.CygxResourceDataIndustrialGroupSubject
+		for _, v := range subjectList {
+			var subjectItem = new(cygx.CygxResourceDataIndustrialGroupSubject)
+			subjectItem.SourceId = sourceId
+			subjectItem.Source = source
+			subjectItem.IndustrialSubjectId = v.IndustrialSubjectId
+			subjectItem.ResourceDataId = resourceDataId
+			subjectItem.CreateTime = time.Now()
+			subjectItems = append(subjectItems, subjectItem)
+		}
+
+		//插入关联信息
+		e = cygx.AddCygxResourceDataGroup(sourceId, source, industrialItems, subjectItems)
+		if e != nil {
+			err = errors.New("AddCygxResourceDataGroup, Err: " + e.Error())
+			return
+		}
+	}
+	return
+}
+
+// 更新晨会精华
+func UpdateMeetingreviewchaptResourceData(sourceId int) {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println("err:", err)
+			go alarm_msg.SendAlarmMsg("更新产品内测 失败,UpdateMeetingreviewchaptResourceData Err:"+err.Error()+"资源ID"+strconv.Itoa(sourceId), 3)
+		}
+	}()
+	var source = utils.CYGX_OBJ_MEETINGREVIEWCHAPT
+	total, e := cygx.GetCygxMorningMeetingReviewChapterCount(sourceId)
+	if e != nil {
+		err = errors.New("GetCygxReportSelection, Err: " + e.Error())
+		return
+	}
+
+	//如果取消发布了就做删除处理
+	if total == 0 {
+		e = cygx.DeleteResourceData(sourceId, source)
+		if e != nil {
+			err = errors.New("DeleteResourceData, Err: " + e.Error())
+			return
+		}
+		//删除 cygx_resource_data 表关联的产业ID,标的ID
+		e = cygx.DeleteCygxResourceDataGroup(sourceId, source)
+		if e != nil {
+			err = errors.New("DeleteCygxResourceDataGroup, Err: " + e.Error())
+			return
+		}
+	} else {
+		//判断是否存在,如果不存在就新增,存在就更新
+		totalData, e := cygx.GetCygxReportSelectionBySourceAndId(sourceId, source)
+		if e != nil {
+			err = errors.New("GetCygxReportSelectionBySourceAndId, Err: " + e.Error())
+			return
+		}
+		detail, e := cygx.GetCygxMorningMeetingReviewChapterDetail(sourceId)
+		if e != nil {
+			err = errors.New("GetCygxMorningMeetingReviewChapterDetail" + e.Error())
+			return
+		}
+		var resourceDataId int
+		publishDate := time.Now().Format(utils.FormatDateTime)
+		item := new(cygx.CygxResourceData)
+		item.SourceId = sourceId
+		item.Source = source
+		item.SearchTag = "晨会精华"
+		item.PublishDate = publishDate
+		item.CreateTime = time.Now()
+		if totalData == 0 {
+			newId, e := cygx.AddCygxResourceData(item)
+			if e != nil {
+				err = errors.New("AddCygxResourceData, Err: " + e.Error())
+				return
+			}
+			resourceDataId = int(newId)
+		} else {
+			e = cygx.UpdateResourceDataByItem(item)
+			if e != nil {
+				err = errors.New("UpdateResourceData, Err: " + e.Error())
+				return
+			}
+			sourceDetail, e := cygx.GetCygxResourceDataByIdAndSource(sourceId, source)
+			if e != nil {
+				err = errors.New("UpdateResourceData, Err: " + e.Error())
+				return
+			}
+			resourceDataId = sourceDetail.Id
+		}
+		var condition string
+		var pars []interface{}
+		condition = " AND industrial_management_id = ? "
+		pars = append(pars, detail.IndustryId)
+		//建立首页资源表,与产业的关系
+		industrialList, e := cygx.GetTopOneMonthArtReadNumIndustryAll(condition, pars)
+		if e != nil && e.Error() != utils.ErrNoRow() {
+			err = errors.New("GetTopOneMonthArtReadNumIndustryAll, Err: " + e.Error() + "sourceId:" + strconv.Itoa(sourceId))
+			return
+		}
+		var industrialItems []*cygx.CygxResourceDataIndustrialGroupManagement
+		for _, v := range industrialList {
+			var industrialItem = new(cygx.CygxResourceDataIndustrialGroupManagement)
+			industrialItem.SourceId = sourceId
+			industrialItem.Source = source
+			industrialItem.IndustrialManagementId = v.IndustrialManagementId
+			industrialItem.ResourceDataId = resourceDataId
+			industrialItem.CreateTime = time.Now()
+			industrialItems = append(industrialItems, industrialItem)
+		}
+		//return
+		var subjectItems []*cygx.CygxResourceDataIndustrialGroupSubject
+		//晨会精华如果关联的标的就做查询
+		if detail.IndustrialSubjectIds != "" {
+			condition = ""
+			pars = make([]interface{}, 0)
+			condition = " AND industrial_subject_id  IN (" + detail.IndustrialSubjectIds + ") "
+			//建立首页资源表,与标的 的关系
+			subjectList, e := cygx.GetCygxIndustrialSubjectListCondition(condition, pars)
+			if e != nil && e.Error() != utils.ErrNoRow() {
+				err = errors.New("GetCygxIndustrialSubjectListCondition, Err: " + e.Error() + "sourceId:" + strconv.Itoa(sourceId))
+				return
+			}
+			for _, v := range subjectList {
+				var subjectItem = new(cygx.CygxResourceDataIndustrialGroupSubject)
+				subjectItem.SourceId = sourceId
+				subjectItem.Source = source
+				subjectItem.IndustrialSubjectId = v.IndustrialSubjectId
+				subjectItem.ResourceDataId = resourceDataId
+				subjectItem.CreateTime = time.Now()
+				subjectItems = append(subjectItems, subjectItem)
+			}
+		}
+
+		//插入关联信息
+		e = cygx.AddCygxResourceDataGroup(sourceId, source, industrialItems, subjectItems)
+		if e != nil {
+			err = errors.New("AddCygxResourceDataGroup, Err: " + e.Error())
+			return
+		}
+
+	}
+	return
+}
+
+// 更新报告精选(重点公司)
+func UpdateReportSelectionResourceData(sourceId int) {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println("err:", err)
+			go alarm_msg.SendAlarmMsg("更新报告精选(重点公司) 失败,UpdateReportSelectionResourceData Err:"+err.Error()+"资源ID"+strconv.Itoa(sourceId), 3)
+		}
+	}()
+	var source = utils.CYGX_OBJ_REPORTSELECTION
+	var condition string
+	var pars []interface{}
+	condition = ` AND visible_range = 1  AND article_id = ? `
+	pars = append(pars, sourceId)
+	total, e := cygx.GetCygxReportSelection(condition, pars)
+	if e != nil {
+		err = errors.New("GetCygxReportSelection, Err: " + e.Error())
+		return
+	}
+	//如果取消发布了就做删除处理
+	if total == 0 {
+		e = cygx.DeleteResourceData(sourceId, source)
+		if e != nil {
+			err = errors.New("DeleteResourceData, Err: " + e.Error())
+			return
+		}
+	} else {
+		//判断是否存在,如果不存在就新增,存在就更新
+		totalData, e := cygx.GetCygxReportSelectionBySourceAndId(sourceId, source)
+		if e != nil {
+			err = errors.New("GetCygxReportSelectionBySourceAndId, Err: " + e.Error())
+			return
+		}
+		detail, e := cygx.GetCygxReportSelectionInfoById(sourceId)
+		if e != nil {
+			err = errors.New("GetCygxReportSelectionInfoById, Err: " + e.Error())
+			return
+		}
+		publishDate := detail.PublishDate
+		item := new(cygx.CygxResourceData)
+		item.SourceId = sourceId
+		item.Source = source
+		item.SearchTag = "重点公司"
+		item.PublishDate = publishDate
+		item.CreateTime = time.Now()
+		if totalData == 0 {
+			_, e := cygx.AddCygxResourceData(item)
+			if e != nil {
+				err = errors.New("AddCygxResourceData, Err: " + e.Error())
+				return
+			}
+		} else {
+			e = cygx.UpdateResourceDataByItem(item)
+			if e != nil {
+				err = errors.New("UpdateResourceDataByItem, Err: " + e.Error())
+				return
+			}
+		}
+	}
+	return
+}
+
+// 更新本周研究汇总
+func UpdateResearchSummaryResourceData(sourceId int) {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println("err:", err)
+			go alarm_msg.SendAlarmMsg("更新本周研究汇总 失败,UpdateResearchSummaryResourceData Err:"+err.Error()+"资源ID"+strconv.Itoa(sourceId), 3)
+		}
+	}()
+	var source = utils.CYGX_OBJ_RESEARCHSUMMARY
+	var condition string
+	var pars []interface{}
+	condition = ` AND visible_range = 1  AND article_id = ? `
+	pars = append(pars, sourceId)
+	total, e := cygx.GetCygxResearchSummary(condition, pars)
+	if e != nil {
+		err = errors.New("GetCygxResearchSummary, Err: " + e.Error())
+		return
+	}
+	//如果取消发布了就做删除处理
+	if total == 0 {
+		e = cygx.DeleteResourceData(sourceId, source)
+		if e != nil {
+			err = errors.New("DeleteResourceData, Err: " + e.Error())
+			return
+		}
+	} else {
+		//判断是否存在,如果不存在就新增,存在就更新
+		totalData, e := cygx.GetCygxReportSelectionBySourceAndId(sourceId, source)
+		if e != nil {
+			err = errors.New("GetCygxReportSelectionBySourceAndId, Err: " + e.Error())
+			return
+		}
+		publishDate := time.Now().Format(utils.FormatDateTime)
+		item := new(cygx.CygxResourceData)
+		item.SourceId = sourceId
+		item.Source = source
+		item.SearchTag = "本周研究汇总"
+		item.PublishDate = publishDate
+		item.CreateTime = time.Now()
+		if totalData == 0 {
+			_, e := cygx.AddCygxResourceData(item)
+			if e != nil {
+				err = errors.New("AddCygxResourceData, Err: " + e.Error())
+				return
+			}
+		} else {
+			e = cygx.UpdateResourceDataByItem(item)
+			if e != nil {
+				err = errors.New("UpdateResourceDataByItem, Err: " + e.Error())
+				return
+			}
+		}
+	}
+	return
+}
+
+// 更新上周纪要汇总
+func UpdateMinutesSummaryResourceData(sourceId int) {
+	var err error
+	defer func() {
+		if err != nil {
+			fmt.Println("err:", err)
+			go alarm_msg.SendAlarmMsg("更新上周纪要汇总 失败,UpdateMinutesSummaryResourceData Err:"+err.Error()+"资源ID"+strconv.Itoa(sourceId), 3)
+		}
+	}()
+	var source = utils.CYGX_OBJ_MINUTESSUMMARY
+	var condition string
+	var pars []interface{}
+	condition = ` AND visible_range = 1  AND article_id = ? `
+	pars = append(pars, sourceId)
+	total, e := cygx.GetCygxMinutesSummary(condition, pars)
+	if e != nil {
+		err = errors.New("GetCygxResearchSummary, Err: " + e.Error())
+		return
+	}
+	//如果取消发布了就做删除处理
+	if total == 0 {
+		e = cygx.DeleteResourceData(sourceId, source)
+		if e != nil {
+			err = errors.New("DeleteResourceData, Err: " + e.Error())
+			return
+		}
+	} else {
+		//判断是否存在,如果不存在就新增,存在就更新
+		totalData, e := cygx.GetCygxReportSelectionBySourceAndId(sourceId, source)
+		if e != nil {
+			err = errors.New("GetCygxReportSelectionBySourceAndId, Err: " + e.Error())
+			return
+		}
+		publishDate := time.Now().Format(utils.FormatDateTime)
+		item := new(cygx.CygxResourceData)
+		item.SourceId = sourceId
+		item.Source = source
+		item.SearchTag = "上周纪要汇总"
+		item.PublishDate = publishDate
+		item.CreateTime = time.Now()
+		if totalData == 0 {
+			_, e := cygx.AddCygxResourceData(item)
+			if e != nil {
+				err = errors.New("AddCygxResourceData, Err: " + e.Error())
+				return
+			}
+		} else {
+			e = cygx.UpdateResourceDataByItem(item)
+			if e != nil {
+				err = errors.New("UpdateResourceDataByItem, Err: " + e.Error())
+				return
+			}
+		}
+	}
+	return
+}
+
+func init1123232() {
+	//var condition string
+	//var pars []interface{}
+	//
+	//{
+	//	//活动
+	//	list, err := cygx.GetCygxResourceDataListBysource(utils.CYGX_OBJ_ACTIVITY)
+	//	if err != nil {
+	//		fmt.Println(err)
+	//	}
+	//	for _, v := range list {
+	//		fmt.Println(v.Source)
+	//		//UpdateActivityResourceData(v.SourceId)
+	//	}
+	//}
+
+	//{
+	//	//专项调研活动
+	//	list, err := cygx.GetCygxResourceDataListBysource(utils.CYGX_OBJ_ACTIVITYSPECIAL)
+	//	if err != nil {
+	//		fmt.Println(err)
+	//	}
+	//	for _, v := range list {
+	//		fmt.Println(v.Source)
+	//		UpdateActivitySpecialResourceData(v.SourceId)
+	//	}
+	//}
+
+	//{
+	//	//文章
+	//	list, err := cygx.GetCygxResourceDataListBysource(utils.CYGX_OBJ_ARTICLE)
+	//	if err != nil {
+	//		fmt.Println(err)
+	//	}
+	//	for _, v := range list {
+	//		fmt.Println(v.Source)
+	//		UpdateArticleResourceData(v.SourceId)
+	//	}
+	//}
+
+	//{
+	//	//产品内测
+	//	list, err := cygx.GetCygxResourceDataListBysource(utils.CYGX_OBJ_PRODUCTINTERIOR)
+	//	if err != nil {
+	//		fmt.Println(err)
+	//	}
+	//	for _, v := range list {
+	//		fmt.Println(v.Source)
+	//		//UpdateProductInteriorResourceData(v.SourceId)
+	//	}
+	//}
+	//
+
+	//{
+	//	//晨会精华
+	//	list, err := cygx.GetCygxResourceDataListBysource(utils.CYGX_OBJ_MEETINGREVIEWCHAPT)
+	//	if err != nil {
+	//		fmt.Println(err)
+	//	}
+	//	for _, v := range list {
+	//		fmt.Println(v.Source)
+	//		UpdateMeetingreviewchaptResourceData(v.SourceId)
+	//	}
+	//}
+
+	//
+	//{
+	//	//重点公司
+	//	list, err := cygx.GetCygxResourceDataListBysource(utils.CYGX_OBJ_REPORTSELECTION)
+	//	if err != nil {
+	//		fmt.Println(err)
+	//	}
+	//	for _, v := range list {
+	//		fmt.Println(v.Source)
+	//		//UpdateReportSelectionResourceData(v.SourceId)
+	//	}
+	//}
+	//
+	//{
+	//	//更新本周研究汇总
+	//	list, err := cygx.GetCygxResourceDataListBysource(utils.CYGX_OBJ_RESEARCHSUMMARY)
+	//	if err != nil {
+	//		fmt.Println(err)
+	//	}
+	//	for _, v := range list {
+	//		fmt.Println(v.Source)
+	//		//UpdateResearchSummaryResourceData(v.SourceId)
+	//	}
+	//}
+	//
+	{
+		//更新本周研究汇总
+		list, err := cygx.GetCygxResourceDataListBysource(utils.CYGX_OBJ_MINUTESSUMMARY)
+		if err != nil {
+			fmt.Println(err)
+		}
+		for _, v := range list {
+			fmt.Println(v.Source)
+			//UpdateMinutesSummaryResourceData(v.SourceId)
+		}
+	}
+}

+ 97 - 20
services/sms.go

@@ -10,16 +10,86 @@ import (
 	"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
+	}
+
+	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
 	}
-	result, err := sendSms(mobile, tplId, vcode)
+	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
@@ -28,31 +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) {
+// 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 {
@@ -66,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"
+	"fmt"
+	"hongze/hz_crm_api/models"
+	"hongze/hz_crm_api/models/company"
+	"hongze/hz_crm_api/models/system"
+	"hongze/hz_crm_api/utils"
+	"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
+}

+ 18 - 1
utils/constants.go

@@ -50,7 +50,7 @@ var (
 	JhGnTplId  = "65692"                            //聚合国内模板编码
 	JhGjTplId  = "10054"                            //聚合国内模板编码
 	JhGnAppKey = "4c8504c49dd335e99cfd7b6a3a9e2415" //聚合国内AppKey
-	JhGjAppKey = "3326ad2c1047a4cd92ace153e6044ca3"
+	JhGjAppKey = "3326ad2c1047a4cd92ace153e6044ca3" //聚合国内AppKey
 )
 
 // 科大讯飞--语音合成
@@ -285,6 +285,9 @@ const (
 	CACHE_IMPORT_MANUAL_DATA          = "import:manual:data"                  //手工数据导入后刷新
 	CACHE_ACCESS_TOKEN_LOGIN          = "pc_admin:login:"                     //管理后台登录
 	CACHE_ACCESS_TOKEN_LOGIN_NO_TRUST = "pc_admin:login:no_trust:"            //管理后台登录(不可信登录态)
+	CACHE_ABNORMAL_LOGIN              = "pc_admin:login:abnormal:"            //管理后台登录-异常登录
+	CACHE_LOGIN_ERR_PASS              = "pc_admin:login:errPass:"             //管理后台登录-输入错误密码次数
+	CACHE_FIND_PASS_VERIFY            = "pc_admin:findPass:verify:"           //找回密码校验成功标记
 	CACHE_KEY_COMPANY_MATCH_PRE       = "admin:company:match:"                //客户名单匹配
 	CACHE_KEY_MYSTEEL_REFRESH         = "mysteel_chemical:refresh"            //钢联化工刷新
 	CACHE_KEY_DAYNEW_REFRESH          = "admin:day_new:refresh"               //每日资讯拉取企业微信聊天记录
@@ -412,6 +415,10 @@ const (
 	CYGX_OBJ_ACTIVITYSPECIAL    string = "activityspecial"    // 对象类型:专项调研活动
 	CYGX_OBJ_MEETINGREVIEWCHAPT string = "meetingreviewchapt" // 对象类型:晨会精华
 	CYGX_OBJ_ROADSHOW           string = "roadshow"           // 对象类型:路演
+	CYGX_OBJ_REPORTSELECTION    string = "reportselection"    // 对象类型:报告精选(重点公司)
+	CYGX_OBJ_PRODUCTINTERIOR    string = "productinterior"    // 对象类型:产品内测
+	CYGX_OBJ_RESEARCHSUMMARY    string = "researchsummary"    // 对象类型:本周研究汇总
+	CYGX_OBJ_MINUTESSUMMARY     string = "minutessummary"     // 对象类型:本周研究汇总
 )
 
 const (
@@ -515,3 +522,13 @@ const BusinessCodeSalt = "dr7WY0OZgGR7upw1"
 const (
 	ApproveUserId = 2 //施琪-出差审批人
 )
+
+// 验证码
+const (
+	CaptchaCachePrefix     = "captcha:lock:crm_" // 验证码缓存Key
+	VerifyCodeExpireMinute = 15                  // 短信/邮箱验证码过期时间-分钟
+	SmsLoginTplId          = "65692"             // 【弘则研究】您的验证码是XXX,如非本人操作,请忽略本短信
+	SmsNewLoginTplId       = "254663"            // 【弘则研究】您的验证码是XXX,有效期15分钟
+)
+
+const CrmEtaAuthorization = "NIi1RbEmH0C2rksXtPGDPBBgRgTZY87Q"