diff --git a/cmd/main.go b/cmd/main.go index 2a5ddaa1..5d166897 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,6 +1,7 @@ package answercmd import ( + "context" "fmt" "os" "time" @@ -39,7 +40,7 @@ var ( // @name Authorization func Main() { log.SetLogger(zap.NewLogger( - log.ParseLevel(logLevel), zap.WithName("answer"), zap.WithPath(logPath), zap.WithCallerFullPath())) + log.ParseLevel(logLevel), zap.WithName("answer"), zap.WithPath(logPath))) Execute() } @@ -59,7 +60,7 @@ func runApp() { fmt.Println("answer Version:", constant.Version, " Revision:", constant.Revision) defer cleanup() - if err := app.Run(); err != nil { + if err := app.Run(context.Background()); err != nil { panic(err) } } diff --git a/cmd/wire_gen.go b/cmd/wire_gen.go index eae73d48..643ad928 100644 --- a/cmd/wire_gen.go +++ b/cmd/wire_gen.go @@ -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) diff --git a/go.mod b/go.mod index 72d1fcac..e3ed74e9 100644 --- a/go.mod +++ b/go.mod @@ -28,8 +28,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.4 - github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221219081300-f734f4a16aa0 + 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 diff --git a/go.sum b/go.sum index c13e74dc..ed09da26 100644 --- a/go.sum +++ b/go.sum @@ -91,6 +91,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= @@ -640,10 +641,14 @@ github.com/scottleedavis/go-exif-remove v0.0.0-20230314195146-7e059d593405 h1:2i github.com/scottleedavis/go-exif-remove v0.0.0-20230314195146-7e059d593405/go.mod h1:rIxVzVLKlBwLxO+lC+k/I4HJfRQcemg/f/76Xmmzsec= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= -github.com/segmentfault/pacman v1.0.4 h1:6UIXuMHUeYMWe5toflV9SXZQizRny1RczjZJLj9kul0= -github.com/segmentfault/pacman v1.0.4/go.mod h1:5lNp5REd8QMThmBUvR3Fi9Y3AsOB4GRq7soCB4QLqOs= -github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221219081300-f734f4a16aa0 h1:4x0qG7H2M3qH7Yo2BhGrVlji1iTmRAWgINY/JyENeHs= -github.com/segmentfault/pacman/contrib/cache/memory v0.0.0-20221219081300-f734f4a16aa0/go.mod h1:rmf1TCwz67dyM+AmTwSd1BxTo2AOYHj262lP93bOZbs= +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= @@ -772,6 +777,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= diff --git a/internal/base/constant/cache_key.go b/internal/base/constant/cache_key.go index c4788dac..c04e56e4 100644 --- a/internal/base/constant/cache_key.go +++ b/internal/base/constant/cache_key.go @@ -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 ) diff --git a/internal/migrations/migrations.go b/internal/migrations/migrations.go index 540e30bc..c77cbd88 100644 --- a/internal/migrations/migrations.go +++ b/internal/migrations/migrations.go @@ -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 { diff --git a/internal/repo/auth/auth.go b/internal/repo/auth/auth.go index 440ec1f8..1fa201c2 100644 --- a/internal/repo/auth/auth.go +++ b/internal/repo/auth/auth.go @@ -27,15 +27,15 @@ func NewAuthRepo(data *data.Data) auth.AuthRepo { // GetUserCacheInfo get user cache info func (ar *authRepo) GetUserCacheInfo(ctx context.Context, accessToken string) (userInfo *entity.UserCacheInfo, err error) { - userInfoCache, err := ar.data.Cache.GetString(ctx, constant.UserTokenCacheKey+accessToken) + userInfoCache, exist, err := ar.data.Cache.GetString(ctx, constant.UserTokenCacheKey+accessToken) if err != nil { return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } + if !exist { + return nil, nil + } userInfo = &entity.UserCacheInfo{} - err = json.Unmarshal([]byte(userInfoCache), userInfo) - if err != nil { - return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() - } + _ = json.Unmarshal([]byte(userInfoCache), userInfo) return userInfo, nil } @@ -81,15 +81,15 @@ func (ar *authRepo) SetUserStatus(ctx context.Context, userID string, userInfo * // GetUserStatus get user status func (ar *authRepo) GetUserStatus(ctx context.Context, userID string) (userInfo *entity.UserCacheInfo, err error) { - userInfoCache, err := ar.data.Cache.GetString(ctx, constant.UserStatusChangedCacheKey+userID) + userInfoCache, exist, err := ar.data.Cache.GetString(ctx, constant.UserStatusChangedCacheKey+userID) if err != nil { return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() } + if !exist { + return nil, nil + } userInfo = &entity.UserCacheInfo{} - err = json.Unmarshal([]byte(userInfoCache), userInfo) - if err != nil { - return nil, errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() - } + _ = json.Unmarshal([]byte(userInfoCache), userInfo) return userInfo, nil } @@ -104,16 +104,16 @@ func (ar *authRepo) RemoveUserStatus(ctx context.Context, userID string) (err er // GetAdminUserCacheInfo get admin user cache info func (ar *authRepo) GetAdminUserCacheInfo(ctx context.Context, accessToken string) (userInfo *entity.UserCacheInfo, err error) { - userInfoCache, err := ar.data.Cache.GetString(ctx, constant.AdminTokenCacheKey+accessToken) + userInfoCache, exist, err := ar.data.Cache.GetString(ctx, constant.AdminTokenCacheKey+accessToken) if err != nil { err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() return } - userInfo = &entity.UserCacheInfo{} - err = json.Unmarshal([]byte(userInfoCache), userInfo) - if err != nil { - return nil, err + if !exist { + return nil, nil } + userInfo = &entity.UserCacheInfo{} + _ = json.Unmarshal([]byte(userInfoCache), userInfo) return userInfo, nil } @@ -144,7 +144,10 @@ func (ar *authRepo) RemoveAdminUserCacheInfo(ctx context.Context, accessToken st // AddUserTokenMapping add user token mapping func (ar *authRepo) AddUserTokenMapping(ctx context.Context, userID, accessToken string) (err error) { key := constant.UserTokenMappingCacheKey + userID - resp, _ := ar.data.Cache.GetString(ctx, key) + resp, _, err := ar.data.Cache.GetString(ctx, key) + if err != nil { + return err + } mapping := make(map[string]bool, 0) if len(resp) > 0 { _ = json.Unmarshal([]byte(resp), &mapping) @@ -157,7 +160,10 @@ func (ar *authRepo) AddUserTokenMapping(ctx context.Context, userID, accessToken // RemoveUserTokens Log out all users under this user id func (ar *authRepo) RemoveUserTokens(ctx context.Context, userID string, remainToken string) { key := constant.UserTokenMappingCacheKey + userID - resp, _ := ar.data.Cache.GetString(ctx, key) + resp, _, err := ar.data.Cache.GetString(ctx, key) + if err != nil { + return + } mapping := make(map[string]bool, 0) if len(resp) > 0 { _ = json.Unmarshal([]byte(resp), &mapping) diff --git a/internal/repo/captcha/captcha.go b/internal/repo/captcha/captcha.go index a332523a..0758fc64 100644 --- a/internal/repo/captcha/captcha.go +++ b/internal/repo/captcha/captcha.go @@ -43,19 +43,19 @@ func (cr *captchaRepo) SetActionType(ctx context.Context, unit, actionType, conf return } -func (cr *captchaRepo) GetActionType(ctx context.Context, unit, actionType string) (actioninfo *entity.ActionRecordInfo, err error) { +func (cr *captchaRepo) GetActionType(ctx context.Context, unit, actionType string) (actionInfo *entity.ActionRecordInfo, err error) { now := time.Now() cacheKey := fmt.Sprintf("ActionRecord:%s@%s@%s", unit, actionType, now.Format("2006-1-02")) - actioninfo = &entity.ActionRecordInfo{} - res, err := cr.data.Cache.GetString(ctx, cacheKey) + res, exist, err := cr.data.Cache.GetString(ctx, cacheKey) if err != nil { - err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + return nil, err } - err = json.Unmarshal([]byte(res), actioninfo) - if err != nil { - return actioninfo, nil + if !exist { + return nil, nil } - return actioninfo, nil + actionInfo = &entity.ActionRecordInfo{} + _ = json.Unmarshal([]byte(res), actionInfo) + return actionInfo, nil } func (cr *captchaRepo) DelActionType(ctx context.Context, unit, actionType string) (err error) { @@ -79,9 +79,12 @@ func (cr *captchaRepo) SetCaptcha(ctx context.Context, key, captcha string) (err // GetCaptcha get captcha from cache func (cr *captchaRepo) GetCaptcha(ctx context.Context, key string) (captcha string, err error) { - captcha, err = cr.data.Cache.GetString(ctx, key) + captcha, exist, err := cr.data.Cache.GetString(ctx, key) if err != nil { - log.Debug(err) + return "", err + } + if !exist { + return "", fmt.Errorf("captcha not exist") } return captcha, nil } diff --git a/internal/repo/config/config_repo.go b/internal/repo/config/config_repo.go index 0d426d70..07f4ce7f 100644 --- a/internal/repo/config/config_repo.go +++ b/internal/repo/config/config_repo.go @@ -28,7 +28,7 @@ func NewConfigRepo(data *data.Data) config.ConfigRepo { func (cr configRepo) GetConfigByID(ctx context.Context, id int) (c *entity.Config, err error) { cacheKey := fmt.Sprintf("%s%d", constant.ConfigID2KEYCacheKeyPrefix, id) - if cacheData, err := cr.data.Cache.GetString(ctx, cacheKey); err == nil && len(cacheData) > 0 { + if cacheData, exist, err := cr.data.Cache.GetString(ctx, cacheKey); err == nil && exist { c = &entity.Config{} c.BuildByJSON([]byte(cacheData)) if c.ID > 0 { @@ -54,7 +54,7 @@ func (cr configRepo) GetConfigByID(ctx context.Context, id int) (c *entity.Confi func (cr configRepo) GetConfigByKey(ctx context.Context, key string) (c *entity.Config, err error) { cacheKey := constant.ConfigKEY2ContentCacheKeyPrefix + key - if cacheData, err := cr.data.Cache.GetString(ctx, cacheKey); err == nil && len(cacheData) > 0 { + if cacheData, exist, err := cr.data.Cache.GetString(ctx, cacheKey); err == nil && exist { c = &entity.Config{} c.BuildByJSON([]byte(cacheData)) if c.ID > 0 { diff --git a/internal/repo/export/email_repo.go b/internal/repo/export/email_repo.go index 33fb31fc..7c2dd952 100644 --- a/internal/repo/export/email_repo.go +++ b/internal/repo/export/email_repo.go @@ -33,9 +33,12 @@ func (e *emailRepo) SetCode(ctx context.Context, code, content string, duration // VerifyCode verify the code if out of date func (e *emailRepo) VerifyCode(ctx context.Context, code string) (content string, err error) { - content, err = e.data.Cache.GetString(ctx, code) + content, exist, err := e.data.Cache.GetString(ctx, code) if err != nil { - err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack() + return "", err } - return + if !exist { + return "", nil + } + return content, nil } diff --git a/internal/repo/question/question_repo.go b/internal/repo/question/question_repo.go index 4f51e6f2..299b8451 100644 --- a/internal/repo/question/question_repo.go +++ b/internal/repo/question/question_repo.go @@ -259,8 +259,8 @@ func (qr *questionRepo) SitemapQuestions(ctx context.Context, page, pageSize int // try to get sitemap data from cache cacheKey := fmt.Sprintf(constant.SiteMapQuestionCacheKeyPrefix, page) - cacheData, err := qr.data.Cache.GetString(ctx, cacheKey) - if err == nil && len(cacheKey) > 0 { + cacheData, exist, err := qr.data.Cache.GetString(ctx, cacheKey) + if err == nil && exist { _ = json.Unmarshal([]byte(cacheData), &questionIDList) return questionIDList, nil } diff --git a/internal/repo/site_info/siteinfo_repo.go b/internal/repo/site_info/siteinfo_repo.go index 03c89416..73734d61 100644 --- a/internal/repo/site_info/siteinfo_repo.go +++ b/internal/repo/site_info/siteinfo_repo.go @@ -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) @@ -61,11 +62,14 @@ func (sr *siteInfoRepo) GetByType(ctx context.Context, siteType string) (siteInf } func (sr *siteInfoRepo) getCache(ctx context.Context, siteType string) (siteInfo *entity.SiteInfo) { - siteInfo = &entity.SiteInfo{} - siteInfoCache, err := sr.data.Cache.GetString(ctx, constant.SiteInfoCacheKey+siteType) + siteInfoCache, exist, err := sr.data.Cache.GetString(ctx, constant.SiteInfoCacheKey+siteType) if err != nil { return nil } + if !exist { + return nil + } + siteInfo = &entity.SiteInfo{} _ = json.Unmarshal([]byte(siteInfoCache), siteInfo) return siteInfo } diff --git a/internal/repo/user_external_login/user_external_login_repo.go b/internal/repo/user_external_login/user_external_login_repo.go index 9c7c5e22..b263c7d6 100644 --- a/internal/repo/user_external_login/user_external_login_repo.go +++ b/internal/repo/user_external_login/user_external_login_repo.go @@ -85,10 +85,14 @@ func (ur *userExternalLoginRepo) SetCacheUserExternalLoginInfo( // GetCacheUserExternalLoginInfo cache user info for external login func (ur *userExternalLoginRepo) GetCacheUserExternalLoginInfo( ctx context.Context, key string) (info *schema.ExternalLoginUserInfoCache, err error) { - res, err := ur.data.Cache.GetString(ctx, constant.ConnectorUserExternalInfoCacheKey+key) + res, exist, err := ur.data.Cache.GetString(ctx, constant.ConnectorUserExternalInfoCacheKey+key) if err != nil { return info, err } + if !exist { + return nil, nil + } + info = &schema.ExternalLoginUserInfoCache{} _ = json.Unmarshal([]byte(res), &info) return info, nil } diff --git a/internal/schema/email_template.go b/internal/schema/email_template.go index 2884eaa7..1d54d92f 100644 --- a/internal/schema/email_template.go +++ b/internal/schema/email_template.go @@ -87,11 +87,12 @@ type NewCommentTemplateData struct { } type NewQuestionTemplateRawData struct { - QuestionTitle string - QuestionID string - UnsubscribeCode string - Tags []string - TagIDs []string + QuestionAuthorUserID string + QuestionTitle string + QuestionID string + UnsubscribeCode string + Tags []string + TagIDs []string } type NewQuestionTemplateData struct { diff --git a/internal/schema/new_question_queue_schema.go b/internal/schema/new_question_queue_schema.go index 2bc05431..4410a050 100644 --- a/internal/schema/new_question_queue_schema.go +++ b/internal/schema/new_question_queue_schema.go @@ -16,12 +16,14 @@ type ExternalNotificationMsg struct { NewQuestionTemplateRawData *NewQuestionTemplateRawData `json:"new_question_template_raw_data,omitempty"` } -func CreateNewQuestionNotificationMsg(questionID, questionTitle string, tags []*entity.Tag) *ExternalNotificationMsg { +func CreateNewQuestionNotificationMsg( + questionID, questionTitle, questionAuthorUserID string, tags []*entity.Tag) *ExternalNotificationMsg { questionID = uid.DeShortID(questionID) msg := &ExternalNotificationMsg{ NewQuestionTemplateRawData: &NewQuestionTemplateRawData{ - QuestionID: questionID, - QuestionTitle: questionTitle, + QuestionAuthorUserID: questionAuthorUserID, + QuestionID: questionID, + QuestionTitle: questionTitle, }, } for _, tag := range tags { diff --git a/internal/service/action/captcha_service.go b/internal/service/action/captcha_service.go index b3cdf087..8ad46b3f 100644 --- a/internal/service/action/captcha_service.go +++ b/internal/service/action/captcha_service.go @@ -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) { diff --git a/internal/service/action/captcha_strategy.go b/internal/service/action/captcha_strategy.go index 5e6ff1d8..09555f87 100644 --- a/internal/service/action/captcha_strategy.go +++ b/internal/service/action/captcha_strategy.go @@ -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: diff --git a/internal/service/auth/auth.go b/internal/service/auth/auth.go index 9053a507..33e987eb 100644 --- a/internal/service/auth/auth.go +++ b/internal/service/auth/auth.go @@ -41,6 +41,9 @@ func (as *AuthService) GetUserCacheInfo(ctx context.Context, accessToken string) if err != nil { return nil, err } + if userCacheInfo == nil { + return nil, nil + } cacheInfo, _ := as.authRepo.GetUserStatus(ctx, userCacheInfo.UserID) if cacheInfo != nil { userCacheInfo.UserStatus = cacheInfo.UserStatus diff --git a/internal/service/dashboard/dashboard_service.go b/internal/service/dashboard/dashboard_service.go index e6b26701..65d5cf77 100644 --- a/internal/service/dashboard/dashboard_service.go +++ b/internal/service/dashboard/dashboard_service.go @@ -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,16 +95,20 @@ func (ds *dashboardService) Statistical(ctx context.Context) (*schema.DashboardI return dashboardInfo, nil } -func (ds *dashboardService) getFromCache(ctx context.Context) (*schema.DashboardInfo, error) { - infoStr, err := ds.data.Cache.GetString(ctx, schema.DashboardCacheKey) +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 } - dashboardInfo := &schema.DashboardInfo{} + if !exist { + return nil + } + dashboardInfo = &schema.DashboardInfo{} 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) { diff --git a/internal/service/export/email_service.go b/internal/service/export/email_service.go index 134a4fb2..0f0985f5 100644 --- a/internal/service/export/email_service.go +++ b/internal/service/export/email_service.go @@ -145,7 +145,7 @@ func (es *EmailService) Send(ctx context.Context, toEmailAddr, subject, body str func (es *EmailService) VerifyUrlExpired(ctx context.Context, code string) (content string) { content, err := es.emailRepo.VerifyCode(ctx, code) if err != nil { - log.Warn(err) + log.Error(err) } return content } diff --git a/internal/service/notification/external_notification.go b/internal/service/notification/external_notification.go index eeaf2e5f..fddaf20c 100644 --- a/internal/service/notification/external_notification.go +++ b/internal/service/notification/external_notification.go @@ -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, diff --git a/internal/service/notification/new_question_notification.go b/internal/service/notification/new_question_notification.go index 8533ada3..8237e9e3 100644 --- a/internal/service/notification/new_question_notification.go +++ b/internal/service/notification/new_question_notification.go @@ -78,7 +78,6 @@ func (ns *ExternalNotificationService) getNewQuestionSubscribers(ctx context.Con UserID: userNotificationConfig.UserID, Channels: schema.NewNotificationChannelsFormJson(userNotificationConfig.Channels), } - subscribers = append(subscribers, subscribersMapping[userNotificationConfig.UserID]) } log.Debugf("get %d subscribers from tags", len(subscribersMapping)) @@ -98,14 +97,36 @@ func (ns *ExternalNotificationService) getNewQuestionSubscribers(ctx context.Con UserID: notificationConfig.UserID, Channels: schema.NewNotificationChannelsFormJson(notificationConfig.Channels), } - subscribers = append(subscribers, subscribersMapping[notificationConfig.UserID]) + } + + // 3. remove question owner + delete(subscribersMapping, msg.NewQuestionTemplateRawData.QuestionAuthorUserID) + for _, subscriber := range subscribersMapping { + subscribers = append(subscribers, subscriber) } 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 + 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 } diff --git a/internal/service/notification/notification_service.go b/internal/service/notification/notification_service.go index 25d8444e..98475e19 100644 --- a/internal/service/notification/notification_service.go +++ b/internal/service/notification/notification_service.go @@ -46,13 +46,13 @@ func (ns *NotificationService) GetRedDot(ctx context.Context, req *schema.GetRed redBot := &schema.RedDot{} inboxKey := fmt.Sprintf("answer_RedDot_%d_%s", schema.NotificationTypeInbox, req.UserID) achievementKey := fmt.Sprintf("answer_RedDot_%d_%s", schema.NotificationTypeAchievement, req.UserID) - inboxValue, err := ns.data.Cache.GetInt64(ctx, inboxKey) + inboxValue, _, err := ns.data.Cache.GetInt64(ctx, inboxKey) if err != nil { redBot.Inbox = 0 } else { redBot.Inbox = inboxValue } - achievementValue, err := ns.data.Cache.GetInt64(ctx, achievementKey) + achievementValue, _, err := ns.data.Cache.GetInt64(ctx, achievementKey) if err != nil { redBot.Achievement = 0 } else { diff --git a/internal/service/question_service.go b/internal/service/question_service.go index 866b2a47..48dbffcd 100644 --- a/internal/service/question_service.go +++ b/internal/service/question_service.go @@ -333,7 +333,7 @@ func (qs *QuestionService) AddQuestion(ctx context.Context, req *schema.Question }) qs.externalNotificationQueueService.Send(ctx, - schema.CreateNewQuestionNotificationMsg(question.ID, question.Title, tags)) + schema.CreateNewQuestionNotificationMsg(question.ID, question.Title, question.UserID, tags)) questionInfo, err = qs.GetQuestion(ctx, question.ID, question.UserID, req.QuestionPermission) return diff --git a/internal/service/user_external_login/user_external_login_service.go b/internal/service/user_external_login/user_external_login_service.go index 7e439ba3..bdf1c57f 100644 --- a/internal/service/user_external_login/user_external_login_service.go +++ b/internal/service/user_external_login/user_external_login_service.go @@ -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) diff --git a/plugin/cache.go b/plugin/cache.go index 993f3fe0..da6ee69d 100644 --- a/plugin/cache.go +++ b/plugin/cache.go @@ -8,12 +8,14 @@ import ( type Cache interface { Base - GetString(ctx context.Context, key string) (string, error) - SetString(ctx context.Context, key, value string, ttl time.Duration) error - GetInt64(ctx context.Context, key string) (int64, error) - SetInt64(ctx context.Context, key string, value int64, ttl time.Duration) error - Del(ctx context.Context, key string) error - Flush(ctx context.Context) error + GetString(ctx context.Context, key string) (data string, exist bool, err error) + SetString(ctx context.Context, key, value string, ttl time.Duration) (err error) + GetInt64(ctx context.Context, key string) (data int64, exist bool, err error) + SetInt64(ctx context.Context, key string, value int64, ttl time.Duration) (err error) + Increase(ctx context.Context, key string, value int64) (data int64, err error) + Decrease(ctx context.Context, key string, value int64) (data int64, err error) + Del(ctx context.Context, key string) (err error) + Flush(ctx context.Context) (err error) } var (