Browse Source

Merge branch 'CRM_12.9'

ziwen 1 year ago
parent
commit
d761b66160
7 changed files with 794 additions and 55 deletions
  1. 21 23
      controller/auth.go
  2. 7 0
      go.mod
  3. 326 0
      models/english_report.go
  4. 235 0
      services/aliyun_email.go
  5. 13 32
      services/email.go
  6. 34 0
      services/english_report/english_report.go
  7. 158 0
      services/tencent_yun.go

+ 21 - 23
controller/auth.go

@@ -1,6 +1,7 @@
 package controller
 
 import (
+	"context"
 	"fmt"
 	"github.com/gin-gonic/gin"
 	"github.com/go-playground/validator/v10"
@@ -117,21 +118,16 @@ func (a *AuthController) Register(c *gin.Context) {
 		return
 	}
 
-	item, err := msg_code.GetMsgCode(req.Email, req.SmsCode)
-	if err != nil {
-		if err != nil {
-			resp.Fail("Verification code error."+err.Error(), c)
-			return
-		}
-	}
-	if item == nil {
-		resp.Fail("验证码错误,请重新输入", c)
+	code := global.Redis.Get(context.TODO(), req.Email).Val()
+	fmt.Println("code:", code)
+	if code == "" || code != req.SmsCode {
+		resp.Fail("Verification code error.", c)
 		return
 	}
 
 	emailItem, err := english_report_email.CheckUser(req.Email)
 	if err != nil && err != utils.ErrNoRow {
-		resp.FailData("检测用户重复错误, Err:",err.Error(), c)
+		resp.FailData("检测用户重复错误, Err:", err.Error(), c)
 		return
 	}
 	userId := 0
@@ -371,15 +367,12 @@ func (a *AuthController) BindMobile(c *gin.Context) {
 		return
 	}
 
-	item, err := msg_code.GetMsgCode(req.Mobile, req.SmsCode)
+	_, err = msg_code.GetMsgCode(req.Mobile, req.SmsCode)
 	if err != nil {
 		resp.Fail("Verification code error."+err.Error(), c)
 		return
 	}
-	if item == nil {
-		resp.Fail("验证码错误,请重新输入", c)
-		return
-	}
+
 
 	user := english_report_email.Email{
 		Id:          userinfo.Id,
@@ -449,16 +442,21 @@ func (a *AuthController) ForgetPwd(c *gin.Context) {
 		}
 	}
 
-	item, err := msg_code.GetMsgCode(req.Account, req.SmsCode)
-	if err != nil {
-		resp.Fail("Verification code error."+err.Error(), c)
-		return
+	if req.Type == 2 {
+		_, err = msg_code.GetMsgCode(req.Account, req.SmsCode)
+		if err != nil {
+			resp.Fail("Verification code error."+err.Error(), c)
+			return
 
+		}
+	} else {
+		code := global.Redis.Get(context.TODO(), req.Account).Val()
+		if code == "" || code != req.SmsCode {
+			resp.Fail("Verification code error.", c)
+			return
+		}
 	}
-	if item == nil {
-		resp.Fail("验证码错误,请重新输入", c)
-		return
-	}
+
 
 	password := utils.MD5(req.Password + utils.KEY)
 	emailitem := english_report_email.Email{

+ 7 - 0
go.mod

@@ -3,6 +3,11 @@ module hongze/hongze_yb_en_api
 go 1.15
 
 require (
+	github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.4
+	github.com/alibabacloud-go/dm-20151123/v2 v2.0.1
+	github.com/alibabacloud-go/tea v1.1.20
+	github.com/alibabacloud-go/tea-utils/v2 v2.0.1
+	github.com/beego/beego/v2 v2.0.7
 	github.com/fsnotify/fsnotify v1.6.0
 	github.com/gin-gonic/gin v1.8.1
 	github.com/go-playground/locales v0.14.0
@@ -18,6 +23,8 @@ require (
 	github.com/rdlucklib/rdluck_tools v1.0.3
 	github.com/spf13/viper v1.14.0
 	github.com/swaggo/swag v1.8.8
+	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.641
+	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ses v1.0.641
 	golang.org/x/image v0.2.0
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
 	gorm.io/driver/mysql v1.4.4

+ 326 - 0
models/english_report.go

@@ -0,0 +1,326 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"time"
+)
+
+type EnglishReport struct {
+	Id                 int       `orm:"column(id)" description:"报告Id"`
+	AddType            int       `description:"新增方式:1:新增报告,2:继承报告"`
+	ClassifyIdFirst    int       `description:"一级分类id"`
+	ClassifyNameFirst  string    `description:"一级分类名称"`
+	ClassifyIdSecond   int       `description:"二级分类id"`
+	ClassifyNameSecond string    `description:"二级分类名称"`
+	Title              string    `description:"标题"`
+	Abstract           string    `description:"摘要"`
+	Author             string    `description:"作者"`
+	Frequency          string    `description:"频度"`
+	CreateTime         string    `description:"创建时间"`
+	ModifyTime         time.Time `description:"修改时间"`
+	State              int       `description:"1:未发布,2:已发布"`
+	PublishTime        time.Time `description:"发布时间"`
+	Stage              int       `description:"期数"`
+	Content            string    `description:"内容"`
+	VideoUrl           string    `description:"音频文件URL"`
+	VideoName          string    `description:"音频文件名称"`
+	VideoPlaySeconds   string    `description:"音频播放时长"`
+	VideoSize          string    `description:"音频文件大小,单位M"`
+	ContentSub         string    `description:"内容前两个章节"`
+	ReportCode         string    `description:"报告唯一编码"`
+	Pv                 int       `description:"Pv"`
+	PvEmail            int       `description:"邮箱PV"`
+	EmailState         int       `description:"群发邮件状态: 0-未发送; 1-已发送"`
+	Overview           string    `description:"英文概述部分"`
+	KeyTakeaways       string    `description:"关键点"`
+	FromReportId       int       `description:"继承的报告ID(英文策略报告ID)"`
+	AdminId            int       `description:"创建者账号"`
+	AdminRealName      string    `description:"创建者姓名"`
+}
+
+type AddEnglishReportReq struct {
+	AddType            int    `description:"新增方式:1:新增报告,2:继承报告"`
+	ClassifyIdFirst    int    `description:"一级分类id"`
+	ClassifyNameFirst  string `description:"一级分类名称"`
+	ClassifyIdSecond   int    `description:"二级分类id"`
+	ClassifyNameSecond string `description:"二级分类名称"`
+	Title              string `description:"标题"`
+	Abstract           string `description:"摘要"`
+	Author             string `description:"作者"`
+	Frequency          string `description:"频度"`
+	State              int    `description:"状态:1:未发布,2:已发布"`
+	Content            string `description:"内容"`
+	CreateTime         string `description:"创建时间"`
+	Overview           string `description:"英文概述部分"`
+}
+
+type AddEnglishReportResp struct {
+	ReportId   int64  `description:"报告id"`
+	ReportCode string `description:"报告code"`
+}
+
+type EditEnglishReportReq struct {
+	ReportId           int64  `description:"报告id"`
+	ClassifyIdFirst    int    `description:"一级分类id"`
+	ClassifyNameFirst  string `description:"一级分类名称"`
+	ClassifyIdSecond   int    `description:"二级分类id"`
+	ClassifyNameSecond string `description:"二级分类名称"`
+	Title              string `description:"标题"`
+	Abstract           string `description:"摘要"`
+	Author             string `description:"作者"`
+	Frequency          string `description:"频度"`
+	State              int    `description:"状态:1:未发布,2:已发布"`
+	Content            string `description:"内容"`
+	CreateTime         string `description:"创建时间"`
+	Overview           string `description:"英文概述部分"`
+}
+
+type EditEnglishReportFromPolicyReq struct {
+	ReportId           int64  `description:"报告id"`
+	Title              string `description:"标题"`
+	Abstract           string `description:"摘要"`
+	Author             string `description:"作者"`
+	Frequency          string `description:"频度"`
+	CreateTime         string `description:"创建时间"`
+	//Overview           string `description:"英文概述部分"`
+}
+type EditEnglishReportResp struct {
+	ReportId   int64  `description:"报告id"`
+	ReportCode string `description:"报告code"`
+}
+
+type ElasticEnglishReportDetail struct {
+	Id                 string `description:"报告id或者线上路演Id"`
+	ReportId           int    `description:"报告id"`
+	VideoId            int    `description:"线上路演Id"`
+	ClassifyIdFirst    int    `description:"一级分类id"`
+	ClassifyNameFirst  string `description:"一级分类名称"`
+	ClassifyIdSecond   int    `description:"二级分类id"`
+	ClassifyNameSecond string `description:"二级分类名称"`
+	StageStr           string `description:"报告期数"`
+	Title              string `description:"标题"`
+	Abstract           string `description:"摘要"`
+	Author             string `description:"作者"`
+	Frequency          string `description:"频度"`
+	PublishState       int    `description:"状态:1:未发布,2:已发布"`
+	BodyContent        string `description:"内容"`
+	ContentSub         string `description:"前两段内容"`
+	CreateTime         string `description:"创建时间"`
+	PublishTime        string `description:"发布时间"`
+	ReportCode         string `description:"报告唯一编码"`
+	Overview           string `description:"英文概述部分"`
+}
+
+
+type EnglishReportDetail struct {
+	Id                 int    `orm:"column(id)" description:"报告Id"`
+	AddType            int    `description:"新增方式:1:新增报告,2:继承报告"`
+	ClassifyIdFirst    int    `description:"一级分类id"`
+	ClassifyNameFirst  string `description:"一级分类名称"`
+	ClassifyIdSecond   int    `description:"二级分类id"`
+	ClassifyNameSecond string `description:"二级分类名称"`
+	Title              string `description:"标题"`
+	Abstract           string `description:"摘要"`
+	Author             string `description:"作者"`
+	Frequency          string `description:"频度"`
+	CreateTime         string `description:"创建时间"`
+	ModifyTime         string `description:"修改时间"`
+	State              int    `description:"1:未发布,2:已发布"`
+	PublishTime        string `description:"发布时间"`
+	Stage              int    `description:"期数"`
+	MsgIsSend          int    `description:"消息是否已发送,0:否,1:是"`
+	ReportCode         string `description:"报告唯一编码"`
+	Content            string `description:"内容"`
+	VideoUrl           string `description:"音频文件URL"`
+	VideoName          string `description:"音频文件名称"`
+	VideoPlaySeconds   string `description:"音频播放时长"`
+	ContentSub         string `description:"内容前两个章节"`
+	Pv                 int    `description:"Pv"`
+	Overview           string `description:"英文概述部分"`
+	FromReportId       int    `description:"继承的报告ID(英文策略报告ID)"`
+	KeyTakeaways       string `description:"关键点"`
+}
+
+type EnglishReportList struct {
+	Id                 int       `description:"报告Id"`
+	AddType            int       `description:"新增方式:1:新增报告,2:继承报告"`
+	ClassifyIdFirst    int       `description:"一级分类id"`
+	ClassifyNameFirst  string    `description:"一级分类名称"`
+	ClassifyIdSecond   int       `description:"二级分类id"`
+	ClassifyNameSecond string    `description:"二级分类名称"`
+	Title              string    `description:"标题"`
+	Abstract           string    `description:"摘要"`
+	Author             string    `description:"作者"`
+	Frequency          string    `description:"频度"`
+	CreateTime         string    `description:"创建时间"`
+	ModifyTime         time.Time `description:"修改时间"`
+	State              int       `description:"1:未发布,2:已发布"`
+	PublishTime        string    `description:"发布时间"`
+	Stage              int       `description:"期数"`
+	Content            string    `description:"内容"`
+	VideoUrl           string    `description:"音频文件URL"`
+	VideoName          string    `description:"音频文件名称"`
+	VideoPlaySeconds   string    `description:"音频播放时长"`
+	ContentSub         string    `description:"内容前两个章节"`
+	ReportCode         string    `description:"报告唯一编码"`
+	Pv                 int       `description:"Pv"`
+	ShareUrl           string    `description:"分享url"`
+	PvEmail            int       `description:"邮箱PV"`
+	EmailState         int       `description:"群发邮件状态: 0-未发送; 1-已发送"`
+	EmailAuth          bool      `description:"是否有权限群发邮件"`
+	EmailHasFail       bool      `description:"是否存在邮件发送失败的记录"`
+	CanEdit            bool      `description:"是否可编辑"`
+	Editor             string    `description:"编辑人"`
+	FromReportId       int       `description:"继承的报告ID(英文策略报告ID)"`
+	AdminId            int       `description:"创建者账号"`
+	AdminRealName      string    `description:"创建者姓名"`
+}
+
+type EnglishReportListResp struct {
+	List   []*EnglishReportList
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+//删除报告
+func DeleteEnglishReport(reportIds int) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := ` DELETE FROM english_report WHERE id =? `
+	_, err = o.Raw(sql, reportIds).Exec()
+	return
+}
+
+
+type EnglishClassifyList struct {
+	Id            int       `orm:"column(id);pk"`
+	ClassifyName  string    `description:"分类名称"`
+	Sort          int       `description:"排序"`
+	ParentId      int       `description:"父级分类id"`
+	CreateTime    time.Time `description:"创建时间"`
+	ModifyTime    time.Time `description:"修改时间"`
+	ClassifyLabel string    `description:"分类标签"`
+	ShowType      int       `description:"展示类型:1-列表 2-专栏"`
+	IsShow        int       `description:"是否在小程序显示:1-显示 0-隐藏"`
+	ClassifyType  int       `description:"分类类型:0英文报告,1英文线上路演"`
+	Child         []*EnglishClassify
+}
+
+type EnglishClassifyListResp struct {
+	List   []*EnglishClassifyList
+	Paging *paging.PagingItem `description:"分页数据"`
+}
+
+
+// Update 更新
+func (item *EnglishReport) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(item, cols...)
+	return
+}
+
+// ModifyEnglishReportAuthor 更改英文报告作者
+func ModifyEnglishReportAuthor(condition string, pars []interface{}, authorName string) (count int, err error) {
+	//产品权限
+	oRddp := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE english_report set author = ? WHERE 1=1 `
+	if condition != "" {
+		sql += condition
+	}
+	err = oRddp.Raw(sql, authorName, pars).QueryRow(&count)
+	return
+}
+
+type EnglishClassify struct {
+	Id            int       `orm:"column(id);pk"`
+	ClassifyName  string    `description:"分类名称"`
+	Sort          int       `description:"排序"`
+	ParentId      int       `description:"父级分类id"`
+	CreateTime    time.Time `description:"创建时间"`
+	ModifyTime    time.Time `description:"修改时间"`
+	ClassifyLabel string    `description:"分类标签"`
+	ShowType      int       `description:"展示类型:1-列表 2-专栏"`
+	IsShow        int       `description:"是否在小程序显示:1-显示 0-隐藏"`
+	ClassifyType  int       `description:"分类类型:0英文报告,1英文线上路演"`
+}
+
+func AddEnglishClassify(item *EnglishClassify) (lastId int64, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	lastId, err = o.Insert(item)
+	return
+}
+
+func ModifyEnglishClassify(item *EnglishClassify) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `UPDATE english_classify
+	SET
+	classify_name = ?,
+	sort = ?,
+	parent_id = ?,
+	modify_time = ? 
+	WHERE id = ? `
+	_, err = o.Raw(sql, item.ClassifyName, item.Sort, item.ParentId, item.ModifyTime, item.Id).Exec()
+	return
+}
+
+// UpdateClassify 更新分类
+func (classifyInfo *EnglishClassify) UpdateEnglishClassify(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(classifyInfo, cols...)
+
+	return
+}
+
+// UpdateReport 更新英文报告
+func (reportInfo *EnglishReport) UpdateReport(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(reportInfo, cols...)
+
+	return
+}
+
+// MarkEditEnReport 标记编辑英文研报的请求数据
+type MarkEditEnReport struct {
+	ReportId int `description:"研报id"`
+	Status   int `description:"标记状态,1:编辑中,2:编辑完成"`
+}
+
+type EnglishClassifyNameParentName struct {
+	Id                 int       `description:"分类ID"`
+	ClassifyName       string    `description:"分类名称"`
+	Sort               int       `description:"排序"`
+	ParentId           int       `description:"父级分类id"`
+	CreateTime         time.Time `description:"创建时间"`
+	ModifyTime         time.Time `description:"修改时间"`
+	ClassifyLabel      string    `description:"分类标签"`
+	ShowType           int       `description:"展示类型:1-列表 2-专栏"`
+	IsShow             int       `description:"是否在小程序显示:1-显示 0-隐藏"`
+	ParentClassifyName string    `description:"父级分类名称"`
+}
+
+type RSClassifyList []*EnglishClassifyList
+
+func (m RSClassifyList) Len() int {
+	return len(m)
+}
+
+func (m RSClassifyList) Less(i, j int) bool {
+	return m[i].Sort < m[j].Sort
+}
+
+func (m RSClassifyList) Swap(i, j int) {
+	m[i], m[j] = m[j], m[i]
+}
+
+type RSChildClassifyList []*EnglishClassify
+
+func (m RSChildClassifyList) Len() int {
+	return len(m)
+}
+
+func (m RSChildClassifyList) Less(i, j int) bool {
+	return m[i].Sort < m[j].Sort
+}
+
+func (m RSChildClassifyList) Swap(i, j int) {
+	m[i], m[j] = m[j], m[i]
+}

+ 235 - 0
services/aliyun_email.go

@@ -0,0 +1,235 @@
+package services
+
+import (
+	"context"
+	"encoding/json"
+	openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
+	dm "github.com/alibabacloud-go/dm-20151123/v2/client"
+	util "github.com/alibabacloud-go/tea-utils/v2/service"
+	"github.com/alibabacloud-go/tea/tea"
+	"hongze/hongze_yb_en_api/global"
+	"hongze/hongze_yb_en_api/utils"
+	"time"
+)
+
+var (
+	AliyunEmailAccountName       = "ficcemail@hzmail.hzinsights.com"
+	AliyunEmailAccessKeyId       = "LTAIFMZYQhS2BTvW"
+	AliyunEmailAccessKeySecret   = "12kk1ptCHoGWedhBnKRVW5hRJzq9Fq"
+	AliyunEmailReplyAddress      = "ficcemail@hzinsights.com"
+	AliyunEmailReplyAddressAlias = "弘则研究"
+)
+
+// AliyunEmail 阿里云邮件
+type AliyunEmail struct {
+	Client *dm.Client
+}
+
+// NewClient
+func (em *AliyunEmail) NewClient() (err error) {
+	config := &openapi.Config{
+		AccessKeyId:     tea.String(AliyunEmailAccessKeyId),
+		AccessKeySecret: tea.String(AliyunEmailAccessKeySecret),
+	}
+	// 访问的域名
+	config.Endpoint = tea.String("dm.aliyuncs.com")
+	_result, _err := dm.NewClient(config)
+	em.Client = _result
+	err = _err
+	return
+}
+
+// AliyunEmailResult 邮件推送响应体
+type AliyunEmailResult struct {
+	Code        string `description:"错误码"`
+	StatusCode  int    `description:"状态码"`
+	Message     string `description:"响应信息"`
+	Data        string `description:"请求体"`
+	Description string `description:"描述"`
+	EnvId       string `description:"请求成功-事件ID"`
+	RequestId   string `description:"请求成功-请求ID"`
+}
+
+// AliyunEmailResultData 邮件推送响应内容
+type AliyunEmailResultData struct {
+	Code       string `description:"错误码"`
+	HostId     string `description:""`
+	Message    string `description:"响应信息"`
+	Recommend  string `description:""`
+	RequestId  string `description:"请求ID"`
+	StatusCode int    `description:"状态码"`
+}
+//
+//// SendEmail 邮件推送
+//func (em *AliyunEmail) SendEmail(item *english_report.EnglishReportSendEmailRequest) (ok bool, result string, err error) {
+//	if em.Client == nil {
+//		if e := em.NewClient(); e != nil {
+//			err = e
+//			return
+//		}
+//	}
+//	singleSendMailRequest := &dm.SingleSendMailRequest{}
+//	singleSendMailRequest.SetAccountName(AliyunEmailAccountName)
+//	singleSendMailRequest.SetAddressType(1)
+//	singleSendMailRequest.SetReplyToAddress(false)
+//	singleSendMailRequest.SetSubject(item.Subject)
+//	singleSendMailRequest.SetToAddress(item.Email)
+//	singleSendMailRequest.SetHtmlBody(item.HtmlBody)
+//	singleSendMailRequest.SetFromAlias(item.FromAlias)
+//
+//	runtime := &util.RuntimeOptions{}
+//	tryErr := func() error {
+//		res, e := em.Client.SingleSendMailWithOptions(singleSendMailRequest, runtime)
+//		if e != nil {
+//			return e
+//		}
+//		// 请求成功
+//		if tea.Int32Value(res.StatusCode) == 200 {
+//			ok = true
+//		}
+//		resByte, e := json.Marshal(res.Body)
+//		if e != nil {
+//			return e
+//		}
+//		result = string(resByte)
+//		return nil
+//	}()
+//
+//	if tryErr != nil {
+//		var e = &tea.SDKError{}
+//		if t, ok := tryErr.(*tea.SDKError); ok {
+//			e = t
+//		} else {
+//			e.Message = tea.String(tryErr.Error())
+//		}
+//		err = e
+//		errByte, _ := json.Marshal(err)
+//		result = string(errByte)
+//	}
+//	return
+//}
+//
+//// BatchSendEmail 批量推送邮件
+//func (em *AliyunEmail) BatchSendEmail(list []*english_report.EnglishReportSendEmailRequest) (results []*english_report.EnglishReportSendEmailResult, err error) {
+//	results = make([]*english_report.EnglishReportSendEmailResult, 0)
+//	if len(list) == 0 {
+//		return
+//	}
+//	if e := em.NewClient(); e != nil {
+//		err = e
+//		return
+//	}
+//
+//	// sendSingleMail接口有QPS100的限制, 保险起见每秒只请求50次接口
+//	max := 50
+//	c := 0
+//	for i := range list {
+//		if c >= max {
+//			time.Sleep(time.Second)
+//			c = 0
+//		}
+//		c += 1
+//
+//		dataByte, _ := json.Marshal(list[i])
+//		ok, result, _ := em.SendEmail(list[i])
+//		results = append(results, &english_report.EnglishReportSendEmailResult{
+//			ReportId:   list[i].ReportId,
+//			EmailId:    list[i].EmailId,
+//			Email:      list[i].Email,
+//			Ok:         ok,
+//			SendData:   string(dataByte),
+//			ResultData: result,
+//			Source:     1,
+//		})
+//	}
+//	return
+//}
+
+// sendEmailVerificationCode 邮件验证码推送
+func (em *AliyunEmail) sendEmailVerificationCode(email, content string) (ok bool, result string, err error) {
+	if em.Client == nil {
+		if e := em.NewClient(); e != nil {
+			err = e
+			return
+		}
+	}
+
+	singleSendMailRequest := &dm.SingleSendMailRequest{}
+	singleSendMailRequest.SetAccountName(AliyunEmailAccountName)
+	singleSendMailRequest.SetAddressType(1)
+	singleSendMailRequest.SetReplyToAddress(false)
+	singleSendMailRequest.SetSubject("Your Email Verification Code")
+	singleSendMailRequest.SetToAddress(email)
+	singleSendMailRequest.SetHtmlBody(content)
+	singleSendMailRequest.SetFromAlias("Horizon FICC")
+
+	runtime := &util.RuntimeOptions{}
+	tryErr := func() error {
+		res, e := em.Client.SingleSendMailWithOptions(singleSendMailRequest, runtime)
+		if e != nil {
+			return e
+		}
+		// 请求成功
+		if tea.Int32Value(res.StatusCode) == 200 {
+			ok = true
+		}
+		resByte, e := json.Marshal(res.Body)
+		if e != nil {
+			return e
+		}
+		result = string(resByte)
+		return nil
+	}()
+
+	if tryErr != nil {
+		var e = &tea.SDKError{}
+		if t, ok := tryErr.(*tea.SDKError); ok {
+			e = t
+		} else {
+			e.Message = tea.String(tryErr.Error())
+		}
+		err = e
+		errByte, _ := json.Marshal(err)
+		result = string(errByte)
+	}
+	return
+}
+
+// SendEmailVerificationCode 邮件验证码
+func (em *AliyunEmail) SendEmailCode(name, email string) (results []*EnglishReportSendEmailCodeResult, err error) {
+	results = make([]*EnglishReportSendEmailCodeResult, 0)
+
+	if e := em.NewClient(); e != nil {
+		err = e
+		return
+	}
+
+	msgCode := utils.GetRandDigit(4)
+	content := "Hi " + name + ":</br>Please enter this verification code on the registration page so we can make sure it's you:</br><p>" + msgCode + "</p>This code will expire in 15 minutes.</br>" +
+		"If you didn't initiate this request, or need help with your registration, please let us know at: stephanie@hzinsights.com </br>Horizon Research Team"
+
+
+	dataByte, _ := json.Marshal(content)
+	ok, result, _ := em.sendEmailVerificationCode(email, content)
+	results = append(results, &EnglishReportSendEmailCodeResult{
+		Email:      email,
+		Ok:         ok,
+		SendData:   string(dataByte),
+		ResultData: result,
+		Source:     1,
+	})
+
+	_ = global.Redis.SetEX(context.TODO(), email, msgCode, 15*time.Minute)
+
+	return
+}
+
+
+// EnglishReportSendEmailCodeResult 英文研报验证码-推送邮件响应体
+type EnglishReportSendEmailCodeResult struct {
+	Email      string `description:"邮箱地址"`
+	Ok         bool   `description:"是否推送成功"`
+	SendData   string `description:"请求数据-JSON"`
+	ResultData string `description:"推送结果-JSON"`
+	Source     int    `description:"服务来源:1-阿里云;2-腾讯云"`
+}

+ 13 - 32
services/email.go

@@ -1,44 +1,25 @@
 package services
 
 import (
-	"errors"
 	"gopkg.in/gomail.v2"
-	"hongze/hongze_yb_en_api/models/msg_code"
-	"hongze/hongze_yb_en_api/utils"
+	"hongze/hongze_yb_en_api/services/alarm_msg"
 	"strings"
-	"time"
 )
 
-
 // SendEmailCode 发送邮件
-func SendEmailCode(name,email string) (err error, errMsg string) {
-	if email == "" {
-		err = errors.New("请输入邮箱地址")
-		return
-	}
-	if !utils.ValidateEmailFormatat(email) {
-		err = errors.New("邮箱格式错误,请重新输入")
-		return
-	}
-	msgCode := utils.GetRandDigit(4)
-	content := "Hi "+ name +":</br>Please enter this verification code on the registration page so we can make sure it's you:</br><p>" + msgCode + "</p>This code will expire in 15 minutes.</br>" +
-		"If you didn't initiate this request, or need help with your registration, please let us know at: stephanie@hzinsights.com </br>Horizon Research Team"
-	title := "Your Email Verification Code"
-	//发送邮件
-	result, err := SendEmailByHz(title, content, email)
-
-	if result {
-		item := &msg_code.MsgCode{
-			Mobile:          email,
-			Code:            msgCode,
-			ExpiredIn:       time.Now().Add(15 * time.Minute).Unix(),
-			Enabled:         1,
-			CreatedTime:     time.Time{},
-			LastUpdatedTime: time.Time{},
+func SendEmailCode(name, email string) (err error, errMsg string) {
+	defer func() {
+		if err != nil {
+			go alarm_msg.SendAlarmMsg("阿里云群发英文研报邮件失败, Err: "+err.Error(), 3)
 		}
-		err = item.Create()
-	} else {
-		err = errors.New("发送失败")
+	}()
+
+	// 请求阿里云接口批量推送
+	aliEmail := new(AliyunEmail)
+	_, e := aliEmail.SendEmailCode(name, email)
+	if e != nil {
+		err = e
+		return
 	}
 
 	return

+ 34 - 0
services/english_report/english_report.go

@@ -0,0 +1,34 @@
+package english_report
+
+// IEnglishEmailSend 英文研报-邮件推送接口
+type IEnglishEmailSend interface {
+	NewClient() (err error)
+	SendEmail(item *EnglishReportSendEmailRequest) (ok bool, result string, err error)
+	BatchSendEmail(list []*EnglishReportSendEmailRequest) (results []*EnglishReportSendEmailResult, err error)
+}
+
+// EnglishReportSendEmailRequest 英文研报-推送邮件请求体
+type EnglishReportSendEmailRequest struct {
+	ReportId        int    `description:"英文报告ID"`
+	EmailId         int    `description:"邮箱ID"`
+	Email           string `description:"邮箱地址"`
+	Subject         string `description:"邮件主题"`
+	FromAlias       string `description:"发信人昵称"`
+	ReportTitle     string `description:"报告标题"`
+	ReportAbstract  string `description:"报告摘要"`
+	ReportContent   string `description:"报告内容"`
+	ReportShareLink string `description:"报告分享链接"`
+	ReportTime      string `description:"报告时间"`
+	HtmlBody        string `description:"模板内容主体"`
+}
+
+// EnglishReportSendEmailResult 英文研报-推送邮件响应体
+type EnglishReportSendEmailResult struct {
+	ReportId   int    `description:"英文报告ID"`
+	EmailId    int    `description:"邮箱ID"`
+	Email      string `description:"邮箱地址"`
+	Ok         bool   `description:"是否推送成功"`
+	SendData   string `description:"请求数据-JSON"`
+	ResultData string `description:"推送结果-JSON"`
+	Source     int    `description:"服务来源:1-阿里云;2-腾讯云"`
+}

+ 158 - 0
services/tencent_yun.go

@@ -0,0 +1,158 @@
+package services
+
+import (
+	"encoding/json"
+	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
+	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
+	"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
+	ses "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ses/v20201002"
+	"hongze/hongze_yb_en_api/services/english_report"
+	"time"
+)
+
+const (
+	TencentSDKSecretId  = "AKIDa4WvJar361NZXkogKoHgtyxvkVDqKclq" // 腾讯云主账号SecretId
+	TencentSDKSecretKey = "61sdMzVvMejjZFj2KTQ9tgR4adYMzR1a"     // 腾讯云主账号SecretKey
+
+	TencentEmailFromEmailAddress = "email@txhzmail.hzinsights.com" // 腾讯云邮件发信地址
+	TencentEmailTemplateID       = 54181                           // 腾讯云邮件模板ID
+)
+
+// TencentEmail 腾讯云邮件
+type TencentEmail struct {
+	Client *ses.Client
+}
+
+// NewClient
+func (te *TencentEmail) NewClient() (err error) {
+	// 实例化一个认证对象,入参需要传入腾讯云账户secretId,secretKey,此处还需注意密钥对的保密
+	// 密钥可前往https://console.cloud.tencent.com/cam/capi网站进行获取
+	credential := common.NewCredential(
+		TencentSDKSecretId,
+		TencentSDKSecretKey,
+	)
+	// 实例化一个client选项,可选的,没有特殊需求可以跳过
+	cpf := profile.NewClientProfile()
+	cpf.HttpProfile.Endpoint = "ses.tencentcloudapi.com"
+	// 实例化要请求产品的client对象,clientProfile是可选的
+	client, _err := ses.NewClient(credential, "ap-hongkong", cpf)
+	te.Client = client
+	err = _err
+	return
+}
+
+// TencentEmailTemplateData 邮箱模板替换数据
+type TencentEmailTemplateData struct {
+	REPORT_TITLE      string `description:"报告标题"`
+	REPORT_ABSTRACT   string `description:"报告摘要"`
+	REPORT_CONTENT    string `description:"报告内容"`
+	REPORT_SHARE_LINK string `description:"报告分享链接"`
+	REPORT_TIME       string `description:"报告时间"`
+}
+
+// TencentEmailResult 邮件推送响应体
+type TencentEmailResult struct {
+	Code      string `description:"错误码"`
+	Message   string `description:"响应信息"`
+	RequestId string `description:"请求成功-请求ID"`
+}
+
+// SendEmail 推送模板邮件
+func (te *TencentEmail) SendEmail(req *english_report.EnglishReportSendEmailRequest) (ok bool, result string, err error) {
+	if te.Client == nil {
+		if e := te.NewClient(); e != nil {
+			err = e
+			return
+		}
+	}
+	// 实例化一个请求对象,每个接口都会对应一个request对象
+	request := ses.NewSendEmailRequest()
+
+	// 模板数据替换
+	tmpData := &TencentEmailTemplateData{
+		REPORT_TITLE:      req.ReportTitle,
+		REPORT_ABSTRACT:   req.ReportAbstract,
+		REPORT_CONTENT:    req.ReportContent,
+		REPORT_SHARE_LINK: req.ReportShareLink,
+		REPORT_TIME:       req.ReportTime,
+	}
+	tmpDataByte, e := json.Marshal(tmpData)
+	if e != nil {
+		err = e
+		return
+	}
+	tmpDataStr := string(tmpDataByte)
+	request.Template = &ses.Template{
+		TemplateID:   common.Uint64Ptr(TencentEmailTemplateID),
+		TemplateData: common.StringPtr(tmpDataStr),
+	}
+	request.FromEmailAddress = common.StringPtr(TencentEmailFromEmailAddress)
+	request.Destination = common.StringPtrs([]string{req.Email})
+	request.Subject = common.StringPtr(req.Subject)
+
+	// 返回的resp是一个SendEmailResponse的实例,与请求对象对应
+	response, e := te.Client.SendEmail(request)
+	if _, o := e.(*errors.TencentCloudSDKError); o {
+		errByte, _ := json.Marshal(e)
+		result = string(errByte)
+		return
+	}
+	if e != nil {
+		err = e
+		return
+	}
+	ok = true
+	result = response.ToJsonString()
+	return
+}
+
+// BatchSendEmail 批量推送邮件
+func (te *TencentEmail) BatchSendEmail(list []*english_report.EnglishReportSendEmailRequest) (results []*english_report.EnglishReportSendEmailResult, err error) {
+	results = make([]*english_report.EnglishReportSendEmailResult, 0)
+	if len(list) == 0 {
+		return
+	}
+	if e := te.NewClient(); e != nil {
+		err = e
+		return
+	}
+
+	// SendEmail接口有QPS20的限制, 保险起见每秒只请求10次接口
+	max := 10
+	c := 0
+	for i := range list {
+		if c >= max {
+			time.Sleep(time.Second)
+			c = 0
+		}
+		c += 1
+
+		dataByte, _ := json.Marshal(list[i])
+		ok, result, _ := te.SendEmail(list[i])
+		results = append(results, &english_report.EnglishReportSendEmailResult{
+			ReportId:   list[i].ReportId,
+			EmailId:    list[i].EmailId,
+			Email:      list[i].Email,
+			Ok:         ok,
+			SendData:   string(dataByte),
+			ResultData: result,
+			Source:     1,
+		})
+	}
+	return
+}
+
+// TencentEmailCallBack 回调请求体
+type TencentEmailCallBack struct {
+	Event      string `description:"事件类型"`
+	Email      string `description:"收件人地址"`
+	Link       string `description:"用户点击的邮件中的链接 URL,仅在event=click时生效"`
+	BulkId     string `description:"SendEmail 接口返回的 MessageId"`
+	Timestamp  int    `description:"事件产生的时间戳"`
+	Reason     string `description:"邮件递送失败的原因"`
+	BounceType string `description:"如果收件人邮件服务商拒信,拒信类型,取值:soft_bounce | hard_bounce,仅在event=bounce的时候生效"`
+	Username   string `description:"腾讯云账号对应的 appId"`
+	From       string `description:"发信地址(不带发件人别名)"`
+	FromDomain string `description:"发信域名"`
+	TemplateId int    `description:"模板 Id"`
+}