PBlog/灵猴社博客系统后端解决方案.txt

323 lines
8.1 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

### 灵猴社博客系统 - 后端详细设计Go + MySQL
版本1.0
日期2025-07-15
---
#### **1. 技术栈**
- **语言**: Go 1.21+
- **Web框架**: Gin
- **ORM**: GORM
- **数据库**: MySQL 8.0+
- **Markdown处理**: goldmark (支持扩展)
- **认证**: JWT
- **缓存**: Redis (可选)
- **部署**: Docker
---
#### **2. 数据库设计**
```sql
-- 用户表
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password_hash CHAR(60) NOT NULL, -- bcrypt加密
role ENUM('user', 'admin') DEFAULT 'user',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- 文章表
CREATE TABLE articles (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(200) NOT NULL,
slug VARCHAR(200) UNIQUE NOT NULL, -- SEO友好URL
markdown_content LONGTEXT NOT NULL,
html_content LONGTEXT NOT NULL, -- 渲染后的HTML
status ENUM('draft', 'published', 'private') DEFAULT 'draft',
published_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- 标签表 (多对多关系)
CREATE TABLE tags (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) UNIQUE NOT NULL
);
-- 文章标签关联表
CREATE TABLE article_tags (
article_id INT NOT NULL,
tag_id INT NOT NULL,
PRIMARY KEY (article_id, tag_id),
FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE,
FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
);
-- 文章版本历史表
CREATE TABLE article_versions (
id INT AUTO_INCREMENT PRIMARY KEY,
article_id INT NOT NULL,
markdown_content LONGTEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE
);
-- 评论表
CREATE TABLE comments (
id INT AUTO_INCREMENT PRIMARY KEY,
article_id INT NOT NULL,
user_id INT NOT NULL,
markdown_content TEXT NOT NULL,
html_content TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
-- 媒体资源表
CREATE TABLE media (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
filename VARCHAR(255) NOT NULL,
url VARCHAR(512) NOT NULL, -- CDN地址
mime_type VARCHAR(50) NOT NULL,
size BIGINT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
```
---
#### **3. 核心模块设计**
##### **3.1 用户管理模块**
```go
// models/user.go
type User struct {
gorm.Model
Username string `gorm:"uniqueIndex"`
Email string `gorm:"uniqueIndex"`
PasswordHash string
Role string
}
// handlers/auth.go
func Register(c *gin.Context) {
// 验证邮箱/密码格式
// 密码bcrypt加密
// 生成JWT token
}
func Login(c *gin.Context) {
// 校验密码
// 生成JWT (包含用户ID和角色)
}
// middleware/auth.go
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
// 验证JWT并注入用户信息到Context
}
}
```
##### **3.2 Markdown处理模块**
```go
// services/markdown.go
func RenderMarkdown(content string) string {
md := goldmark.New(
goldmark.WithExtensions(extension.GFM), // GitHub Flavored Markdown
goldmark.WithExtensions(extension.Footnote),
goldmark.WithParserOptions(parser.WithAutoHeadingID()),
)
var buf bytes.Buffer
md.Convert([]byte(content), &buf)
return sanitizeHTML(buf.String()) // XSS过滤
}
// 支持扩展渲染
func RenderWithExtensions(content string) string {
// 保留LaTeX公式: $$...$$
// 保留Mermaid代码块: ```mermaid
// 前端通过额外JS库渲染
}
```
##### **3.3 文章管理模块**
```go
// models/article.go
type Article struct {
gorm.Model
UserID uint
Title string
Slug string `gorm:"uniqueIndex"`
MarkdownContent string
HTMLContent string
Status string
PublishedAt *time.Time
Tags []Tag `gorm:"many2many:article_tags;"`
}
// handlers/article.go
func CreateArticle(c *gin.Context) {
// 自动生成slug (如: "hello-world")
// 渲染Markdown为HTML
// 处理标签(新建或关联)
// 创建初始版本记录
}
func UpdateArticle(c *gin.Context) {
// 保存新版本到article_versions
// 定时发布逻辑:
if article.PublishedAt.After(time.Now()) {
go schedulePublish(article.ID, article.PublishedAt)
}
}
// 定时发布任务
func schedulePublish(articleID uint, publishTime time.Time) {
duration := time.Until(publishTime)
time.AfterFunc(duration, func() {
db.Model(&Article{}).Where("id=?", articleID).Update("status", "published")
})
}
```
##### **3.4 媒体上传模块**
```go
// handlers/media.go
func UploadMedia(c *gin.Context) {
file, _ := c.FormFile("file")
// 校验文件类型(图片/视频)
// 生成唯一文件名: UUID+时间戳
filename := fmt.Sprintf("%d_%s", time.Now().Unix(), file.Filename)
// 上传到CDN (伪代码)
url := cdn.Upload(file, filename)
// 保存记录到media表
media := Media{UserID: userID, Filename: filename, URL: url}
db.Create(&media)
c.JSON(200, gin.H{"url": url})
}
```
##### **3.5 评论模块**
```go
// models/comment.go
type Comment struct {
gorm.Model
ArticleID uint
UserID uint
MarkdownContent string
HTMLContent string
}
// handlers/comment.go
func AddComment(c *gin.Context) {
// Markdown内容渲染
// 敏感词过滤:
content := filter.SensitiveWords(c.PostForm("content"))
}
```
---
#### **4. API 接口设计**
##### **4.1 认证接口**
| 端点 | 方法 | 功能 |
|------|------|------|
| `/api/auth/register` | POST | 用户注册 |
| `/api/auth/login` | POST | 用户登录 |
| `/api/auth/refresh` | POST | 刷新Token |
##### **4.2 文章接口**
| 端点 | 方法 | 功能 |
|------|------|------|
| `/api/articles` | POST | 创建文章 |
| `/api/articles/:id` | PUT | 更新文章 |
| `/api/articles?status=published` | GET | 获取文章列表 |
| `/api/articles/:slug` | GET | 获取文章详情 |
| `/api/articles/:id/versions` | GET | 获取版本历史 |
##### **4.3 媒体接口**
| 端点 | 方法 | 功能 |
|------|------|------|
| `/api/media/upload` | POST | 上传文件 |
##### **4.4 管理接口**
| 端点 | 方法 | 权限 | 功能 |
|------|------|------|------|
| `/api/admin/users` | GET | admin | 用户列表 |
| `/api/admin/articles` | DELETE | admin | 批量删除文章 |
| `/api/comments/:id` | DELETE | admin/owner | 删除评论 |
---
#### **5. 安全设计**
1. **数据加密**
- 密码使用bcrypt哈希存储
- 敏感数据(邮箱)在数据库加密存储
2. **JWT 验证**
```go
// 生成Token
token := jwt.NewWithClaims(jwt.SigningMethodHS256, Claims{
UserID: user.ID,
Role: user.Role,
Exp: time.Now().Add(24*time.Hour).Unix(),
})
```
3. **XSS防护**
- Markdown渲染后使用`bluemonday`进行HTML净化
```go
import "github.com/microcosm-cc/bluemonday"
func sanitizeHTML(html string) string {
p := bluemonday.UGCPolicy()
return p.Sanitize(html)
}
```
4. **SQL注入防护**
- 使用GORM参数化查询
```go
db.Where("email = ?", email).First(&user)
```
---
#### **6. 性能优化**
1. **缓存策略**
- 热点文章HTML内容缓存到Redis
```go
func GetArticle(slug string) (Article, error) {
if html, err := redis.Get("article:"+slug); err == nil {
return Article{HTMLContent: html}, nil
}
// 数据库查询...
}
```
2. **异步处理**
- Markdown渲染使用goroutine池
```go
var renderPool = make(chan struct{}, 10) // 限流10并发
func AsyncRender(content string) string {
renderPool <- struct{}{}
defer func() { <-renderPool }()
return RenderMarkdown(content)
}
```
3. **CDN加速**
- 静态资源(图片/JS/CSS)通过CDN分发
> **文档说明**此设计满足需求文档所有功能点支持后续扩展API插件系统。