diff --git a/api/consts/consts.go b/api/consts/consts.go index 4c1e78bb..a2edbf59 100644 --- a/api/consts/consts.go +++ b/api/consts/consts.go @@ -67,6 +67,8 @@ const ( AuditTaskCollectionReflectKey = "audit_task_collection_reflect" StreamerFilterKey = "streamer_filter" SmsSplitRatioKey = "sms_split_ratio" + RavenIQTestEmailConfigKey = "Raven_IQ_test_email_config" + OfficialEmailKey = "official_email" ) // del_flag diff --git a/api/consts/status.go b/api/consts/status.go index 4ce638fd..47d7dbcd 100644 --- a/api/consts/status.go +++ b/api/consts/status.go @@ -321,3 +321,10 @@ const ( VideoManuallyReview_Passed = 1 //已复审通过 VideoManuallyReview_Rejected = 2 //已复审拒绝 ) + +// 邮件 +const ( + Email_Created = 0 // 已创建 + Email_Success = 1 // 已发送 + Email_Fail = 2 // 发送失败 +) diff --git a/api/errcode/errcode.go b/api/errcode/errcode.go index f1f91ac5..0c0b7e6e 100644 --- a/api/errcode/errcode.go +++ b/api/errcode/errcode.go @@ -246,6 +246,9 @@ var ErrCodeMsgMap = map[ErrCode]string{ ErrCodeVideoModerationSrvFail: "视频审核服务错误", ErrCodeVideoModerationNotExist: "视频审核不存在", + ErrCodeEmailSrvFail: "电子邮件表服务错误", + ErrCodeEmailNotExist: "电子邮件表不存在", + ErrCodeRavenIQTestSrvFail: "瑞文智商测试表服务错误", ErrCodeRavenIQTestNotExist: "瑞文智商测试表不存在", ErrCodeRavenIQTestQuestionNotExist: "瑞文智商测试表题目不存在", @@ -584,6 +587,11 @@ const ( ErrCodeVideoModerationSrvFail ErrCode = -44001 // 视频审核服务错误 ErrCodeVideoModerationNotExist ErrCode = -44002 // 视频审核不存在 + // Email: 45xxx + ErrCodeEmailSrvOk ErrCode = ErrCodeOk + ErrCodeEmailSrvFail ErrCode = -45001 // 电子邮件表服务错误 + ErrCodeEmailNotExist ErrCode = -45002 // 电子邮件表不存在 + // Media: 60xxx ErrCodeMediaSrvOk ErrCode = ErrCodeOk ErrCodeMediaSrvFail ErrCode = -60001 // 媒体服务错误 diff --git a/api/proto/email/proto/email_api.go b/api/proto/email/proto/email_api.go new file mode 100644 index 00000000..ee13a357 --- /dev/null +++ b/api/proto/email/proto/email_api.go @@ -0,0 +1,20 @@ +package proto + +import ( + "service/api/base" + "service/dbstruct" +) + +// op 创建 +type ApiCreateReq struct { + base.BaseRequest + *dbstruct.Email +} + +type ApiCreateData struct { +} + +type ApiCreateResp struct { + base.BaseResponse + Data *ApiCreateData `json:"data"` +} diff --git a/api/proto/email/proto/email_op.go b/api/proto/email/proto/email_op.go new file mode 100644 index 00000000..693a6c9a --- /dev/null +++ b/api/proto/email/proto/email_op.go @@ -0,0 +1,66 @@ +package proto + +import ( + "service/api/base" + "service/dbstruct" +) + +// op 创建 +type OpCreateReq struct { + base.BaseRequest + *dbstruct.Email +} + +type OpCreateData struct { +} + +type OpCreateResp struct { + base.BaseResponse + Data *OpCreateData `json:"data"` +} + +// op 删除 +type OpDeleteReq struct { + base.BaseRequest + Id int64 `json:"id"` +} + +type OpDeleteData struct { +} + +type OpDeleteResp struct { + base.BaseResponse + Data *OpDeleteData `json:"data"` +} + +// op 更新 +type OpUpdateReq struct { + base.BaseRequest + *dbstruct.Email +} + +type OpUpdateData struct { +} + +type OpUpdateResp struct { + base.BaseResponse + Data *OpUpdateData `json:"data"` +} + +// op 列表 +type OpListReq struct { + base.BaseRequest + Offset int `json:"offset"` + Limit int `json:"limit"` +} + +type OpListData struct { + List []*dbstruct.Email `json:"list"` + Offset int `json:"offset"` + More int `json:"more"` +} + +type OpListResp struct { + base.BaseResponse + Data *OpListData `json:"data"` +} diff --git a/api/proto/email/proto/not_null_def_api.go b/api/proto/email/proto/not_null_def_api.go new file mode 100644 index 00000000..d4b851aa --- /dev/null +++ b/api/proto/email/proto/not_null_def_api.go @@ -0,0 +1,11 @@ +package proto + +import "service/library/validator" + +func (p *ApiCreateReq) ProvideNotNullValue() (params []*validator.JsonParam) { + params = make([]*validator.JsonParam, 0) + + params = append(params, validator.NewStringPtrParam("请输入您的邮箱地址!", p.Email.To)) + + return +} diff --git a/apollostruct/emailconfig.go b/apollostruct/emailconfig.go new file mode 100644 index 00000000..95db6358 --- /dev/null +++ b/apollostruct/emailconfig.go @@ -0,0 +1,13 @@ +package apollostruct + +type EmailContentConfig struct { + Subject string `json:"subject"` + Text string `json:"text"` +} + +type EmailInfoConfig struct { + Host string `json:"host"` + Port int `json:"port"` + Address string `json:"address"` + Password string `json:"password"` +} diff --git a/app/mix/controller/email_api.go b/app/mix/controller/email_api.go new file mode 100644 index 00000000..2a352d15 --- /dev/null +++ b/app/mix/controller/email_api.go @@ -0,0 +1,23 @@ +package controller + +import ( + "service/api/errcode" + emailproto "service/api/proto/email/proto" + "service/app/mix/service" + "service/bizcommon/util" + "service/library/logger" + + "github.com/gin-gonic/gin" +) + +func ApiSendRavenIQTestResultAsEmail(ctx *gin.Context) { + req := ctx.MustGet("client_req").(*emailproto.ApiCreateReq) + ec := service.DefaultService.ApiSendRavenIQTestResultAsEmail(ctx, req) + if ec != errcode.ErrCodeEmailSrvOk { + logger.Error("ApiSendRavenIQTestResultAsEmail fail, req: %v, ec: %v", util.ToJson(req), ec) + ReplyErrCodeMsg(ctx, ec) + return + } + + ReplyOk(ctx, nil) +} diff --git a/app/mix/controller/init.go b/app/mix/controller/init.go index bea12fb3..a6c25c2f 100644 --- a/app/mix/controller/init.go +++ b/app/mix/controller/init.go @@ -30,6 +30,7 @@ import ( contact_customer_service_sessionproto "service/api/proto/contact_customer_service_session/proto" daily_statementproto "service/api/proto/daily_statement/proto" daily_statement_zone_info_proto "service/api/proto/daily_statement_zone_info/proto" + emailproto "service/api/proto/email/proto" feedbackproto "service/api/proto/feedback/proto" footprintproto "service/api/proto/footprint/proto" hvyogoproto "service/api/proto/hvyogo/proto" @@ -290,6 +291,10 @@ func Init(r *gin.Engine) { apiHvyogoGroup.POST("single_distribute", middleware.JSONParamValidator(hvyogoproto.ApiSingleDistributeReq{}), middleware.JwtAuthenticator(), middleware.RequestDecryptor(), ApiHvyogoSingleDistribute) apiHvyogoGroup.POST("worker_update", middleware.JSONParamValidator(hvyogoproto.ApiWorkerUpdateReq{}), middleware.JwtAuthenticator(), middleware.RequestDecryptor(), ApiHvyogoWorkerUpdate) + // 电子邮件表 + apiEmailGroup := r.Group("/api/email", PrepareToC()) + apiEmailGroup.POST("send_Raven_IQ_test_result", middleware.JSONParamValidator(emailproto.ApiCreateReq{}), ApiSendRavenIQTestResultAsEmail) + // 瑞文智商测试表 apiRavenIQTestGroup := r.Group("/api/Raven_IQ_test", PrepareToC()) apiRavenIQTestGroup.POST("create", middleware.JSONParamValidator(Raven_IQ_testproto.ApiCreateReq{}), ApiCreateRavenIQTest) diff --git a/app/mix/dao/mongo.go b/app/mix/dao/mongo.go index 66a6502b..7ff21994 100644 --- a/app/mix/dao/mongo.go +++ b/app/mix/dao/mongo.go @@ -28,6 +28,7 @@ import ( contact_customer_service_sessionproto "service/api/proto/contact_customer_service_session/proto" daily_statementproto "service/api/proto/daily_statement/proto" daily_statement_zone_info_proto "service/api/proto/daily_statement_zone_info/proto" + emailproto "service/api/proto/email/proto" feedbackproto "service/api/proto/feedback/proto" footprintproto "service/api/proto/footprint/proto" imageaudittaskproto "service/api/proto/imageaudittask/proto" @@ -238,6 +239,9 @@ const ( COLSingleDistributeHis = "single_distribute_his" COLSingleDistributeLock = "single_distribute_lock" + DBEmail = "email" + COLEmail = "email" + DBRavenIQTest = "Raven_IQ_test" COLRavenIQTest = "Raven_IQ_test" ) @@ -621,6 +625,11 @@ func (m *Mongo) getColSingleDistributeLock() *qmgo.Collection { return m.clientMix.Database(DBSingleDistributeHis).Collection(COLSingleDistributeLock) } +// 电子邮件表表 +func (m *Mongo) getColEmail() *qmgo.Collection { + return m.clientMix.Database(DBEmail).Collection(COLEmail) +} + // 瑞文智商测试表表 func (m *Mongo) getColRavenIQTest() *qmgo.Collection { return m.clientMix.Database(DBRavenIQTest).Collection(COLRavenIQTest) @@ -6204,6 +6213,49 @@ func (m *Mongo) GetSingleDistributeHisById(ctx *gin.Context, id string) (*dbstru return one, err } +// 电子邮件表相关 +func (m *Mongo) CreateEmail(ctx *gin.Context, email *dbstruct.Email) error { + col := m.getColEmail() + _, err := col.InsertOne(ctx, email) + return err +} + +func (m *Mongo) UpdateEmail(ctx *gin.Context, email *dbstruct.Email) error { + col := m.getColEmail() + set := util.EntityToM(email) + set["ut"] = time.Now().Unix() + up := qmgo.M{ + "$set": set, + } + err := col.UpdateId(ctx, email.Id, up) + return err +} + +func (m *Mongo) DeleteEmail(ctx *gin.Context, id int64) error { + col := m.getColEmail() + update := qmgo.M{ + "$set": qmgo.M{ + "del_flag": 1, + }, + } + err := col.UpdateId(ctx, id, update) + return err +} + +func (m *Mongo) GetEmailList(ctx *gin.Context, req *emailproto.OpListReq) ([]*dbstruct.Email, error) { + list := make([]*dbstruct.Email, 0) + col := m.getColEmail() + query := qmgo.M{ + "del_flag": 0, + } + err := col.Find(ctx, query).Sort("-ct").Skip(int64(req.Offset)).Limit(int64(req.Limit)).All(&list) + if err == qmgo.ErrNoSuchDocuments { + err = nil + return list, err + } + return list, err +} + // 瑞文智商测试表相关 func (m *Mongo) CreateRavenIQTest(ctx *gin.Context, Raven_IQ_test *dbstruct.RavenIQTest) error { col := m.getColRavenIQTest() diff --git a/app/mix/service/apiservice.go b/app/mix/service/apiservice.go index 979efe56..37051b41 100644 --- a/app/mix/service/apiservice.go +++ b/app/mix/service/apiservice.go @@ -15,6 +15,7 @@ import ( appconfigproto "service/api/proto/app_config/proto" contact_customer_service_proto "service/api/proto/contact_customer_service/proto" contact_customer_service_sessionproto "service/api/proto/contact_customer_service_session/proto" + emailproto "service/api/proto/email/proto" feedbackproto "service/api/proto/feedback/proto" hvyogoproto "service/api/proto/hvyogo/proto" loginproto "service/api/proto/login/proto" @@ -47,7 +48,9 @@ import ( "service/bizcommon/util" "service/dbstruct" "service/library/apollo" + "service/library/email" "service/library/logger" + "service/library/mediafiller" "service/library/redis" interceptor "service/library/taginterceptor" "strconv" @@ -4092,6 +4095,75 @@ func (s *Service) ApiHvyogoWorkerUpdate(ctx *gin.Context, req *hvyogoproto.ApiWo return } +// Email +func (s *Service) ApiSendRavenIQTestResultAsEmail(ctx *gin.Context, req *emailproto.ApiCreateReq) (ec errcode.ErrCode) { + ec = errcode.ErrCodeEmailSrvOk + + emailContentCfg := apollostruct.EmailContentConfig{} + err := apollo.GetJson(consts.RavenIQTestEmailConfigKey, &emailContentCfg, apollo.ApolloOpts().SetNamespace("application")) + if err != nil { + logger.Error("Apollo read failed : %v", err) + ec = errcode.ErrCodeApolloReadFail + return + } + + emailInfoCfg := apollostruct.EmailInfoConfig{} + err = apollo.GetJson(consts.OfficialEmailKey, &emailInfoCfg, apollo.ApolloOpts().SetNamespace("application")) + if err != nil { + logger.Error("Apollo read failed : %v", err) + ec = errcode.ErrCodeApolloReadFail + return + } + + req.Email.Mid = goproto.Int64(req.BaseRequest.Mid) + req.Email.From = goproto.String(emailInfoCfg.Address) + req.Email.Subject = goproto.String(emailContentCfg.Subject) + req.Email.Text = goproto.String(emailContentCfg.Text) + req.Email.Status = goproto.Int64(consts.Email_Created) + + err = _DefaultEmail.OpCreate(ctx, &emailproto.OpCreateReq{ + BaseRequest: req.BaseRequest, + Email: req.Email, + }) + if err != nil { + logger.Error("OpCreate fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeEmailSrvFail + return + } + + // 填充媒体获取资源 + err = mediafiller.FillEntity(ctx, req.Email.MediaComponent) + if err != nil { + logger.Error("FillEntity fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeMediaSrvFail + return + } + + go func(eml *dbstruct.Email, emailInfoCfg apollostruct.EmailInfoConfig) { + err = email.SendRavenIQTestResult(eml, emailInfoCfg.Host, emailInfoCfg.Port, emailInfoCfg.Address, emailInfoCfg.Password) + if err != nil { + logger.Error("SendRavenIQTestResult fail, req: %v, err: %v", util.ToJson(req), err) + // 更新状态为失败 + _DefaultEmail.OpUpdate(ctx, &emailproto.OpUpdateReq{ + Email: &dbstruct.Email{ + Id: req.Id, + Status: goproto.Int64(consts.Email_Fail), + }, + }) + return + } + // 更新状态为成功 + _DefaultEmail.OpUpdate(ctx, &emailproto.OpUpdateReq{ + Email: &dbstruct.Email{ + Id: req.Id, + Status: goproto.Int64(consts.Email_Success), + }, + }) + }(req.Email, emailInfoCfg) + + return +} + // RavenIQTest func (s *Service) ApiCreateRavenIQTest(ctx *gin.Context, req *Raven_IQ_testproto.ApiCreateReq) (id int64, ec errcode.ErrCode) { ec = errcode.ErrCodeRavenIQTestSrvOk diff --git a/app/mix/service/logic/email.go b/app/mix/service/logic/email.go new file mode 100644 index 00000000..181b3dea --- /dev/null +++ b/app/mix/service/logic/email.go @@ -0,0 +1,65 @@ +package logic + +import ( + "service/api/consts" + emailproto "service/api/proto/email/proto" + "service/app/mix/dao" + "service/dbstruct" + "service/library/idgenerator" + "service/library/logger" + "time" + + "github.com/gin-gonic/gin" + goproto "google.golang.org/protobuf/proto" +) + +type Email struct { + store *dao.Store +} + +func NewEmail(store *dao.Store) (a *Email) { + a = &Email{ + store: store, + } + return +} + +func (p *Email) OpCreate(ctx *gin.Context, req *emailproto.OpCreateReq) error { + req.Email.Id = goproto.Int64(idgenerator.GenEmailId()) + req.Email.Ct = goproto.Int64(time.Now().Unix()) + req.Email.Ut = goproto.Int64(time.Now().Unix()) + req.Email.DelFlag = goproto.Int64(consts.Exist) + err := p.store.CreateEmail(ctx, req.Email) + if err != nil { + logger.Error("CreateEmail fail, err: %v", err) + return err + } + return nil +} + +func (p *Email) OpUpdate(ctx *gin.Context, req *emailproto.OpUpdateReq) error { + err := p.store.UpdateEmail(ctx, req.Email) + if err != nil { + logger.Error("UpdateEmail fail, err: %v", err) + return err + } + return nil +} + +func (p *Email) OpDelete(ctx *gin.Context, id int64) error { + err := p.store.DeleteEmail(ctx, id) + if err != nil { + logger.Error("DeleteEmail fail, err: %v", err) + return err + } + return nil +} + +func (p *Email) OpList(ctx *gin.Context, req *emailproto.OpListReq) ([]*dbstruct.Email, error) { + list, err := p.store.GetEmailList(ctx, req) + if err != nil { + logger.Error("GetEmailList fail, err: %v", err) + return make([]*dbstruct.Email, 0), err + } + return list, nil +} diff --git a/app/mix/service/service.go b/app/mix/service/service.go index 66d4d1af..09794504 100644 --- a/app/mix/service/service.go +++ b/app/mix/service/service.go @@ -150,6 +150,7 @@ var ( _DefaultWorkerId *logic.WorkerId _DefaultSingleDistributeHis *logic.SingleDistributeHis _DefaultAutoResponseCreateTimes *logic.AutoResponseCreateTimes + _DefaultEmail *logic.Email _DefaultRavenIQTest *logic.RavenIQTest _DefaultStreamerDecrtByEs *logic.StreamerDecrtByEs @@ -254,6 +255,7 @@ func (s *Service) Init(c any) (err error) { _DefaultWorkerId = logic.NewWorkerId(store) _DefaultSingleDistributeHis = logic.NewSingleDistributeHis(store) _DefaultAutoResponseCreateTimes = logic.NewAutoResponseCreateTimes(store) + _DefaultEmail = logic.NewEmail(store) _DefaultRavenIQTest = logic.NewRavenIQTest(store) _DefaultVas = logic.NewVas(store, _DefaultStreamer, _DefaultAccount, _DefaultZone, _DefaultZoneThirdPartner, _DefaultZoneCollaborator) diff --git a/codecreate/codecreate.go b/codecreate/codecreate.go index 723a640e..92407846 100644 --- a/codecreate/codecreate.go +++ b/codecreate/codecreate.go @@ -9,10 +9,10 @@ import ( func main() { genSource := &generator.GenSource{ - EntityName: "RavenIQTest", - ModuleName: "Raven_IQ_test", - EntityCNName: "瑞文智商测试表", - ErrCodeSeq: "101", + EntityName: "Email", + ModuleName: "email", + EntityCNName: "电子邮件表", + ErrCodeSeq: "45", } generator.CreateFileDirectory(genSource) diff --git a/codecreate/resource/EntityDefine.xlsx b/codecreate/resource/EntityDefine.xlsx index 45cf4458..254f9577 100644 Binary files a/codecreate/resource/EntityDefine.xlsx and b/codecreate/resource/EntityDefine.xlsx differ diff --git a/dbstruct/email.go b/dbstruct/email.go new file mode 100644 index 00000000..cb7d4e4f --- /dev/null +++ b/dbstruct/email.go @@ -0,0 +1,43 @@ +package dbstruct + +type Email struct { + Id *int64 `json:"id" bson:"_id"` // 电子邮件表id + Mid *int64 `json:"mid" bson:"mid"` // 用户id + From *string `json:"from" bson:"from"` // 发送人邮箱 + To *string `json:"to" bson:"to"` // 收件人邮箱 + Subject *string `json:"subject" bson:"subject"` // 标题 + Text *string `json:"text" bson:"text"` // 文本 + MediaComponent *MediaComponent `json:"media_component" bson:"media_component"` // 媒体 + Status *int64 `json:"status" bson:"status"` // 状态 + Ct *int64 `json:"ct" bson:"ct"` // 创建时间 + Ut *int64 `json:"ut" bson:"ut"` // 更新时间 + DelFlag *int64 `json:"del_flag" bson:"del_flag"` // 删除标记 +} + +func (p *Email) GetFrom() string { + if p == nil || p.From == nil { + return "" + } + return *p.From +} + +func (p *Email) GetTo() string { + if p == nil || p.To == nil { + return "" + } + return *p.To +} + +func (p *Email) GetSubject() string { + if p == nil || p.Subject == nil { + return "" + } + return *p.Subject +} + +func (p *Email) GetText() string { + if p == nil || p.Text == nil { + return "" + } + return *p.Text +} diff --git a/library/email/send.go b/library/email/send.go new file mode 100644 index 00000000..9bde1e32 --- /dev/null +++ b/library/email/send.go @@ -0,0 +1,53 @@ +package email + +import ( + "fmt" + "io" + "net/http" + "service/dbstruct" + "service/library/logger" + "strings" + + "gopkg.in/gomail.v2" +) + +func SendRavenIQTestResult(mail *dbstruct.Email, host string, port int, address, password string) error { + + url := mail.MediaComponent.Images[0].Urls[0] + format := mail.MediaComponent.Images[0].Fmt + body := &strings.Builder{} + h2 := fmt.Sprintf("

%v

", mail.GetText()) + img := fmt.Sprintf("\"图片加载错误,请联系管理员\"", url) + body.WriteString(h2) + body.WriteString(img) + + response, err := http.Get(url) + if err != nil { + return err + } + attach, err := io.ReadAll(response.Body) + response.Body.Close() + if err != nil { + return err + } + attachName := fmt.Sprintf("测评结果.%v", format[6:]) + + m := gomail.NewMessage() + + m.SetHeader("From", mail.GetFrom()) + m.SetHeader("To", mail.GetTo()) + m.SetHeader("Subject", mail.GetSubject()) + m.SetBody("text/html", body.String()) + m.Attach(attachName, gomail.SetCopyFunc(func(w io.Writer) error { //设置邮件附件 第一个参数是附件名称 第二个参数是二进制文件流 + _, err := w.Write(attach) + return err + })) + + err = gomail.NewDialer(host, port, address, password).DialAndSend(m) + if err != nil { + logger.Error("Send Email Fail", err) + return err + } + + return nil +} diff --git a/library/idgenerator/genid.go b/library/idgenerator/genid.go index 3d9f4711..dac15289 100644 --- a/library/idgenerator/genid.go +++ b/library/idgenerator/genid.go @@ -56,6 +56,7 @@ const ( NodeYeepayRefund // node 易宝支付退款 NodeWorkerId // node 用户职业者id映射表 NodeSingleDistributeHis // node 慧用工下发打款历史表 + NodeEmail // node 电子邮件表 ) func GenIdInt64(node int64) (int64, error) { @@ -273,3 +274,9 @@ func GenSingleDistributeHisId() string { id, _ := GenIdString(NodeSingleDistributeHis) return id } + +// email +func GenEmailId() int64 { + id, _ := GenIdInt64(NodeEmail) + return id +}