统一标题锚点定位、编辑底栏固定与操作栏分层,列表徽章前置并区分置顶/问答配色。 Co-authored-by: Cursor <cursoragent@cursor.com>
235 lines
10 KiB
Go
235 lines
10 KiB
Go
package model
|
||
|
||
import (
|
||
"time"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// Role 用户角色
|
||
type Role string
|
||
|
||
const (
|
||
RoleUser Role = "user"
|
||
RoleAdmin Role = "admin"
|
||
)
|
||
|
||
// 内容审核状态(帖子 / 评论)
|
||
const (
|
||
ContentStatusPending = "pending" // 审核中(仅作者与管理员可见)
|
||
ContentStatusPublished = "published" // 已公开
|
||
ContentStatusRejected = "rejected" // 未通过(仅作者与管理员可见)
|
||
)
|
||
|
||
// 帖子类型
|
||
const (
|
||
PostTypeNormal = "normal" // 普通讨论
|
||
PostTypeQuestion = "question" // 问答(未解决 / 已解决)
|
||
)
|
||
|
||
// User 用户表
|
||
// Email / Password / LastLogin* 默认不随帖子等嵌套 User 序列化;
|
||
// 个人中心与后台列表请用 UserSelf / UserAdmin。
|
||
type User struct {
|
||
ID uint `gorm:"primaryKey" json:"id"`
|
||
Username string `gorm:"uniqueIndex;size:128;not null" json:"username"`
|
||
Email string `gorm:"index;size:128;default:''" json:"-"`
|
||
Password string `gorm:"size:128;not null" json:"-"`
|
||
Nickname string `gorm:"size:64" json:"nickname"`
|
||
Signature string `gorm:"size:512;default:''" json:"signature"` // 个人签名
|
||
Avatar string `gorm:"size:512" json:"avatar"` // 兼容 CDN / S3 较长绝对 URL
|
||
Role Role `gorm:"size:16;default:user" json:"role"`
|
||
Banned bool `gorm:"default:false" json:"banned"`
|
||
BannedAt *time.Time `json:"banned_at,omitempty"`
|
||
LastLoginAt *time.Time `json:"-"`
|
||
LastLoginIP string `gorm:"size:45;default:''" json:"-"` // 兼容 IPv6
|
||
CreatedAt time.Time `json:"created_at"`
|
||
UpdatedAt time.Time `json:"updated_at"`
|
||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||
}
|
||
|
||
// Board 论坛板块
|
||
type Board struct {
|
||
ID uint `gorm:"primaryKey" json:"id"`
|
||
Name string `gorm:"size:64;not null" json:"name"`
|
||
Description string `gorm:"size:512" json:"description"`
|
||
Icon string `gorm:"size:64;default:''" json:"icon"`
|
||
ColorIndex int `gorm:"default:-1" json:"color_index"` // -1 表示按 id 自动取色
|
||
SortOrder int `gorm:"default:0" json:"sort_order"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
UpdatedAt time.Time `json:"updated_at"`
|
||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||
}
|
||
|
||
// Post 帖子
|
||
type Post struct {
|
||
ID uint `gorm:"primaryKey" json:"id"`
|
||
BoardID uint `gorm:"index;not null" json:"board_id"`
|
||
UserID uint `gorm:"index;not null" json:"user_id"`
|
||
Title string `gorm:"size:256;not null" json:"title"`
|
||
Content string `gorm:"type:text;not null" json:"content"`
|
||
ContentPlain string `gorm:"type:text" json:"-"` // 正文纯文本,供搜索索引
|
||
Tags string `gorm:"size:256" json:"tags"`
|
||
PostType string `gorm:"size:16;default:normal;index" json:"post_type"` // normal|question
|
||
QuestionResolved bool `gorm:"default:false;index" json:"question_resolved"` // 仅 question 有意义
|
||
Pinned bool `gorm:"default:false" json:"pinned"`
|
||
Featured bool `gorm:"default:false;index" json:"featured"` // 精华帖
|
||
EditLocked bool `gorm:"default:false" json:"edit_locked"`
|
||
Status string `gorm:"size:16;default:published;index" json:"status"` // pending|published|rejected
|
||
LikeCount int `gorm:"default:0" json:"like_count"`
|
||
ViewCount int `gorm:"default:0" json:"view_count"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
UpdatedAt time.Time `json:"updated_at"`
|
||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||
|
||
Board Board `gorm:"foreignKey:BoardID" json:"board,omitempty"`
|
||
User User `gorm:"foreignKey:UserID" json:"user,omitempty"`
|
||
Comments []Comment `gorm:"foreignKey:PostID" json:"comments,omitempty"`
|
||
}
|
||
|
||
// PostRevision 帖子编辑历史(每次修改前保存旧版本)
|
||
type PostRevision struct {
|
||
ID uint `gorm:"primaryKey" json:"id"`
|
||
PostID uint `gorm:"index;not null" json:"post_id"`
|
||
EditorID uint `gorm:"index;not null" json:"editor_id"`
|
||
Title string `gorm:"size:256;not null" json:"title"`
|
||
Content string `gorm:"type:text;not null" json:"content"`
|
||
Tags string `gorm:"size:256" json:"tags"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
|
||
Editor User `gorm:"foreignKey:EditorID" json:"editor,omitempty"`
|
||
}
|
||
|
||
// CommentRevision 评论编辑历史(每次修改前保存旧版本,管理员可查看)
|
||
type CommentRevision struct {
|
||
ID uint `gorm:"primaryKey" json:"id"`
|
||
CommentID uint `gorm:"index;not null" json:"comment_id"`
|
||
EditorID uint `gorm:"index;not null" json:"editor_id"`
|
||
Content string `gorm:"type:text;not null" json:"content"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
|
||
Editor User `gorm:"foreignKey:EditorID" json:"editor,omitempty"`
|
||
}
|
||
|
||
// ForumSetting 论坛全局设置(键值对)
|
||
type ForumSetting struct {
|
||
Key string `gorm:"primaryKey;size:64" json:"key"`
|
||
Value string `gorm:"size:2048" json:"value"`
|
||
}
|
||
|
||
// Comment 楼层评论
|
||
type Comment struct {
|
||
ID uint `gorm:"primaryKey" json:"id"`
|
||
PostID uint `gorm:"index;not null" json:"post_id"`
|
||
UserID uint `gorm:"index" json:"user_id"` // 0 表示游客
|
||
Floor int `gorm:"not null" json:"floor"`
|
||
Content string `gorm:"type:text;not null" json:"content"`
|
||
ReplyTo *uint `gorm:"index" json:"reply_to,omitempty"`
|
||
GuestNick string `gorm:"size:64" json:"guest_nick,omitempty"`
|
||
GuestEmail string `gorm:"size:128" json:"guest_email,omitempty"`
|
||
GuestURL string `gorm:"size:256" json:"guest_url,omitempty"`
|
||
IsPrivate bool `gorm:"default:false" json:"is_private"`
|
||
Status string `gorm:"size:16;default:published;index" json:"status"` // pending|published|rejected
|
||
CreatedAt time.Time `json:"created_at"`
|
||
UpdatedAt time.Time `json:"updated_at"`
|
||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||
|
||
User User `gorm:"foreignKey:UserID" json:"user,omitempty"`
|
||
Post Post `gorm:"foreignKey:PostID" json:"post,omitempty"`
|
||
ReplyTarget *Comment `gorm:"-" json:"reply_target,omitempty"`
|
||
// ThreadParentID 嵌套展示用父评论(父评论不可见时回挂到最近可见祖先)
|
||
ThreadParentID *uint `gorm:"-" json:"thread_parent_id,omitempty"`
|
||
ContentHidden bool `gorm:"-" json:"content_hidden"`
|
||
}
|
||
|
||
// PostLike 帖子点赞
|
||
type PostLike struct {
|
||
ID uint `gorm:"primaryKey" json:"id"`
|
||
PostID uint `gorm:"uniqueIndex:idx_post_user;not null" json:"post_id"`
|
||
UserID uint `gorm:"uniqueIndex:idx_post_user;not null" json:"user_id"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
}
|
||
|
||
// PostFavorite 帖子收藏
|
||
type PostFavorite struct {
|
||
ID uint `gorm:"primaryKey" json:"id"`
|
||
PostID uint `gorm:"uniqueIndex:idx_fav_post_user;not null" json:"post_id"`
|
||
UserID uint `gorm:"uniqueIndex:idx_fav_post_user;not null" json:"user_id"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
|
||
Post Post `gorm:"foreignKey:PostID" json:"post,omitempty"`
|
||
}
|
||
|
||
// 私信类型
|
||
const (
|
||
MessageKindUser = "user" // 用户互发
|
||
MessageKindSystem = "system" // 系统通知
|
||
MessageKindReject = "reject" // 帖子被拒/下架
|
||
MessageKindReportResult = "report_result" // 举报处理结果
|
||
)
|
||
|
||
// PrivateMessage 站内私信
|
||
type PrivateMessage struct {
|
||
ID uint `gorm:"primaryKey" json:"id"`
|
||
FromUserID uint `gorm:"index;not null" json:"from_user_id"` // 0 表示系统
|
||
ToUserID uint `gorm:"index;not null" json:"to_user_id"`
|
||
Subject string `gorm:"size:256;not null" json:"subject"`
|
||
Content string `gorm:"type:text;not null" json:"content"`
|
||
Kind string `gorm:"size:32;default:user;index" json:"kind"`
|
||
RelatedPostID *uint `gorm:"index" json:"related_post_id,omitempty"`
|
||
RelatedReportID *uint `gorm:"index" json:"related_report_id,omitempty"`
|
||
IsRead bool `gorm:"default:false;index" json:"is_read"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
|
||
FromUser User `gorm:"foreignKey:FromUserID" json:"from_user,omitempty"`
|
||
ToUser User `gorm:"foreignKey:ToUserID" json:"to_user,omitempty"`
|
||
}
|
||
|
||
// 举报状态
|
||
const (
|
||
ReportStatusPending = "pending"
|
||
ReportStatusResolved = "resolved"
|
||
ReportStatusDismissed = "dismissed"
|
||
)
|
||
|
||
// 举报原因
|
||
const (
|
||
ReportReasonSpam = "spam"
|
||
ReportReasonAbuse = "abuse"
|
||
ReportReasonIllegal = "illegal"
|
||
ReportReasonIrrelevant = "irrelevant"
|
||
ReportReasonOther = "other"
|
||
)
|
||
|
||
// PostReport 帖子举报
|
||
type PostReport struct {
|
||
ID uint `gorm:"primaryKey" json:"id"`
|
||
PostID uint `gorm:"index;not null" json:"post_id"`
|
||
ReporterID uint `gorm:"index;not null" json:"reporter_id"`
|
||
Reason string `gorm:"size:32;not null" json:"reason"`
|
||
Detail string `gorm:"size:1000" json:"detail"`
|
||
Status string `gorm:"size:16;default:pending;index" json:"status"`
|
||
HandlerID *uint `gorm:"index" json:"handler_id,omitempty"`
|
||
HandleNote string `gorm:"size:1000" json:"handle_note"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
HandledAt *time.Time `json:"handled_at,omitempty"`
|
||
|
||
Post Post `gorm:"foreignKey:PostID" json:"post,omitempty"`
|
||
Reporter User `gorm:"foreignKey:ReporterID" json:"reporter,omitempty"`
|
||
Handler *User `gorm:"foreignKey:HandlerID" json:"handler,omitempty"`
|
||
}
|
||
|
||
// Media 上传媒体索引(真实文件在本地 uploads 或 S3;本表供后台列表与统计)
|
||
type Media struct {
|
||
ID uint `gorm:"primaryKey" json:"id"`
|
||
Category string `gorm:"size:32;index;not null" json:"category"` // avatars|posts|site
|
||
Name string `gorm:"size:255;not null" json:"name"`
|
||
URL string `gorm:"size:512;uniqueIndex;not null" json:"url"`
|
||
Size int64 `gorm:"not null;default:0" json:"size"`
|
||
ContentType string `gorm:"size:64;default:''" json:"content_type"`
|
||
StorageType string `gorm:"size:16;index;default:local" json:"storage_type"` // local|s3
|
||
UserID *uint `gorm:"index" json:"user_id,omitempty"`
|
||
CreatedAt time.Time `json:"created_at"`
|
||
UpdatedAt time.Time `json:"updated_at"`
|
||
}
|