mirror of https://gitee.com/answerdev/answer.git
feat(notification): add notification limit
This commit is contained in:
parent
8a037f00db
commit
e4a77367a4
|
@ -178,7 +178,7 @@ 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)
|
||||
externalNotificationService := notification.NewExternalNotificationService(userNotificationConfigRepo, followRepo, emailService, userRepo, externalNotificationQueueService)
|
||||
externalNotificationService := notification.NewExternalNotificationService(dataData, 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)
|
||||
|
|
4
go.mod
4
go.mod
|
@ -29,8 +29,8 @@ require (
|
|||
github.com/ory/dockertest/v3 v3.9.1
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/scottleedavis/go-exif-remove v0.0.0-20230314195146-7e059d593405
|
||||
github.com/segmentfault/pacman v1.0.5-0.20230822075009-309985fb8700
|
||||
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20230822075009-309985fb8700
|
||||
github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f
|
||||
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20230822083413-c0075a2d401f
|
||||
github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05
|
||||
github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093754-b76aef1c1150
|
||||
github.com/segmentfault/pacman/contrib/log/zap v0.0.0-20221018072427-a15dd1434e05
|
||||
|
|
6
go.sum
6
go.sum
|
@ -89,6 +89,7 @@ github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZw
|
|||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
|
@ -644,8 +645,12 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
|
|||
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||
github.com/segmentfault/pacman v1.0.5-0.20230822075009-309985fb8700 h1:VqxiuNGQg86GEKxnzmJegZR2Ufr7EVXo68mdKaf+/MQ=
|
||||
github.com/segmentfault/pacman v1.0.5-0.20230822075009-309985fb8700/go.mod h1:5lNp5REd8QMThmBUvR3Fi9Y3AsOB4GRq7soCB4QLqOs=
|
||||
github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f h1:9f2Bjf6bdMvNyUop32wAGJCdp+Jdm/d6nKBYvFvkRo0=
|
||||
github.com/segmentfault/pacman v1.0.5-0.20230822083413-c0075a2d401f/go.mod h1:5lNp5REd8QMThmBUvR3Fi9Y3AsOB4GRq7soCB4QLqOs=
|
||||
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20230822075009-309985fb8700 h1:VZpexPTcr7sOxxYUGa/9cneyCESfisAhCNbaEuTpcwI=
|
||||
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20230822075009-309985fb8700/go.mod h1:rmf1TCwz67dyM+AmTwSd1BxTo2AOYHj262lP93bOZbs=
|
||||
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20230822083413-c0075a2d401f h1:1KHe0uN6p798E7XJZPhZkgm/hXk5CTjisCvFMqaZSKI=
|
||||
github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20230822083413-c0075a2d401f/go.mod h1:rmf1TCwz67dyM+AmTwSd1BxTo2AOYHj262lP93bOZbs=
|
||||
github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05 h1:BlqTgc3/MYKG6vMI2MI+6o+7P4Gy5PXlawu185wPXAk=
|
||||
github.com/segmentfault/pacman/contrib/conf/viper v0.0.0-20221018072427-a15dd1434e05/go.mod h1:prPjFam7MyZ5b3S9dcDOt2tMPz6kf7C9c243s9zSwPY=
|
||||
github.com/segmentfault/pacman/contrib/i18n v0.0.0-20230516093754-b76aef1c1150 h1:OEuW1D7RGDE0CZDr0oGMw9Eiq7fAbD9C4WMrvSixamk=
|
||||
|
@ -774,6 +779,7 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
|||
go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
|
||||
go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
|
|
|
@ -3,20 +3,23 @@ package constant
|
|||
import "time"
|
||||
|
||||
const (
|
||||
UserStatusChangedCacheKey = "answer:user:status:"
|
||||
UserStatusChangedCacheTime = 7 * 24 * time.Hour
|
||||
UserTokenCacheKey = "answer:user:token:"
|
||||
UserTokenCacheTime = 7 * 24 * time.Hour
|
||||
AdminTokenCacheKey = "answer:admin:token:"
|
||||
AdminTokenCacheTime = 7 * 24 * time.Hour
|
||||
UserTokenMappingCacheKey = "answer:user-token:mapping:"
|
||||
SiteInfoCacheKey = "answer:site-info:"
|
||||
SiteInfoCacheTime = 1 * time.Hour
|
||||
ConfigID2KEYCacheKeyPrefix = "answer:config:id:"
|
||||
ConfigKEY2ContentCacheKeyPrefix = "answer:config:key:"
|
||||
ConnectorUserExternalInfoCacheKey = "answer:connector:"
|
||||
ConnectorUserExternalInfoCacheTime = 10 * time.Minute
|
||||
SiteMapQuestionCacheKeyPrefix = "answer:sitemap:question:%d"
|
||||
SiteMapQuestionCacheTime = time.Hour
|
||||
SitemapMaxSize = 50000
|
||||
UserStatusChangedCacheKey = "answer:user:status:"
|
||||
UserStatusChangedCacheTime = 7 * 24 * time.Hour
|
||||
UserTokenCacheKey = "answer:user:token:"
|
||||
UserTokenCacheTime = 7 * 24 * time.Hour
|
||||
AdminTokenCacheKey = "answer:admin:token:"
|
||||
AdminTokenCacheTime = 7 * 24 * time.Hour
|
||||
UserTokenMappingCacheKey = "answer:user-token:mapping:"
|
||||
SiteInfoCacheKey = "answer:site-info:"
|
||||
SiteInfoCacheTime = 1 * time.Hour
|
||||
ConfigID2KEYCacheKeyPrefix = "answer:config:id:"
|
||||
ConfigKEY2ContentCacheKeyPrefix = "answer:config:key:"
|
||||
ConnectorUserExternalInfoCacheKey = "answer:connector:"
|
||||
ConnectorUserExternalInfoCacheTime = 10 * time.Minute
|
||||
SiteMapQuestionCacheKeyPrefix = "answer:sitemap:question:%d"
|
||||
SiteMapQuestionCacheTime = time.Hour
|
||||
SitemapMaxSize = 50000
|
||||
NewQuestionNotificationLimitCacheKeyPrefix = "answer:new-question-notification-limit:"
|
||||
NewQuestionNotificationLimitCacheTime = 7 * 24 * time.Hour
|
||||
NewQuestionNotificationLimitMax = 50
|
||||
)
|
||||
|
|
|
@ -71,7 +71,7 @@ var migrations = []Migration{
|
|||
NewMigration("v1.1.0-beta.2", "update question post time", updateQuestionPostTime, true),
|
||||
NewMigration("v1.1.0", "add gravatar base url", updateCount, true),
|
||||
NewMigration("v1.1.1", "update the length of revision content", updateTheLengthOfRevisionContent, false),
|
||||
NewMigration("v1.1.2", "add notification config", addNoticeConfig, false),
|
||||
NewMigration("v1.1.2", "add notification config", addNoticeConfig, true),
|
||||
}
|
||||
|
||||
func GetMigrations() []Migration {
|
||||
|
|
|
@ -31,10 +31,10 @@ func (ar *authRepo) GetUserCacheInfo(ctx context.Context, accessToken string) (u
|
|||
if err != nil {
|
||||
return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
}
|
||||
userInfo = &entity.UserCacheInfo{}
|
||||
if !exist {
|
||||
return nil, nil
|
||||
}
|
||||
userInfo = &entity.UserCacheInfo{}
|
||||
_ = json.Unmarshal([]byte(userInfoCache), userInfo)
|
||||
return userInfo, nil
|
||||
}
|
||||
|
|
|
@ -50,10 +50,11 @@ func (cr *captchaRepo) GetActionType(ctx context.Context, unit, actionType strin
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
actionInfo = &entity.ActionRecordInfo{}
|
||||
if exist {
|
||||
_ = json.Unmarshal([]byte(res), actionInfo)
|
||||
if !exist {
|
||||
return nil, nil
|
||||
}
|
||||
actionInfo = &entity.ActionRecordInfo{}
|
||||
_ = json.Unmarshal([]byte(res), actionInfo)
|
||||
return actionInfo, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ func (sr *siteInfoRepo) GetByType(ctx context.Context, siteType string) (siteInf
|
|||
exist, err = sr.data.DB.Context(ctx).Where(builder.Eq{"type": siteType}).Get(siteInfo)
|
||||
if err != nil {
|
||||
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
|
||||
return nil, false, err
|
||||
}
|
||||
if exist {
|
||||
sr.setCache(ctx, siteType, siteInfo)
|
||||
|
@ -65,10 +66,11 @@ func (sr *siteInfoRepo) getCache(ctx context.Context, siteType string) (siteInfo
|
|||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
siteInfo = &entity.SiteInfo{}
|
||||
if exist {
|
||||
_ = json.Unmarshal([]byte(siteInfoCache), siteInfo)
|
||||
if !exist {
|
||||
return nil
|
||||
}
|
||||
siteInfo = &entity.SiteInfo{}
|
||||
_ = json.Unmarshal([]byte(siteInfoCache), siteInfo)
|
||||
return siteInfo
|
||||
}
|
||||
|
||||
|
|
|
@ -89,9 +89,10 @@ func (ur *userExternalLoginRepo) GetCacheUserExternalLoginInfo(
|
|||
if err != nil {
|
||||
return info, err
|
||||
}
|
||||
info = &schema.ExternalLoginUserInfoCache{}
|
||||
if exist {
|
||||
_ = json.Unmarshal([]byte(res), &info)
|
||||
if !exist {
|
||||
return nil, nil
|
||||
}
|
||||
info = &schema.ExternalLoginUserInfoCache{}
|
||||
_ = json.Unmarshal([]byte(res), &info)
|
||||
return info, nil
|
||||
}
|
||||
|
|
|
@ -111,17 +111,20 @@ func (cs *CaptchaService) ActionRecordVerifyCaptcha(
|
|||
}
|
||||
|
||||
func (cs *CaptchaService) ActionRecordAdd(ctx context.Context, actionType string, unit string) (int, error) {
|
||||
var err error
|
||||
info, cahceErr := cs.captchaRepo.GetActionType(ctx, unit, actionType)
|
||||
if cahceErr != nil {
|
||||
info, err := cs.captchaRepo.GetActionType(ctx, unit, actionType)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return 0, err
|
||||
}
|
||||
info.Num++
|
||||
err = cs.captchaRepo.SetActionType(ctx, unit, actionType, "", info.Num)
|
||||
amount := 1
|
||||
if info != nil {
|
||||
amount = info.Num + 1
|
||||
}
|
||||
err = cs.captchaRepo.SetActionType(ctx, unit, actionType, "", amount)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return info.Num, nil
|
||||
return amount, nil
|
||||
}
|
||||
|
||||
func (cs *CaptchaService) ActionRecordDel(ctx context.Context, actionType string, unit string) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package action
|
|||
|
||||
import (
|
||||
"context"
|
||||
"github.com/segmentfault/pacman/log"
|
||||
"time"
|
||||
|
||||
"github.com/answerdev/answer/internal/entity"
|
||||
|
@ -13,8 +14,14 @@ import (
|
|||
func (cs *CaptchaService) ValidationStrategy(ctx context.Context, unit, actionType string) bool {
|
||||
info, err := cs.captchaRepo.GetActionType(ctx, unit, actionType)
|
||||
if err != nil {
|
||||
//No record, no processing
|
||||
//
|
||||
log.Error(err)
|
||||
return false
|
||||
}
|
||||
if info == nil {
|
||||
info = &entity.ActionRecordInfo{
|
||||
LastTime: time.Now().Unix(),
|
||||
Num: 1,
|
||||
}
|
||||
}
|
||||
switch actionType {
|
||||
case entity.CaptchaActionEmail:
|
||||
|
|
|
@ -70,8 +70,8 @@ type DashboardService interface {
|
|||
}
|
||||
|
||||
func (ds *dashboardService) Statistical(ctx context.Context) (*schema.DashboardInfo, error) {
|
||||
dashboardInfo, err := ds.getFromCache(ctx)
|
||||
if err != nil {
|
||||
dashboardInfo := ds.getFromCache(ctx)
|
||||
if dashboardInfo == nil {
|
||||
dashboardInfo = &schema.DashboardInfo{}
|
||||
dashboardInfo.QuestionCount = ds.questionCount(ctx)
|
||||
dashboardInfo.AnswerCount = ds.answerCount(ctx)
|
||||
|
@ -95,19 +95,20 @@ func (ds *dashboardService) Statistical(ctx context.Context) (*schema.DashboardI
|
|||
return dashboardInfo, nil
|
||||
}
|
||||
|
||||
func (ds *dashboardService) getFromCache(ctx context.Context) (dashboardInfo *schema.DashboardInfo, err error) {
|
||||
func (ds *dashboardService) getFromCache(ctx context.Context) (dashboardInfo *schema.DashboardInfo) {
|
||||
infoStr, exist, err := ds.data.Cache.GetString(ctx, schema.DashboardCacheKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
log.Errorf("get dashboard statistical from cache failed: %s", err)
|
||||
return nil
|
||||
}
|
||||
if !exist {
|
||||
return nil
|
||||
}
|
||||
dashboardInfo = &schema.DashboardInfo{}
|
||||
if !exist {
|
||||
return dashboardInfo, nil
|
||||
}
|
||||
if err = json.Unmarshal([]byte(infoStr), dashboardInfo); err != nil {
|
||||
return nil, err
|
||||
return nil
|
||||
}
|
||||
return dashboardInfo, nil
|
||||
return dashboardInfo
|
||||
}
|
||||
|
||||
func (ds *dashboardService) setCache(ctx context.Context, info *schema.DashboardInfo) {
|
||||
|
|
|
@ -22,6 +22,7 @@ type ExternalNotificationService struct {
|
|||
}
|
||||
|
||||
func NewExternalNotificationService(
|
||||
data *data.Data,
|
||||
userNotificationConfigRepo user_notification_config.UserNotificationConfigRepo,
|
||||
followRepo activity_common.FollowRepo,
|
||||
emailService *export.EmailService,
|
||||
|
@ -29,6 +30,7 @@ func NewExternalNotificationService(
|
|||
notificationQueueService notice_queue.ExternalNotificationQueueService,
|
||||
) *ExternalNotificationService {
|
||||
n := &ExternalNotificationService{
|
||||
data: data,
|
||||
userNotificationConfigRepo: userNotificationConfigRepo,
|
||||
followRepo: followRepo,
|
||||
emailService: emailService,
|
||||
|
|
|
@ -105,7 +105,24 @@ func (ns *ExternalNotificationService) getNewQuestionSubscribers(ctx context.Con
|
|||
}
|
||||
|
||||
func (ns *ExternalNotificationService) checkSendNewQuestionNotificationEmailLimit(ctx context.Context, userID string) bool {
|
||||
// TODO: check if reach send limit
|
||||
key := constant.NewQuestionNotificationLimitCacheKeyPrefix + userID
|
||||
old, exist, err := ns.data.Cache.GetInt64(ctx, key)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return false
|
||||
}
|
||||
if exist && old >= constant.NewQuestionNotificationLimitMax {
|
||||
log.Debugf("%s user reach new question notification limit", userID)
|
||||
return true
|
||||
}
|
||||
if !exist {
|
||||
err = ns.data.Cache.SetInt64(ctx, key, 1, constant.NewQuestionNotificationLimitCacheTime)
|
||||
} else {
|
||||
_, err = ns.data.Cache.Increase(ctx, key, 1)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
|
@ -252,7 +252,7 @@ func (us *UserExternalLoginService) ExternalLoginBindingUserSendEmail(
|
|||
}
|
||||
resp = &schema.ExternalLoginBindingUserSendEmailResp{}
|
||||
externalLoginInfo, err := us.userExternalLoginRepo.GetCacheUserExternalLoginInfo(ctx, req.BindingKey)
|
||||
if err != nil || len(externalLoginInfo.ExternalID) == 0 {
|
||||
if err != nil || externalLoginInfo == nil {
|
||||
return nil, errors.BadRequest(reason.UserNotFound)
|
||||
}
|
||||
if len(externalLoginInfo.Email) > 0 {
|
||||
|
@ -308,7 +308,7 @@ func (us *UserExternalLoginService) ExternalLoginBindingUserSendEmail(
|
|||
func (us *UserExternalLoginService) ExternalLoginBindingUser(
|
||||
ctx context.Context, bindingKey string, oldUserInfo *entity.User) (err error) {
|
||||
externalLoginInfo, err := us.userExternalLoginRepo.GetCacheUserExternalLoginInfo(ctx, bindingKey)
|
||||
if err != nil || len(externalLoginInfo.ExternalID) == 0 {
|
||||
if err != nil || externalLoginInfo == nil {
|
||||
return errors.BadRequest(reason.UserNotFound)
|
||||
}
|
||||
return us.bindOldUser(ctx, externalLoginInfo, oldUserInfo)
|
||||
|
|
Loading…
Reference in New Issue