chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
210
internal/store/memory/themes.go
Normal file
210
internal/store/memory/themes.go
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
package memory
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
// ThemeStore 是 store.ThemeStore 的内存实现(测试/单实例)。
|
||||
type ThemeStore struct {
|
||||
mu sync.RWMutex
|
||||
byID map[int64]domain.Theme
|
||||
bySlug map[string]int64
|
||||
installs map[int64]map[int64]themeInstall // userID -> themeID -> 安装项
|
||||
seq int64 // 单调序,模拟 postgres installed_at 排序
|
||||
}
|
||||
|
||||
type themeInstall struct {
|
||||
dark bool
|
||||
order int64
|
||||
}
|
||||
|
||||
// NewThemeStore 创建内存主题 store。
|
||||
func NewThemeStore() *ThemeStore {
|
||||
return &ThemeStore{
|
||||
byID: make(map[int64]domain.Theme),
|
||||
bySlug: make(map[string]int64),
|
||||
installs: make(map[int64]map[int64]themeInstall),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ThemeStore) CreateTheme(_ context.Context, t domain.Theme) error {
|
||||
if t.ID == 0 || t.CreatorUserID == 0 {
|
||||
return domain.ErrThemeInvalid
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if _, ok := s.byID[t.ID]; ok {
|
||||
return domain.ErrThemeInvalid
|
||||
}
|
||||
if t.Slug != "" {
|
||||
if _, ok := s.bySlug[t.Slug]; ok {
|
||||
return domain.ErrThemeSlugTaken
|
||||
}
|
||||
}
|
||||
s.byID[t.ID] = t.Clone()
|
||||
if t.Slug != "" {
|
||||
s.bySlug[t.Slug] = t.ID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ThemeStore) GetThemeByID(_ context.Context, id int64) (domain.Theme, bool, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
t, ok := s.byID[id]
|
||||
if !ok {
|
||||
return domain.Theme{}, false, nil
|
||||
}
|
||||
return t.Clone(), true, nil
|
||||
}
|
||||
|
||||
func (s *ThemeStore) GetThemeBySlug(_ context.Context, slug string) (domain.Theme, bool, error) {
|
||||
if slug == "" {
|
||||
return domain.Theme{}, false, nil
|
||||
}
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
id, ok := s.bySlug[slug]
|
||||
if !ok {
|
||||
return domain.Theme{}, false, nil
|
||||
}
|
||||
t, ok := s.byID[id]
|
||||
if !ok {
|
||||
return domain.Theme{}, false, nil
|
||||
}
|
||||
return t.Clone(), true, nil
|
||||
}
|
||||
|
||||
func (s *ThemeStore) SlugExists(_ context.Context, slug string) (bool, error) {
|
||||
if slug == "" {
|
||||
return false, nil
|
||||
}
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
_, ok := s.bySlug[slug]
|
||||
return ok, nil
|
||||
}
|
||||
|
||||
func (s *ThemeStore) UpdateTheme(_ context.Context, t domain.Theme) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
prev, ok := s.byID[t.ID]
|
||||
if !ok {
|
||||
return domain.ErrThemeNotFound
|
||||
}
|
||||
if t.Slug != "" && t.Slug != prev.Slug {
|
||||
if _, taken := s.bySlug[t.Slug]; taken {
|
||||
return domain.ErrThemeSlugTaken
|
||||
}
|
||||
}
|
||||
if prev.Slug != "" && prev.Slug != t.Slug {
|
||||
delete(s.bySlug, prev.Slug)
|
||||
}
|
||||
s.byID[t.ID] = t.Clone()
|
||||
if t.Slug != "" {
|
||||
s.bySlug[t.Slug] = t.ID
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ThemeStore) IncrementInstalls(_ context.Context, id int64) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
t, ok := s.byID[id]
|
||||
if !ok {
|
||||
return domain.ErrThemeNotFound
|
||||
}
|
||||
t.InstallsCount++
|
||||
s.byID[id] = t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ThemeStore) SetInstalled(_ context.Context, userID, themeID int64, dark bool) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if _, ok := s.byID[themeID]; !ok {
|
||||
return domain.ErrThemeNotFound
|
||||
}
|
||||
byUser := s.installs[userID]
|
||||
if byUser == nil {
|
||||
byUser = make(map[int64]themeInstall)
|
||||
s.installs[userID] = byUser
|
||||
}
|
||||
if prev, ok := byUser[themeID]; ok {
|
||||
byUser[themeID] = themeInstall{dark: dark, order: prev.order}
|
||||
return nil
|
||||
}
|
||||
s.seq++
|
||||
byUser[themeID] = themeInstall{dark: dark, order: s.seq}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ThemeStore) RemoveInstalled(_ context.Context, userID, themeID int64) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
if byUser := s.installs[userID]; byUser != nil {
|
||||
delete(byUser, themeID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ThemeStore) ListInstalledByUser(_ context.Context, userID int64) ([]domain.Theme, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
byUser := s.installs[userID]
|
||||
if len(byUser) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
type row struct {
|
||||
t domain.Theme
|
||||
order int64
|
||||
}
|
||||
rows := make([]row, 0, len(byUser))
|
||||
for themeID, inst := range byUser {
|
||||
t, ok := s.byID[themeID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
rows = append(rows, row{t: t.Clone(), order: inst.order})
|
||||
}
|
||||
sort.Slice(rows, func(i, j int) bool { return rows[i].order < rows[j].order })
|
||||
out := make([]domain.Theme, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
out = append(out, r.t)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (s *ThemeStore) ListThemesForUser(_ context.Context, userID int64) ([]domain.Theme, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
seen := make(map[int64]bool)
|
||||
out := make([]domain.Theme, 0)
|
||||
for _, t := range s.byID {
|
||||
if t.CreatorUserID == userID {
|
||||
out = append(out, t.Clone())
|
||||
seen[t.ID] = true
|
||||
}
|
||||
}
|
||||
if byUser := s.installs[userID]; byUser != nil {
|
||||
for themeID := range byUser {
|
||||
if seen[themeID] {
|
||||
continue
|
||||
}
|
||||
if t, ok := s.byID[themeID]; ok {
|
||||
out = append(out, t.Clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
sort.Slice(out, func(i, j int) bool {
|
||||
if out[i].CreatedAt != out[j].CreatedAt {
|
||||
return out[i].CreatedAt < out[j].CreatedAt
|
||||
}
|
||||
return out[i].ID < out[j].ID
|
||||
})
|
||||
return out, nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue