fix(notification): refactor new question notification

This commit is contained in:
LinkinStars
2024-01-02 17:53:12 +08:00
parent 0c8c8fd464
commit efc8b9ea04
8 changed files with 169 additions and 115 deletions
@@ -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++
}
}()
}
+3
View File
@@ -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)
}