# 内容系统设计文档

> **状态**: 计划中 | **优先级**: MVP

---

## 1. 内容模型

### Post（帖子/文章）

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | uint (PK) | 自增主键 |
| `title` | varchar(200) | 标题，非空 |
| `body` | text | Markdown 原文 |
| `body_html` | text | 渲染后 HTML（写时缓存） |
| `user_id` | uint (FK → users) | 作者 |
| `status` | varchar(20) | `draft` / `pending` / `approved` / `rejected` / `locked` |
| `reject_reason` | varchar(500) | 退回理由（管理员填写） |
| `allow_comment` | bool | 是否允许评论（MVP 预留） |
| `deleted_at` | gorm.DeletedAt | 软删除 |
| `created_at` / `updated_at` | timestamp | |

**链接格式**: `/posts/{id}`（如 `/posts/42`）

**状态说明**:

| 状态 | 含义 | 公开可见 | 可编辑 |
|------|------|---------|--------|
| `draft` | 草稿，未被审核或审核已关闭 | 否（作者本人可见） | 是 |
| `pending` | 待审，已提交等待管理员操作 | 否 | 否 |
| `approved` | 审核通过，公开发布 | 是 | 是 |
| `rejected` | 退回，审核未通过，可修改后重提 | 否 | 是 |
| `locked` | 锁定，禁止编辑只能删除 | 否 | 否 |

**状态流转**:

```
创建 → draft ──(提交/审核开启)──→ pending
                                    ├──(通过)──→ approved ──(撤回)──→ rejected
                                    │                │
                                    ├──(退回)──→ rejected           │
                                    │                │              │
                                    └──(退回+锁定)──→ locked ←──────┘
                                                             ←(任意状态锁定)

rejected ──(修改后重提)──→ pending
locked ──(解锁)──→ 回到锁定前状态

任何状态均可软删除（→ deleted_at）
```

**锁定场景**:
- **审核退回+锁定**: 严重违规/侵权内容，无修改空间，禁止再次提交
- **审核通过后退回+锁定**: 已发布内容被撤回并禁止再次编辑/提交
- **直接锁定**: 任何时候管理员可锁定任意帖子

---

## 2. 分层结构

对齐现有四层架构：

```
internal/
├── model/post.go              # Post + 状态常量
├── repository/post_repo.go    # DB CRUD
├── service/post_service.go    # 业务逻辑 (依赖 postRepository 接口)
├── controller/
│   ├── post_controller.go     # SSR 页面 + API
│   └── interfaces.go          # 新增 postUseCase 接口
├── router/                    # 注册路由
```

**依赖方向**: router → controller → service → repository → model  
**接口定义**: service 层定义 repository 接口，controller 层定义 service 接口（ISP）  
**控制器位置**: 放在 controller 根包，与 auth/settings 并列

---

## 3. 接口定义

### Repository 接口（service/repository.go 定义）

```go
type postRepository interface {
    Create(post *model.Post) error
    FindByID(id uint) (*model.Post, error)
    FindPageable(keyword string, status string, page, pageSize int) ([]model.Post, int64, error)
    Update(post *model.Post) error
    SoftDelete(id uint) error
}
```

### Service 接口（controller/interfaces.go 定义）

```go
type postUseCase interface {
    Create(userID uint, title, body string) (*model.Post, error)
    GetByID(id uint) (*model.Post, error)
    List(keyword string, page, pageSize int) ([]model.Post, int64, error)
    Update(userID uint, postID uint, title, body string) error
    Delete(userID uint, postID uint) error
    SubmitForAudit(postID uint) error        // 草稿 → 待审
    Approve(postID uint) error               // 审核通过
    Reject(postID uint, reason string) error // 退回
    Lock(postID uint) error                  // 锁定（禁止编辑）
    Unlock(postID uint) error                // 解锁
}
```

---

## 4. Service 业务逻辑

### 发帖流程

```
用户提交 → 
  1. 鉴权（中间件 authMdw.Required）
  2. 校验标题/正文非空
  3. body → body_html（Goldmark 渲染）
  4. 检查审核开关：
     - 审核关闭 → status = approved，直接可见
     - 审核开启 → status = pending，创建 AuditSubmission
  5. Save → 返回
```

### 列表

- 仅返回 `status = approved`
- keyword 标题模糊搜索
- 分页默认 page_size=20
- 排序: created_at DESC
- 每条附带作者用户名

### 详情

- 通过 ID 查找
- 公开: 仅 `approved`
- 作者本人 + admin+: 可预览 `draft` / `pending` / `rejected` / `locked`
- 已渲染的 body_html 直接输出（带 XSS 防护）

### 编辑 / 删除

- 权限: 作者本人 或 admin+
- 状态约束: `pending` / `locked` 禁止编辑
- `rejected` 状态编辑后自动重置为 `draft`
- 删除: 软删除，任何状态均可

---

## 5. 路由设计

### SSR 页面（frontend）

```
GET  /posts              列表页
GET  /posts/:id          详情页
GET  /posts/new          编辑器（需登录）
GET  /posts/:id/edit     编辑页（作者/admin，locked 状态禁止）
```

### API（api）

```
POST   /api/posts              发帖
PUT    /api/posts/:id           编辑
DELETE /api/posts/:id           删除
POST   /api/posts/:id/submit   草稿提交审核
GET    /api/posts               列表 JSON
GET    /api/posts/:id           详情 JSON
POST   /api/posts/preview       Markdown 预览
```

### 管理后台（admin）

```
GET   /admin/posts                所有帖子（全状态）
POST  /admin/posts/:id/approve    审核通过
POST  /admin/posts/:id/reject     退回（需填写理由）
POST  /admin/posts/:id/lock       锁定（禁止编辑）
POST  /admin/posts/:id/unlock     解锁
POST  /admin/posts/:id/restore    恢复软删除
```

---

## 6. 模板设计

```
templates/MetaLab-2026/html/posts/
├── index.html            列表（卡片式: 标题+作者+时间+摘要）
├── show.html             详情（Markdown 渲染 → 主题 CSS）
├── new.html              编辑器（EasyMDE + 实时预览）
└── edit.html             复用 new.html，注入现有内容

templates/admin/html/posts/
└── index.html            管理列表（全量状态 + 操作按钮）
```

- **列表摘要**: body 前 200 字符纯文本剥离
- **详情样式**: 代码高亮、响应式图片、表格自适应
- **编辑器**: EasyMDE + marked.js + DOMPurify

---

## 7. Markdown 安全策略

| 层级 | 措施 |
|------|------|
| 服务端渲染 | Goldmark (Go) + html.EscapeString |
| 客户端预览 | marked.js + DOMPurify |
| 外部链接 | `rel="nofollow noopener noreferrer"` |
| 图片 | 限制域名白名单 + CSS max-width: 100% |

---

## 8. 审核集成

复用现有 `AuditSubmission` 表，新增 `type = "post"`:

| 用户角色 | 发帖后状态 | 如何可见 |
|----------|-----------|---------|
| user（审核开启） | `pending` → AuditSubmission | 审核通过 → approved |
| moderator+ | `approved` | 直接可见 |
| user（审核关闭） | `approved` | 直接可见 |

**审核操作对应状态变更**:

| 操作 | 状态变更 | 条件 |
|------|---------|------|
| 提交审核 | `draft` → `pending` | 仅作者本人 |
| 审核通过 | `pending` → `approved` | admin+ |
| 退回 | `pending`/`approved` → `rejected` | admin+，需填写理由 |
| 退回+锁定 | `pending`/`approved` → `locked` | admin+，严重违规/侵权 |

---

## 9. 管理后台扩展

侧边栏新增 "内容管理" 入口（admin+）：

- 全状态列表: 标题 / 作者 / 状态 / 时间 / 退回理由
- 操作: 通过 / 退回 / 锁定 / 解锁 / 恢复
- 状态标签: draft（灰）/ pending（黄）/ approved（绿）/ rejected（橙）/ locked（红）

---

## 10. MVP vs 后续

| 维度 | MVP | 后续 |
|------|-----|------|
| 内容 | 帖子 CRUD | 评论系统 |
| 编辑器 | EasyMDE + 实时预览 | 图片拖拽上传 |
| 列表 | 卡片 + 分页 | 分类 / 标签 / 置顶 |
| 审核 | 复用 Audit 表 | 编辑也走审核 |
| 管理 | 列表 + 基本操作 | 批量操作 / 搜索 |
| 通知 | — | 审核通过 / 新评论通知 |

---

## 11. 预计新增/修改文件

**新增（~12）**:
```
internal/model/post.go
internal/repository/post_repo.go
internal/service/post_service.go
internal/controller/post_controller.go
templates/MetaLab-2026/html/posts/index.html
templates/MetaLab-2026/html/posts/show.html
templates/MetaLab-2026/html/posts/new.html
templates/admin/html/posts/index.html
templates/MetaLab-2026/static/css/posts.css
templates/admin/static/css/posts.css
templates/MetaLab-2026/static/js/editor.js
templates/admin/static/js/posts.js
```

**修改（~6）**:
```
internal/controller/interfaces.go    + postUseCase
internal/service/repository.go       + postRepository (接口)
internal/router/frontend.go          + post SSR 路由
internal/router/api.go               + post API 路由
internal/router/admin.go             + admin post 路由
templates/admin/html/layout/base.html + 侧边栏
cmd/server/main.go                   + AutoMigrate Post
```
