⚡ Bring cache into preference notification
This commit is contained in:
		
							
								
								
									
										2
									
								
								.idea/dataSources.local.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								.idea/dataSources.local.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <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"> | ||||
|       <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>"</identifier-quote-string> | ||||
|   | ||||
							
								
								
									
										15
									
								
								.idea/workspace.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										15
									
								
								.idea/workspace.xml
									
									
									
										generated
									
									
									
								
							| @@ -4,16 +4,7 @@ | ||||
|     <option name="autoReloadType" value="ALL" /> | ||||
|   </component> | ||||
|   <component name="ChangeListManager"> | ||||
|     <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> | ||||
|     <list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":sparkles: Account deletion" /> | ||||
|     <option name="SHOW_DIALOG" value="false" /> | ||||
|     <option name="HIGHLIGHT_CONFLICTS" value="true" /> | ||||
|     <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> | ||||
| @@ -118,8 +109,8 @@ | ||||
|   <component name="SharedIndexes"> | ||||
|     <attachedChunks> | ||||
|       <set> | ||||
|         <option value="bundled-gosdk-5df93f7ad4aa-df9ad98b711f-org.jetbrains.plugins.go.sharedIndexes.bundled-GO-242.21829.220" /> | ||||
|         <option value="bundled-js-predefined-d6986cc7102b-7c0b70fcd90d-JavaScript-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-5c90d61e3bab-JavaScript-GO-242.22855.85" /> | ||||
|       </set> | ||||
|     </attachedChunks> | ||||
|   </component> | ||||
|   | ||||
| @@ -1,23 +1,58 @@ | ||||
| package services | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"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/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" | ||||
| 	"gorm.io/datatypes" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
|  | ||||
| func GetNotificationPreferenceCacheKey(accountId uint) string { | ||||
| 	return fmt.Sprintf("notification-preference#%d", accountId) | ||||
| } | ||||
|  | ||||
| func GetNotificationPreference(account models.Account) (models.PreferenceNotification, error) { | ||||
| 	var notification models.PreferenceNotification | ||||
| 	if err := database.C.Where("account_id = ?", account.ID).First(¬ification).Error; err != nil { | ||||
| 		return notification, err | ||||
| 	cacheManager := cache.New[any](localCache.S) | ||||
| 	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(¬ification).Error; err != nil { | ||||
| 			return notification, err | ||||
| 		} | ||||
| 		CacheNotificationPreference(notification) | ||||
| 	} | ||||
|  | ||||
| 	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) { | ||||
| 	var notification models.PreferenceNotification | ||||
| 	var err error | ||||
| @@ -35,17 +70,31 @@ func UpdateNotificationPreference(account models.Account, config map[string]bool | ||||
| 	} | ||||
|  | ||||
| 	err = database.C.Save(¬ification).Error | ||||
| 	if err == nil { | ||||
| 		CacheNotificationPreference(notification) | ||||
| 	} | ||||
|  | ||||
| 	return notification, err | ||||
| } | ||||
|  | ||||
| func CheckNotificationNotifiable(account models.Account, topic string) bool { | ||||
| 	var notification models.PreferenceNotification | ||||
| 	if err := database.C.Where("account_id = ?", account.ID).First(¬ification).Error; err != nil { | ||||
| 		if errors.Is(err, gorm.ErrRecordNotFound) { | ||||
| 			return true | ||||
| 	cacheManager := cache.New[any](localCache.S) | ||||
| 	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(¬ification).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 status, ok := val.(bool); ok { | ||||
| 			return status | ||||
| @@ -55,24 +104,62 @@ func CheckNotificationNotifiable(account models.Account, topic string) bool { | ||||
| } | ||||
|  | ||||
| func CheckNotificationNotifiableBatch(accounts []models.Account, topic string) []bool { | ||||
| 	var notifications []models.PreferenceNotification | ||||
| 	if err := database.C.Where("account_id IN ?", lo.Map(accounts, func(item models.Account, index int) uint { | ||||
| 		return item.ID | ||||
| 	})).Find(¬ifications).Error; err != nil { | ||||
| 		return lo.Map(accounts, func(item models.Account, index int) bool { | ||||
| 			return false | ||||
| 		}) | ||||
| 	} | ||||
| 	cacheManager := cache.New[any](localCache.S) | ||||
| 	marshal := marshaler.New(cacheManager) | ||||
| 	contx := context.Background() | ||||
|  | ||||
| 	var notifiable = make([]bool, len(accounts)) | ||||
| 	for idx, notification := range notifications { | ||||
| 		if val, ok := notification.Config[topic]; ok { | ||||
| 			if status, ok := val.(bool); ok { | ||||
| 				notifiable[idx] = status | ||||
| 				continue | ||||
| 	queryNeededIdx := []uint{} | ||||
| 	notificationMap := make(map[uint]models.PreferenceNotification) | ||||
|  | ||||
| 	// Check cache for each account | ||||
| 	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 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user