feat: SEO backend output (2/4)

This commit is contained in:
Florian Sylvain committed 2026-10-15 21:48:55 +02:00
1 parent 0eb46b0d74
commit eb9876fde3
32 files changed
+3859 -65

No files matched your search

@@ -6,6 +6,7 @@ import (
"RenewCMS/internal/infrastructure/persistence/mappers"
entity "RenewCMS/internal/infrastructure/persistence/models"
"errors"
"sort"
"strings"
"unicode"
@@ -59,6 +60,7 @@ var sortColumns = map[domain.SortField]string{
domain.SortByStatus: "is_online",
domain.SortByCreatedAt: "created_at",
domain.SortByUpdatedAt: "updated_at",
domain.SortByID: "id",
}
var sortDirections = map[domain.SortDirection]string{
@@ -72,6 +74,9 @@ func orderClause(sort domain.Sort) string {
if !ok || !directionOk {
column, direction = sortColumns[domain.SortByUpdatedAt], sortDirections[domain.SortDesc]
}
if sort.Field == domain.SortByID {
return column + " " + direction
}
return column + " " + direction + ", id DESC"
}
@@ -87,7 +92,11 @@ func (a *ArticleRepository) FindPage(filters domain.Filters) (domain.PageResult,
return domain.PageResult{}, err
}
var entityArticles []entity.Article
err := a.scoped(filters).Order(orderClause(filters.Sort)).Limit(filters.Size).Offset((filters.Page - 1) * filters.Size).Find(&entityArticles).Error
ordered := a.scoped(filters).Order(orderClause(filters.Sort))
if !filters.Unpaged {
ordered = ordered.Limit(filters.Size).Offset((filters.Page - 1) * filters.Size)
}
err := ordered.Find(&entityArticles).Error
if err != nil {
return domain.PageResult{}, err
}
@@ -110,16 +119,23 @@ func (a *ArticleRepository) findMatchingTitle(filters domain.Filters) (domain.Pa
matchingIds = append(matchingIds, candidate.ID)
}
}
start := min((filters.Page-1)*filters.Size, len(matchingIds))
end := min(start+filters.Size, len(matchingIds))
pageIds := matchingIds[start:end]
pageIds := matchingIds
if !filters.Unpaged {
start := min((filters.Page-1)*filters.Size, len(matchingIds))
end := min(start+filters.Size, len(matchingIds))
pageIds = matchingIds[start:end]
}
page := domain.PageResult{Items: []domain.Article{}, Total: len(matchingIds)}
if len(pageIds) == 0 {
return page, nil
}
var entityArticles []entity.Article
if err := a.db.Model(&entity.Article{}).Where("id IN ?", pageIds).Find(&entityArticles).Error; err != nil {
return domain.PageResult{}, err
for _, chunk := range chunkIDs(pageIds) {
var loaded []entity.Article
if err := a.db.Model(&entity.Article{}).Where("id IN ?", chunk).Find(&loaded).Error; err != nil {
return domain.PageResult{}, err
}
entityArticles = append(entityArticles, loaded...)
}
byId := make(map[uint32]entity.Article, len(entityArticles))
for _, entityArticle := range entityArticles {
@@ -186,15 +202,35 @@ func (a *ArticleRepository) withAuthorsOne(db *gorm.DB, article domain.Article)
return loaded[0], nil
}
const idChunkSize = 500
func chunkIDs(ids []uint32) [][]uint32 {
chunks := make([][]uint32, 0, len(ids)/idChunkSize+1)
for start := 0; start < len(ids); start += idChunkSize {
chunks = append(chunks, ids[start:min(start+idChunkSize, len(ids))])
}
return chunks
}
func loadBylines(db *gorm.DB, articleIDs []uint32) (map[uint32][]authordomain.Byline, error) {
bylines := make(map[uint32][]authordomain.Byline, len(articleIDs))
if len(articleIDs) == 0 {
return bylines, nil
}
var credits []entity.ArticleAuthor
if err := db.Where("article_id IN ?", articleIDs).Order("position ASC, id ASC").Find(&credits).Error; err != nil {
return nil, err
for _, chunk := range chunkIDs(articleIDs) {
var loaded []entity.ArticleAuthor
if err := db.Where("article_id IN ?", chunk).Order("position ASC, id ASC").Find(&loaded).Error; err != nil {
return nil, err
}
credits = append(credits, loaded...)
}
sort.SliceStable(credits, func(i, j int) bool {
if credits[i].Position != credits[j].Position {
return credits[i].Position < credits[j].Position
}
return credits[i].ID < credits[j].ID
})
if len(credits) == 0 {
return bylines, nil
}
@@ -203,8 +239,12 @@ func loadBylines(db *gorm.DB, articleIDs []uint32) (map[uint32][]authordomain.By
authorIDs = append(authorIDs, credit.AuthorID)
}
var found []entity.Author
if err := db.Where("id IN ?", authorIDs).Find(&found).Error; err != nil {
return nil, err
for _, chunk := range chunkIDs(authorIDs) {
var loaded []entity.Author
if err := db.Where("id IN ?", chunk).Find(&loaded).Error; err != nil {
return nil, err
}
found = append(found, loaded...)
}
byID := make(map[uint32]entity.Author, len(found))
for _, model := range found {