3 回复
Discourse 是一个很好的选择,如果你想要一个简单易用的论坛。
更多关于如何在网站中用Golang搭建社区论坛的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
使用Golang搭建社区论坛的实现方案
以下是一个完整的社区论坛实现方案,包含核心功能和代码示例:
1. 项目结构设计
forum/
├── cmd/
│ └── main.go
├── internal/
│ ├── handlers/
│ ├── models/
│ ├── database/
│ └── middleware/
├── templates/
├── static/
└── go.mod
2. 数据库模型定义
// internal/models/forum.go
package models
import (
"time"
"gorm.io/gorm"
)
type User struct {
ID uint `gorm:"primaryKey"`
Username string `gorm:"uniqueIndex;not null"`
Email string `gorm:"uniqueIndex;not null"`
Password string `gorm:"not null"`
CreatedAt time.Time
Posts []Post
Comments []Comment
}
type Category struct {
ID uint `gorm:"primaryKey"`
Name string `gorm:"uniqueIndex;not null"`
Slug string `gorm:"uniqueIndex;not null"`
Posts []Post
}
type Post struct {
ID uint `gorm:"primaryKey"`
Title string `gorm:"not null"`
Content string `gorm:"type:text;not null"`
UserID uint `gorm:"not null"`
CategoryID uint `gorm:"not null"`
Views int `gorm:"default:0"`
CreatedAt time.Time
UpdatedAt time.Time
User User
Category Category
Comments []Comment
}
type Comment struct {
ID uint `gorm:"primaryKey"`
Content string `gorm:"type:text;not null"`
UserID uint `gorm:"not null"`
PostID uint `gorm:"not null"`
CreatedAt time.Time
User User
Post Post
}
3. 核心路由和处理器
// internal/handlers/forum.go
package handlers
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"your-project/internal/models"
)
type ForumHandler struct {
DB *gorm.DB
}
func (h *ForumHandler) CreatePost(c *gin.Context) {
var post models.Post
if err := c.ShouldBind(&post); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
userID, _ := c.Get("userID")
post.UserID = userID.(uint)
if err := h.DB.Create(&post).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, post)
}
func (h *ForumHandler) GetPosts(c *gin.Context) {
var posts []models.Post
categoryID := c.Query("category_id")
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "20"))
offset := (page - 1) * limit
query := h.DB.Preload("User").Preload("Category")
if categoryID != "" {
query = query.Where("category_id = ?", categoryID)
}
query = query.Order("created_at DESC").Offset(offset).Limit(limit)
if err := query.Find(&posts).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusOK, posts)
}
func (h *ForumHandler) AddComment(c *gin.Context) {
postID, _ := strconv.Atoi(c.Param("id"))
var comment models.Comment
if err := c.ShouldBind(&comment); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
userID, _ := c.Get("userID")
comment.UserID = userID.(uint)
comment.PostID = uint(postID)
if err := h.DB.Create(&comment).Error; err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, comment)
}
4. 主程序入口
// cmd/main.go
package main
import (
"log"
"github.com/gin-gonic/gin"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"your-project/internal/handlers"
"your-project/internal/middleware"
)
func main() {
// 数据库连接
dsn := "host=localhost user=forum password=forum dbname=forum port=5432 sslmode=disable"
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
log.Fatal("Failed to connect to database:", err)
}
// 自动迁移
db.AutoMigrate(&models.User{}, &models.Category{}, &models.Post{}, &models.Comment{})
// 初始化处理器
forumHandler := &handlers.ForumHandler{DB: db}
// 设置路由
r := gin.Default()
// 静态文件
r.Static("/static", "./static")
// API路由
api := r.Group("/api")
{
api.POST("/register", handlers.Register)
api.POST("/login", handlers.Login)
auth := api.Group("/")
auth.Use(middleware.AuthMiddleware())
{
auth.POST("/posts", forumHandler.CreatePost)
auth.POST("/posts/:id/comments", forumHandler.AddComment)
}
api.GET("/posts", forumHandler.GetPosts)
api.GET("/posts/:id", forumHandler.GetPost)
api.GET("/categories", forumHandler.GetCategories)
}
// 启动服务器
r.Run(":8080")
}
5. 认证中间件
// internal/middleware/auth.go
package middleware
import (
"net/http"
"strings"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v4"
)
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authorization header required"})
c.Abort()
return
}
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte("your-secret-key"), nil
})
if err != nil || !token.Valid {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid token"})
c.Abort()
return
}
claims := token.Claims.(jwt.MapClaims)
c.Set("userID", uint(claims["user_id"].(float64)))
c.Next()
}
}
6. 数据库初始化脚本
-- 创建基础表结构
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS categories (
id SERIAL PRIMARY KEY,
name VARCHAR(50) UNIQUE NOT NULL,
slug VARCHAR(50) UNIQUE NOT NULL
);
-- 插入默认分类
INSERT INTO categories (name, slug) VALUES
('General', 'general'),
('Technical', 'technical'),
('Feedback', 'feedback')
ON CONFLICT DO NOTHING;
7. 环境配置
// config/config.go
package config
import (
"os"
)
type Config struct {
DatabaseURL string
JWTSecret string
Port string
}
func Load() *Config {
return &Config{
DatabaseURL: getEnv("DATABASE_URL", "postgres://forum:forum@localhost:5432/forum"),
JWTSecret: getEnv("JWT_SECRET", "your-secret-key"),
Port: getEnv("PORT", "8080"),
}
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
这个实现方案包含了论坛的核心功能:用户认证、帖子发布、分类管理、评论系统。使用Gin作为Web框架,GORM进行数据库操作,PostgreSQL作为数据库。可以根据silicophilic.com的具体需求进行扩展和定制。

