2026-04-28 14:53:04 +08:00
|
|
|
|
package service
|
2026-03-24 22:27:53 +08:00
|
|
|
|
|
|
|
|
|
|
import (
|
2026-03-25 01:03:34 +08:00
|
|
|
|
"time"
|
|
|
|
|
|
|
2026-04-22 16:01:59 +08:00
|
|
|
|
"with_you/internal/cache"
|
|
|
|
|
|
"with_you/internal/model"
|
|
|
|
|
|
"with_you/internal/repository"
|
2026-03-24 22:27:53 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
2026-03-25 01:03:34 +08:00
|
|
|
|
// 频道全量列表缓存 TTL(频道变更少,可较长;管理端写入后会主动失效)
|
|
|
|
|
|
const channelAllListCacheTTL = 10 * time.Minute
|
|
|
|
|
|
|
2026-03-24 22:27:53 +08:00
|
|
|
|
type ChannelService interface {
|
|
|
|
|
|
ListActive() ([]*model.Channel, error)
|
|
|
|
|
|
ListAll() ([]*model.Channel, error)
|
2026-03-25 00:58:41 +08:00
|
|
|
|
MapByIDs(ids []string) (map[string]*model.Channel, error)
|
2026-03-24 22:27:53 +08:00
|
|
|
|
Create(name, slug, description string, sortOrder int, isActive bool) (*model.Channel, error)
|
|
|
|
|
|
Update(id, name, slug, description string, sortOrder int, isActive bool) (*model.Channel, error)
|
|
|
|
|
|
Delete(id string) error
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
type channelServiceImpl struct {
|
2026-03-26 18:14:16 +08:00
|
|
|
|
channelRepo repository.ChannelRepository
|
2026-03-25 01:03:34 +08:00
|
|
|
|
cache cache.Cache
|
2026-03-24 22:27:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-26 18:14:16 +08:00
|
|
|
|
func NewChannelService(channelRepo repository.ChannelRepository, c cache.Cache) ChannelService {
|
2026-03-25 01:03:34 +08:00
|
|
|
|
return &channelServiceImpl{channelRepo: channelRepo, cache: c}
|
2026-03-24 22:27:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-25 01:03:34 +08:00
|
|
|
|
func (s *channelServiceImpl) invalidateChannelListCache() {
|
|
|
|
|
|
if s.cache != nil {
|
|
|
|
|
|
cache.InvalidateChannelList(s.cache)
|
|
|
|
|
|
}
|
2026-03-24 22:27:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-25 01:03:34 +08:00
|
|
|
|
func (s *channelServiceImpl) listAllFromDB() ([]*model.Channel, error) {
|
2026-03-24 22:27:53 +08:00
|
|
|
|
return s.channelRepo.ListAll()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-25 01:03:34 +08:00
|
|
|
|
func (s *channelServiceImpl) ListAll() ([]*model.Channel, error) {
|
|
|
|
|
|
if s.cache == nil {
|
|
|
|
|
|
return s.listAllFromDB()
|
|
|
|
|
|
}
|
|
|
|
|
|
cs := cache.GetSettings()
|
|
|
|
|
|
return cache.GetOrLoadTyped(s.cache, cache.ChannelsAllListKey(), channelAllListCacheTTL, cs.JitterRatio, cs.NullTTL, s.listAllFromDB)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *channelServiceImpl) ListActive() ([]*model.Channel, error) {
|
|
|
|
|
|
all, err := s.ListAll()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
out := make([]*model.Channel, 0, len(all))
|
|
|
|
|
|
for _, ch := range all {
|
|
|
|
|
|
if ch != nil && ch.IsActive {
|
|
|
|
|
|
c := *ch
|
|
|
|
|
|
out = append(out, &c)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return out, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MapByIDs 始终基于全量频道列表(Redis 命中则零 SQL,否则一次 ListAll),与 ListActive / 公开列表共用同一缓存快照。
|
2026-03-25 00:58:41 +08:00
|
|
|
|
func (s *channelServiceImpl) MapByIDs(ids []string) (map[string]*model.Channel, error) {
|
2026-03-25 01:03:34 +08:00
|
|
|
|
if len(ids) == 0 {
|
|
|
|
|
|
return nil, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
all, err := s.ListAll()
|
2026-03-25 00:58:41 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
2026-03-25 01:03:34 +08:00
|
|
|
|
byID := make(map[string]*model.Channel, len(all))
|
|
|
|
|
|
for _, ch := range all {
|
2026-03-25 00:58:41 +08:00
|
|
|
|
if ch != nil {
|
2026-03-25 01:03:34 +08:00
|
|
|
|
byID[ch.ID] = ch
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
out := make(map[string]*model.Channel, len(ids))
|
|
|
|
|
|
for _, id := range ids {
|
|
|
|
|
|
if ch, ok := byID[id]; ok {
|
|
|
|
|
|
out[id] = ch
|
2026-03-25 00:58:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-25 01:03:34 +08:00
|
|
|
|
return out, nil
|
2026-03-25 00:58:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-24 22:27:53 +08:00
|
|
|
|
func (s *channelServiceImpl) Create(name, slug, description string, sortOrder int, isActive bool) (*model.Channel, error) {
|
|
|
|
|
|
channel := &model.Channel{
|
|
|
|
|
|
Name: name,
|
|
|
|
|
|
Slug: slug,
|
|
|
|
|
|
Description: description,
|
|
|
|
|
|
SortOrder: sortOrder,
|
|
|
|
|
|
IsActive: isActive,
|
|
|
|
|
|
}
|
|
|
|
|
|
if err := s.channelRepo.Create(channel); err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
2026-03-25 01:03:34 +08:00
|
|
|
|
s.invalidateChannelListCache()
|
2026-03-24 22:27:53 +08:00
|
|
|
|
return channel, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *channelServiceImpl) Update(id, name, slug, description string, sortOrder int, isActive bool) (*model.Channel, error) {
|
|
|
|
|
|
channel, err := s.channelRepo.GetByID(id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
channel.Name = name
|
|
|
|
|
|
channel.Slug = slug
|
|
|
|
|
|
channel.Description = description
|
|
|
|
|
|
channel.SortOrder = sortOrder
|
|
|
|
|
|
channel.IsActive = isActive
|
|
|
|
|
|
if err := s.channelRepo.Update(channel); err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
2026-03-25 01:03:34 +08:00
|
|
|
|
s.invalidateChannelListCache()
|
2026-03-24 22:27:53 +08:00
|
|
|
|
return channel, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *channelServiceImpl) Delete(id string) error {
|
2026-03-25 01:03:34 +08:00
|
|
|
|
err := s.channelRepo.Delete(id)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
s.invalidateChannelListCache()
|
|
|
|
|
|
return nil
|
2026-03-24 22:27:53 +08:00
|
|
|
|
}
|