This repository has been archived on 2026-06-21. You can view files and clone it, but cannot push or open issues or pull requests.
Files
MetaLab/internal/repository/post_repo.go
Victor_Jay 03a01a09be feat: 实现内容系统 MVC(Post CRUD + 审核流转)
按 content-system.md 设计文档,实现帖子内容系统的最小可行 MVC。

新增:
- Model: Post 模型,含 5 种状态(draft/pending/approved/rejected/locked)
- Repository: post_repo.go,分页查询(前台 approved 列表 + 后台全状态筛选)
- Service: post_service.go,核心业务逻辑(状态流转、Markdown 渲染 Goldmark)
- Controller: post_controller + admin_post_controller,SSR 页面 6 个 + API 13 个
- Template: 列表/详情/编辑器/404 + 管理员列表,各配 CSS/JS
- 路由: /posts, /posts/:id, /posts/new, /posts/:id/edit, REST API, Admin API
- gomod: + github.com/yuin/goldmark v1.8.2

修改:
- Main: AutoMigrate Post 表
- Router: deps 注册 PostService/Controller,路由注册
- Nav: 新增“帖子”导航链接
- Admin Sidebar: 新增“内容管理”入口

设计:
- 审核开关复用 audit.enabled,开启时帖子为 draft,关闭后直接 approved
- 仅 approved 公开可见,作者/admin 可预览非公开状态帖子
- 严格分层 ISP 接口: Controller → postUseCase, Service → postStore, Repo → *PostRepo
- 状态保护: pending/locked 禁止编辑,rejected 编辑后自动重置为 draft
2026-05-27 18:22:48 +08:00

118 lines
2.9 KiB
Go

package repository
import (
"metazone.cc/metalab/internal/model"
"gorm.io/gorm"
)
// PostRepo 帖子数据访问
type PostRepo struct {
db *gorm.DB
}
// NewPostRepo 构造函数
func NewPostRepo(db *gorm.DB) *PostRepo {
return &PostRepo{db: db}
}
// Create 创建帖子
func (r *PostRepo) Create(post *model.Post) error {
return r.db.Create(post).Error
}
// FindByID 按 ID 查找帖子
func (r *PostRepo) FindByID(id uint) (*model.Post, error) {
var post model.Post
err := r.db.First(&post, id).Error
if err != nil {
return nil, err
}
return &post, nil
}
// FindByIDWithAuthor 按 ID 查找帖子(含作者用户名)
func (r *PostRepo) FindByIDWithAuthor(id uint) (*model.Post, error) {
var post model.Post
err := r.db.Table("posts").
Select("posts.*, users.username as author_name").
Joins("LEFT JOIN users ON users.uid = posts.user_id").
Where("posts.id = ?", id).
First(&post).Error
if err != nil {
return nil, err
}
return &post, nil
}
// FindPageable 分页查询帖子列表(仅 approved 状态,关键词模糊搜索标题)
func (r *PostRepo) FindPageable(keyword string, offset, limit int) ([]model.Post, int64, error) {
query := r.db.Table("posts").
Select("posts.*, users.username as author_name").
Joins("LEFT JOIN users ON users.uid = posts.user_id").
Where("posts.deleted_at IS NULL").
Where("posts.status = ?", model.PostStatusApproved)
if keyword != "" {
like := "%" + keyword + "%"
query = query.Where("posts.title LIKE ?", like)
}
var total int64
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
var posts []model.Post
err := query.Order("posts.created_at DESC").
Offset(offset).Limit(limit).Find(&posts).Error
if err != nil {
return nil, 0, err
}
return posts, total, nil
}
// FindAdminPageable 管理后台分页查询(全状态筛选)
func (r *PostRepo) FindAdminPageable(keyword, status string, offset, limit int) ([]model.Post, int64, error) {
query := r.db.Table("posts").
Select("posts.*, users.username as author_name").
Joins("LEFT JOIN users ON users.uid = posts.user_id").
Where("posts.deleted_at IS NULL")
if keyword != "" {
like := "%" + keyword + "%"
query = query.Where("posts.title LIKE ?", like)
}
if status != "" {
query = query.Where("posts.status = ?", status)
}
var total int64
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
var posts []model.Post
err := query.Order("posts.created_at DESC").
Offset(offset).Limit(limit).Find(&posts).Error
if err != nil {
return nil, 0, err
}
return posts, total, nil
}
// Update 更新帖子
func (r *PostRepo) Update(post *model.Post) error {
return r.db.Save(post).Error
}
// SoftDelete 软删除帖子
func (r *PostRepo) SoftDelete(id uint) error {
return r.db.Delete(&model.Post{}, id).Error
}
// Restore 恢复软删除
func (r *PostRepo) Restore(id uint) error {
return r.db.Unscoped().Model(&model.Post{}).Where("id = ?", id).Update("deleted_at", nil).Error
}