fix(notification): refactor new question notification
This commit is contained in:
@@ -21,6 +21,7 @@ package plugin_config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/apache/incubator-answer/internal/base/pager"
|
||||
"xorm.io/xorm"
|
||||
|
||||
"github.com/apache/incubator-answer/internal/base/data"
|
||||
@@ -86,3 +87,13 @@ func (ur *pluginUserConfigRepo) GetPluginUserConfig(ctx context.Context, userID,
|
||||
}
|
||||
return pluginUserConfig, exist, err
|
||||
}
|
||||
|
||||
func (ur *pluginUserConfigRepo) GetPluginUserConfigPage(ctx context.Context, page, pageSize int) (
|
||||
pluginUserConfigs []*entity.PluginUserConfig, total int64, err error) {
|
||||
pluginUserConfigs = make([]*entity.PluginUserConfig, 0)
|
||||
total, err = pager.Help(page, pageSize, &pluginUserConfigs, &entity.PluginUserConfig{}, ur.data.DB.Context(ctx))
|
||||
if err != nil {
|
||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ package notification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/apache/incubator-answer/internal/base/constant"
|
||||
"github.com/apache/incubator-answer/internal/base/data"
|
||||
"github.com/apache/incubator-answer/internal/schema"
|
||||
"github.com/apache/incubator-answer/internal/service/activity_common"
|
||||
@@ -31,10 +30,7 @@ import (
|
||||
usercommon "github.com/apache/incubator-answer/internal/service/user_common"
|
||||
"github.com/apache/incubator-answer/internal/service/user_external_login"
|
||||
"github.com/apache/incubator-answer/internal/service/user_notification_config"
|
||||
"github.com/apache/incubator-answer/pkg/display"
|
||||
"github.com/apache/incubator-answer/plugin"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ExternalNotificationService struct {
|
||||
@@ -90,64 +86,3 @@ func (ns *ExternalNotificationService) Handler(ctx context.Context, msg *schema.
|
||||
log.Errorf("unknown notification message: %+v", msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ns *ExternalNotificationService) syncNotificationToPlugin(ctx context.Context,
|
||||
source constant.NotificationSource, msg *schema.ExternalNotificationMsg) {
|
||||
pluginNotificationMsg := &plugin.NotificationMessage{
|
||||
ReceiverUserID: msg.ReceiverUserID,
|
||||
ReceiverLang: msg.ReceiverLang,
|
||||
}
|
||||
|
||||
switch source {
|
||||
case constant.InboxSource:
|
||||
return
|
||||
case constant.AllNewQuestionSource:
|
||||
pluginNotificationMsg.Type = plugin.NotificationNewQuestion
|
||||
pluginNotificationMsg = ns.newPluginQuestionNotification(ctx, msg)
|
||||
case constant.AllNewQuestionForFollowingTagsSource:
|
||||
pluginNotificationMsg.Type = plugin.NotificationNewQuestionFollowedTag
|
||||
pluginNotificationMsg = ns.newPluginQuestionNotification(ctx, msg)
|
||||
}
|
||||
|
||||
if len(msg.ReceiverLang) == 0 && len(msg.ReceiverUserID) > 0 {
|
||||
userInfo, _, _ := ns.userRepo.GetByUserID(ctx, msg.ReceiverUserID)
|
||||
if userInfo != nil {
|
||||
pluginNotificationMsg.ReceiverLang = userInfo.Language
|
||||
}
|
||||
}
|
||||
|
||||
_ = plugin.CallNotification(func(fn plugin.Notification) error {
|
||||
userInfo, exist, err := ns.userExternalLoginRepo.GetByUserID(ctx, fn.Info().SlugName, msg.ReceiverUserID)
|
||||
if err != nil {
|
||||
log.Errorf("get user external login info failed: %v", err)
|
||||
return nil
|
||||
}
|
||||
if exist {
|
||||
pluginNotificationMsg.ReceiverExternalID = userInfo.ExternalID
|
||||
}
|
||||
fn.Notify(pluginNotificationMsg)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ns *ExternalNotificationService) newPluginQuestionNotification(
|
||||
ctx context.Context, msg *schema.ExternalNotificationMsg) (raw *plugin.NotificationMessage) {
|
||||
raw = &plugin.NotificationMessage{
|
||||
ReceiverUserID: msg.ReceiverUserID,
|
||||
ReceiverLang: msg.ReceiverLang,
|
||||
QuestionTitle: msg.NewQuestionTemplateRawData.QuestionTitle,
|
||||
QuestionTags: strings.Join(msg.NewQuestionTemplateRawData.Tags, ","),
|
||||
}
|
||||
siteInfo, err := ns.siteInfoService.GetSiteGeneral(ctx)
|
||||
if err != nil {
|
||||
return raw
|
||||
}
|
||||
seoInfo, err := ns.siteInfoService.GetSiteSeo(ctx)
|
||||
if err != nil {
|
||||
return raw
|
||||
}
|
||||
raw.QuestionUrl = display.QuestionURL(
|
||||
seoInfo.Permalink, siteInfo.SiteUrl,
|
||||
msg.NewQuestionTemplateRawData.QuestionID, msg.NewQuestionTemplateRawData.QuestionTitle)
|
||||
return raw
|
||||
}
|
||||
|
||||
@@ -49,7 +49,6 @@ func (ns *ExternalNotificationService) handleInviteAnswerNotification(ctx contex
|
||||
ns.sendInviteAnswerNotificationEmail(ctx, msg.ReceiverUserID, msg.ReceiverEmail, msg.ReceiverLang, msg.NewInviteAnswerTemplateRawData)
|
||||
}
|
||||
}
|
||||
ns.syncNotificationToPlugin(ctx, constant.InboxSource, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ func (ns *ExternalNotificationService) handleNewAnswerNotification(ctx context.C
|
||||
ns.sendNewAnswerNotificationEmail(ctx, msg.ReceiverUserID, msg.ReceiverEmail, msg.ReceiverLang, msg.NewAnswerTemplateRawData)
|
||||
}
|
||||
}
|
||||
ns.syncNotificationToPlugin(ctx, constant.InboxSource, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ func (ns *ExternalNotificationService) handleNewCommentNotification(ctx context.
|
||||
ns.sendNewCommentNotificationEmail(ctx, msg.ReceiverUserID, msg.ReceiverEmail, msg.ReceiverLang, msg.NewCommentTemplateRawData)
|
||||
}
|
||||
}
|
||||
ns.syncNotificationToPlugin(ctx, constant.InboxSource, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -23,10 +23,12 @@ import (
|
||||
"context"
|
||||
"github.com/apache/incubator-answer/internal/base/constant"
|
||||
"github.com/apache/incubator-answer/internal/schema"
|
||||
"github.com/apache/incubator-answer/pkg/display"
|
||||
"github.com/apache/incubator-answer/pkg/token"
|
||||
"github.com/jinzhu/copier"
|
||||
"github.com/apache/incubator-answer/plugin"
|
||||
"github.com/segmentfault/pacman/i18n"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -61,11 +63,9 @@ func (ns *ExternalNotificationService) handleNewQuestionNotification(ctx context
|
||||
})
|
||||
}
|
||||
}
|
||||
pluginMsg := &schema.ExternalNotificationMsg{}
|
||||
_ = copier.Copy(pluginMsg, msg)
|
||||
pluginMsg.ReceiverUserID = subscriber.UserID
|
||||
ns.syncNotificationToPlugin(ctx, subscriber.NotificationSource, pluginMsg)
|
||||
}
|
||||
|
||||
ns.syncNewQuestionNotificationToPlugin(ctx, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -190,3 +190,78 @@ func (ns *ExternalNotificationService) sendNewQuestionNotificationEmail(ctx cont
|
||||
ns.emailService.SendAndSaveCodeWithTime(
|
||||
ctx, userInfo.EMail, title, body, rawData.UnsubscribeCode, codeContent.ToJSONString(), 1*24*time.Hour)
|
||||
}
|
||||
|
||||
func (ns *ExternalNotificationService) syncNewQuestionNotificationToPlugin(ctx context.Context,
|
||||
msg *schema.ExternalNotificationMsg) {
|
||||
_ = plugin.CallNotification(func(fn plugin.Notification) error {
|
||||
// 1. get all this new question's tags followers
|
||||
subscribersMapping := make(map[string]plugin.NotificationType)
|
||||
for _, tagID := range msg.NewQuestionTemplateRawData.TagIDs {
|
||||
userIDs, err := ns.followRepo.GetFollowUserIDs(ctx, tagID)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
continue
|
||||
}
|
||||
for _, userID := range userIDs {
|
||||
subscribersMapping[userID] = plugin.NotificationNewQuestion
|
||||
}
|
||||
}
|
||||
|
||||
// 2. get all new question's followers
|
||||
questionSubscribers := fn.GetNewQuestionSubscribers()
|
||||
for _, subscriber := range questionSubscribers {
|
||||
subscribersMapping[subscriber] = plugin.NotificationNewQuestionFollowedTag
|
||||
}
|
||||
|
||||
// 3. remove question owner
|
||||
delete(subscribersMapping, msg.NewQuestionTemplateRawData.QuestionAuthorUserID)
|
||||
|
||||
pluginNotificationMsg := ns.newPluginQuestionNotification(ctx, msg)
|
||||
|
||||
// 4. send notification
|
||||
for subscriber, notificationType := range subscribersMapping {
|
||||
pluginNotificationMsg.ReceiverUserID = subscriber
|
||||
pluginNotificationMsg.Type = notificationType
|
||||
|
||||
if len(msg.ReceiverLang) == 0 && len(msg.ReceiverUserID) > 0 {
|
||||
userInfo, _, _ := ns.userRepo.GetByUserID(ctx, msg.ReceiverUserID)
|
||||
if userInfo != nil {
|
||||
pluginNotificationMsg.ReceiverLang = userInfo.Language
|
||||
}
|
||||
}
|
||||
|
||||
userInfo, exist, err := ns.userExternalLoginRepo.GetByUserID(ctx, fn.Info().SlugName, msg.ReceiverUserID)
|
||||
if err != nil {
|
||||
log.Errorf("get user external login info failed: %v", err)
|
||||
return nil
|
||||
}
|
||||
if exist {
|
||||
pluginNotificationMsg.ReceiverExternalID = userInfo.ExternalID
|
||||
}
|
||||
fn.Notify(pluginNotificationMsg)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (ns *ExternalNotificationService) newPluginQuestionNotification(
|
||||
ctx context.Context, msg *schema.ExternalNotificationMsg) (raw *plugin.NotificationMessage) {
|
||||
raw = &plugin.NotificationMessage{
|
||||
ReceiverUserID: msg.ReceiverUserID,
|
||||
ReceiverLang: msg.ReceiverLang,
|
||||
QuestionTitle: msg.NewQuestionTemplateRawData.QuestionTitle,
|
||||
QuestionTags: strings.Join(msg.NewQuestionTemplateRawData.Tags, ","),
|
||||
}
|
||||
siteInfo, err := ns.siteInfoService.GetSiteGeneral(ctx)
|
||||
if err != nil {
|
||||
return raw
|
||||
}
|
||||
seoInfo, err := ns.siteInfoService.GetSiteSeo(ctx)
|
||||
if err != nil {
|
||||
return raw
|
||||
}
|
||||
raw.QuestionUrl = display.QuestionURL(
|
||||
seoInfo.Permalink, siteInfo.SiteUrl,
|
||||
msg.NewQuestionTemplateRawData.QuestionID, msg.NewQuestionTemplateRawData.QuestionTitle)
|
||||
return raw
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ type PluginUserConfigRepo interface {
|
||||
SaveUserPluginConfig(ctx context.Context, userID string, pluginSlugName, configValue string) (err error)
|
||||
GetPluginUserConfig(ctx context.Context, userID, pluginSlugName string) (
|
||||
pluginUserConfig *entity.PluginUserConfig, exist bool, err error)
|
||||
GetPluginUserConfigPage(ctx context.Context, page, pageSize int) (
|
||||
pluginUserConfigs []*entity.PluginUserConfig, total int64, err error)
|
||||
}
|
||||
|
||||
// PluginCommonService user service
|
||||
@@ -61,53 +63,14 @@ func NewPluginCommonService(
|
||||
data *data.Data,
|
||||
) *PluginCommonService {
|
||||
|
||||
// init plugin status
|
||||
pluginStatus, err := configService.GetStringValue(context.TODO(), constant.PluginStatus)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
} else {
|
||||
if err := plugin.StatusManager.UnmarshalJSON([]byte(pluginStatus)); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// init plugin config
|
||||
pluginConfigs, err := pluginConfigRepo.GetPluginConfigAll(context.Background())
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
} else {
|
||||
for _, pluginConfig := range pluginConfigs {
|
||||
err := plugin.CallConfig(func(fn plugin.Config) error {
|
||||
if fn.Info().SlugName == pluginConfig.PluginSlugName {
|
||||
return fn.ConfigReceiver([]byte(pluginConfig.Value))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("parse plugin config failed: %s %v", pluginConfig.PluginSlugName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// init plugin user config
|
||||
plugin.RegisterGetPluginUserConfigFunc(func(userID, pluginSlugName string) []byte {
|
||||
pluginUserConfig, exist, err := pluginUserConfigRepo.GetPluginUserConfig(context.Background(), userID, pluginSlugName)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil
|
||||
}
|
||||
if !exist {
|
||||
return nil
|
||||
}
|
||||
return []byte(pluginUserConfig.Value)
|
||||
})
|
||||
|
||||
return &PluginCommonService{
|
||||
p := &PluginCommonService{
|
||||
configService: configService,
|
||||
pluginConfigRepo: pluginConfigRepo,
|
||||
pluginUserConfigRepo: pluginUserConfigRepo,
|
||||
data: data,
|
||||
}
|
||||
p.initPluginData()
|
||||
return p
|
||||
}
|
||||
|
||||
// UpdatePluginStatus update plugin status
|
||||
@@ -158,3 +121,73 @@ func (ps *PluginCommonService) GetUserPluginConfig(ctx context.Context, req *sch
|
||||
}
|
||||
return pluginUserConfig.Value, nil
|
||||
}
|
||||
|
||||
func (ps *PluginCommonService) initPluginData() {
|
||||
// init plugin status
|
||||
pluginStatus, err := ps.configService.GetStringValue(context.TODO(), constant.PluginStatus)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
} else {
|
||||
if err := plugin.StatusManager.UnmarshalJSON([]byte(pluginStatus)); err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// init plugin config
|
||||
pluginConfigs, err := ps.pluginConfigRepo.GetPluginConfigAll(context.Background())
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
} else {
|
||||
for _, pluginConfig := range pluginConfigs {
|
||||
err := plugin.CallConfig(func(fn plugin.Config) error {
|
||||
if fn.Info().SlugName == pluginConfig.PluginSlugName {
|
||||
return fn.ConfigReceiver([]byte(pluginConfig.Value))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("parse plugin config failed: %s %v", pluginConfig.PluginSlugName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// init plugin user config
|
||||
plugin.RegisterGetPluginUserConfigFunc(func(userID, pluginSlugName string) []byte {
|
||||
pluginUserConfig, exist, err := ps.pluginUserConfigRepo.GetPluginUserConfig(context.Background(), userID, pluginSlugName)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil
|
||||
}
|
||||
if !exist {
|
||||
return nil
|
||||
}
|
||||
return []byte(pluginUserConfig.Value)
|
||||
})
|
||||
|
||||
// init plugin user config data
|
||||
go func() {
|
||||
page, pageSize := 1, 1000
|
||||
for {
|
||||
userConfigs, _, err := ps.pluginUserConfigRepo.GetPluginUserConfigPage(context.Background(), page, pageSize)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
if len(userConfigs) == 0 {
|
||||
return
|
||||
}
|
||||
for _, userConfig := range userConfigs {
|
||||
err := plugin.CallUserConfig(func(fn plugin.UserConfig) error {
|
||||
if fn.Info().SlugName == userConfig.PluginSlugName {
|
||||
return fn.UserConfigReceiver(userConfig.UserID, []byte(userConfig.Value))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("parse plugin user config failed: %s %v", userConfig.PluginSlugName, err)
|
||||
}
|
||||
}
|
||||
page++
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -48,6 +48,9 @@ const (
|
||||
type Notification interface {
|
||||
Base
|
||||
|
||||
// GetNewQuestionSubscribers returns the subscribers of the new question notification
|
||||
GetNewQuestionSubscribers() (userIDs []string)
|
||||
|
||||
// Notify sends a notification to the user
|
||||
Notify(msg *NotificationMessage)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user