feat: 文章属性前端完成 — Phase 3

- 写文章页:可见性/类型/转载来源/创作声明/禁止转载 属性表单
- 类型切换联动(转载显示来源,原创显示禁止转载复选框)
- 文章详情页:私密角标、置顶标记、创作声明横幅、转载来源
- 管理后台:置顶/取消置顶操作按钮
- PostService Create/Update 签名扩展接受文章属性
- DTO/接口同步更新
This commit is contained in:
2026-06-22 00:20:52 +08:00
parent 9c452afb40
commit 977c52513a
13 changed files with 225 additions and 18 deletions

View File

@ -42,9 +42,9 @@ type postUseCase interface {
// studioUseCase StudioController 对 PostService 的最小依赖ISP9 个方法)
type studioUseCase interface {
Create(userID uint, title, body string) (*model.Post, error)
Create(userID uint, title, body, visibility, postType, reprintSource, declaration string, reprintProhibited bool) (*model.Post, error)
GetByID(id uint) (*model.Post, error)
Update(postID uint, title, body string) error
Update(postID uint, title, body, visibility, postType, reprintSource, declaration string, reprintProhibited bool) error
Delete(postID uint) error
SubmitForAudit(postID uint) error
ListByUser(userID uint, status string, page, pageSize int) ([]model.Post, int64, error)

View File

@ -30,7 +30,7 @@ func (ctrl *StudioController) Create(c *gin.Context) {
return
}
post, err := ctrl.postService.Create(uid, req.Title, req.Body)
post, err := ctrl.postService.Create(uid, req.Title, req.Body, req.Visibility, req.PostType, req.ReprintSource, req.Declaration, req.ReprintProhibited)
if err != nil {
common.Error(c, http.StatusInternalServerError, "发布失败")
return
@ -69,7 +69,7 @@ func (ctrl *StudioController) Update(c *gin.Context) {
return
}
if err := ctrl.postService.Update(uint(id), req.Title, req.Body); err != nil {
if err := ctrl.postService.Update(uint(id), req.Title, req.Body, req.Visibility, req.PostType, req.ReprintSource, req.Declaration, req.ReprintProhibited); err != nil {
common.Error(c, http.StatusInternalServerError, "编辑失败")
return
}

View File

@ -32,14 +32,24 @@ type PostListResult struct {
// PostCreateRequest 发帖请求
type PostCreateRequest struct {
Title string `json:"title" binding:"required,min=1,max=200"`
Body string `json:"body" binding:"required,min=1"`
Title string `json:"title" binding:"required,min=1,max=200"`
Body string `json:"body" binding:"required,min=1"`
Visibility string `json:"visibility"`
PostType string `json:"post_type"`
ReprintSource string `json:"reprint_source"`
Declaration string `json:"declaration"`
ReprintProhibited bool `json:"reprint_prohibited"`
}
// PostUpdateRequest 编辑请求
type PostUpdateRequest struct {
Title string `json:"title" binding:"required,min=1,max=200"`
Body string `json:"body" binding:"required,min=1"`
Title string `json:"title" binding:"required,min=1,max=200"`
Body string `json:"body" binding:"required,min=1"`
Visibility string `json:"visibility"`
PostType string `json:"post_type"`
ReprintSource string `json:"reprint_source"`
Declaration string `json:"declaration"`
ReprintProhibited bool `json:"reprint_prohibited"`
}
// PostRejectRequest 退回请求

View File

@ -6,7 +6,7 @@ import (
)
// Update 编辑帖子(权限在 controller 层检查)
func (s *PostService) Update(postID uint, title, body string) error {
func (s *PostService) Update(postID uint, title, body string, visibility, postType, reprintSource, declaration string, reprintProhibited bool) error {
post, err := s.findPost(postID)
if err != nil {
return err
@ -16,6 +16,13 @@ func (s *PostService) Update(postID uint, title, body string) error {
return common.ErrPostCannotEdit
}
// 文章属性随时可更新(不影响已发布内容展示)
post.Visibility = visibility
post.PostType = postType
post.ReprintSource = reprintSource
post.Declaration = declaration
post.ReprintProhibited = reprintProhibited
// 已通过帖子:编辑内容保存为待审修订,不影响当前展示
if post.Status == model.PostStatusApproved {
post.PendingTitle = title

View File

@ -68,19 +68,24 @@ func (s *PostService) findPostWithAuthor(id uint) (*model.Post, error) {
// Create 创建帖子
// body 为 Vditor 输出的 Markdown后端直接存储不做 HTML 消毒
// MD 是纯文本,无 XSS 注入风险;前端渲染时由 Vditor 转 HTML
func (s *PostService) Create(userID uint, title, body string) (*model.Post, error) {
func (s *PostService) Create(userID uint, title, body string, visibility, postType, reprintSource, declaration string, reprintProhibited bool) (*model.Post, error) {
status := model.PostStatusApproved
if s.auditEnabled() {
status = model.PostStatusDraft
}
post := &model.Post{
Title: title,
Body: body,
Excerpt: generateExcerpt(body),
UserID: userID,
Status: status,
AllowComment: true,
Title: title,
Body: body,
Excerpt: generateExcerpt(body),
UserID: userID,
Status: status,
AllowComment: true,
Visibility: visibility,
PostType: postType,
ReprintSource: reprintSource,
Declaration: declaration,
ReprintProhibited: reprintProhibited,
}
if err := s.repo.Create(post); err != nil {
return nil, err