浏览代码

邮箱验证码

ziwen 1 年之前
父节点
当前提交
54531cb777
共有 8 个文件被更改,包括 1110 次插入49 次删除
  1. 17 17
      controller/auth.go
  2. 7 0
      go.mod
  3. 326 0
      models/english_report.go
  4. 142 0
      models/english_report_email_log.go
  5. 236 0
      services/aliyun_email.go
  6. 13 32
      services/email.go
  7. 186 0
      services/english_report/english_report.go
  8. 183 0
      services/tencent_yun.go

+ 17 - 17
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,15 @@ 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 {
+	code := global.Redis.Get(context.TODO(), req.Email).String()
+	if code == "" || code != req.SmsCode {
 		resp.Fail("验证码错误,请重新输入", 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
@@ -449,16 +444,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).String()
+		if code == "" || code != req.SmsCode {
+			resp.Fail("验证码错误,请重新输入", 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]
+}

+ 142 - 0
models/english_report_email_log.go

@@ -0,0 +1,142 @@
+package models
+
+import (
+	"github.com/beego/beego/v2/client/orm"
+	"github.com/rdlucklib/rdluck_tools/paging"
+	"hongze/hongze_yb_en_api/utils"
+	"time"
+)
+
+const (
+	EnglishReportEmailLogSourceAli     = 1 // 来源-阿里云
+	EnglishReportEmailLogSourceTencent = 2 // 来源-腾讯云
+
+	EnglishReportEmailLogStatusIng     = -1 // 推送状态-已发送
+	EnglishReportEmailLogStatusFail    = 0  // 推送状态-发送失败
+	EnglishReportEmailLogStatusSuccess = 1  // 推送状态-发送成功
+)
+
+// EnglishReportEmailLog 英文研报-邮件推送记录
+type EnglishReportEmailLog struct {
+	Id           int       `orm:"column(id);pk;auto" description:"邮箱ID"`
+	ReportId     int       `description:"报告ID或者线上路演ID"`
+	ReportType   int       `description:"类型:0英文研报,1英文线上路演"`
+	EmailId      int       `description:"邮箱ID"`
+	Email        string    `description:"邮箱地址"`
+	SendData     string    `description:"请求信息"`
+	Result       string    `description:"响应信息"`
+	SendStatus   int       `description:"发送状态:-1-已发送(一个中间状态,重新推送时可能会产生这种状态);0-发送失败;1-发送成功"`
+	CreateTime   time.Time `description:"请求时间"`
+	Source       int       `description:"服务商:1-阿里云;2-腾讯云"`
+	IsDeleted    int       `description:"是否已删除: 0-正常; 1-已删除"`
+	CallbackData string    `description:"回调信息"`
+	ErrMsg       string    `description:"错误信息(Result/CallbackData里面的取起来麻烦=_=!)"`
+}
+
+func (item *EnglishReportEmailLog) TableName() string {
+	return "english_report_email_log"
+}
+
+func (item *EnglishReportEmailLog) Create() (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Insert(item)
+	return
+}
+
+func (item *EnglishReportEmailLog) Update(cols []string) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.Update(item, cols...)
+	return
+}
+
+func (item *EnglishReportEmailLog) InsertMulti(items []*EnglishReportEmailLog) (err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	_, err = o.InsertMulti(len(items), items)
+	return
+}
+
+// EnglishReportEmailLogPageListResp 英文研报-邮件推送记录分页列表响应体
+type EnglishReportEmailLogPageListResp struct {
+	List   []*EnglishReportEmailLogPageList `description:"列表数据"`
+	Paging *paging.PagingItem               `description:"分页数据"`
+}
+
+// EnglishReportEmailLogPageList 英文研报-邮件推送记录分页列表
+type EnglishReportEmailLogPageList struct {
+	SendId     int    `description:"推送ID"`
+	ReportId   int    `description:"报告ID"`
+	EmailId    int    `description:"邮箱ID"`
+	Email      string `description:"邮箱地址"`
+	ResultMsg  string `description:"结果详情"`
+	SendStatus int    `description:"发送状态:-1-已发送;0-发送失败;1-发送成功"`
+	Source     int    `description:"服务商:1-阿里云;2-腾讯云"`
+	CreateTime string `description:"创建时间"`
+}
+
+// GetEnglishReportEmailLogPageList 获取日志列表-分页
+func GetEnglishReportEmailLogPageList(condition string, pars []interface{}, order string, startSize, pageSize int) (total int, list []*EnglishReportEmailLog, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM english_report_email_log WHERE is_deleted = 0 `
+	sql += condition
+	if order != "" {
+		sql += order
+	} else {
+		sql += ` ORDER BY send_status ASC, create_time DESC`
+	}
+	totalSQl := `SELECT COUNT(1) total FROM (` + sql + `) z`
+	if err = o.Raw(totalSQl, pars).QueryRow(&total); err != nil {
+		return
+	}
+	sql += ` LIMIT ?,?`
+	_, err = o.Raw(sql, pars, startSize, pageSize).QueryRows(&list)
+	return
+}
+
+// GetEnglishReportEmailLogById 主键获取日志
+func GetEnglishReportEmailLogById(id int) (item *EnglishReportEmailLog, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM english_report_email_log WHERE is_deleted = 0 AND id = ? LIMIT 1`
+	err = o.Raw(sql, id).QueryRow(&item)
+	return
+}
+
+
+// EnglishReportEmailLogFail 群发消息日志失败列表
+type EnglishReportEmailLogFail struct {
+	HasFail  int `description:"是否有失败记录: 0-无; 1-有"`
+	ReportId int `description:"报告ID"`
+}
+
+// GetEnglishReportEmailLogFailList 获取群发消息日志失败列表
+func GetEnglishReportEmailLogFailList(reportType int) (list []*EnglishReportEmailLogFail, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT 1 AS has_fail, report_id FROM english_report_email_log WHERE report_type=? and is_deleted = 0 AND send_status = 0 GROUP BY report_id`
+	_, err = o.Raw(sql, reportType).QueryRows(&list)
+	return
+}
+
+// GetEnglishReportEmailLog 获取邮件推送日志
+func GetEnglishReportEmailLog(condition string, pars []interface{}) (item *EnglishReportEmailLog, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT * FROM english_report_email_log WHERE 1 = 1 `
+	sql += condition
+	sql += ` ORDER BY id DESC LIMIT 1`
+	err = o.Raw(sql, pars).QueryRow(&item)
+	return
+}
+
+// GetHasFailEmailLogReportIds 获取存在邮件推送失败记录的英文报告IDs
+func GetHasFailEmailLogReportIds() (reportIds []int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT DISTINCT report_id FROM english_report_email_log WHERE is_deleted = 0 AND send_status = 0`
+	_, err = o.Raw(sql).QueryRows(&reportIds)
+	return
+}
+
+// GetSuccessEmailLogReportIds 获取邮件推送记录均为成功的英文报告IDs
+func GetSuccessEmailLogReportIds() (reportIds []int, err error) {
+	o := orm.NewOrmUsingDB("rddp")
+	sql := `SELECT DISTINCT report_id FROM english_report_email_log WHERE is_deleted = 0 AND (send_status != 0 AND send_status != -1)`
+	_, err = o.Raw(sql).QueryRows(&reportIds)
+	return
+}

+ 236 - 0
services/aliyun_email.go

@@ -0,0 +1,236 @@
+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/services/english_report"
+	"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

+ 186 - 0
services/english_report/english_report.go

@@ -0,0 +1,186 @@
+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-腾讯云"`
+}
+
+// BatchSendAliEnglishReportEmail 批量推送英文研报邮件
+//func BatchSendAliEnglishReportEmail(list []*EnglishReportSendEmailRequest) (err error) {
+//	defer func() {
+//		if err != nil {
+//			go alarm_msg.SendAlarmMsg("阿里云群发英文研报邮件失败, Err: "+err.Error(), 3)
+//		}
+//	}()
+//	if len(list) == 0 {
+//		return
+//	}
+//	requestMap := make(map[int]*EnglishReportSendEmailRequest, 0)
+//	for i := range list {
+//		requestMap[list[i].EmailId] = list[i]
+//	}
+//
+//	// 请求阿里云接口批量推送
+//	aliEmail := new(services.AliyunEmail)
+//	resultList, e := aliEmail.BatchSendEmail(list)
+//	if e != nil {
+//		err = e
+//		return
+//	}
+//
+//	// 返回的结果更新日志
+//	resendList := make([]*EnglishReportSendEmailRequest, 0)
+//	failLogIds := make([]int, 0)
+//	updateCols := []string{"SendData", "Result", "SendStatus", "ErrMsg"}
+//	for i := range resultList {
+//		var cond string
+//		var pars []interface{}
+//		cond = ` AND is_deleted = 0 AND report_id = ? AND email_id = ? AND source = ? AND send_status = ?`
+//		pars = append(pars, resultList[i].ReportId, resultList[i].EmailId, models.EnglishReportEmailLogSourceAli, models.EnglishReportEmailLogStatusIng)
+//		l, e := models.GetEnglishReportEmailLog(cond, pars)
+//		if e != nil {
+//			continue
+//		}
+//		l.SendData = resultList[i].SendData
+//		l.Result = resultList[i].ResultData
+//		if resultList[i].Ok {
+//			l.SendStatus = models.EnglishReportEmailLogStatusSuccess
+//		} else {
+//			l.SendStatus = models.EnglishReportEmailLogStatusFail
+//			failLogIds = append(failLogIds, l.Id)
+//			if requestMap[resultList[i].EmailId] != nil {
+//				resendList = append(resendList, requestMap[resultList[i].EmailId])
+//			}
+//			// 取出错误信息
+//			r := new(services.AliyunEmailResult)
+//			if e = json.Unmarshal([]byte(resultList[i].ResultData), &r); e != nil {
+//				continue
+//			}
+//			rd := new(services.AliyunEmailResultData)
+//			res := strings.Replace(r.Data, `\`, ``, -1)
+//			if e = json.Unmarshal([]byte(res), &rd); e != nil {
+//				continue
+//			}
+//			l.ErrMsg = rd.Message
+//		}
+//		if e = l.Update(updateCols); e != nil {
+//			continue
+//		}
+//	}
+//
+//	// 推送失败的重新腾讯云, 若腾讯云也失败将不再自动重推, 用户手动去重推
+//	if len(resendList) > 0 && len(failLogIds) > 0 {
+//		_ = ResendTencentEnglishReportEmail(resendList, failLogIds)
+//	}
+//	return
+//}
+
+// ResendTencentEnglishReportEmail 腾讯云邮件重新推送
+//func ResendTencentEnglishReportEmail(resendList []*EnglishReportSendEmailRequest, failLogIds []int) (err error) {
+//	defer func() {
+//		if err != nil {
+//			go alarm_msg.SendAlarmMsg("腾讯云重发英文研报邮件失败, Err: "+err.Error(), 3)
+//		}
+//	}()
+//	if len(resendList) == 0 || len(failLogIds) == 0 {
+//		return
+//	}
+//
+//	// 标记原有日志为已删除
+//	if len(failLogIds) > 0 {
+//		if e := models.DeleteEnglishReportEmailLogByIds(failLogIds); e != nil {
+//			err = errors.New("删除原邮件日志失败, Err: " + e.Error())
+//			return
+//		}
+//	}
+//
+//	// 写入新的日志
+//	nowTime := time.Now().Local()
+//	logData := make([]*models.EnglishReportEmailLog, 0)
+//	for i := range resendList {
+//		sendByte, e := json.Marshal(resendList[i])
+//		if e != nil {
+//			err = errors.New("sendByte json.Marshal Err, Err: " + e.Error())
+//			return
+//		}
+//
+//		logData = append(logData, &models.EnglishReportEmailLog{
+//			ReportId:   resendList[i].ReportId,
+//			EmailId:    resendList[i].EmailId,
+//			Email:      resendList[i].Email,
+//			SendData:   string(sendByte),
+//			Source:     models.EnglishReportEmailLogSourceTencent,
+//			SendStatus: models.EnglishReportEmailLogStatusIng,
+//			CreateTime: nowTime,
+//		})
+//	}
+//	emailLog := new(models.EnglishReportEmailLog)
+//	if e := emailLog.InsertMulti(logData); e != nil {
+//		err = errors.New("批量写入群发邮件日志失败, Err: " + e.Error())
+//		return
+//	}
+//
+//	// 请求腾讯云
+//	tecentEmail := new(services.TencentEmail)
+//	resultList, e := tecentEmail.BatchSendEmail(resendList)
+//	if e != nil {
+//		err = e
+//		return
+//	}
+//	updateCols := []string{"SendData", "Result", "SendStatus", "ErrMsg"}
+//	for i := range resultList {
+//		var cond string
+//		var pars []interface{}
+//		cond = ` AND is_deleted = 0 AND report_id = ? AND email_id = ? AND source = ? AND send_status = ?`
+//		pars = append(pars, resultList[i].ReportId, resultList[i].EmailId, models.EnglishReportEmailLogSourceTencent, models.EnglishReportEmailLogStatusIng)
+//		l, e := models.GetEnglishReportEmailLog(cond, pars)
+//		if e != nil {
+//			continue
+//		}
+//		l.SendData = resultList[i].SendData
+//		l.Result = resultList[i].ResultData
+//		if resultList[i].Ok {
+//			l.SendStatus = models.EnglishReportEmailLogStatusSuccess
+//		} else {
+//			l.SendStatus = models.EnglishReportEmailLogStatusFail
+//			r := new(services.TencentEmailResult)
+//			if e = json.Unmarshal([]byte(resultList[i].ResultData), &r); e != nil {
+//				continue
+//			}
+//			l.ErrMsg = r.Message
+//		}
+//		if e = l.Update(updateCols); e != nil {
+//			continue
+//		}
+//	}
+//	return
+//}

+ 183 - 0
services/tencent_yun.go

@@ -0,0 +1,183 @@
+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"`
+}
+
+//func init() {
+//	fmt.Println("start email init")
+//
+//	te := new(TencentEmail)
+//	te.NewClient()
+//	req := new(EnglishReportSendEmailRequest)
+//	req.Subject = "这是一封邮件的主题"
+//	req.Email = ""
+//	req.ReportTitle = "Report Hello World"
+//	req.ReportAbstract = "This is abstract"
+//	req.ReportContent = "This is content"
+//	req.ReportShareLink = "https://share.hzinsights.com/reportEn?code=a226e450e214f350856e2980b6e55ac9"
+//	req.ReportTime = "2022/11/23"
+//
+//	ok, result, e := te.SendEmail(req)
+//	fmt.Println(ok)
+//	if e != nil {
+//		fmt.Println("出错了: ", e)
+//	} else {
+//		fmt.Println("没出错: ", result)
+//	}
+//
+//	fmt.Println("end email init")
+//}