feat: 增加友链申请、独立页面、投票/悬赏/抽奖帖与侧栏签到,并统一开发数据目录
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
176
service/site_page.go
Normal file
176
service/site_page.go
Normal file
@@ -0,0 +1,176 @@
|
||||
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(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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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(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
|
||||
}
|
||||
Reference in New Issue
Block a user