feat(help-center): align article payloads
This commit is contained in:
@@ -2,6 +2,7 @@ package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/gochat/gochat/internal/model"
|
||||
"github.com/gochat/gochat/internal/search"
|
||||
@@ -23,7 +24,15 @@ func (r *ArticleRepo) Create(ctx context.Context, article *model.Article) error
|
||||
|
||||
func (r *ArticleRepo) GetByID(ctx context.Context, id uint) (*model.Article, error) {
|
||||
var article model.Article
|
||||
if err := r.db.WithContext(ctx).Preload("Category").Preload("Folder").Preload("Author").First(&article, id).Error; err != nil {
|
||||
if err := r.articlePreloads(r.db.WithContext(ctx)).First(&article, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &article, nil
|
||||
}
|
||||
|
||||
func (r *ArticleRepo) GetByPortalAndID(ctx context.Context, portalID, id uint) (*model.Article, error) {
|
||||
var article model.Article
|
||||
if err := r.articlePreloads(r.db.WithContext(ctx)).Where("portal_id = ?", portalID).First(&article, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &article, nil
|
||||
@@ -43,7 +52,11 @@ func (r *ArticleRepo) FindByPortalID(ctx context.Context, portalID uint, offset,
|
||||
var count int64
|
||||
db := r.db.WithContext(ctx).Model(&model.Article{}).Where("portal_id = ?", portalID)
|
||||
db.Count(&count)
|
||||
if err := db.Offset(offset).Limit(limit).Order("position ASC").Find(&articles).Error; err != nil {
|
||||
query := r.articlePreloads(db).Offset(offset).Limit(limit).Order("position ASC")
|
||||
if limit <= 0 {
|
||||
query = r.articlePreloads(db).Order("position ASC")
|
||||
}
|
||||
if err := query.Find(&articles).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return articles, count, nil
|
||||
@@ -55,7 +68,7 @@ func (r *ArticleRepo) FindByCategoryID(ctx context.Context, categoryID uint, off
|
||||
var count int64
|
||||
db := r.db.WithContext(ctx).Model(&model.Article{}).Where("category_id = ?", categoryID)
|
||||
db.Count(&count)
|
||||
if err := db.Offset(offset).Limit(limit).Order("position ASC").Find(&articles).Error; err != nil {
|
||||
if err := r.articlePreloads(db).Offset(offset).Limit(limit).Order("position ASC").Find(&articles).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return articles, count, nil
|
||||
@@ -67,7 +80,7 @@ func (r *ArticleRepo) FindByFolderID(ctx context.Context, folderID uint, offset,
|
||||
var count int64
|
||||
db := r.db.WithContext(ctx).Model(&model.Article{}).Where("folder_id = ?", folderID)
|
||||
db.Count(&count)
|
||||
if err := db.Offset(offset).Limit(limit).Order("position ASC").Find(&articles).Error; err != nil {
|
||||
if err := r.articlePreloads(db).Offset(offset).Limit(limit).Order("position ASC").Find(&articles).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return articles, count, nil
|
||||
@@ -79,7 +92,7 @@ func (r *ArticleRepo) FindByStatus(ctx context.Context, portalID uint, status mo
|
||||
var count int64
|
||||
db := r.db.WithContext(ctx).Model(&model.Article{}).Where("portal_id = ? AND status = ?", portalID, status)
|
||||
db.Count(&count)
|
||||
if err := db.Offset(offset).Limit(limit).Order("updated_at DESC").Find(&articles).Error; err != nil {
|
||||
if err := r.articlePreloads(db).Offset(offset).Limit(limit).Order("updated_at DESC").Find(&articles).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return articles, count, nil
|
||||
@@ -114,7 +127,36 @@ func (r *ArticleRepo) Search(ctx context.Context, params ArticleSearchParams) ([
|
||||
var articles []model.Article
|
||||
var count int64
|
||||
|
||||
db := r.db.WithContext(ctx).Model(&model.Article{}).Where("portal_id = ?", params.PortalID)
|
||||
db := r.applyArticleSearchParams(r.db.WithContext(ctx).Model(&model.Article{}).Where("articles.portal_id = ?", params.PortalID), params)
|
||||
|
||||
// Count before pagination
|
||||
db.Count(&count)
|
||||
|
||||
// Sorting
|
||||
switch params.SortBy {
|
||||
case "newest":
|
||||
db = db.Order("articles.updated_at DESC")
|
||||
case "popular":
|
||||
db = db.Order("articles.views DESC")
|
||||
default:
|
||||
if params.CategorySlug != "" || params.Status != "" {
|
||||
db = db.Order("articles.position ASC")
|
||||
} else {
|
||||
db = db.Order("articles.updated_at DESC")
|
||||
}
|
||||
}
|
||||
|
||||
query := r.articlePreloads(db)
|
||||
if params.Limit > 0 {
|
||||
query = query.Offset(params.Offset).Limit(params.Limit)
|
||||
}
|
||||
if err := query.Find(&articles).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return articles, count, nil
|
||||
}
|
||||
|
||||
func (r *ArticleRepo) applyArticleSearchParams(db *gorm.DB, params ArticleSearchParams) *gorm.DB {
|
||||
|
||||
// Filter by category slug — join categories table
|
||||
if params.CategorySlug != "" {
|
||||
@@ -146,33 +188,24 @@ func (r *ArticleRepo) Search(ctx context.Context, params ArticleSearchParams) ([
|
||||
} else {
|
||||
// ILIKE substring match (default)
|
||||
searchPattern := "%" + params.Query + "%"
|
||||
db = db.Where("articles.title ILIKE ? OR articles.description ILIKE ? OR articles.content ILIKE ?",
|
||||
operator := "ILIKE"
|
||||
if strings.EqualFold(db.Dialector.Name(), "sqlite") {
|
||||
operator = "LIKE"
|
||||
}
|
||||
db = db.Where("articles.title "+operator+" ? OR articles.description "+operator+" ? OR articles.content "+operator+" ?",
|
||||
searchPattern, searchPattern, searchPattern)
|
||||
}
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
// Count before pagination
|
||||
db.Count(&count)
|
||||
|
||||
// Sorting
|
||||
switch params.SortBy {
|
||||
case "newest":
|
||||
db = db.Order("articles.updated_at DESC")
|
||||
case "popular":
|
||||
db = db.Order("articles.views DESC")
|
||||
default:
|
||||
// When category is present, sort by position; otherwise by updated_at
|
||||
if params.CategorySlug != "" || params.Status != "" {
|
||||
db = db.Order("articles.position ASC")
|
||||
} else {
|
||||
db = db.Order("articles.updated_at DESC")
|
||||
}
|
||||
func (r *ArticleRepo) CountSearch(ctx context.Context, params ArticleSearchParams) (int64, error) {
|
||||
var count int64
|
||||
db := r.applyArticleSearchParams(r.db.WithContext(ctx).Model(&model.Article{}).Where("articles.portal_id = ?", params.PortalID), params)
|
||||
if err := db.Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if err := db.Offset(params.Offset).Limit(params.Limit).Find(&articles).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return articles, count, nil
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// StatusCounts returns the count of articles by status for a portal.
|
||||
@@ -195,11 +228,11 @@ func (r *ArticleRepo) StatusCounts(ctx context.Context, portalID uint) (map[stri
|
||||
}
|
||||
|
||||
counts := map[string]int64{
|
||||
"all_count": 0,
|
||||
"mine_count": 0,
|
||||
"all_count": 0,
|
||||
"mine_count": 0,
|
||||
"published_count": 0,
|
||||
"draft_count": 0,
|
||||
"archived_count": 0,
|
||||
"draft_count": 0,
|
||||
"archived_count": 0,
|
||||
}
|
||||
var total int64
|
||||
for _, r := range results {
|
||||
@@ -222,6 +255,15 @@ func (r *ArticleRepo) UpdatePositions(ctx context.Context, positions map[uint]in
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *ArticleRepo) UpdatePositionsForPortal(ctx context.Context, portalID uint, positions map[uint]int) error {
|
||||
for id, pos := range positions {
|
||||
if err := r.db.WithContext(ctx).Model(&model.Article{}).Where("id = ? AND portal_id = ?", id, portalID).Update("position", pos).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BulkUpdateStatus updates status for multiple articles.
|
||||
func (r *ArticleRepo) BulkUpdateStatus(ctx context.Context, ids []uint, status string) error {
|
||||
return r.db.WithContext(ctx).Model(&model.Article{}).
|
||||
@@ -229,14 +271,53 @@ func (r *ArticleRepo) BulkUpdateStatus(ctx context.Context, ids []uint, status s
|
||||
Update("status", status).Error
|
||||
}
|
||||
|
||||
func (r *ArticleRepo) BulkUpdateStatusForPortal(ctx context.Context, portalID uint, ids []uint, status string) error {
|
||||
return r.db.WithContext(ctx).Model(&model.Article{}).
|
||||
Where("portal_id = ? AND id IN ?", portalID, ids).
|
||||
Update("status", status).Error
|
||||
}
|
||||
|
||||
func (r *ArticleRepo) BulkUpdateCategoryForPortal(ctx context.Context, portalID uint, ids []uint, categoryID uint) error {
|
||||
return r.db.WithContext(ctx).Model(&model.Article{}).
|
||||
Where("portal_id = ? AND id IN ?", portalID, ids).
|
||||
Update("category_id", categoryID).Error
|
||||
}
|
||||
|
||||
// BulkDelete deletes multiple articles.
|
||||
func (r *ArticleRepo) BulkDelete(ctx context.Context, ids []uint) error {
|
||||
return r.db.WithContext(ctx).Where("id IN ?", ids).Delete(&model.Article{}).Error
|
||||
}
|
||||
|
||||
func (r *ArticleRepo) BulkDeleteForPortal(ctx context.Context, portalID uint, ids []uint) error {
|
||||
return r.db.WithContext(ctx).Where("portal_id = ? AND id IN ?", portalID, ids).Delete(&model.Article{}).Error
|
||||
}
|
||||
|
||||
func (r *ArticleRepo) CountByPortalAndIDs(ctx context.Context, portalID uint, ids []uint) (int64, error) {
|
||||
var count int64
|
||||
if len(ids) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if err := r.db.WithContext(ctx).Model(&model.Article{}).Where("portal_id = ? AND id IN ?", portalID, ids).Count(&count).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return count, nil
|
||||
}
|
||||
|
||||
func (r *ArticleRepo) CategoryExistsForPortal(ctx context.Context, portalID uint, categoryID uint) (bool, error) {
|
||||
var count int64
|
||||
if err := r.db.WithContext(ctx).Model(&model.Category{}).Where("portal_id = ? AND id = ?", portalID, categoryID).Count(&count).Error; err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// IncrementViews increments the view count for an article by 1.
|
||||
func (r *ArticleRepo) IncrementViews(ctx context.Context, id uint) error {
|
||||
return r.db.WithContext(ctx).Model(&model.Article{}).
|
||||
Where("id = ?", id).
|
||||
UpdateColumn("views", gorm.Expr("views + 1")).Error
|
||||
}
|
||||
}
|
||||
|
||||
func (r *ArticleRepo) articlePreloads(db *gorm.DB) *gorm.DB {
|
||||
return db.Preload("Category").Preload("Folder").Preload("Author").Preload("Portal").Preload("AssociatedArticles.Category").Preload("AssociatedArticles.Author").Preload("AssociatedArticles.Portal")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user