Files
urldb/db/repo/resource_repository.go
2025-07-17 14:08:52 +08:00

347 lines
11 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package repo
import (
"fmt"
"time"
"github.com/ctwj/panResManage/db/entity"
"gorm.io/gorm"
)
// ResourceRepository Resource的Repository接口
type ResourceRepository interface {
BaseRepository[entity.Resource]
FindWithRelations() ([]entity.Resource, error)
FindWithRelationsPaginated(page, limit int) ([]entity.Resource, int64, error)
FindByCategoryID(categoryID uint) ([]entity.Resource, error)
FindByCategoryIDPaginated(categoryID uint, page, limit int) ([]entity.Resource, int64, error)
FindByPanID(panID uint) ([]entity.Resource, error)
FindByPanIDPaginated(panID uint, page, limit int) ([]entity.Resource, int64, error)
FindByIsValid(isValid bool) ([]entity.Resource, error)
FindByIsPublic(isPublic bool) ([]entity.Resource, error)
Search(query string, categoryID *uint, page, limit int) ([]entity.Resource, int64, error)
SearchByPanID(query string, panID uint, page, limit int) ([]entity.Resource, int64, error)
SearchWithFilters(params map[string]interface{}) ([]entity.Resource, int64, error)
IncrementViewCount(id uint) error
FindWithTags() ([]entity.Resource, error)
UpdateWithTags(resource *entity.Resource, tagIDs []uint) error
GetLatestResources(limit int) ([]entity.Resource, error)
GetCachedLatestResources(limit int) ([]entity.Resource, error)
InvalidateCache() error
FindExists(url string, excludeID ...uint) (bool, error)
}
// ResourceRepositoryImpl Resource的Repository实现
type ResourceRepositoryImpl struct {
BaseRepositoryImpl[entity.Resource]
cache map[string]interface{}
}
// NewResourceRepository 创建Resource Repository
func NewResourceRepository(db *gorm.DB) ResourceRepository {
return &ResourceRepositoryImpl{
BaseRepositoryImpl: BaseRepositoryImpl[entity.Resource]{db: db},
cache: make(map[string]interface{}),
}
}
// FindWithRelations 查找包含关联关系的资源
func (r *ResourceRepositoryImpl) FindWithRelations() ([]entity.Resource, error) {
var resources []entity.Resource
err := r.db.Preload("Category").Preload("Pan").Preload("Tags").Find(&resources).Error
return resources, err
}
// FindWithRelationsPaginated 分页查找包含关联关系的资源
func (r *ResourceRepositoryImpl) FindWithRelationsPaginated(page, limit int) ([]entity.Resource, int64, error) {
var resources []entity.Resource
var total int64
offset := (page - 1) * limit
// 优化查询:只预加载必要的关联,并添加排序
db := r.db.Model(&entity.Resource{}).
Preload("Category").
Preload("Pan").
Order("updated_at DESC") // 按更新时间倒序,显示最新内容
// 获取总数(使用缓存键)
cacheKey := fmt.Sprintf("resources_total_%d_%d", page, limit)
if cached, exists := r.cache[cacheKey]; exists {
if totalCached, ok := cached.(int64); ok {
total = totalCached
}
} else {
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
// 缓存总数5分钟
r.cache[cacheKey] = total
go func() {
time.Sleep(5 * time.Minute)
delete(r.cache, cacheKey)
}()
}
// 获取分页数据
err := db.Offset(offset).Limit(limit).Find(&resources).Error
return resources, total, err
}
// FindByCategoryID 根据分类ID查找
func (r *ResourceRepositoryImpl) FindByCategoryID(categoryID uint) ([]entity.Resource, error) {
var resources []entity.Resource
err := r.db.Where("category_id = ?", categoryID).Preload("Category").Preload("Tags").Find(&resources).Error
return resources, err
}
// FindByCategoryIDPaginated 分页根据分类ID查找
func (r *ResourceRepositoryImpl) FindByCategoryIDPaginated(categoryID uint, page, limit int) ([]entity.Resource, int64, error) {
var resources []entity.Resource
var total int64
offset := (page - 1) * limit
db := r.db.Model(&entity.Resource{}).Where("category_id = ?", categoryID).Preload("Category").Preload("Tags").Order("updated_at DESC")
// 获取总数
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
// 获取分页数据
err := db.Offset(offset).Limit(limit).Find(&resources).Error
return resources, total, err
}
// FindByPanID 根据平台ID查找
func (r *ResourceRepositoryImpl) FindByPanID(panID uint) ([]entity.Resource, error) {
var resources []entity.Resource
err := r.db.Where("pan_id = ?", panID).Preload("Category").Preload("Tags").Find(&resources).Error
return resources, err
}
// FindByPanIDPaginated 分页根据平台ID查找
func (r *ResourceRepositoryImpl) FindByPanIDPaginated(panID uint, page, limit int) ([]entity.Resource, int64, error) {
var resources []entity.Resource
var total int64
offset := (page - 1) * limit
db := r.db.Model(&entity.Resource{}).Where("pan_id = ?", panID).Preload("Category").Preload("Tags").Order("updated_at DESC")
// 获取总数
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
// 获取分页数据
err := db.Offset(offset).Limit(limit).Find(&resources).Error
return resources, total, err
}
// FindByIsValid 根据有效性查找
func (r *ResourceRepositoryImpl) FindByIsValid(isValid bool) ([]entity.Resource, error) {
var resources []entity.Resource
err := r.db.Where("is_valid = ?", isValid).Preload("Category").Preload("Tags").Find(&resources).Error
return resources, err
}
// FindByIsPublic 根据公开性查找
func (r *ResourceRepositoryImpl) FindByIsPublic(isPublic bool) ([]entity.Resource, error) {
var resources []entity.Resource
err := r.db.Where("is_public = ?", isPublic).Preload("Category").Preload("Tags").Find(&resources).Error
return resources, err
}
// Search 搜索资源
func (r *ResourceRepositoryImpl) Search(query string, categoryID *uint, page, limit int) ([]entity.Resource, int64, error) {
var resources []entity.Resource
var total int64
offset := (page - 1) * limit
db := r.db.Model(&entity.Resource{}).Preload("Category").Preload("Tags")
// 构建查询条件
if query != "" {
db = db.Where("title ILIKE ? OR description ILIKE ?", "%"+query+"%", "%"+query+"%")
}
if categoryID != nil {
db = db.Where("category_id = ?", *categoryID)
}
// 获取总数
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
// 获取分页数据,按更新时间倒序
err := db.Order("updated_at DESC").Offset(offset).Limit(limit).Find(&resources).Error
return resources, total, err
}
// SearchByPanID 在指定平台内搜索资源
func (r *ResourceRepositoryImpl) SearchByPanID(query string, panID uint, page, limit int) ([]entity.Resource, int64, error) {
var resources []entity.Resource
var total int64
offset := (page - 1) * limit
db := r.db.Model(&entity.Resource{}).Preload("Category").Preload("Tags").Where("pan_id = ?", panID)
// 构建查询条件
if query != "" {
db = db.Where("title ILIKE ? OR description ILIKE ?", "%"+query+"%", "%"+query+"%")
}
// 获取总数
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
// 获取分页数据,按更新时间倒序
err := db.Order("updated_at DESC").Offset(offset).Limit(limit).Find(&resources).Error
return resources, total, err
}
// SearchWithFilters 根据参数进行搜索
func (r *ResourceRepositoryImpl) SearchWithFilters(params map[string]interface{}) ([]entity.Resource, int64, error) {
var resources []entity.Resource
var total int64
db := r.db.Model(&entity.Resource{})
// 处理参数
for key, value := range params {
switch key {
case "query":
if query, ok := value.(string); ok && query != "" {
db = db.Where("title ILIKE ? OR description ILIKE ?", "%"+query+"%", "%"+query+"%")
}
case "category_id":
if categoryID, ok := value.(uint); ok {
db = db.Where("category_id = ?", categoryID)
}
case "is_valid":
if isValid, ok := value.(bool); ok {
db = db.Where("is_valid = ?", isValid)
}
case "is_public":
if isPublic, ok := value.(bool); ok {
db = db.Where("is_public = ?", isPublic)
}
case "pan_id":
if panID, ok := value.(uint); ok {
db = db.Where("pan_id = ?", panID)
}
}
}
// 获取总数
if err := db.Count(&total).Error; err != nil {
return nil, 0, err
}
// 获取分页数据,按更新时间倒序
err := db.Order("updated_at DESC").Find(&resources).Error
return resources, total, err
}
// IncrementViewCount 增加浏览次数
func (r *ResourceRepositoryImpl) IncrementViewCount(id uint) error {
return r.db.Model(&entity.Resource{}).Where("id = ?", id).
UpdateColumn("view_count", gorm.Expr("view_count + ?", 1)).Error
}
// FindWithTags 查找包含标签的资源
func (r *ResourceRepositoryImpl) FindWithTags() ([]entity.Resource, error) {
var resources []entity.Resource
err := r.db.Preload("Tags").Find(&resources).Error
return resources, err
}
// UpdateWithTags 更新资源及其标签
func (r *ResourceRepositoryImpl) UpdateWithTags(resource *entity.Resource, tagIDs []uint) error {
return r.db.Transaction(func(tx *gorm.DB) error {
// 更新资源
if err := tx.Save(resource).Error; err != nil {
return err
}
// 删除旧的标签关联
if err := tx.Where("resource_id = ?", resource.ID).Delete(&entity.ResourceTag{}).Error; err != nil {
return err
}
// 创建新的标签关联
for _, tagID := range tagIDs {
resourceTag := &entity.ResourceTag{
ResourceID: resource.ID,
TagID: tagID,
}
if err := tx.Create(resourceTag).Error; err != nil {
return err
}
}
return nil
})
}
// GetLatestResources 获取最新资源
func (r *ResourceRepositoryImpl) GetLatestResources(limit int) ([]entity.Resource, error) {
var resources []entity.Resource
err := r.db.Order("created_at DESC").Limit(limit).Find(&resources).Error
return resources, err
}
// GetCachedLatestResources 获取缓存的最新资源
func (r *ResourceRepositoryImpl) GetCachedLatestResources(limit int) ([]entity.Resource, error) {
cacheKey := fmt.Sprintf("latest_resources_%d", limit)
// 检查缓存
if cached, exists := r.cache[cacheKey]; exists {
if resources, ok := cached.([]entity.Resource); ok {
return resources, nil
}
}
// 从数据库获取
resources, err := r.GetLatestResources(limit)
if err != nil {
return nil, err
}
// 缓存结果5分钟过期
r.cache[cacheKey] = resources
go func() {
time.Sleep(5 * time.Minute)
delete(r.cache, cacheKey)
}()
return resources, nil
}
// InvalidateCache 清除缓存
func (r *ResourceRepositoryImpl) InvalidateCache() error {
r.cache = make(map[string]interface{})
return nil
}
// FindExists 检查是否存在相同URL的资源
func (r *ResourceRepositoryImpl) FindExists(url string, excludeID ...uint) (bool, error) {
var count int64
query := r.db.Model(&entity.Resource{}).Where("url = ?", url)
// 如果有排除ID则排除该记录用于更新时排除自己
if len(excludeID) > 0 {
query = query.Where("id != ?", excludeID[0])
}
err := query.Count(&count).Error
if err != nil {
return false, err
}
return count > 0, nil
}