Merge branch 'feat/1.1.2/notification' into test

This commit is contained in:
LinkinStars 2023-08-22 17:40:49 +08:00
commit 119c7ddf6d
26 changed files with 177 additions and 102 deletions

View File

@ -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)
}
}

View File

@ -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
View File

@ -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

14
go.sum
View File

@ -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=

View File

@ -19,4 +19,7 @@ const (
SiteMapQuestionCacheKeyPrefix = "answer:sitemap:question:%d"
SiteMapQuestionCacheTime = time.Hour
SitemapMaxSize = 50000
NewQuestionNotificationLimitCacheKeyPrefix = "answer:new-question-notification-limit:"
NewQuestionNotificationLimitCacheTime = 7 * 24 * time.Hour
NewQuestionNotificationLimitMax = 50
)

View File

@ -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 {

View File

@ -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)

View File

@ -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
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -87,6 +87,7 @@ type NewCommentTemplateData struct {
}
type NewQuestionTemplateRawData struct {
QuestionAuthorUserID string
QuestionTitle string
QuestionID string
UnsubscribeCode string

View File

@ -16,10 +16,12 @@ 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{
QuestionAuthorUserID: questionAuthorUserID,
QuestionID: questionID,
QuestionTitle: questionTitle,
},

View File

@ -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) {

View File

@ -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:

View File

@ -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

View File

@ -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) {

View File

@ -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
}

View File

@ -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,

View File

@ -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
}

View File

@ -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 {

View File

@ -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

View File

@ -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)

View File

@ -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 (