feat: 新增创作中心(/studio),支持数据概览、内容管理、草稿箱、写文章

- 新增 StudioController,依赖 studioUseCase 接口,遵循 ISP 接口隔离
- PostRepo 新增 FindByUserIDAndStatus 方法,按用户+状态分页查询
- PostService 新增 ListByUser、GetOverview 及 StudioOverview 统计
- /studio 页面路由(概览/管理/草稿箱/写文章/数据分析)+ /api/studio API
- 复用 Vditor 编辑器,studio-editor.js 对接 /api/studio/posts 端点
- 新增 studio.css(B站风格三栏布局+统计卡片+状态筛选tabs)
- 模板引擎注册 add/subtract 函数,分页模板中直接使用
This commit is contained in:
2026-05-30 19:55:29 +08:00
parent bacfd4945d
commit 7659aee468
18 changed files with 1781 additions and 2 deletions

View File

@ -169,6 +169,43 @@ func (s *PostService) ListAdmin(keyword, status string, page, pageSize int) ([]m
})
}
// ListByUser 查询某用户指定状态的帖子(空 status=全状态)
func (s *PostService) ListByUser(userID uint, status string, page, pageSize int) ([]model.Post, int64, error) {
return s.listPageable(page, pageSize, func(offset, limit int) ([]model.Post, int64, error) {
return s.repo.FindByUserIDAndStatus(userID, status, offset, limit)
})
}
// StudioOverview 创作中心数据概览
type StudioOverview struct {
TotalPosts int64 `json:"total_posts"`
DraftCount int64 `json:"draft_count"`
PendingCount int64 `json:"pending_count"`
ApprovedCount int64 `json:"approved_count"`
RejectedCount int64 `json:"rejected_count"`
}
// GetOverview 获取创作中心数据概览
func (s *PostService) GetOverview(userID uint) (*StudioOverview, error) {
// 获取各状态文章数量(分页传 1 条只取 total
_, totalPosts, err := s.repo.FindByUserIDAndStatus(userID, "", 0, 1)
if err != nil {
return nil, err
}
_, draftCount, _ := s.repo.FindByUserIDAndStatus(userID, model.PostStatusDraft, 0, 1)
_, pendingCount, _ := s.repo.FindByUserIDAndStatus(userID, model.PostStatusPending, 0, 1)
_, approvedCount, _ := s.repo.FindByUserIDAndStatus(userID, model.PostStatusApproved, 0, 1)
_, rejectedCount, _ := s.repo.FindByUserIDAndStatus(userID, model.PostStatusRejected, 0, 1)
return &StudioOverview{
TotalPosts: totalPosts,
DraftCount: draftCount,
PendingCount: pendingCount,
ApprovedCount: approvedCount,
RejectedCount: rejectedCount,
}, nil
}
// Update 编辑帖子(权限在 controller 层检查)
func (s *PostService) Update(postID uint, title, body string) error {
post, err := s.findPost(postID)

View File

@ -31,13 +31,14 @@ type userAdminStore interface {
IncrementTokenVersion(userID uint) error
}
// postStore PostService 所需的最小仓储接口ISP8 个方法)
// postStore PostService 所需的最小仓储接口ISP9 个方法)
type postStore interface {
Create(post *model.Post) error
FindByID(id uint) (*model.Post, error)
FindByIDWithAuthor(id uint) (*model.Post, error)
FindPageable(keyword string, offset, limit int) ([]model.Post, int64, error)
FindAdminPageable(keyword, status string, offset, limit int) ([]model.Post, int64, error)
FindByUserIDAndStatus(userID uint, status string, offset, limit int) ([]model.Post, int64, error)
Update(post *model.Post) error
SoftDelete(id uint) error
Restore(id uint) error