mirror of https://gitee.com/answerdev/answer.git
feat(notification): add new question notification
This commit is contained in:
parent
6a3d2f95d2
commit
2bd8f7f771
|
@ -26,7 +26,7 @@ import (
|
|||
"github.com/answerdev/answer/internal/repo/config"
|
||||
"github.com/answerdev/answer/internal/repo/export"
|
||||
"github.com/answerdev/answer/internal/repo/meta"
|
||||
"github.com/answerdev/answer/internal/repo/notification"
|
||||
notification2 "github.com/answerdev/answer/internal/repo/notification"
|
||||
"github.com/answerdev/answer/internal/repo/plugin_config"
|
||||
"github.com/answerdev/answer/internal/repo/question"
|
||||
"github.com/answerdev/answer/internal/repo/rank"
|
||||
|
@ -41,6 +41,7 @@ import (
|
|||
"github.com/answerdev/answer/internal/repo/unique"
|
||||
"github.com/answerdev/answer/internal/repo/user"
|
||||
"github.com/answerdev/answer/internal/repo/user_external_login"
|
||||
"github.com/answerdev/answer/internal/repo/user_notification_config"
|
||||
"github.com/answerdev/answer/internal/router"
|
||||
"github.com/answerdev/answer/internal/service"
|
||||
"github.com/answerdev/answer/internal/service/action"
|
||||
|
@ -58,7 +59,7 @@ import (
|
|||
"github.com/answerdev/answer/internal/service/follow"
|
||||
meta2 "github.com/answerdev/answer/internal/service/meta"
|
||||
"github.com/answerdev/answer/internal/service/notice_queue"
|
||||
notification2 "github.com/answerdev/answer/internal/service/notification"
|
||||
"github.com/answerdev/answer/internal/service/notification"
|
||||
"github.com/answerdev/answer/internal/service/notification_common"
|
||||
"github.com/answerdev/answer/internal/service/object_info"
|
||||
"github.com/answerdev/answer/internal/service/plugin_common"
|
||||
|
@ -80,6 +81,7 @@ import (
|
|||
"github.com/answerdev/answer/internal/service/user_admin"
|
||||
"github.com/answerdev/answer/internal/service/user_common"
|
||||
user_external_login2 "github.com/answerdev/answer/internal/service/user_external_login"
|
||||
user_notification_config2 "github.com/answerdev/answer/internal/service/user_notification_config"
|
||||
"github.com/segmentfault/pacman"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
)
|
||||
|
@ -127,10 +129,12 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
|
|||
userCommon := usercommon.NewUserCommon(userRepo, userRoleRelService, authService, siteInfoCommonService)
|
||||
userExternalLoginRepo := user_external_login.NewUserExternalLoginRepo(dataData)
|
||||
userExternalLoginService := user_external_login2.NewUserExternalLoginService(userRepo, userCommon, userExternalLoginRepo, emailService, siteInfoCommonService, userActiveActivityRepo)
|
||||
userService := service.NewUserService(userRepo, userActiveActivityRepo, activityRepo, emailService, authService, siteInfoCommonService, userRoleRelService, userCommon, userExternalLoginService)
|
||||
userNotificationConfigRepo := user_notification_config.NewUserNotificationConfigRepo(dataData)
|
||||
userService := service.NewUserService(userRepo, userActiveActivityRepo, activityRepo, emailService, authService, siteInfoCommonService, userRoleRelService, userCommon, userExternalLoginService, userNotificationConfigRepo)
|
||||
captchaRepo := captcha.NewCaptchaRepo(dataData)
|
||||
captchaService := action.NewCaptchaService(captchaRepo)
|
||||
userController := controller.NewUserController(authService, userService, captchaService, emailService, siteInfoCommonService)
|
||||
userNotificationConfigService := user_notification_config2.NewUserNotificationConfigService(userRepo, userNotificationConfigRepo)
|
||||
userController := controller.NewUserController(authService, userService, captchaService, emailService, siteInfoCommonService, userNotificationConfigService)
|
||||
commentRepo := comment.NewCommentRepo(dataData, uniqueIDRepo)
|
||||
commentCommonRepo := comment.NewCommentCommonRepo(dataData, uniqueIDRepo)
|
||||
answerRepo := answer.NewAnswerRepo(dataData, uniqueIDRepo, userRankRepo, activityRepo)
|
||||
|
@ -145,7 +149,8 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
|
|||
objService := object_info.NewObjService(answerRepo, questionRepo, commentCommonRepo, tagCommonRepo, tagCommonService)
|
||||
voteRepo := activity_common.NewVoteRepo(dataData, activityRepo)
|
||||
notificationQueueService := notice_queue.NewNotificationQueueService()
|
||||
commentService := comment2.NewCommentService(commentRepo, commentCommonRepo, userCommon, objService, voteRepo, emailService, userRepo, notificationQueueService, activityQueueService)
|
||||
externalNotificationQueueService := notice_queue.NewNewQuestionNotificationQueueService()
|
||||
commentService := comment2.NewCommentService(commentRepo, commentCommonRepo, userCommon, objService, voteRepo, emailService, userRepo, notificationQueueService, externalNotificationQueueService, activityQueueService)
|
||||
rolePowerRelRepo := role.NewRolePowerRelRepo(dataData)
|
||||
rolePowerRelService := role2.NewRolePowerRelService(rolePowerRelRepo, userRoleRelService)
|
||||
rankService := rank2.NewRankService(userCommon, userRankRepo, objService, userRoleRelService, rolePowerRelService, configService)
|
||||
|
@ -173,8 +178,9 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
|
|||
collectionController := controller.NewCollectionController(collectionService)
|
||||
answerActivityRepo := activity.NewAnswerActivityRepo(dataData, activityRepo, userRankRepo, notificationQueueService)
|
||||
answerActivityService := activity2.NewAnswerActivityService(answerActivityRepo, configService)
|
||||
questionService := service.NewQuestionService(questionRepo, tagCommonService, questionCommon, userCommon, userRepo, revisionService, metaService, collectionCommon, answerActivityService, emailService, notificationQueueService, activityQueueService, siteInfoCommonService)
|
||||
answerService := service.NewAnswerService(answerRepo, questionRepo, questionCommon, userCommon, collectionCommon, userRepo, revisionService, answerActivityService, answerCommon, voteRepo, emailService, userRoleRelService, notificationQueueService, activityQueueService)
|
||||
externalNotificationService := notification.NewExternalNotificationService(userNotificationConfigRepo, followRepo, emailService, userRepo, externalNotificationQueueService)
|
||||
questionService := service.NewQuestionService(questionRepo, tagCommonService, questionCommon, userCommon, userRepo, revisionService, metaService, collectionCommon, answerActivityService, emailService, notificationQueueService, externalNotificationQueueService, activityQueueService, siteInfoCommonService, externalNotificationService)
|
||||
answerService := service.NewAnswerService(answerRepo, questionRepo, questionCommon, userCommon, collectionCommon, userRepo, revisionService, answerActivityService, answerCommon, voteRepo, emailService, userRoleRelService, notificationQueueService, externalNotificationQueueService, activityQueueService)
|
||||
questionController := controller.NewQuestionController(questionService, answerService, rankService, siteInfoCommonService, captchaService)
|
||||
answerController := controller.NewAnswerController(answerService, rankService, captchaService)
|
||||
searchParser := search_parser.NewSearchParser(tagCommonService, userCommon)
|
||||
|
@ -197,9 +203,9 @@ func initApplication(debug bool, serverConf *conf.Server, dbConf *data.Database,
|
|||
siteInfoService := siteinfo.NewSiteInfoService(siteInfoRepo, siteInfoCommonService, emailService, tagCommonService, configService, questionCommon)
|
||||
siteInfoController := controller_admin.NewSiteInfoController(siteInfoService)
|
||||
controllerSiteInfoController := controller.NewSiteInfoController(siteInfoCommonService)
|
||||
notificationRepo := notification.NewNotificationRepo(dataData)
|
||||
notificationRepo := notification2.NewNotificationRepo(dataData)
|
||||
notificationCommon := notificationcommon.NewNotificationCommon(dataData, notificationRepo, userCommon, activityRepo, followRepo, objService, notificationQueueService)
|
||||
notificationService := notification2.NewNotificationService(dataData, notificationRepo, notificationCommon, revisionService)
|
||||
notificationService := notification.NewNotificationService(dataData, notificationRepo, notificationCommon, revisionService)
|
||||
notificationController := controller.NewNotificationController(notificationService, rankService)
|
||||
dashboardService := dashboard.NewDashboardService(questionRepo, answerRepo, commentCommonRepo, voteRepo, userRepo, reportRepo, configService, siteInfoCommonService, serviceConf, dataData)
|
||||
dashboardController := controller.NewDashboardController(dashboardService)
|
||||
|
|
73
docs/docs.go
73
docs/docs.go
|
@ -5166,29 +5166,6 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/user/email/notification": {
|
||||
"put": {
|
||||
"description": "unsubscribe email notification",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "unsubscribe email notification",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/user/email/verification": {
|
||||
"post": {
|
||||
"description": "UserVerifyEmail",
|
||||
|
@ -5603,6 +5580,40 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/user/notification/unsubscribe": {
|
||||
"put": {
|
||||
"description": "unsubscribe notification",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "unsubscribe notification",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "UserUnsubscribeNotificationReq",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/schema.UserUnsubscribeNotificationReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/user/password": {
|
||||
"put": {
|
||||
"security": [
|
||||
|
@ -6224,7 +6235,7 @@ const docTemplate = `{
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"constant.NotificationChannel": {
|
||||
"constant.NotificationChannelKey": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"email"
|
||||
|
@ -7774,7 +7785,7 @@ const docTemplate = `{
|
|||
"type": "boolean"
|
||||
},
|
||||
"key": {
|
||||
"$ref": "#/definitions/constant.NotificationChannel"
|
||||
"$ref": "#/definitions/constant.NotificationChannelKey"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9589,6 +9600,18 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"schema.UserUnsubscribeNotificationReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"code"
|
||||
],
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string",
|
||||
"maxLength": 500
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.VoteReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
|
@ -5154,29 +5154,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/user/email/notification": {
|
||||
"put": {
|
||||
"description": "unsubscribe email notification",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "unsubscribe email notification",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/user/email/verification": {
|
||||
"post": {
|
||||
"description": "UserVerifyEmail",
|
||||
|
@ -5591,6 +5568,40 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/user/notification/unsubscribe": {
|
||||
"put": {
|
||||
"description": "unsubscribe notification",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"User"
|
||||
],
|
||||
"summary": "unsubscribe notification",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "UserUnsubscribeNotificationReq",
|
||||
"name": "data",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/schema.UserUnsubscribeNotificationReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handler.RespBody"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/answer/api/v1/user/password": {
|
||||
"put": {
|
||||
"security": [
|
||||
|
@ -6212,7 +6223,7 @@
|
|||
}
|
||||
},
|
||||
"definitions": {
|
||||
"constant.NotificationChannel": {
|
||||
"constant.NotificationChannelKey": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"email"
|
||||
|
@ -7762,7 +7773,7 @@
|
|||
"type": "boolean"
|
||||
},
|
||||
"key": {
|
||||
"$ref": "#/definitions/constant.NotificationChannel"
|
||||
"$ref": "#/definitions/constant.NotificationChannelKey"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -9577,6 +9588,18 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"schema.UserUnsubscribeNotificationReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"code"
|
||||
],
|
||||
"properties": {
|
||||
"code": {
|
||||
"type": "string",
|
||||
"maxLength": 500
|
||||
}
|
||||
}
|
||||
},
|
||||
"schema.VoteReq": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
definitions:
|
||||
constant.NotificationChannel:
|
||||
constant.NotificationChannelKey:
|
||||
enum:
|
||||
- email
|
||||
type: string
|
||||
|
@ -1092,7 +1092,7 @@ definitions:
|
|||
enable:
|
||||
type: boolean
|
||||
key:
|
||||
$ref: '#/definitions/constant.NotificationChannel'
|
||||
$ref: '#/definitions/constant.NotificationChannelKey'
|
||||
type: object
|
||||
schema.NotificationClearIDRequest:
|
||||
properties:
|
||||
|
@ -2351,6 +2351,14 @@ definitions:
|
|||
required:
|
||||
- e_mail
|
||||
type: object
|
||||
schema.UserUnsubscribeNotificationReq:
|
||||
properties:
|
||||
code:
|
||||
maxLength: 500
|
||||
type: string
|
||||
required:
|
||||
- code
|
||||
type: object
|
||||
schema.VoteReq:
|
||||
properties:
|
||||
captcha_code:
|
||||
|
@ -5513,21 +5521,6 @@ paths:
|
|||
summary: send email to the user email then change their email
|
||||
tags:
|
||||
- User
|
||||
/answer/api/v1/user/email/notification:
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: unsubscribe email notification
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.RespBody'
|
||||
summary: unsubscribe email notification
|
||||
tags:
|
||||
- User
|
||||
/answer/api/v1/user/email/verification:
|
||||
post:
|
||||
consumes:
|
||||
|
@ -5778,6 +5771,28 @@ paths:
|
|||
summary: update user's notification config
|
||||
tags:
|
||||
- User
|
||||
/answer/api/v1/user/notification/unsubscribe:
|
||||
put:
|
||||
consumes:
|
||||
- application/json
|
||||
description: unsubscribe notification
|
||||
parameters:
|
||||
- description: UserUnsubscribeNotificationReq
|
||||
in: body
|
||||
name: data
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/schema.UserUnsubscribeNotificationReq'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handler.RespBody'
|
||||
summary: unsubscribe notification
|
||||
tags:
|
||||
- User
|
||||
/answer/api/v1/user/password:
|
||||
put:
|
||||
consumes:
|
||||
|
|
|
@ -431,6 +431,11 @@ backend:
|
|||
other: "[{{.SiteName}}] {{.DisplayName}} commented on your post"
|
||||
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}}'>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_question:
|
||||
title:
|
||||
other: "[{{.SiteName}}] New question: {{.QuestionTitle}}"
|
||||
body:
|
||||
other: "<strong><a href='{{.QuestionUrl}}'>{{.QuestionTitle}}</a></strong><br><br>\n\n<small>{{.Tags}}</small><br><br>\n\n<small><a href='{{.UnsubscribeUrl}}'>Unsubscribe</a></small>"
|
||||
pass_reset:
|
||||
title:
|
||||
other: "[{{.SiteName }}] Password reset"
|
||||
|
|
|
@ -21,4 +21,7 @@ const (
|
|||
|
||||
EmailTplKeyInvitedAnswerTitle = "email_tpl.invited_you_to_answer.title"
|
||||
EmailTplKeyInvitedAnswerBody = "email_tpl.invited_you_to_answer.body"
|
||||
|
||||
EmailTplKeyNewQuestionTitle = "email_tpl.new_question.title"
|
||||
EmailTplKeyNewQuestionBody = "email_tpl.new_question.body"
|
||||
)
|
||||
|
|
|
@ -39,17 +39,17 @@ const (
|
|||
NotificationInvitedYouToAnswer = "notification.action.invited_you_to_answer"
|
||||
)
|
||||
|
||||
type NotificationChannel string
|
||||
type NotificationChannelKey string
|
||||
type NotificationSource string
|
||||
|
||||
const (
|
||||
InboxChannel NotificationSource = "inbox"
|
||||
AllNewQuestionChannel NotificationSource = "all_new_question"
|
||||
AllNewQuestionForFollowingTagsChannel NotificationSource = "all_new_question_for_following_tags"
|
||||
InboxSource NotificationSource = "inbox"
|
||||
AllNewQuestionSource NotificationSource = "all_new_question"
|
||||
AllNewQuestionForFollowingTagsSource NotificationSource = "all_new_question_for_following_tags"
|
||||
)
|
||||
|
||||
const (
|
||||
EmailChannel NotificationChannel = "email"
|
||||
EmailChannel NotificationChannelKey = "email"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/answerdev/answer/internal/service/auth"
|
||||
"github.com/answerdev/answer/internal/service/export"
|
||||
"github.com/answerdev/answer/internal/service/siteinfo_common"
|
||||
"github.com/answerdev/answer/internal/service/user_notification_config"
|
||||
"github.com/answerdev/answer/pkg/checker"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
|
@ -21,11 +22,12 @@ import (
|
|||
|
||||
// UserController user controller
|
||||
type UserController struct {
|
||||
userService *service.UserService
|
||||
authService *auth.AuthService
|
||||
actionService *action.CaptchaService
|
||||
emailService *export.EmailService
|
||||
siteInfoCommonService siteinfo_common.SiteInfoCommonService
|
||||
userService *service.UserService
|
||||
authService *auth.AuthService
|
||||
actionService *action.CaptchaService
|
||||
emailService *export.EmailService
|
||||
siteInfoCommonService siteinfo_common.SiteInfoCommonService
|
||||
userNotificationConfigService *user_notification_config.UserNotificationConfigService
|
||||
}
|
||||
|
||||
// NewUserController new controller
|
||||
|
@ -35,13 +37,15 @@ func NewUserController(
|
|||
actionService *action.CaptchaService,
|
||||
emailService *export.EmailService,
|
||||
siteInfoCommonService siteinfo_common.SiteInfoCommonService,
|
||||
userNotificationConfigService *user_notification_config.UserNotificationConfigService,
|
||||
) *UserController {
|
||||
return &UserController{
|
||||
authService: authService,
|
||||
userService: userService,
|
||||
actionService: actionService,
|
||||
emailService: emailService,
|
||||
siteInfoCommonService: siteInfoCommonService,
|
||||
authService: authService,
|
||||
userService: userService,
|
||||
actionService: actionService,
|
||||
emailService: emailService,
|
||||
siteInfoCommonService: siteInfoCommonService,
|
||||
userNotificationConfigService: userNotificationConfigService,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -499,7 +503,7 @@ func (uc *UserController) UserRegisterCaptcha(ctx *gin.Context) {
|
|||
// @Router /answer/api/v1/user/notification/config [post]
|
||||
func (uc *UserController) GetUserNotificationConfig(ctx *gin.Context) {
|
||||
userID := middleware.GetLoginUserIDFromContext(ctx)
|
||||
resp, err := uc.userService.GetUserNotificationConfig(ctx, userID)
|
||||
resp, err := uc.userNotificationConfigService.GetUserNotificationConfig(ctx, userID)
|
||||
handler.HandleResponse(ctx, err, resp)
|
||||
}
|
||||
|
||||
|
@ -520,7 +524,7 @@ func (uc *UserController) UpdateUserNotificationConfig(ctx *gin.Context) {
|
|||
}
|
||||
|
||||
req.UserID = middleware.GetLoginUserIDFromContext(ctx)
|
||||
err := uc.userService.UpdateUserNotificationConfig(ctx, req)
|
||||
err := uc.userNotificationConfigService.UpdateUserNotificationConfig(ctx, req)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
}
|
||||
|
||||
|
@ -623,16 +627,17 @@ func (uc *UserController) UserRanking(ctx *gin.Context) {
|
|||
handler.HandleResponse(ctx, err, resp)
|
||||
}
|
||||
|
||||
// UserUnsubscribeEmailNotification unsubscribe email notification
|
||||
// @Summary unsubscribe email notification
|
||||
// @Description unsubscribe email notification
|
||||
// UserUnsubscribeNotification unsubscribe notification
|
||||
// @Summary unsubscribe notification
|
||||
// @Description unsubscribe notification
|
||||
// @Tags User
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param data body schema.UserUnsubscribeNotificationReq true "UserUnsubscribeNotificationReq"
|
||||
// @Success 200 {object} handler.RespBody{}
|
||||
// @Router /answer/api/v1/user/email/notification [put]
|
||||
func (uc *UserController) UserUnsubscribeEmailNotification(ctx *gin.Context) {
|
||||
req := &schema.UserUnsubscribeEmailNotificationReq{}
|
||||
// @Router /answer/api/v1/user/notification/unsubscribe [put]
|
||||
func (uc *UserController) UserUnsubscribeNotification(ctx *gin.Context) {
|
||||
req := &schema.UserUnsubscribeNotificationReq{}
|
||||
if handler.BindAndCheck(ctx, req) {
|
||||
return
|
||||
}
|
||||
|
@ -644,7 +649,7 @@ func (uc *UserController) UserUnsubscribeEmailNotification(ctx *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
err := uc.userService.UserUnsubscribeEmailNotification(ctx, req)
|
||||
err := uc.userService.UserUnsubscribeNotification(ctx, req)
|
||||
handler.HandleResponse(ctx, err, nil)
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,6 @@ type User struct {
|
|||
IPInfo string `xorm:"not null default '' VARCHAR(255) ip_info"`
|
||||
IsAdmin bool `xorm:"not null default false BOOL is_admin"`
|
||||
Language string `xorm:"not null default '' VARCHAR(100) language"`
|
||||
NoticeConfig string `xorm:"not null TEXT notice_config"`
|
||||
}
|
||||
|
||||
// TableName user table name
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
package entity
|
||||
|
||||
import "time"
|
||||
|
||||
// UserNotificationConfig user notification config
|
||||
type UserNotificationConfig struct {
|
||||
ID string `xorm:"not null pk autoincr BIGINT(20) id"`
|
||||
CreatedAt time.Time `xorm:"created TIMESTAMP created_at"`
|
||||
UpdatedAt time.Time `xorm:"updated TIMESTAMP updated_at"`
|
||||
UserID string `xorm:"not null default 0 INDEX UNIQUE(uk_us) BIGINT(20) INDEX user_id"`
|
||||
Source string `xorm:"not null default '' INDEX UNIQUE(uk_us) VARCHAR(64) source"`
|
||||
Channels string `xorm:"not null TEXT channels"`
|
||||
Enabled bool `xorm:"not null default false BOOL enabled"`
|
||||
}
|
||||
|
||||
// TableName notification table name
|
||||
func (UserNotificationConfig) TableName() string {
|
||||
return "user_notification_config"
|
||||
}
|
|
@ -2,12 +2,10 @@ package migrations
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func addNoticeConfig(ctx context.Context, x *xorm.Engine) error {
|
||||
type User struct {
|
||||
NoticeConfig string `xorm:"not null TEXT notice_config"`
|
||||
}
|
||||
return x.Context(ctx).Sync(new(User))
|
||||
return x.Context(ctx).Sync(new(entity.UserNotificationConfig))
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/answerdev/answer/internal/repo/unique"
|
||||
"github.com/answerdev/answer/internal/repo/user"
|
||||
"github.com/answerdev/answer/internal/repo/user_external_login"
|
||||
"github.com/answerdev/answer/internal/repo/user_notification_config"
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
|
@ -73,4 +74,5 @@ var ProviderSetRepo = wire.NewSet(
|
|||
role.NewPowerRepo,
|
||||
user_external_login.NewUserExternalLoginRepo,
|
||||
plugin_config.NewPluginConfigRepo,
|
||||
user_notification_config.NewUserNotificationConfigRepo,
|
||||
)
|
||||
|
|
|
@ -120,16 +120,6 @@ func (ur *userRepo) UpdateNoticeStatus(ctx context.Context, userID string, notic
|
|||
return nil
|
||||
}
|
||||
|
||||
// UpdateNoticeConfig update notice config
|
||||
func (ur *userRepo) UpdateNoticeConfig(ctx context.Context, userID string, noticeConfig string) error {
|
||||
cond := &entity.User{NoticeConfig: noticeConfig}
|
||||
_, err := ur.data.DB.Context(ctx).Where("id = ?", userID).Cols("notice_config").Update(cond)
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ur *userRepo) UpdatePass(ctx context.Context, userID, pass string) error {
|
||||
_, err := ur.data.DB.Context(ctx).Where("id = ?", userID).Cols("pass").Update(&entity.User{Pass: pass})
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
package user_notification_config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/base/data"
|
||||
"github.com/answerdev/answer/internal/base/reason"
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/service/user_notification_config"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
)
|
||||
|
||||
// userNotificationConfigRepo notification repository
|
||||
type userNotificationConfigRepo struct {
|
||||
data *data.Data
|
||||
}
|
||||
|
||||
// NewUserNotificationConfigRepo new repository
|
||||
func NewUserNotificationConfigRepo(data *data.Data) user_notification_config.UserNotificationConfigRepo {
|
||||
return &userNotificationConfigRepo{
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Save save notification config, if existed, update, if not exist, insert
|
||||
func (ur *userNotificationConfigRepo) Save(ctx context.Context, uc *entity.UserNotificationConfig) (err error) {
|
||||
old := &entity.UserNotificationConfig{UserID: uc.UserID, Source: uc.Source}
|
||||
exist, err := ur.data.DB.Context(ctx).Get(old)
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
if exist {
|
||||
old.Channels = uc.Channels
|
||||
old.Enabled = uc.Enabled
|
||||
_, err = ur.data.DB.Context(ctx).ID(old.ID).UseBool("enabled").Cols("channels", "enabled").Update(old)
|
||||
} else {
|
||||
_, err = ur.data.DB.Context(ctx).Insert(uc)
|
||||
}
|
||||
if err != nil {
|
||||
return errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetByUserID get notification config by user id
|
||||
func (ur *userNotificationConfigRepo) GetByUserID(ctx context.Context, userID string) (
|
||||
[]*entity.UserNotificationConfig, error) {
|
||||
var configs []*entity.UserNotificationConfig
|
||||
err := ur.data.DB.Context(ctx).Where("user_id = ?", userID).Find(&configs)
|
||||
if err != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// GetBySource get notification config by source
|
||||
func (ur *userNotificationConfigRepo) GetBySource(ctx context.Context, source constant.NotificationSource) (
|
||||
[]*entity.UserNotificationConfig, error) {
|
||||
var configs []*entity.UserNotificationConfig
|
||||
err := ur.data.DB.Context(ctx).UseBool("enabled").
|
||||
Find(&configs, &entity.UserNotificationConfig{Source: string(source), Enabled: true})
|
||||
if err != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// GetByUserIDAndSource get notification config by user id and source
|
||||
func (ur *userNotificationConfigRepo) GetByUserIDAndSource(ctx context.Context, userID string, source constant.NotificationSource) (
|
||||
conf *entity.UserNotificationConfig, exist bool, err error) {
|
||||
config := &entity.UserNotificationConfig{UserID: userID, Source: string(source)}
|
||||
exist, err = ur.data.DB.Context(ctx).Get(config)
|
||||
if err != nil {
|
||||
return nil, false, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return config, exist, nil
|
||||
}
|
||||
|
||||
// GetByUsersAndSource get notification config by user ids and source
|
||||
func (ur *userNotificationConfigRepo) GetByUsersAndSource(
|
||||
ctx context.Context, userIDs []string, source constant.NotificationSource) (
|
||||
[]*entity.UserNotificationConfig, error) {
|
||||
var configs []*entity.UserNotificationConfig
|
||||
err := ur.data.DB.Context(ctx).UseBool("enabled").In("user_id", userIDs).
|
||||
Find(&configs, &entity.UserNotificationConfig{Source: string(source), Enabled: true})
|
||||
if err != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return configs, nil
|
||||
}
|
|
@ -113,7 +113,7 @@ func (a *AnswerAPIRouter) RegisterMustUnAuthAnswerAPIRouter(r *gin.RouterGroup)
|
|||
routerGroup.PUT("/user/email", a.userController.UserChangeEmailVerify)
|
||||
routerGroup.POST("/user/password/reset", a.userController.RetrievePassWord)
|
||||
routerGroup.POST("/user/password/replacement", a.userController.UseRePassWord)
|
||||
routerGroup.PUT("/user/email/notification", a.userController.UserUnsubscribeEmailNotification)
|
||||
routerGroup.PUT("/user/notification/unsubscribe", a.userController.UserUnsubscribeNotification)
|
||||
}
|
||||
|
||||
func (a *AnswerAPIRouter) RegisterUnAuthAnswerAPIRouter(r *gin.RouterGroup) {
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package schema
|
||||
|
||||
import "encoding/json"
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
)
|
||||
|
||||
const (
|
||||
AccountActivationSourceType EmailSourceType = "account-activation"
|
||||
|
@ -16,8 +19,10 @@ type EmailCodeContent struct {
|
|||
SourceType EmailSourceType `json:"source_type"`
|
||||
Email string `json:"e_mail"`
|
||||
UserID string `json:"user_id"`
|
||||
// Used for unsubscribe notification
|
||||
NotificationSources []constant.NotificationSource `json:"notification_source,omitempty"`
|
||||
// Used for third-party login account binding
|
||||
BindingKey string `json:"binding_key"`
|
||||
BindingKey string `json:"binding_key,omitempty"`
|
||||
}
|
||||
|
||||
func (r *EmailCodeContent) ToJSONString() string {
|
||||
|
@ -80,3 +85,19 @@ type NewCommentTemplateData struct {
|
|||
CommentSummary string
|
||||
UnsubscribeUrl string
|
||||
}
|
||||
|
||||
type NewQuestionTemplateRawData struct {
|
||||
QuestionTitle string
|
||||
QuestionID string
|
||||
UnsubscribeCode string
|
||||
Tags []string
|
||||
TagIDs []string
|
||||
}
|
||||
|
||||
type NewQuestionTemplateData struct {
|
||||
SiteName string
|
||||
QuestionTitle string
|
||||
QuestionUrl string
|
||||
Tags string
|
||||
UnsubscribeUrl string
|
||||
}
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package schema
|
||||
|
||||
import (
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/pkg/uid"
|
||||
)
|
||||
|
||||
type ExternalNotificationMsg struct {
|
||||
ReceiverUserID string `json:"receiver_user_id"`
|
||||
ReceiverEmail string `json:"receiver_email"`
|
||||
ReceiverLang string `json:"receiver_lang"`
|
||||
|
||||
NewAnswerTemplateRawData *NewAnswerTemplateRawData `json:"new_answer_template_raw_data,omitempty"`
|
||||
NewInviteAnswerTemplateRawData *NewInviteAnswerTemplateRawData `json:"new_invite_answer_template_raw_data,omitempty"`
|
||||
NewCommentTemplateRawData *NewCommentTemplateRawData `json:"new_comment_template_raw_data,omitempty"`
|
||||
NewQuestionTemplateRawData *NewQuestionTemplateRawData `json:"new_question_template_raw_data,omitempty"`
|
||||
}
|
||||
|
||||
func CreateNewQuestionNotificationMsg(questionID, questionTitle string, tags []*entity.Tag) *ExternalNotificationMsg {
|
||||
questionID = uid.DeShortID(questionID)
|
||||
msg := &ExternalNotificationMsg{
|
||||
NewQuestionTemplateRawData: &NewQuestionTemplateRawData{
|
||||
QuestionID: questionID,
|
||||
QuestionTitle: questionTitle,
|
||||
},
|
||||
}
|
||||
for _, tag := range tags {
|
||||
msg.NewQuestionTemplateRawData.Tags = append(msg.NewQuestionTemplateRawData.Tags, tag.SlugName)
|
||||
msg.NewQuestionTemplateRawData.TagIDs = append(msg.NewQuestionTemplateRawData.TagIDs, tag.ID)
|
||||
}
|
||||
return msg
|
||||
}
|
|
@ -3,28 +3,35 @@ package schema
|
|||
import (
|
||||
"encoding/json"
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
)
|
||||
|
||||
type NotificationChannelConfig struct {
|
||||
Key constant.NotificationChannel `json:"key"`
|
||||
Enable bool `json:"enable"`
|
||||
Key constant.NotificationChannelKey `json:"key"`
|
||||
Enable bool `json:"enable"`
|
||||
}
|
||||
|
||||
type NotificationChannelConfigList []*NotificationChannelConfig
|
||||
type NotificationChannels []*NotificationChannelConfig
|
||||
|
||||
func (n *NotificationChannelConfigList) Format(sequences []constant.NotificationChannel) {
|
||||
func NewNotificationChannelsFormJson(jsonStr string) NotificationChannels {
|
||||
var list NotificationChannels
|
||||
_ = json.Unmarshal([]byte(jsonStr), &list)
|
||||
return list
|
||||
}
|
||||
|
||||
func (n *NotificationChannels) Format(sequences []constant.NotificationChannelKey) {
|
||||
if n == nil {
|
||||
*n = make([]*NotificationChannelConfig, 0)
|
||||
return
|
||||
}
|
||||
newList := make([]*NotificationChannelConfig, 0)
|
||||
mapping := make(map[constant.NotificationChannel]*NotificationChannelConfig)
|
||||
mapping := make(map[constant.NotificationChannelKey]*NotificationChannelConfig)
|
||||
for _, item := range *n {
|
||||
mapping[item.Key] = &NotificationChannelConfig{
|
||||
Key: item.Key,
|
||||
Enable: item.Enable,
|
||||
}
|
||||
}
|
||||
newList := make([]*NotificationChannelConfig, 0)
|
||||
for _, ch := range sequences {
|
||||
if c, ok := mapping[ch]; ok {
|
||||
newList = append(newList, c)
|
||||
|
@ -37,7 +44,7 @@ func (n *NotificationChannelConfigList) Format(sequences []constant.Notification
|
|||
*n = newList
|
||||
}
|
||||
|
||||
func (n *NotificationChannelConfigList) CheckEnable(ch constant.NotificationChannel) bool {
|
||||
func (n *NotificationChannels) CheckEnable(ch constant.NotificationChannelKey) bool {
|
||||
if n == nil {
|
||||
return false
|
||||
}
|
||||
|
@ -49,10 +56,15 @@ func (n *NotificationChannelConfigList) CheckEnable(ch constant.NotificationChan
|
|||
return false
|
||||
}
|
||||
|
||||
func (n *NotificationChannels) ToJsonString() string {
|
||||
data, _ := json.Marshal(n)
|
||||
return string(data)
|
||||
}
|
||||
|
||||
type NotificationConfig struct {
|
||||
Inbox NotificationChannelConfigList `json:"inbox"`
|
||||
AllNewQuestion NotificationChannelConfigList `json:"all_new_question"`
|
||||
AllNewQuestionForFollowingTags NotificationChannelConfigList `json:"all_new_question_for_following_tags"`
|
||||
Inbox NotificationChannels `json:"inbox"`
|
||||
AllNewQuestion NotificationChannels `json:"all_new_question"`
|
||||
AllNewQuestionForFollowingTags NotificationChannels `json:"all_new_question_for_following_tags"`
|
||||
}
|
||||
|
||||
func (n *NotificationConfig) ToJsonString() string {
|
||||
|
@ -60,9 +72,21 @@ func (n *NotificationConfig) ToJsonString() string {
|
|||
return string(data)
|
||||
}
|
||||
|
||||
func NewNotificationConfig(data string) *NotificationConfig {
|
||||
nc := &NotificationConfig{}
|
||||
nc.FromJsonString(data)
|
||||
func NewNotificationConfig(configs []*entity.UserNotificationConfig) NotificationConfig {
|
||||
nc := NotificationConfig{}
|
||||
nc.Inbox = make([]*NotificationChannelConfig, 0)
|
||||
nc.AllNewQuestion = make([]*NotificationChannelConfig, 0)
|
||||
nc.AllNewQuestionForFollowingTags = make([]*NotificationChannelConfig, 0)
|
||||
for _, item := range configs {
|
||||
switch item.Source {
|
||||
case string(constant.InboxSource):
|
||||
nc.Inbox = NewNotificationChannelsFormJson(item.Channels)
|
||||
case string(constant.AllNewQuestionSource):
|
||||
nc.AllNewQuestion = NewNotificationChannelsFormJson(item.Channels)
|
||||
case string(constant.AllNewQuestionForFollowingTagsSource):
|
||||
nc.AllNewQuestionForFollowingTags = NewNotificationChannelsFormJson(item.Channels)
|
||||
}
|
||||
}
|
||||
return nc
|
||||
}
|
||||
|
||||
|
@ -78,19 +102,19 @@ func (n *NotificationConfig) FromJsonString(data string) {
|
|||
}
|
||||
|
||||
func (n *NotificationConfig) Format() {
|
||||
n.Inbox.Format([]constant.NotificationChannel{constant.EmailChannel})
|
||||
n.AllNewQuestion.Format([]constant.NotificationChannel{constant.EmailChannel})
|
||||
n.AllNewQuestionForFollowingTags.Format([]constant.NotificationChannel{constant.EmailChannel})
|
||||
n.Inbox.Format([]constant.NotificationChannelKey{constant.EmailChannel})
|
||||
n.AllNewQuestion.Format([]constant.NotificationChannelKey{constant.EmailChannel})
|
||||
n.AllNewQuestionForFollowingTags.Format([]constant.NotificationChannelKey{constant.EmailChannel})
|
||||
}
|
||||
|
||||
func (n *NotificationConfig) CheckEnable(
|
||||
source constant.NotificationSource, channel constant.NotificationChannel) bool {
|
||||
source constant.NotificationSource, channel constant.NotificationChannelKey) bool {
|
||||
switch source {
|
||||
case constant.InboxChannel:
|
||||
case constant.InboxSource:
|
||||
return n.Inbox.CheckEnable(channel)
|
||||
case constant.AllNewQuestionChannel:
|
||||
case constant.AllNewQuestionSource:
|
||||
return n.AllNewQuestion.CheckEnable(channel)
|
||||
case constant.AllNewQuestionForFollowingTagsChannel:
|
||||
case constant.AllNewQuestionForFollowingTagsSource:
|
||||
return n.AllNewQuestionForFollowingTags.CheckEnable(channel)
|
||||
}
|
||||
return false
|
||||
|
|
|
@ -364,8 +364,8 @@ type UserRankingSimpleInfo struct {
|
|||
Avatar string `json:"avatar"`
|
||||
}
|
||||
|
||||
// UserUnsubscribeEmailNotificationReq user unsubscribe email notification request
|
||||
type UserUnsubscribeEmailNotificationReq struct {
|
||||
// UserUnsubscribeNotificationReq user unsubscribe email notification request
|
||||
type UserUnsubscribeNotificationReq struct {
|
||||
Code string `validate:"required,gt=0,lte=500" json:"code"`
|
||||
Content string `json:"-"`
|
||||
}
|
||||
|
|
|
@ -25,26 +25,26 @@ import (
|
|||
"github.com/answerdev/answer/pkg/encryption"
|
||||
"github.com/answerdev/answer/pkg/uid"
|
||||
"github.com/segmentfault/pacman/errors"
|
||||
"github.com/segmentfault/pacman/i18n"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
)
|
||||
|
||||
// AnswerService user service
|
||||
type AnswerService struct {
|
||||
answerRepo answercommon.AnswerRepo
|
||||
questionRepo questioncommon.QuestionRepo
|
||||
questionCommon *questioncommon.QuestionCommon
|
||||
answerActivityService *activity.AnswerActivityService
|
||||
userCommon *usercommon.UserCommon
|
||||
collectionCommon *collectioncommon.CollectionCommon
|
||||
userRepo usercommon.UserRepo
|
||||
revisionService *revision_common.RevisionService
|
||||
AnswerCommon *answercommon.AnswerCommon
|
||||
voteRepo activity_common.VoteRepo
|
||||
emailService *export.EmailService
|
||||
roleService *role.UserRoleRelService
|
||||
notificationQueueService notice_queue.NotificationQueueService
|
||||
activityQueueService activity_queue.ActivityQueueService
|
||||
answerRepo answercommon.AnswerRepo
|
||||
questionRepo questioncommon.QuestionRepo
|
||||
questionCommon *questioncommon.QuestionCommon
|
||||
answerActivityService *activity.AnswerActivityService
|
||||
userCommon *usercommon.UserCommon
|
||||
collectionCommon *collectioncommon.CollectionCommon
|
||||
userRepo usercommon.UserRepo
|
||||
revisionService *revision_common.RevisionService
|
||||
AnswerCommon *answercommon.AnswerCommon
|
||||
voteRepo activity_common.VoteRepo
|
||||
emailService *export.EmailService
|
||||
roleService *role.UserRoleRelService
|
||||
notificationQueueService notice_queue.NotificationQueueService
|
||||
externalNotificationQueueService notice_queue.ExternalNotificationQueueService
|
||||
activityQueueService activity_queue.ActivityQueueService
|
||||
}
|
||||
|
||||
func NewAnswerService(
|
||||
|
@ -61,23 +61,25 @@ func NewAnswerService(
|
|||
emailService *export.EmailService,
|
||||
roleService *role.UserRoleRelService,
|
||||
notificationQueueService notice_queue.NotificationQueueService,
|
||||
externalNotificationQueueService notice_queue.ExternalNotificationQueueService,
|
||||
activityQueueService activity_queue.ActivityQueueService,
|
||||
) *AnswerService {
|
||||
return &AnswerService{
|
||||
answerRepo: answerRepo,
|
||||
questionRepo: questionRepo,
|
||||
userCommon: userCommon,
|
||||
collectionCommon: collectionCommon,
|
||||
questionCommon: questionCommon,
|
||||
userRepo: userRepo,
|
||||
revisionService: revisionService,
|
||||
answerActivityService: answerAcceptActivityRepo,
|
||||
AnswerCommon: answerCommon,
|
||||
voteRepo: voteRepo,
|
||||
emailService: emailService,
|
||||
roleService: roleService,
|
||||
notificationQueueService: notificationQueueService,
|
||||
activityQueueService: activityQueueService,
|
||||
answerRepo: answerRepo,
|
||||
questionRepo: questionRepo,
|
||||
userCommon: userCommon,
|
||||
collectionCommon: collectionCommon,
|
||||
questionCommon: questionCommon,
|
||||
userRepo: userRepo,
|
||||
revisionService: revisionService,
|
||||
answerActivityService: answerAcceptActivityRepo,
|
||||
AnswerCommon: answerCommon,
|
||||
voteRepo: voteRepo,
|
||||
emailService: emailService,
|
||||
roleService: roleService,
|
||||
notificationQueueService: notificationQueueService,
|
||||
externalNotificationQueueService: externalNotificationQueueService,
|
||||
activityQueueService: activityQueueService,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -604,7 +606,7 @@ func (as *AnswerService) notificationAnswerTheQuestion(ctx context.Context,
|
|||
msg.NotificationAction = constant.NotificationAnswerTheQuestion
|
||||
as.notificationQueueService.Send(ctx, msg)
|
||||
|
||||
userInfo, exist, err := as.userRepo.GetByUserID(ctx, questionUserID)
|
||||
receiverUserInfo, exist, err := as.userRepo.GetByUserID(ctx, questionUserID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
|
@ -613,38 +615,23 @@ func (as *AnswerService) notificationAnswerTheQuestion(ctx context.Context,
|
|||
log.Warnf("user %s not found", questionUserID)
|
||||
return
|
||||
}
|
||||
if len(userInfo.EMail) == 0 ||
|
||||
schema.NewNotificationConfig(userInfo.NoticeConfig).CheckEnable(constant.InboxChannel, constant.EmailChannel) {
|
||||
return
|
||||
}
|
||||
|
||||
externalNotificationMsg := &schema.ExternalNotificationMsg{
|
||||
ReceiverUserID: receiverUserInfo.ID,
|
||||
ReceiverEmail: receiverUserInfo.EMail,
|
||||
ReceiverLang: receiverUserInfo.Language,
|
||||
}
|
||||
rawData := &schema.NewAnswerTemplateRawData{
|
||||
QuestionTitle: questionTitle,
|
||||
QuestionID: questionID,
|
||||
AnswerID: answerID,
|
||||
AnswerSummary: answerSummary,
|
||||
UnsubscribeCode: encryption.MD5(userInfo.Pass),
|
||||
UnsubscribeCode: encryption.MD5(receiverUserInfo.Pass),
|
||||
}
|
||||
answerUser, _, _ := as.userCommon.GetUserBasicInfoByID(ctx, answerUserID)
|
||||
if answerUser != nil {
|
||||
rawData.AnswerUserDisplayName = answerUser.DisplayName
|
||||
}
|
||||
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 := as.emailService.NewAnswerTemplate(ctx, rawData)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
go as.emailService.SendAndSaveCodeWithTime(
|
||||
ctx, userInfo.EMail, title, body, rawData.UnsubscribeCode, codeContent.ToJSONString(), 7*24*time.Hour)
|
||||
externalNotificationMsg.NewAnswerTemplateRawData = rawData
|
||||
as.externalNotificationQueueService.Send(ctx, externalNotificationMsg)
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
"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"
|
||||
)
|
||||
|
||||
|
@ -58,15 +57,16 @@ func (c *CommentQuery) GetOrderBy() string {
|
|||
|
||||
// CommentService user service
|
||||
type CommentService struct {
|
||||
commentRepo CommentRepo
|
||||
commentCommonRepo comment_common.CommentCommonRepo
|
||||
userCommon *usercommon.UserCommon
|
||||
voteCommon activity_common.VoteRepo
|
||||
objectInfoService *object_info.ObjService
|
||||
emailService *export.EmailService
|
||||
userRepo usercommon.UserRepo
|
||||
notificationQueueService notice_queue.NotificationQueueService
|
||||
activityQueueService activity_queue.ActivityQueueService
|
||||
commentRepo CommentRepo
|
||||
commentCommonRepo comment_common.CommentCommonRepo
|
||||
userCommon *usercommon.UserCommon
|
||||
voteCommon activity_common.VoteRepo
|
||||
objectInfoService *object_info.ObjService
|
||||
emailService *export.EmailService
|
||||
userRepo usercommon.UserRepo
|
||||
notificationQueueService notice_queue.NotificationQueueService
|
||||
externalNotificationQueueService notice_queue.ExternalNotificationQueueService
|
||||
activityQueueService activity_queue.ActivityQueueService
|
||||
}
|
||||
|
||||
// NewCommentService new comment service
|
||||
|
@ -79,18 +79,20 @@ func NewCommentService(
|
|||
emailService *export.EmailService,
|
||||
userRepo usercommon.UserRepo,
|
||||
notificationQueueService notice_queue.NotificationQueueService,
|
||||
externalNotificationQueueService notice_queue.ExternalNotificationQueueService,
|
||||
activityQueueService activity_queue.ActivityQueueService,
|
||||
) *CommentService {
|
||||
return &CommentService{
|
||||
commentRepo: commentRepo,
|
||||
commentCommonRepo: commentCommonRepo,
|
||||
userCommon: userCommon,
|
||||
voteCommon: voteCommon,
|
||||
objectInfoService: objectInfoService,
|
||||
emailService: emailService,
|
||||
userRepo: userRepo,
|
||||
notificationQueueService: notificationQueueService,
|
||||
activityQueueService: activityQueueService,
|
||||
commentRepo: commentRepo,
|
||||
commentCommonRepo: commentCommonRepo,
|
||||
userCommon: userCommon,
|
||||
voteCommon: voteCommon,
|
||||
objectInfoService: objectInfoService,
|
||||
emailService: emailService,
|
||||
userRepo: userRepo,
|
||||
notificationQueueService: notificationQueueService,
|
||||
externalNotificationQueueService: externalNotificationQueueService,
|
||||
activityQueueService: activityQueueService,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -474,6 +476,7 @@ func (cs *CommentService) notificationQuestionComment(ctx context.Context, quest
|
|||
if questionUserID == commentUserID {
|
||||
return
|
||||
}
|
||||
// send internal notification
|
||||
msg := &schema.NotificationMsg{
|
||||
ReceiverUserID: questionUserID,
|
||||
TriggerUserID: commentUserID,
|
||||
|
@ -484,6 +487,7 @@ func (cs *CommentService) notificationQuestionComment(ctx context.Context, quest
|
|||
msg.NotificationAction = constant.NotificationCommentQuestion
|
||||
cs.notificationQueueService.Send(ctx, msg)
|
||||
|
||||
// send external notification
|
||||
receiverUserInfo, exist, err := cs.userRepo.GetByUserID(ctx, questionUserID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
@ -493,12 +497,12 @@ func (cs *CommentService) notificationQuestionComment(ctx context.Context, quest
|
|||
log.Warnf("user %s not found", questionUserID)
|
||||
return
|
||||
}
|
||||
if len(receiverUserInfo.EMail) == 0 ||
|
||||
schema.NewNotificationConfig(receiverUserInfo.NoticeConfig).
|
||||
CheckEnable(constant.InboxChannel, constant.EmailChannel) {
|
||||
return
|
||||
}
|
||||
|
||||
externalNotificationMsg := &schema.ExternalNotificationMsg{
|
||||
ReceiverUserID: receiverUserInfo.ID,
|
||||
ReceiverEmail: receiverUserInfo.EMail,
|
||||
ReceiverLang: receiverUserInfo.Language,
|
||||
}
|
||||
rawData := &schema.NewCommentTemplateRawData{
|
||||
QuestionTitle: questionTitle,
|
||||
QuestionID: questionID,
|
||||
|
@ -510,24 +514,8 @@ func (cs *CommentService) notificationQuestionComment(ctx context.Context, quest
|
|||
if commentUser != nil {
|
||||
rawData.CommentUserDisplayName = commentUser.DisplayName
|
||||
}
|
||||
codeContent := &schema.EmailCodeContent{
|
||||
SourceType: schema.UnsubscribeSourceType,
|
||||
Email: receiverUserInfo.EMail,
|
||||
UserID: receiverUserInfo.ID,
|
||||
}
|
||||
|
||||
// If receiver has set language, use it to send email.
|
||||
if len(receiverUserInfo.Language) > 0 {
|
||||
ctx = context.WithValue(ctx, constant.AcceptLanguageFlag, i18n.Language(receiverUserInfo.Language))
|
||||
}
|
||||
title, body, err := cs.emailService.NewCommentTemplate(ctx, rawData)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
go cs.emailService.SendAndSaveCodeWithTime(
|
||||
ctx, receiverUserInfo.EMail, title, body, rawData.UnsubscribeCode, codeContent.ToJSONString(), 7*24*time.Hour)
|
||||
externalNotificationMsg.NewCommentTemplateRawData = rawData
|
||||
cs.externalNotificationQueueService.Send(ctx, externalNotificationMsg)
|
||||
}
|
||||
|
||||
func (cs *CommentService) notificationAnswerComment(ctx context.Context,
|
||||
|
@ -535,6 +523,8 @@ func (cs *CommentService) notificationAnswerComment(ctx context.Context,
|
|||
if answerUserID == commentUserID {
|
||||
return
|
||||
}
|
||||
|
||||
// Send internal notification.
|
||||
msg := &schema.NotificationMsg{
|
||||
ReceiverUserID: answerUserID,
|
||||
TriggerUserID: commentUserID,
|
||||
|
@ -545,6 +535,7 @@ func (cs *CommentService) notificationAnswerComment(ctx context.Context,
|
|||
msg.NotificationAction = constant.NotificationCommentAnswer
|
||||
cs.notificationQueueService.Send(ctx, msg)
|
||||
|
||||
// Send external notification.
|
||||
receiverUserInfo, exist, err := cs.userRepo.GetByUserID(ctx, answerUserID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
@ -554,12 +545,11 @@ func (cs *CommentService) notificationAnswerComment(ctx context.Context,
|
|||
log.Warnf("user %s not found", answerUserID)
|
||||
return
|
||||
}
|
||||
if len(receiverUserInfo.EMail) == 0 ||
|
||||
schema.NewNotificationConfig(receiverUserInfo.NoticeConfig).
|
||||
CheckEnable(constant.InboxChannel, constant.EmailChannel) {
|
||||
return
|
||||
externalNotificationMsg := &schema.ExternalNotificationMsg{
|
||||
ReceiverUserID: receiverUserInfo.ID,
|
||||
ReceiverEmail: receiverUserInfo.EMail,
|
||||
ReceiverLang: receiverUserInfo.Language,
|
||||
}
|
||||
|
||||
rawData := &schema.NewCommentTemplateRawData{
|
||||
QuestionTitle: questionTitle,
|
||||
QuestionID: questionID,
|
||||
|
@ -572,24 +562,8 @@ func (cs *CommentService) notificationAnswerComment(ctx context.Context,
|
|||
if commentUser != nil {
|
||||
rawData.CommentUserDisplayName = commentUser.DisplayName
|
||||
}
|
||||
codeContent := &schema.EmailCodeContent{
|
||||
SourceType: schema.UnsubscribeSourceType,
|
||||
Email: receiverUserInfo.EMail,
|
||||
UserID: receiverUserInfo.ID,
|
||||
}
|
||||
|
||||
// If receiver has set language, use it to send email.
|
||||
if len(receiverUserInfo.Language) > 0 {
|
||||
ctx = context.WithValue(ctx, constant.AcceptLanguageFlag, i18n.Language(receiverUserInfo.Language))
|
||||
}
|
||||
title, body, err := cs.emailService.NewCommentTemplate(ctx, rawData)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
go cs.emailService.SendAndSaveCodeWithTime(
|
||||
ctx, receiverUserInfo.EMail, title, body, rawData.UnsubscribeCode, codeContent.ToJSONString(), 7*24*time.Hour)
|
||||
externalNotificationMsg.NewCommentTemplateRawData = rawData
|
||||
cs.externalNotificationQueueService.Send(ctx, externalNotificationMsg)
|
||||
}
|
||||
|
||||
func (cs *CommentService) notificationCommentReply(ctx context.Context, replyUserID, commentID, commentUserID string) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"fmt"
|
||||
"mime"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
|
@ -149,7 +150,7 @@ func (es *EmailService) VerifyUrlExpired(ctx context.Context, code string) (cont
|
|||
return content
|
||||
}
|
||||
|
||||
func (es *EmailService) GetSiteGeneral(ctx context.Context) (resp schema.SiteGeneralResp, err error) {
|
||||
func (es *EmailService) getSiteGeneral(ctx context.Context) (resp schema.SiteGeneralResp, err error) {
|
||||
var (
|
||||
siteType = "general"
|
||||
siteInfo *entity.SiteInfo
|
||||
|
@ -167,7 +168,7 @@ func (es *EmailService) GetSiteGeneral(ctx context.Context) (resp schema.SiteGen
|
|||
}
|
||||
|
||||
func (es *EmailService) RegisterTemplate(ctx context.Context, registerUrl string) (title, body string, err error) {
|
||||
siteInfo, err := es.GetSiteGeneral(ctx)
|
||||
siteInfo, err := es.getSiteGeneral(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -183,7 +184,7 @@ func (es *EmailService) RegisterTemplate(ctx context.Context, registerUrl string
|
|||
}
|
||||
|
||||
func (es *EmailService) PassResetTemplate(ctx context.Context, passResetUrl string) (title, body string, err error) {
|
||||
siteInfo, err := es.GetSiteGeneral(ctx)
|
||||
siteInfo, err := es.getSiteGeneral(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -197,7 +198,7 @@ func (es *EmailService) PassResetTemplate(ctx context.Context, passResetUrl stri
|
|||
}
|
||||
|
||||
func (es *EmailService) ChangeEmailTemplate(ctx context.Context, changeEmailUrl string) (title, body string, err error) {
|
||||
siteInfo, err := es.GetSiteGeneral(ctx)
|
||||
siteInfo, err := es.getSiteGeneral(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -214,7 +215,7 @@ func (es *EmailService) ChangeEmailTemplate(ctx context.Context, changeEmailUrl
|
|||
|
||||
// TestTemplate send test email template parse
|
||||
func (es *EmailService) TestTemplate(ctx context.Context) (title, body string, err error) {
|
||||
siteInfo, err := es.GetSiteGeneral(ctx)
|
||||
siteInfo, err := es.getSiteGeneral(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -229,7 +230,7 @@ func (es *EmailService) TestTemplate(ctx context.Context) (title, body string, e
|
|||
// NewAnswerTemplate new answer template
|
||||
func (es *EmailService) NewAnswerTemplate(ctx context.Context, raw *schema.NewAnswerTemplateRawData) (
|
||||
title, body string, err error) {
|
||||
siteInfo, err := es.GetSiteGeneral(ctx)
|
||||
siteInfo, err := es.getSiteGeneral(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -251,7 +252,7 @@ func (es *EmailService) NewAnswerTemplate(ctx context.Context, raw *schema.NewAn
|
|||
// 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)
|
||||
siteInfo, err := es.getSiteGeneral(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -272,7 +273,7 @@ func (es *EmailService) NewInviteAnswerTemplate(ctx context.Context, raw *schema
|
|||
// NewCommentTemplate new comment template
|
||||
func (es *EmailService) NewCommentTemplate(ctx context.Context, raw *schema.NewCommentTemplateRawData) (
|
||||
title, body string, err error) {
|
||||
siteInfo, err := es.GetSiteGeneral(ctx)
|
||||
siteInfo, err := es.getSiteGeneral(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
@ -297,6 +298,27 @@ func (es *EmailService) NewCommentTemplate(ctx context.Context, raw *schema.NewC
|
|||
return title, body, nil
|
||||
}
|
||||
|
||||
// NewQuestionTemplate new question template
|
||||
func (es *EmailService) NewQuestionTemplate(ctx context.Context, raw *schema.NewQuestionTemplateRawData) (
|
||||
title, body string, err error) {
|
||||
siteInfo, err := es.getSiteGeneral(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
templateData := &schema.NewQuestionTemplateData{
|
||||
SiteName: siteInfo.Name,
|
||||
QuestionTitle: raw.QuestionTitle,
|
||||
Tags: strings.Join(raw.Tags, ", "),
|
||||
UnsubscribeUrl: fmt.Sprintf("%s/users/unsubscribe?code=%s", siteInfo.SiteUrl, raw.UnsubscribeCode),
|
||||
}
|
||||
templateData.QuestionUrl = fmt.Sprintf("%s/questions/%s", siteInfo.SiteUrl, raw.QuestionID)
|
||||
|
||||
lang := handler.GetLangByCtx(ctx)
|
||||
title = translator.TrWithData(lang, constant.EmailTplKeyNewQuestionTitle, templateData)
|
||||
body = translator.TrWithData(lang, constant.EmailTplKeyNewQuestionBody, templateData)
|
||||
return title, body, nil
|
||||
}
|
||||
|
||||
func (es *EmailService) GetEmailConfig(ctx context.Context) (ec *EmailConfig, err error) {
|
||||
emailConf, err := es.configService.GetStringValue(ctx, "email.config")
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package notice_queue
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
)
|
||||
|
||||
type ExternalNotificationQueueService interface {
|
||||
Send(ctx context.Context, msg *schema.ExternalNotificationMsg)
|
||||
RegisterHandler(handler func(ctx context.Context, msg *schema.ExternalNotificationMsg) error)
|
||||
}
|
||||
|
||||
type externalNotificationQueueService struct {
|
||||
Queue chan *schema.ExternalNotificationMsg
|
||||
Handler func(ctx context.Context, msg *schema.ExternalNotificationMsg) error
|
||||
}
|
||||
|
||||
func (ns *externalNotificationQueueService) Send(ctx context.Context, msg *schema.ExternalNotificationMsg) {
|
||||
ns.Queue <- msg
|
||||
}
|
||||
|
||||
func (ns *externalNotificationQueueService) RegisterHandler(
|
||||
handler func(ctx context.Context, msg *schema.ExternalNotificationMsg) error) {
|
||||
ns.Handler = handler
|
||||
}
|
||||
|
||||
func (ns *externalNotificationQueueService) working() {
|
||||
go func() {
|
||||
for msg := range ns.Queue {
|
||||
log.Debugf("received notification %+v", msg)
|
||||
if ns.Handler == nil {
|
||||
log.Warnf("no handler for notification")
|
||||
continue
|
||||
}
|
||||
if err := ns.Handler(context.Background(), msg); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// NewNewQuestionNotificationQueueService create a new notification queue service
|
||||
func NewNewQuestionNotificationQueueService() ExternalNotificationQueueService {
|
||||
ns := &externalNotificationQueueService{}
|
||||
ns.Queue = make(chan *schema.ExternalNotificationMsg, 128)
|
||||
ns.working()
|
||||
return ns
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/answerdev/answer/internal/base/data"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/answerdev/answer/internal/service/activity_common"
|
||||
"github.com/answerdev/answer/internal/service/export"
|
||||
"github.com/answerdev/answer/internal/service/notice_queue"
|
||||
usercommon "github.com/answerdev/answer/internal/service/user_common"
|
||||
"github.com/answerdev/answer/internal/service/user_notification_config"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
)
|
||||
|
||||
type ExternalNotificationService struct {
|
||||
data *data.Data
|
||||
userNotificationConfigRepo user_notification_config.UserNotificationConfigRepo
|
||||
followRepo activity_common.FollowRepo
|
||||
emailService *export.EmailService
|
||||
userRepo usercommon.UserRepo
|
||||
notificationQueueService notice_queue.ExternalNotificationQueueService
|
||||
}
|
||||
|
||||
func NewExternalNotificationService(
|
||||
userNotificationConfigRepo user_notification_config.UserNotificationConfigRepo,
|
||||
followRepo activity_common.FollowRepo,
|
||||
emailService *export.EmailService,
|
||||
userRepo usercommon.UserRepo,
|
||||
notificationQueueService notice_queue.ExternalNotificationQueueService,
|
||||
) *ExternalNotificationService {
|
||||
n := &ExternalNotificationService{
|
||||
userNotificationConfigRepo: userNotificationConfigRepo,
|
||||
followRepo: followRepo,
|
||||
emailService: emailService,
|
||||
userRepo: userRepo,
|
||||
notificationQueueService: notificationQueueService,
|
||||
}
|
||||
notificationQueueService.RegisterHandler(n.Handler)
|
||||
return n
|
||||
}
|
||||
|
||||
func (ns *ExternalNotificationService) Handler(ctx context.Context, msg *schema.ExternalNotificationMsg) error {
|
||||
log.Debugf("try to send external notification %+v", msg)
|
||||
|
||||
if msg.NewQuestionTemplateRawData != nil {
|
||||
return ns.handleNewQuestionNotification(ctx, msg)
|
||||
}
|
||||
if msg.NewCommentTemplateRawData != nil {
|
||||
return ns.handleNewCommentNotification(ctx, msg)
|
||||
}
|
||||
if msg.NewAnswerTemplateRawData != nil {
|
||||
return ns.handleNewAnswerNotification(ctx, msg)
|
||||
}
|
||||
if msg.NewInviteAnswerTemplateRawData != nil {
|
||||
return ns.handleInviteAnswerNotification(ctx, msg)
|
||||
}
|
||||
log.Errorf("unknown notification message: %+v", msg)
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/segmentfault/pacman/i18n"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (ns *ExternalNotificationService) handleInviteAnswerNotification(ctx context.Context,
|
||||
msg *schema.ExternalNotificationMsg) error {
|
||||
log.Debugf("try to send invite answer notification %+v", msg)
|
||||
|
||||
notificationConfig, exist, err := ns.userNotificationConfigRepo.GetByUserIDAndSource(ctx, msg.ReceiverUserID, constant.InboxSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return nil
|
||||
}
|
||||
channels := schema.NewNotificationChannelsFormJson(notificationConfig.Channels)
|
||||
for _, channel := range channels {
|
||||
if !channel.Enable {
|
||||
continue
|
||||
}
|
||||
switch channel.Key {
|
||||
case constant.EmailChannel:
|
||||
ns.sendInviteAnswerNotificationEmail(ctx, msg.ReceiverUserID, msg.ReceiverEmail, msg.ReceiverLang, msg.NewInviteAnswerTemplateRawData)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ExternalNotificationService) sendInviteAnswerNotificationEmail(ctx context.Context,
|
||||
userID, email, lang string, rawData *schema.NewInviteAnswerTemplateRawData) {
|
||||
codeContent := &schema.EmailCodeContent{
|
||||
SourceType: schema.UnsubscribeSourceType,
|
||||
NotificationSources: []constant.NotificationSource{
|
||||
constant.InboxSource,
|
||||
},
|
||||
Email: email,
|
||||
UserID: userID,
|
||||
}
|
||||
|
||||
// If receiver has set language, use it to send email.
|
||||
if len(lang) > 0 {
|
||||
ctx = context.WithValue(ctx, constant.AcceptLanguageFlag, i18n.Language(lang))
|
||||
}
|
||||
title, body, err := ns.emailService.NewInviteAnswerTemplate(ctx, rawData)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
ns.emailService.SendAndSaveCodeWithTime(
|
||||
ctx, email, title, body, rawData.UnsubscribeCode, codeContent.ToJSONString(), 1*24*time.Hour)
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/segmentfault/pacman/i18n"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (ns *ExternalNotificationService) handleNewAnswerNotification(ctx context.Context,
|
||||
msg *schema.ExternalNotificationMsg) error {
|
||||
log.Debugf("try to send new comment notification %+v", msg)
|
||||
|
||||
notificationConfig, exist, err := ns.userNotificationConfigRepo.GetByUserIDAndSource(ctx, msg.ReceiverUserID, constant.InboxSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return nil
|
||||
}
|
||||
channels := schema.NewNotificationChannelsFormJson(notificationConfig.Channels)
|
||||
for _, channel := range channels {
|
||||
if !channel.Enable {
|
||||
continue
|
||||
}
|
||||
switch channel.Key {
|
||||
case constant.EmailChannel:
|
||||
ns.sendNewAnswerNotificationEmail(ctx, msg.ReceiverUserID, msg.ReceiverEmail, msg.ReceiverLang, msg.NewAnswerTemplateRawData)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ExternalNotificationService) sendNewAnswerNotificationEmail(ctx context.Context,
|
||||
userID, email, lang string, rawData *schema.NewAnswerTemplateRawData) {
|
||||
codeContent := &schema.EmailCodeContent{
|
||||
SourceType: schema.UnsubscribeSourceType,
|
||||
NotificationSources: []constant.NotificationSource{
|
||||
constant.InboxSource,
|
||||
},
|
||||
Email: email,
|
||||
UserID: userID,
|
||||
}
|
||||
|
||||
// If receiver has set language, use it to send email.
|
||||
if len(lang) > 0 {
|
||||
ctx = context.WithValue(ctx, constant.AcceptLanguageFlag, i18n.Language(lang))
|
||||
}
|
||||
title, body, err := ns.emailService.NewAnswerTemplate(ctx, rawData)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
ns.emailService.SendAndSaveCodeWithTime(
|
||||
ctx, email, title, body, rawData.UnsubscribeCode, codeContent.ToJSONString(), 1*24*time.Hour)
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/segmentfault/pacman/i18n"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (ns *ExternalNotificationService) handleNewCommentNotification(ctx context.Context,
|
||||
msg *schema.ExternalNotificationMsg) error {
|
||||
log.Debugf("try to send new comment notification %+v", msg)
|
||||
|
||||
notificationConfig, exist, err := ns.userNotificationConfigRepo.GetByUserIDAndSource(ctx, msg.ReceiverUserID, constant.InboxSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return nil
|
||||
}
|
||||
channels := schema.NewNotificationChannelsFormJson(notificationConfig.Channels)
|
||||
for _, channel := range channels {
|
||||
if !channel.Enable {
|
||||
continue
|
||||
}
|
||||
switch channel.Key {
|
||||
case constant.EmailChannel:
|
||||
ns.sendNewCommentNotificationEmail(ctx, msg.ReceiverUserID, msg.ReceiverEmail, msg.ReceiverLang, msg.NewCommentTemplateRawData)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ExternalNotificationService) sendNewCommentNotificationEmail(ctx context.Context,
|
||||
userID, email, lang string, rawData *schema.NewCommentTemplateRawData) {
|
||||
codeContent := &schema.EmailCodeContent{
|
||||
SourceType: schema.UnsubscribeSourceType,
|
||||
NotificationSources: []constant.NotificationSource{
|
||||
constant.InboxSource,
|
||||
},
|
||||
Email: email,
|
||||
UserID: userID,
|
||||
}
|
||||
// If receiver has set language, use it to send email.
|
||||
if len(lang) > 0 {
|
||||
ctx = context.WithValue(ctx, constant.AcceptLanguageFlag, i18n.Language(lang))
|
||||
}
|
||||
title, body, err := ns.emailService.NewCommentTemplate(ctx, rawData)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
ns.emailService.SendAndSaveCodeWithTime(
|
||||
ctx, email, title, body, rawData.UnsubscribeCode, codeContent.ToJSONString(), 1*24*time.Hour)
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
package notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
"github.com/segmentfault/pacman/i18n"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"time"
|
||||
)
|
||||
|
||||
type NewQuestionSubscriber struct {
|
||||
UserID string `json:"user_id"`
|
||||
Channels schema.NotificationChannels `json:"channels"`
|
||||
}
|
||||
|
||||
func (ns *ExternalNotificationService) handleNewQuestionNotification(ctx context.Context,
|
||||
msg *schema.ExternalNotificationMsg) error {
|
||||
log.Debugf("try to send new question notification %+v", msg)
|
||||
subscribers, err := ns.getNewQuestionSubscribers(ctx, msg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debugf("get subscribers %d for question %s", len(subscribers), msg.NewQuestionTemplateRawData.QuestionID)
|
||||
|
||||
for _, subscriber := range subscribers {
|
||||
for _, channel := range subscriber.Channels {
|
||||
if !channel.Enable {
|
||||
continue
|
||||
}
|
||||
switch channel.Key {
|
||||
case constant.EmailChannel:
|
||||
ns.sendNewQuestionNotificationEmail(ctx, subscriber.UserID, &schema.NewQuestionTemplateRawData{
|
||||
QuestionTitle: msg.NewQuestionTemplateRawData.QuestionTitle,
|
||||
QuestionID: msg.NewQuestionTemplateRawData.QuestionID,
|
||||
Tags: msg.NewQuestionTemplateRawData.Tags,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ExternalNotificationService) getNewQuestionSubscribers(ctx context.Context, msg *schema.ExternalNotificationMsg) (
|
||||
subscribers []*NewQuestionSubscriber, err error) {
|
||||
subscribersMapping := make(map[string]*NewQuestionSubscriber)
|
||||
|
||||
// 1. get all this new question's tags followers
|
||||
tagsFollowerIDs := make([]string, 0)
|
||||
followerMapping := make(map[string]bool)
|
||||
for _, tagID := range msg.NewQuestionTemplateRawData.TagIDs {
|
||||
userIDs, err := ns.followRepo.GetFollowUserIDs(ctx, tagID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
continue
|
||||
}
|
||||
for _, userID := range userIDs {
|
||||
if _, ok := followerMapping[userID]; ok {
|
||||
continue
|
||||
}
|
||||
followerMapping[userID] = true
|
||||
tagsFollowerIDs = append(tagsFollowerIDs, userID)
|
||||
}
|
||||
}
|
||||
userNotificationConfigs, err := ns.userNotificationConfigRepo.GetByUsersAndSource(
|
||||
ctx, tagsFollowerIDs, constant.AllNewQuestionForFollowingTagsSource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, userNotificationConfig := range userNotificationConfigs {
|
||||
if _, ok := subscribersMapping[userNotificationConfig.UserID]; ok {
|
||||
continue
|
||||
}
|
||||
subscribersMapping[userNotificationConfig.UserID] = &NewQuestionSubscriber{
|
||||
UserID: userNotificationConfig.UserID,
|
||||
Channels: schema.NewNotificationChannelsFormJson(userNotificationConfig.Channels),
|
||||
}
|
||||
subscribers = append(subscribers, subscribersMapping[userNotificationConfig.UserID])
|
||||
}
|
||||
log.Debugf("get %d subscribers from tags", len(subscribersMapping))
|
||||
|
||||
// 2. get all new question's followers
|
||||
notificationConfigs, err := ns.userNotificationConfigRepo.GetBySource(ctx, constant.AllNewQuestionSource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, notificationConfig := range notificationConfigs {
|
||||
if _, ok := subscribersMapping[notificationConfig.UserID]; ok {
|
||||
continue
|
||||
}
|
||||
if ns.checkSendNewQuestionNotificationEmailLimit(ctx, notificationConfig.UserID) {
|
||||
continue
|
||||
}
|
||||
subscribersMapping[notificationConfig.UserID] = &NewQuestionSubscriber{
|
||||
UserID: notificationConfig.UserID,
|
||||
Channels: schema.NewNotificationChannelsFormJson(notificationConfig.Channels),
|
||||
}
|
||||
subscribers = append(subscribers, subscribersMapping[notificationConfig.UserID])
|
||||
}
|
||||
log.Debugf("get %d subscribers from all new question config", len(subscribers))
|
||||
return subscribers, nil
|
||||
}
|
||||
|
||||
func (ns *ExternalNotificationService) checkSendNewQuestionNotificationEmailLimit(ctx context.Context, userID string) bool {
|
||||
// TODO: check if reach send limit
|
||||
return false
|
||||
}
|
||||
|
||||
func (ns *ExternalNotificationService) sendNewQuestionNotificationEmail(ctx context.Context,
|
||||
userID string, rawData *schema.NewQuestionTemplateRawData) {
|
||||
userInfo, exist, err := ns.userRepo.GetByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
if !exist {
|
||||
log.Errorf("user %s not exist", userID)
|
||||
return
|
||||
}
|
||||
// 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 := ns.emailService.NewQuestionTemplate(ctx, rawData)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
codeContent := &schema.EmailCodeContent{
|
||||
SourceType: schema.UnsubscribeSourceType,
|
||||
Email: userInfo.EMail,
|
||||
UserID: userID,
|
||||
NotificationSources: []constant.NotificationSource{
|
||||
constant.AllNewQuestionSource,
|
||||
constant.AllNewQuestionForFollowingTagsSource,
|
||||
},
|
||||
}
|
||||
ns.emailService.SendAndSaveCodeWithTime(
|
||||
ctx, userInfo.EMail, title, body, rawData.UnsubscribeCode, codeContent.ToJSONString(), 1*24*time.Hour)
|
||||
}
|
|
@ -37,6 +37,7 @@ import (
|
|||
"github.com/answerdev/answer/internal/service/user_admin"
|
||||
usercommon "github.com/answerdev/answer/internal/service/user_common"
|
||||
"github.com/answerdev/answer/internal/service/user_external_login"
|
||||
"github.com/answerdev/answer/internal/service/user_notification_config"
|
||||
"github.com/google/wire"
|
||||
)
|
||||
|
||||
|
@ -90,4 +91,7 @@ var ProviderSetService = wire.NewSet(
|
|||
config.NewConfigService,
|
||||
notice_queue.NewNotificationQueueService,
|
||||
activity_queue.NewActivityQueueService,
|
||||
user_notification_config.NewUserNotificationConfigService,
|
||||
notification.NewExternalNotificationService,
|
||||
notice_queue.NewNewQuestionNotificationQueueService,
|
||||
)
|
||||
|
|
|
@ -3,6 +3,7 @@ package service
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/answerdev/answer/internal/service/notification"
|
||||
"github.com/answerdev/answer/internal/service/siteinfo_common"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -32,7 +33,6 @@ import (
|
|||
"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"
|
||||
)
|
||||
|
@ -41,19 +41,21 @@ import (
|
|||
|
||||
// QuestionService user service
|
||||
type QuestionService struct {
|
||||
questionRepo questioncommon.QuestionRepo
|
||||
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
|
||||
emailService *export.EmailService
|
||||
notificationQueueService notice_queue.NotificationQueueService
|
||||
activityQueueService activity_queue.ActivityQueueService
|
||||
siteInfoService siteinfo_common.SiteInfoCommonService
|
||||
questionRepo questioncommon.QuestionRepo
|
||||
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
|
||||
emailService *export.EmailService
|
||||
notificationQueueService notice_queue.NotificationQueueService
|
||||
externalNotificationQueueService notice_queue.ExternalNotificationQueueService
|
||||
activityQueueService activity_queue.ActivityQueueService
|
||||
siteInfoService siteinfo_common.SiteInfoCommonService
|
||||
newQuestionNotificationService *notification.ExternalNotificationService
|
||||
}
|
||||
|
||||
func NewQuestionService(
|
||||
|
@ -68,23 +70,27 @@ func NewQuestionService(
|
|||
answerActivityService *activity.AnswerActivityService,
|
||||
emailService *export.EmailService,
|
||||
notificationQueueService notice_queue.NotificationQueueService,
|
||||
externalNotificationQueueService notice_queue.ExternalNotificationQueueService,
|
||||
activityQueueService activity_queue.ActivityQueueService,
|
||||
siteInfoService siteinfo_common.SiteInfoCommonService,
|
||||
newQuestionNotificationService *notification.ExternalNotificationService,
|
||||
) *QuestionService {
|
||||
return &QuestionService{
|
||||
questionRepo: questionRepo,
|
||||
tagCommon: tagCommon,
|
||||
questioncommon: questioncommon,
|
||||
userCommon: userCommon,
|
||||
userRepo: userRepo,
|
||||
revisionService: revisionService,
|
||||
metaService: metaService,
|
||||
collectionCommon: collectionCommon,
|
||||
answerActivityService: answerActivityService,
|
||||
emailService: emailService,
|
||||
notificationQueueService: notificationQueueService,
|
||||
activityQueueService: activityQueueService,
|
||||
siteInfoService: siteInfoService,
|
||||
questionRepo: questionRepo,
|
||||
tagCommon: tagCommon,
|
||||
questioncommon: questioncommon,
|
||||
userCommon: userCommon,
|
||||
userRepo: userRepo,
|
||||
revisionService: revisionService,
|
||||
metaService: metaService,
|
||||
collectionCommon: collectionCommon,
|
||||
answerActivityService: answerActivityService,
|
||||
emailService: emailService,
|
||||
notificationQueueService: notificationQueueService,
|
||||
externalNotificationQueueService: externalNotificationQueueService,
|
||||
activityQueueService: activityQueueService,
|
||||
siteInfoService: siteInfoService,
|
||||
newQuestionNotificationService: newQuestionNotificationService,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,12 +247,12 @@ func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.Question
|
|||
tag.SlugName = strings.ReplaceAll(tag.SlugName, " ", "-")
|
||||
tagNameList = append(tagNameList, tag.SlugName)
|
||||
}
|
||||
Tags, tagerr := qs.tagCommon.GetTagListByNames(ctx, tagNameList)
|
||||
tags, tagerr := qs.tagCommon.GetTagListByNames(ctx, tagNameList)
|
||||
if tagerr != nil {
|
||||
return questionInfo, tagerr
|
||||
}
|
||||
if !req.QuestionPermission.CanUseReservedTag {
|
||||
taglist, err := qs.AddQuestionCheckTags(ctx, Tags)
|
||||
taglist, err := qs.AddQuestionCheckTags(ctx, tags)
|
||||
errMsg := fmt.Sprintf(`"%s" can only be used by moderators.`,
|
||||
strings.Join(taglist, ","))
|
||||
if err != nil {
|
||||
|
@ -296,7 +302,7 @@ func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.Question
|
|||
Title: question.Title,
|
||||
}
|
||||
|
||||
questionWithTagsRevision, err := qs.changeQuestionToRevision(ctx, question, Tags)
|
||||
questionWithTagsRevision, err := qs.changeQuestionToRevision(ctx, question, tags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -326,6 +332,9 @@ func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.Question
|
|||
RevisionID: revisionID,
|
||||
})
|
||||
|
||||
qs.externalNotificationQueueService.Send(ctx,
|
||||
schema.CreateNewQuestionNotificationMsg(question.ID, question.Title, tags))
|
||||
|
||||
questionInfo, err = qs.GetQuestion(ctx, question.ID, question.UserID, req.QuestionPermission)
|
||||
return
|
||||
}
|
||||
|
@ -640,41 +649,24 @@ func (qs *QuestionService) notificationInviteUser(
|
|||
msg.NotificationAction = constant.NotificationInvitedYouToAnswer
|
||||
qs.notificationQueueService.Send(ctx, msg)
|
||||
|
||||
userInfo, ok := invitee[userID]
|
||||
receiverUserInfo, ok := invitee[userID]
|
||||
if !ok {
|
||||
log.Warnf("user %s not found", userID)
|
||||
return
|
||||
}
|
||||
if len(userInfo.EMail) == 0 ||
|
||||
schema.NewNotificationConfig(userInfo.NoticeConfig).
|
||||
CheckEnable(constant.InboxChannel, constant.EmailChannel) {
|
||||
return
|
||||
externalNotificationMsg := &schema.ExternalNotificationMsg{
|
||||
ReceiverUserID: receiverUserInfo.ID,
|
||||
ReceiverEmail: receiverUserInfo.EMail,
|
||||
ReceiverLang: receiverUserInfo.Language,
|
||||
}
|
||||
|
||||
rawData := &schema.NewInviteAnswerTemplateRawData{
|
||||
InviterDisplayName: inviter.DisplayName,
|
||||
QuestionTitle: questionTitle,
|
||||
QuestionID: questionID,
|
||||
UnsubscribeCode: encryption.MD5(userInfo.Pass),
|
||||
UnsubscribeCode: encryption.MD5(receiverUserInfo.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)
|
||||
externalNotificationMsg.NewInviteAnswerTemplateRawData = rawData
|
||||
qs.externalNotificationQueueService.Send(ctx, externalNotificationMsg)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ type UserRepo interface {
|
|||
UpdateLastLoginDate(ctx context.Context, userID string) (err error)
|
||||
UpdateEmailStatus(ctx context.Context, userID string, emailStatus int) error
|
||||
UpdateNoticeStatus(ctx context.Context, userID string, noticeStatus int) error
|
||||
UpdateNoticeConfig(ctx context.Context, userID string, noticeConfig string) error
|
||||
UpdateEmail(ctx context.Context, userID, email string) error
|
||||
UpdateLanguage(ctx context.Context, userID, language string) error
|
||||
UpdatePass(ctx context.Context, userID, pass string) error
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
package user_notification_config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
"github.com/answerdev/answer/internal/schema"
|
||||
usercommon "github.com/answerdev/answer/internal/service/user_common"
|
||||
)
|
||||
|
||||
type UserNotificationConfigRepo interface {
|
||||
Save(ctx context.Context, uc *entity.UserNotificationConfig) (err error)
|
||||
GetByUserID(ctx context.Context, userID string) ([]*entity.UserNotificationConfig, error)
|
||||
GetBySource(ctx context.Context, source constant.NotificationSource) ([]*entity.UserNotificationConfig, error)
|
||||
GetByUserIDAndSource(ctx context.Context, userID string, source constant.NotificationSource) (
|
||||
conf *entity.UserNotificationConfig, exist bool, err error)
|
||||
GetByUsersAndSource(ctx context.Context, userIDs []string, source constant.NotificationSource) (
|
||||
[]*entity.UserNotificationConfig, error)
|
||||
}
|
||||
|
||||
type UserNotificationConfigService struct {
|
||||
userRepo usercommon.UserRepo
|
||||
userNotificationConfigRepo UserNotificationConfigRepo
|
||||
}
|
||||
|
||||
func NewUserNotificationConfigService(
|
||||
userRepo usercommon.UserRepo,
|
||||
userNotificationConfigRepo UserNotificationConfigRepo,
|
||||
) *UserNotificationConfigService {
|
||||
return &UserNotificationConfigService{
|
||||
userRepo: userRepo,
|
||||
userNotificationConfigRepo: userNotificationConfigRepo,
|
||||
}
|
||||
}
|
||||
|
||||
func (us *UserNotificationConfigService) GetUserNotificationConfig(ctx context.Context, userID string) (
|
||||
resp *schema.GetUserNotificationConfigResp, err error) {
|
||||
notificationConfigs, err := us.userNotificationConfigRepo.GetByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp = &schema.GetUserNotificationConfigResp{}
|
||||
resp.NotificationConfig = schema.NewNotificationConfig(notificationConfigs)
|
||||
resp.Format()
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (us *UserNotificationConfigService) UpdateUserNotificationConfig(
|
||||
ctx context.Context, req *schema.UpdateUserNotificationConfigReq) (err error) {
|
||||
req.NotificationConfig.Format()
|
||||
|
||||
err = us.userNotificationConfigRepo.Save(ctx,
|
||||
us.convertToEntity(ctx, req.UserID, constant.InboxSource, req.NotificationConfig.Inbox))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = us.userNotificationConfigRepo.Save(ctx,
|
||||
us.convertToEntity(ctx, req.UserID, constant.AllNewQuestionSource, req.NotificationConfig.AllNewQuestion))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = us.userNotificationConfigRepo.Save(ctx,
|
||||
us.convertToEntity(ctx, req.UserID, constant.AllNewQuestionForFollowingTagsSource,
|
||||
req.NotificationConfig.AllNewQuestionForFollowingTags))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (us *UserNotificationConfigService) convertToEntity(ctx context.Context, userID string,
|
||||
source constant.NotificationSource, channels schema.NotificationChannels) (c *entity.UserNotificationConfig) {
|
||||
c = &entity.UserNotificationConfig{
|
||||
UserID: userID,
|
||||
Source: string(source),
|
||||
Channels: channels.ToJsonString(),
|
||||
}
|
||||
for _, ch := range channels {
|
||||
if ch.Enable {
|
||||
c.Enabled = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (us *UserNotificationConfigService) CheckEnable(
|
||||
ctx context.Context, userID string, source constant.NotificationSource,
|
||||
channel constant.NotificationChannelKey) (enable bool, err error) {
|
||||
conf, exist, err := us.userNotificationConfigRepo.GetByUserIDAndSource(ctx, userID, source)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !exist {
|
||||
return false, nil
|
||||
}
|
||||
notificationChannels := schema.NewNotificationChannelsFormJson(conf.Channels)
|
||||
return notificationChannels.CheckEnable(channel), nil
|
||||
}
|
|
@ -4,6 +4,8 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/answerdev/answer/internal/base/constant"
|
||||
"github.com/answerdev/answer/internal/service/user_notification_config"
|
||||
"time"
|
||||
|
||||
"github.com/answerdev/answer/internal/base/handler"
|
||||
|
@ -30,15 +32,16 @@ import (
|
|||
|
||||
// UserService user service
|
||||
type UserService struct {
|
||||
userCommonService *usercommon.UserCommon
|
||||
userRepo usercommon.UserRepo
|
||||
userActivity activity.UserActiveActivityRepo
|
||||
activityRepo activity_common.ActivityRepo
|
||||
emailService *export.EmailService
|
||||
authService *auth.AuthService
|
||||
siteInfoService siteinfo_common.SiteInfoCommonService
|
||||
userRoleService *role.UserRoleRelService
|
||||
userExternalLoginService *user_external_login.UserExternalLoginService
|
||||
userCommonService *usercommon.UserCommon
|
||||
userRepo usercommon.UserRepo
|
||||
userActivity activity.UserActiveActivityRepo
|
||||
activityRepo activity_common.ActivityRepo
|
||||
emailService *export.EmailService
|
||||
authService *auth.AuthService
|
||||
siteInfoService siteinfo_common.SiteInfoCommonService
|
||||
userRoleService *role.UserRoleRelService
|
||||
userExternalLoginService *user_external_login.UserExternalLoginService
|
||||
userNotificationConfigRepo user_notification_config.UserNotificationConfigRepo
|
||||
}
|
||||
|
||||
func NewUserService(userRepo usercommon.UserRepo,
|
||||
|
@ -50,17 +53,19 @@ func NewUserService(userRepo usercommon.UserRepo,
|
|||
userRoleService *role.UserRoleRelService,
|
||||
userCommonService *usercommon.UserCommon,
|
||||
userExternalLoginService *user_external_login.UserExternalLoginService,
|
||||
userNotificationConfigRepo user_notification_config.UserNotificationConfigRepo,
|
||||
) *UserService {
|
||||
return &UserService{
|
||||
userCommonService: userCommonService,
|
||||
userRepo: userRepo,
|
||||
userActivity: userActivity,
|
||||
activityRepo: activityRepo,
|
||||
emailService: emailService,
|
||||
authService: authService,
|
||||
siteInfoService: siteInfoService,
|
||||
userRoleService: userRoleService,
|
||||
userExternalLoginService: userExternalLoginService,
|
||||
userCommonService: userCommonService,
|
||||
userRepo: userRepo,
|
||||
userActivity: userActivity,
|
||||
activityRepo: activityRepo,
|
||||
emailService: emailService,
|
||||
authService: authService,
|
||||
siteInfoService: siteInfoService,
|
||||
userRoleService: userRoleService,
|
||||
userExternalLoginService: userExternalLoginService,
|
||||
userNotificationConfigRepo: userNotificationConfigRepo,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -459,33 +464,6 @@ func (us *UserService) UserVerifyEmailSend(ctx context.Context, userID string) e
|
|||
return nil
|
||||
}
|
||||
|
||||
func (us *UserService) GetUserNotificationConfig(ctx context.Context, userID string) (
|
||||
resp *schema.GetUserNotificationConfigResp, err error) {
|
||||
userInfo, exist, err := us.userRepo.GetByUserID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !exist {
|
||||
return nil, errors.BadRequest(reason.UserNotFound)
|
||||
}
|
||||
resp = &schema.GetUserNotificationConfigResp{}
|
||||
resp.FromJsonString(userInfo.NoticeConfig)
|
||||
resp.Format()
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (us *UserService) UpdateUserNotificationConfig(ctx context.Context, req *schema.UpdateUserNotificationConfigReq) (err error) {
|
||||
_, exist, err := us.userRepo.GetByUserID(ctx, req.UserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
return errors.BadRequest(reason.UserNotFound)
|
||||
}
|
||||
req.NotificationConfig.Format()
|
||||
return us.userRepo.UpdateNoticeConfig(ctx, req.UserID, req.NotificationConfig.ToJsonString())
|
||||
}
|
||||
|
||||
func (us *UserService) UserVerifyEmail(ctx context.Context, req *schema.UserVerifyEmailReq) (resp *schema.UserLoginResp, err error) {
|
||||
data := &schema.EmailCodeContent{}
|
||||
err = data.FromJSONString(req.Content)
|
||||
|
@ -717,23 +695,37 @@ func (us *UserService) UserRanking(ctx context.Context) (resp *schema.UserRankin
|
|||
return us.warpStatRankingResp(userInfoMapping, rankStat, voteStat, userRoleRels), nil
|
||||
}
|
||||
|
||||
// UserUnsubscribeEmailNotification user unsubscribe email notification
|
||||
func (us *UserService) UserUnsubscribeEmailNotification(
|
||||
ctx context.Context, req *schema.UserUnsubscribeEmailNotificationReq) (err error) {
|
||||
// UserUnsubscribeNotification user unsubscribe email notification
|
||||
func (us *UserService) UserUnsubscribeNotification(
|
||||
ctx context.Context, req *schema.UserUnsubscribeNotificationReq) (err error) {
|
||||
data := &schema.EmailCodeContent{}
|
||||
err = data.FromJSONString(req.Content)
|
||||
if err != nil || len(data.UserID) == 0 {
|
||||
return errors.BadRequest(reason.EmailVerifyURLExpired)
|
||||
}
|
||||
|
||||
userInfo, exist, err := us.userRepo.GetByUserID(ctx, data.UserID)
|
||||
if err != nil {
|
||||
return err
|
||||
for _, source := range data.NotificationSources {
|
||||
notificationConfig, exist, err := us.userNotificationConfigRepo.GetByUserIDAndSource(
|
||||
ctx, data.UserID, source)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exist {
|
||||
continue
|
||||
}
|
||||
channels := schema.NewNotificationChannelsFormJson(notificationConfig.Channels)
|
||||
// unsubscribe email notification
|
||||
for _, channel := range channels {
|
||||
if channel.Key == constant.EmailChannel {
|
||||
channel.Enable = false
|
||||
}
|
||||
}
|
||||
notificationConfig.Channels = channels.ToJsonString()
|
||||
if err = us.userNotificationConfigRepo.Save(ctx, notificationConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if !exist {
|
||||
return errors.BadRequest(reason.UserNotFound)
|
||||
}
|
||||
return us.userRepo.UpdateNoticeStatus(ctx, userInfo.ID, schema.NoticeStatusOff)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (us *UserService) getActivityUserRankStat(ctx context.Context, startTime, endTime time.Time, limit int,
|
||||
|
|
Loading…
Reference in New Issue