mirror of https://gitee.com/answerdev/answer.git
feat(notification): add email notification for invited to answer
This commit is contained in:
parent
8b55c05522
commit
e12e0094d2
|
@ -170,7 +170,7 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
|
|||
answerActivityRepo := activity.NewAnswerActivityRepo(dataData, activityRepo, userRankRepo)
|
||||
questionActivityRepo := activity.NewQuestionActivityRepo(dataData, activityRepo, userRankRepo)
|
||||
answerActivityService := activity2.NewAnswerActivityService(answerActivityRepo, questionActivityRepo)
|
||||
questionService := service.NewQuestionService(questionRepo, tagCommonService, questionCommon, userCommon, revisionService, metaService, collectionCommon, answerActivityService, dataData)
|
||||
questionService := service.NewQuestionService(questionRepo, tagCommonService, questionCommon, userCommon, userRepo, revisionService, metaService, collectionCommon, answerActivityService, dataData, emailService)
|
||||
answerService := service.NewAnswerService(answerRepo, questionRepo, questionCommon, userCommon, collectionCommon, userRepo, revisionService, answerActivityService, answerCommon, voteRepo, emailService, userRoleRelService)
|
||||
questionController := controller.NewQuestionController(questionService, answerService, rankService)
|
||||
dashboardService := dashboard.NewDashboardService(questionRepo, answerRepo, commentCommonRepo, voteRepo, userRepo, reportRepo, configRepo, siteInfoCommonService, serviceConf, dataData)
|
||||
|
|
|
@ -401,6 +401,11 @@ backend:
|
|||
other: "[{{.SiteName}}] {{.DisplayName}} answered your question"
|
||||
body:
|
||||
other: "<strong><a href='{{.AnswerUrl}}'>{{.QuestionTitle}}</a></strong><br><br>\n\n<small>{{.DisplayName}}:</small><br>\n<blockquote>{{.AnswerSummary}}</blockquote><br>\n<a href='{{.AnswerUrl}}'>View it on {{.SiteName}}</a><br><br>\n\n<small>You are receiving this because you authored the thread. <a href='{{.UnsubscribeUrl}}'>Unsubscribe</a></small>"
|
||||
invited_you_to_answer:
|
||||
title:
|
||||
other: "[{{.SiteName}}] {{.DisplayName}} invited you to answer"
|
||||
body:
|
||||
other: "<strong><a href='{{.InviteUrl}}'>{{.QuestionTitle}}</a></strong><br><br>\n\n<small>{{.DisplayName}}:</small><br>\n<blockquote>I think you may know the answer.</blockquote><br>\n<a href='{{.InviteUrl}}'>View it on {{.SiteName}}</a><br><br>\n\n<small>You are receiving this because you authored the thread. <a href='{{.UnsubscribeUrl}}'>Unsubscribe</a></small>"
|
||||
new_comment:
|
||||
title:
|
||||
other: "[{{.SiteName}}] {{.DisplayName}} commented on your post"
|
||||
|
|
|
@ -378,6 +378,8 @@ backend:
|
|||
other: 踩了答案
|
||||
up_voted_comment:
|
||||
other: 赞了评论
|
||||
invited_you_to_answer:
|
||||
other: 邀请你回答问题
|
||||
email_tpl:
|
||||
change_email:
|
||||
title:
|
||||
|
@ -388,12 +390,17 @@ backend:
|
|||
title:
|
||||
other: "[{{.SiteName}}] {{.DisplayName}} 回答了您的问题"
|
||||
body:
|
||||
other: "<strong><a href='{{.AnswerUrl}}'>{{.QuestionTitle}}</a></strong><br><br>\n\n<small>{{.DisplayName}}:</small><br>\n<blockquote>{{.AnswerSummary}}</blockquote><br>\n<a href='{{.AnswerUrl}}'>在 {{.SiteName}} 上查看</a><br><br>\n\n<small>您会收到此邮件是因为您是该讨论的作者。<a href='{{.UnsubscribeUrl}}'>取消订阅</a></small>"
|
||||
other: "<strong><a href='{{.AnswerUrl}}'>{{.QuestionTitle}}</a></strong><br><br>\n\n<small>{{.DisplayName}}:</small><br>\n<blockquote>{{.AnswerSummary}}</blockquote><br>\n<a href='{{.AnswerUrl}}'>在 {{.SiteName}} 上查看</a><br><br>\n\n<small>您会收到此邮件是因为您开启了订阅。<a href='{{.UnsubscribeUrl}}'>取消订阅</a></small>"
|
||||
invited_you_to_answer:
|
||||
title:
|
||||
other: "[{{.SiteName}}] {{.DisplayName}} 邀请您回答问题"
|
||||
body:
|
||||
other: "<strong><a href='{{.InviteUrl}}'>{{.QuestionTitle}}</a></strong><br><br>\n\n<small>{{.DisplayName}}:</small><br>\n<blockquote>我想你可能知道答案。</blockquote><br>\n<a href='{{.InviteUrl}}'>在 {{.SiteName}} 上查看</a><br><br>\n\n<small>您会收到此邮件是因为您开启了订阅. <a href='{{.UnsubscribeUrl}}'>取消订阅</a></small>"
|
||||
new_comment:
|
||||
title:
|
||||
other: "[{{.SiteName}}] {{.DisplayName}} 评论了您的帖子"
|
||||
body:
|
||||
other: "<strong><a href='{{.CommentUrl}}'>{{.QuestionTitle}}</a></strong><br><br>\n\n<small>{{.DisplayName}}:</small><br>\n<blockquote>{{.CommentSummary}}</blockquote><br>\n<a href='{{.CommentUrl}}'>在 {{.SiteName}} 上查看</a><br><br>\n\n<small>您会收到此邮件是因为您是该讨论的作者。<a href='{{.UnsubscribeUrl}}'>取消订阅</a></small>"
|
||||
other: "<strong><a href='{{.CommentUrl}}'>{{.QuestionTitle}}</a></strong><br><br>\n\n<small>{{.DisplayName}}:</small><br>\n<blockquote>{{.CommentSummary}}</blockquote><br>\n<a href='{{.CommentUrl}}'>在 {{.SiteName}} 上查看</a><br><br>\n\n<small>您会收到此邮件是因为您开启了订阅。<a href='{{.UnsubscribeUrl}}'>取消订阅</a></small>"
|
||||
pass_reset:
|
||||
title:
|
||||
other: "[{{.SiteName }}] 重置密码"
|
||||
|
|
|
@ -18,4 +18,7 @@ const (
|
|||
|
||||
EmailTplKeyTestTitle = "email_tpl.test.title"
|
||||
EmailTplKeyTestBody = "email_tpl.test.body"
|
||||
|
||||
EmailTplKeyInvitedAnswerTitle = "email_tpl.invited_you_to_answer.title"
|
||||
EmailTplKeyInvitedAnswerBody = "email_tpl.invited_you_to_answer.body"
|
||||
)
|
||||
|
|
|
@ -47,6 +47,21 @@ type NewAnswerTemplateData struct {
|
|||
UnsubscribeUrl string
|
||||
}
|
||||
|
||||
type NewInviteAnswerTemplateRawData struct {
|
||||
InviterDisplayName string
|
||||
QuestionTitle string
|
||||
QuestionID string
|
||||
UnsubscribeCode string
|
||||
}
|
||||
|
||||
type NewInviteAnswerTemplateData struct {
|
||||
SiteName string
|
||||
DisplayName string
|
||||
QuestionTitle string
|
||||
InviteUrl string
|
||||
UnsubscribeUrl string
|
||||
}
|
||||
|
||||
type NewCommentTemplateRawData struct {
|
||||
CommentUserDisplayName string
|
||||
QuestionTitle string
|
||||
|
|
|
@ -242,7 +242,6 @@ func (es *EmailService) NewAnswerTemplate(ctx context.Context, raw *schema.NewAn
|
|||
AnswerSummary: raw.AnswerSummary,
|
||||
UnsubscribeUrl: fmt.Sprintf("%s/users/unsubscribe?code=%s", siteInfo.SiteUrl, raw.UnsubscribeCode),
|
||||
}
|
||||
templateData.SiteName = siteInfo.Name
|
||||
|
||||
lang := handler.GetLangByCtx(ctx)
|
||||
title = translator.TrWithData(lang, constant.EmailTplKeyNewAnswerTitle, templateData)
|
||||
|
@ -250,6 +249,27 @@ func (es *EmailService) NewAnswerTemplate(ctx context.Context, raw *schema.NewAn
|
|||
return title, body, nil
|
||||
}
|
||||
|
||||
// NewInviteAnswerTemplate new invite answer template
|
||||
func (es *EmailService) NewInviteAnswerTemplate(ctx context.Context, raw *schema.NewInviteAnswerTemplateRawData) (
|
||||
title, body string, err error) {
|
||||
siteInfo, err := es.GetSiteGeneral(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
templateData := &schema.NewInviteAnswerTemplateData{
|
||||
SiteName: siteInfo.Name,
|
||||
DisplayName: raw.InviterDisplayName,
|
||||
QuestionTitle: raw.QuestionTitle,
|
||||
InviteUrl: fmt.Sprintf("%s/questions/%s", siteInfo.SiteUrl, raw.QuestionID),
|
||||
UnsubscribeUrl: fmt.Sprintf("%s/users/unsubscribe?code=%s", siteInfo.SiteUrl, raw.UnsubscribeCode),
|
||||
}
|
||||
|
||||
lang := handler.GetLangByCtx(ctx)
|
||||
title = translator.TrWithData(lang, constant.EmailTplKeyInvitedAnswerTitle, templateData)
|
||||
body = translator.TrWithData(lang, constant.EmailTplKeyInvitedAnswerBody, templateData)
|
||||
return title, body, nil
|
||||
}
|
||||
|
||||
// NewCommentTemplate new comment template
|
||||
func (es *EmailService) NewCommentTemplate(ctx context.Context, raw *schema.NewCommentTemplateRawData) (
|
||||
title, body string, err error) {
|
||||
|
@ -271,7 +291,6 @@ func (es *EmailService) NewCommentTemplate(ctx context.Context, raw *schema.NewC
|
|||
templateData.CommentUrl = fmt.Sprintf("%s/questions/%s?commentId=%s", siteInfo.SiteUrl,
|
||||
raw.QuestionID, raw.CommentID)
|
||||
}
|
||||
templateData.SiteName = siteInfo.Name
|
||||
|
||||
lang := handler.GetLangByCtx(ctx)
|
||||
title = translator.TrWithData(lang, constant.EmailTplKeyNewCommentTitle, templateData)
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/answerdev/answer/internal/service/activity"
|
||||
"github.com/answerdev/answer/internal/service/activity_queue"
|
||||
collectioncommon "github.com/answerdev/answer/internal/service/collection_common"
|
||||
"github.com/answerdev/answer/internal/service/export"
|
||||
"github.com/answerdev/answer/internal/service/meta"
|
||||
"github.com/answerdev/answer/internal/service/notice_queue"
|
||||
"github.com/answerdev/answer/internal/service/permission"
|
||||
|
@ -26,10 +27,12 @@ import (
|
|||
"github.com/answerdev/answer/internal/service/revision_common"
|
||||
tagcommon "github.com/answerdev/answer/internal/service/tag_common"
|
||||
usercommon "github.com/answerdev/answer/internal/service/user_common"
|
||||
"github.com/answerdev/answer/pkg/encryption"
|
||||
"github.com/answerdev/answer/pkg/htmltext"
|
||||
"github.com/answerdev/answer/pkg/uid"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
"github.com/segmentfault/pacman/i18n"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
@ -42,11 +45,13 @@ type QuestionService struct {
|
|||
tagCommon *tagcommon.TagCommonService
|
||||
questioncommon *questioncommon.QuestionCommon
|
||||
userCommon *usercommon.UserCommon
|
||||
userRepo usercommon.UserRepo
|
||||
revisionService *revision_common.RevisionService
|
||||
metaService *meta.MetaService
|
||||
collectionCommon *collectioncommon.CollectionCommon
|
||||
answerActivityService *activity.AnswerActivityService
|
||||
data *data.Data
|
||||
emailService *export.EmailService
|
||||
}
|
||||
|
||||
func NewQuestionService(
|
||||
|
@ -54,23 +59,26 @@ func NewQuestionService(
|
|||
tagCommon *tagcommon.TagCommonService,
|
||||
questioncommon *questioncommon.QuestionCommon,
|
||||
userCommon *usercommon.UserCommon,
|
||||
userRepo usercommon.UserRepo,
|
||||
revisionService *revision_common.RevisionService,
|
||||
metaService *meta.MetaService,
|
||||
collectionCommon *collectioncommon.CollectionCommon,
|
||||
answerActivityService *activity.AnswerActivityService,
|
||||
data *data.Data,
|
||||
|
||||
emailService *export.EmailService,
|
||||
) *QuestionService {
|
||||
return &QuestionService{
|
||||
questionRepo: questionRepo,
|
||||
tagCommon: tagCommon,
|
||||
questioncommon: questioncommon,
|
||||
userCommon: userCommon,
|
||||
userRepo: userRepo,
|
||||
revisionService: revisionService,
|
||||
metaService: metaService,
|
||||
collectionCommon: collectionCommon,
|
||||
answerActivityService: answerActivityService,
|
||||
data: data,
|
||||
emailService: emailService,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -536,20 +544,28 @@ func (qs *QuestionService) UpdateQuestionCheckTags(ctx context.Context, req *sch
|
|||
}
|
||||
|
||||
func (qs *QuestionService) UpdateQuestionInviteUser(ctx context.Context, req *schema.QuestionUpdateInviteUser) (err error) {
|
||||
originQuestion, exist, err := qs.questionRepo.GetQuestion(ctx, req.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return errors.NotFound(reason.ObjectNotFound)
|
||||
}
|
||||
|
||||
//verify invite user
|
||||
inviteUserInfoList, err := qs.userCommon.BatchGetUserBasicInfoByUserNames(ctx, req.InviteUser)
|
||||
if err != nil {
|
||||
log.Error("BatchGetUserBasicInfoByUserNames error", err.Error())
|
||||
}
|
||||
inviteUser := make([]string, 0)
|
||||
inviteUserIDs := make([]string, 0)
|
||||
for _, item := range req.InviteUser {
|
||||
_, ok := inviteUserInfoList[item]
|
||||
if ok {
|
||||
inviteUser = append(inviteUser, inviteUserInfoList[item].ID)
|
||||
inviteUserIDs = append(inviteUserIDs, inviteUserInfoList[item].ID)
|
||||
}
|
||||
}
|
||||
inviteUserStr := ""
|
||||
inviteUserByte, err := json.Marshal(inviteUser)
|
||||
inviteUserByte, err := json.Marshal(inviteUserIDs)
|
||||
if err != nil {
|
||||
log.Error("json.Marshal error", err.Error())
|
||||
inviteUserStr = "[]"
|
||||
|
@ -564,12 +580,31 @@ func (qs *QuestionService) UpdateQuestionInviteUser(ctx context.Context, req *sc
|
|||
if saveerr != nil {
|
||||
return saveerr
|
||||
}
|
||||
qs.notificationInviteUser(ctx, inviteUser, req.ID, req.UserID)
|
||||
go qs.notificationInviteUser(ctx, inviteUserIDs, originQuestion.ID, originQuestion.Title, req.UserID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (qs *QuestionService) notificationInviteUser(
|
||||
ctx context.Context, invitedUserIDs []string, questionID, questionUserID string) {
|
||||
ctx context.Context, invitedUserIDs []string, questionID, questionTitle, questionUserID string) {
|
||||
inviter, exist, err := qs.userCommon.GetUserBasicInfoByID(ctx, questionUserID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
if !exist {
|
||||
log.Warnf("user %s not found", questionUserID)
|
||||
return
|
||||
}
|
||||
|
||||
users, err := qs.userRepo.BatchGetByID(ctx, invitedUserIDs)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
invitee := make(map[string]*entity.User, len(users))
|
||||
for _, user := range users {
|
||||
invitee[user.ID] = user
|
||||
}
|
||||
for _, userID := range invitedUserIDs {
|
||||
msg := &schema.NotificationMsg{
|
||||
ReceiverUserID: userID,
|
||||
|
@ -580,6 +615,40 @@ func (qs *QuestionService) notificationInviteUser(
|
|||
msg.ObjectType = constant.QuestionObjectType
|
||||
msg.NotificationAction = constant.NotificationInvitedYouToAnswer
|
||||
notice_queue.AddNotification(msg)
|
||||
|
||||
userInfo, ok := invitee[userID]
|
||||
if !ok {
|
||||
log.Warnf("user %s not found", userID)
|
||||
return
|
||||
}
|
||||
if userInfo.NoticeStatus == schema.NoticeStatusOff || len(userInfo.EMail) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
rawData := &schema.NewInviteAnswerTemplateRawData{
|
||||
InviterDisplayName: inviter.DisplayName,
|
||||
QuestionTitle: questionTitle,
|
||||
QuestionID: questionID,
|
||||
UnsubscribeCode: encryption.MD5(userInfo.Pass),
|
||||
}
|
||||
codeContent := &schema.EmailCodeContent{
|
||||
SourceType: schema.UnsubscribeSourceType,
|
||||
Email: userInfo.EMail,
|
||||
UserID: userInfo.ID,
|
||||
}
|
||||
|
||||
// If receiver has set language, use it to send email.
|
||||
if len(userInfo.Language) > 0 {
|
||||
ctx = context.WithValue(ctx, constant.AcceptLanguageFlag, i18n.Language(userInfo.Language))
|
||||
}
|
||||
title, body, err := qs.emailService.NewInviteAnswerTemplate(ctx, rawData)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
go qs.emailService.SendAndSaveCodeWithTime(
|
||||
ctx, userInfo.EMail, title, body, rawData.UnsubscribeCode, codeContent.ToJSONString(), 7*24*time.Hour)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue