将 model/service/handler/middleware 迁至 models/services/routers/api/modules/auth,并删除 frontend、embed_static、scripts 及误入库缓存/二进制。 Co-authored-by: Cursor <cursoragent@cursor.com>
188 lines
5.0 KiB
Go
188 lines
5.0 KiB
Go
package service
|
||
|
||
import (
|
||
"errors"
|
||
"strings"
|
||
|
||
"git.iioio.com/freefire/jiang13-forum/model"
|
||
)
|
||
|
||
var (
|
||
ErrSitePageNotFound = errors.New("单页不存在")
|
||
ErrSitePageSlugUsed = errors.New("slug 已被占用")
|
||
)
|
||
|
||
// SitePageService 自定义单页
|
||
type SitePageService struct {
|
||
filter *SensitiveFilter
|
||
}
|
||
|
||
func NewSitePageService(filter *SensitiveFilter) *SitePageService {
|
||
return &SitePageService{filter: filter}
|
||
}
|
||
|
||
// SitePageSummary 公开列表摘要
|
||
type SitePageSummary struct {
|
||
ID uint `json:"id"`
|
||
Title string `json:"title"`
|
||
Slug string `json:"slug"`
|
||
ShowInFooter bool `json:"show_in_footer"`
|
||
ShowInNav bool `json:"show_in_nav"`
|
||
SortOrder int `json:"sort_order"`
|
||
}
|
||
|
||
func (s *SitePageService) ListPublished() ([]SitePageSummary, error) {
|
||
var rows []model.SitePage
|
||
err := model.DB.Where("published = ?", true).
|
||
Order("sort_order ASC, id ASC").
|
||
Find(&rows).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
out := make([]SitePageSummary, len(rows))
|
||
for i, p := range rows {
|
||
out[i] = SitePageSummary{
|
||
ID: p.ID, Title: p.Title, Slug: p.Slug,
|
||
ShowInFooter: p.ShowInFooter, ShowInNav: p.ShowInNav, SortOrder: p.SortOrder,
|
||
}
|
||
}
|
||
return out, nil
|
||
}
|
||
|
||
func (s *SitePageService) ListAll() ([]model.SitePage, error) {
|
||
var rows []model.SitePage
|
||
err := model.DB.Order("sort_order ASC, id ASC").Find(&rows).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return rows, nil
|
||
}
|
||
|
||
func (s *SitePageService) GetBySlug(slug string, allowUnpublished bool) (*model.SitePage, error) {
|
||
slug, ok := NormalizePageSlug(slug)
|
||
if !ok {
|
||
return nil, ErrSitePageNotFound
|
||
}
|
||
var page model.SitePage
|
||
q := model.DB.Where("slug = ?", slug)
|
||
if !allowUnpublished {
|
||
q = q.Where("published = ?", true)
|
||
}
|
||
if err := q.First(&page).Error; err != nil {
|
||
return nil, ErrSitePageNotFound
|
||
}
|
||
page.Content = SanitizePostHTML(UnwrapContentGateTags(page.Content))
|
||
return &page, nil
|
||
}
|
||
|
||
func (s *SitePageService) GetByID(id uint) (*model.SitePage, error) {
|
||
var page model.SitePage
|
||
if err := model.DB.First(&page, id).Error; err != nil {
|
||
return nil, ErrSitePageNotFound
|
||
}
|
||
page.Content = SanitizePostHTML(UnwrapContentGateTags(page.Content))
|
||
return &page, nil
|
||
}
|
||
|
||
type SitePageInput struct {
|
||
Title string `json:"title"`
|
||
Slug string `json:"slug"`
|
||
Content string `json:"content"`
|
||
Published bool `json:"published"`
|
||
SortOrder int `json:"sort_order"`
|
||
ShowInFooter bool `json:"show_in_footer"`
|
||
ShowInNav bool `json:"show_in_nav"`
|
||
}
|
||
|
||
func (s *SitePageService) Create(in SitePageInput) (*model.SitePage, error) {
|
||
page, err := s.normalizeInput(in)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
var exists int64
|
||
model.DB.Model(&model.SitePage{}).Where("slug = ?", page.Slug).Count(&exists)
|
||
if exists > 0 {
|
||
return nil, ErrSitePageSlugUsed
|
||
}
|
||
if err := model.DB.Create(page).Error; err != nil {
|
||
return nil, err
|
||
}
|
||
return page, nil
|
||
}
|
||
|
||
func (s *SitePageService) Update(id uint, in SitePageInput) error {
|
||
page, err := s.GetByID(id)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
next, err := s.normalizeInput(in)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
var exists int64
|
||
model.DB.Model(&model.SitePage{}).Where("slug = ? AND id <> ?", next.Slug, id).Count(&exists)
|
||
if exists > 0 {
|
||
return ErrSitePageSlugUsed
|
||
}
|
||
return model.DB.Model(page).Updates(map[string]interface{}{
|
||
"title": next.Title,
|
||
"slug": next.Slug,
|
||
"content": next.Content,
|
||
"published": next.Published,
|
||
"sort_order": next.SortOrder,
|
||
"show_in_footer": next.ShowInFooter,
|
||
"show_in_nav": next.ShowInNav,
|
||
}).Error
|
||
}
|
||
|
||
func (s *SitePageService) Delete(id uint) error {
|
||
res := model.DB.Delete(&model.SitePage{}, id)
|
||
if res.Error != nil {
|
||
return res.Error
|
||
}
|
||
if res.RowsAffected == 0 {
|
||
return ErrSitePageNotFound
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// SetPublished 仅切换发布状态(列表快捷操作)
|
||
func (s *SitePageService) SetPublished(id uint, published bool) error {
|
||
page, err := s.GetByID(id)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return model.DB.Model(page).Update("published", published).Error
|
||
}
|
||
|
||
func (s *SitePageService) ListSitemap(limit int) ([]model.SitePage, error) {
|
||
if limit <= 0 {
|
||
limit = 500
|
||
}
|
||
var rows []model.SitePage
|
||
err := model.DB.Where("published = ?", true).
|
||
Order("updated_at DESC").Limit(limit).Find(&rows).Error
|
||
return rows, err
|
||
}
|
||
|
||
func (s *SitePageService) normalizeInput(in SitePageInput) (*model.SitePage, error) {
|
||
title := s.filter.Filter(strings.TrimSpace(in.Title))
|
||
slug, ok := NormalizePageSlug(in.Slug)
|
||
if !ok {
|
||
return nil, errors.New("slug 格式无效(2-64 位小写字母、数字、连字符)")
|
||
}
|
||
// 单页不支持登录/回复/积分可见:保存前剥离外壳,保留内部正文
|
||
content := s.filter.Filter(SanitizePostHTML(UnwrapContentGateTags(in.Content)))
|
||
if title == "" {
|
||
return nil, errors.New("标题不能为空")
|
||
}
|
||
if content == "" {
|
||
return nil, errors.New("正文不能为空")
|
||
}
|
||
return &model.SitePage{
|
||
Title: title, Slug: slug, Content: content,
|
||
Published: in.Published, SortOrder: in.SortOrder,
|
||
ShowInFooter: in.ShowInFooter, ShowInNav: in.ShowInNav,
|
||
}, nil
|
||
}
|