Bring cache into preference notification

This commit is contained in:
LittleSheep 2024-09-22 14:04:56 +08:00
parent bbceb65dbf
commit 27b625e0c8
3 changed files with 111 additions and 33 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="dataSourceStorageLocal" created-in="GO-242.21829.220"> <component name="dataSourceStorageLocal" created-in="GO-242.22855.85">
<data-source name="hy_passport@localhost" uuid="74bcf3ef-a2b9-435b-b9e5-f32902a33b25"> <data-source name="hy_passport@localhost" uuid="74bcf3ef-a2b9-435b-b9e5-f32902a33b25">
<database-info product="PostgreSQL" version="16.3 (Homebrew)" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.6.0" dbms="POSTGRES" exact-version="16.3" exact-driver-version="42.6"> <database-info product="PostgreSQL" version="16.3 (Homebrew)" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.6.0" dbms="POSTGRES" exact-version="16.3" exact-driver-version="42.6">
<identifier-quote-string>&quot;</identifier-quote-string> <identifier-quote-string>&quot;</identifier-quote-string>

15
.idea/workspace.xml generated
View File

@ -4,16 +4,7 @@
<option name="autoReloadType" value="ALL" /> <option name="autoReloadType" value="ALL" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":sparkles: Account deletion"> <list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":sparkles: Account deletion" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/go.mod" beforeDir="false" afterPath="$PROJECT_DIR$/go.mod" afterDir="false" />
<change beforePath="$PROJECT_DIR$/go.sum" beforeDir="false" afterPath="$PROJECT_DIR$/go.sum" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/models/tokens.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/models/tokens.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/api/accounts_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/accounts_api.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/server/api/index.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/index.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/services/accounts.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/accounts.go" afterDir="false" />
<change beforePath="$PROJECT_DIR$/pkg/internal/services/tokens.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/services/tokens.go" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
@ -118,8 +109,8 @@
<component name="SharedIndexes"> <component name="SharedIndexes">
<attachedChunks> <attachedChunks>
<set> <set>
<option value="bundled-gosdk-5df93f7ad4aa-df9ad98b711f-org.jetbrains.plugins.go.sharedIndexes.bundled-GO-242.21829.220" /> <option value="bundled-gosdk-5df93f7ad4aa-df9ad98b711f-org.jetbrains.plugins.go.sharedIndexes.bundled-GO-242.22855.85" />
<option value="bundled-js-predefined-d6986cc7102b-7c0b70fcd90d-JavaScript-GO-242.21829.220" /> <option value="bundled-js-predefined-d6986cc7102b-5c90d61e3bab-JavaScript-GO-242.22855.85" />
</set> </set>
</attachedChunks> </attachedChunks>
</component> </component>

View File

@ -1,23 +1,58 @@
package services package services
import ( import (
"context"
"errors" "errors"
"fmt"
"time"
localCache "git.solsynth.dev/hydrogen/passport/pkg/internal/cache"
"git.solsynth.dev/hydrogen/passport/pkg/internal/database" "git.solsynth.dev/hydrogen/passport/pkg/internal/database"
"git.solsynth.dev/hydrogen/passport/pkg/internal/models" "git.solsynth.dev/hydrogen/passport/pkg/internal/models"
"github.com/eko/gocache/lib/v4/cache"
"github.com/eko/gocache/lib/v4/marshaler"
"github.com/eko/gocache/lib/v4/store"
"github.com/samber/lo" "github.com/samber/lo"
"gorm.io/datatypes" "gorm.io/datatypes"
"gorm.io/gorm" "gorm.io/gorm"
) )
func GetNotificationPreferenceCacheKey(accountId uint) string {
return fmt.Sprintf("notification-preference#%d", accountId)
}
func GetNotificationPreference(account models.Account) (models.PreferenceNotification, error) { func GetNotificationPreference(account models.Account) (models.PreferenceNotification, error) {
var notification models.PreferenceNotification var notification models.PreferenceNotification
if err := database.C.Where("account_id = ?", account.ID).First(&notification).Error; err != nil { cacheManager := cache.New[any](localCache.S)
return notification, err marshal := marshaler.New(cacheManager)
contx := context.Background()
if val, err := marshal.Get(contx, GetNotificationPreferenceCacheKey(account.ID), new(models.PreferenceNotification)); err == nil {
notification = val.(models.PreferenceNotification)
} else {
if err := database.C.Where("account_id = ?", account.ID).First(&notification).Error; err != nil {
return notification, err
}
CacheNotificationPreference(notification)
} }
return notification, nil return notification, nil
} }
func CacheNotificationPreference(prefs models.PreferenceNotification) {
cacheManager := cache.New[any](localCache.S)
marshal := marshaler.New(cacheManager)
contx := context.Background()
marshal.Set(
contx,
GetNotificationPreferenceCacheKey(prefs.AccountID),
prefs,
store.WithExpiration(60*time.Minute),
store.WithTags([]string{"notification-preference", fmt.Sprintf("user#%d", prefs.AccountID)}),
)
}
func UpdateNotificationPreference(account models.Account, config map[string]bool) (models.PreferenceNotification, error) { func UpdateNotificationPreference(account models.Account, config map[string]bool) (models.PreferenceNotification, error) {
var notification models.PreferenceNotification var notification models.PreferenceNotification
var err error var err error
@ -35,17 +70,31 @@ func UpdateNotificationPreference(account models.Account, config map[string]bool
} }
err = database.C.Save(&notification).Error err = database.C.Save(&notification).Error
if err == nil {
CacheNotificationPreference(notification)
}
return notification, err return notification, err
} }
func CheckNotificationNotifiable(account models.Account, topic string) bool { func CheckNotificationNotifiable(account models.Account, topic string) bool {
var notification models.PreferenceNotification var notification models.PreferenceNotification
if err := database.C.Where("account_id = ?", account.ID).First(&notification).Error; err != nil { cacheManager := cache.New[any](localCache.S)
if errors.Is(err, gorm.ErrRecordNotFound) { marshal := marshaler.New(cacheManager)
return true contx := context.Background()
if val, err := marshal.Get(contx, GetNotificationPreferenceCacheKey(account.ID), new(models.PreferenceNotification)); err == nil {
notification = val.(models.PreferenceNotification)
} else {
if err := database.C.Where("account_id = ?", account.ID).First(&notification).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return true
}
return false
} }
return false CacheNotificationPreference(notification)
} }
if val, ok := notification.Config[topic]; ok { if val, ok := notification.Config[topic]; ok {
if status, ok := val.(bool); ok { if status, ok := val.(bool); ok {
return status return status
@ -55,24 +104,62 @@ func CheckNotificationNotifiable(account models.Account, topic string) bool {
} }
func CheckNotificationNotifiableBatch(accounts []models.Account, topic string) []bool { func CheckNotificationNotifiableBatch(accounts []models.Account, topic string) []bool {
var notifications []models.PreferenceNotification cacheManager := cache.New[any](localCache.S)
if err := database.C.Where("account_id IN ?", lo.Map(accounts, func(item models.Account, index int) uint { marshal := marshaler.New(cacheManager)
return item.ID contx := context.Background()
})).Find(&notifications).Error; err != nil {
return lo.Map(accounts, func(item models.Account, index int) bool {
return false
})
}
var notifiable = make([]bool, len(accounts)) var notifiable = make([]bool, len(accounts))
for idx, notification := range notifications { queryNeededIdx := []uint{}
if val, ok := notification.Config[topic]; ok { notificationMap := make(map[uint]models.PreferenceNotification)
if status, ok := val.(bool); ok {
notifiable[idx] = status // Check cache for each account
continue for idx, account := range accounts {
cacheKey := GetNotificationPreferenceCacheKey(account.ID)
if val, err := marshal.Get(contx, cacheKey, new(models.PreferenceNotification)); err == nil {
notification := val.(models.PreferenceNotification)
notificationMap[account.ID] = notification
// Determine if the account is notifiable based on the topic config
if val, ok := notification.Config[topic]; ok {
if status, ok := val.(bool); ok {
notifiable[idx] = status
continue
}
}
notifiable[idx] = true
} else {
// Add to the list of accounts that need to be queried
queryNeededIdx = append(queryNeededIdx, account.ID)
}
}
// Query the database for missing account IDs
if len(queryNeededIdx) > 0 {
var dbNotifications []models.PreferenceNotification
if err := database.C.Where("account_id IN ?", queryNeededIdx).Find(&dbNotifications).Error; err != nil {
// Handle error by returning false for accounts without cached notifications
return lo.Map(accounts, func(item models.Account, index int) bool {
return false
})
}
// Cache the newly fetched notifications and add to the notificationMap
for _, notification := range dbNotifications {
notificationMap[notification.AccountID] = notification
CacheNotificationPreference(notification) // Cache the result
}
// Process the notifiable status for the fetched notifications
for idx, account := range accounts {
if notification, exists := notificationMap[account.ID]; exists {
if val, ok := notification.Config[topic]; ok {
if status, ok := val.(bool); ok {
notifiable[idx] = status
continue
}
}
notifiable[idx] = true
} }
} }
notifiable[idx] = true
} }
return notifiable return notifiable