Files
go_blog/models/comment.go
T
kevin f307781f58 docs: 全部 Go 代码注释汉化
- 48 个 Go 文件所有注释(行注释/块注释/行尾注释,含 _test.go)翻译为中文
- 保留技术标识符:SECURITY_TODO(n)、unsafe-inline、sqlite/mysql、路由参数等
- 代码、字符串字面量、日志消息保持英文原文,零逻辑改动
- go build/vet 通过,go test -count=1 ./... 全绿
2026-08-27 19:03:03 +08:00

105 lines
3.2 KiB
Go

package models
import (
"crypto/md5"
"encoding/hex"
"fmt"
"strings"
"time"
"gorm.io/gorm"
)
// 评论状态常量。
const (
CommentPending = 0 // 待审核
CommentApproved = 1 // 已通过
CommentRejected = 2 // 已拒绝(软拒绝;后台仍可查看,但前台不再显示)
)
// Comment 表示读者对文章提交的一条评论。评论可通过 ParentID 进行嵌套,
// 作者可以是已登录用户(UserID),也可以通过长期有效的 GuestToken Cookie
// 标识的匿名访客。
type Comment struct {
ID uint `gorm:"primarykey" json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
ArticleID uint `gorm:"not null;index" json:"article_id"`
ParentID *uint `gorm:"index" json:"parent_id,omitempty"`
// 作者标识:已登录用户使用 UserID;匿名访客使用随机的
// GuestToken(保存于 Cookie 中),以便后续页面加载时能查看
// 自己待审核/私密的评论。
UserID *uint `gorm:"index" json:"user_id,omitempty"`
GuestToken string `gorm:"size:64;index" json:"-"`
AuthorName string `gorm:"not null;size:64" json:"author_name"`
Email string `gorm:"not null;size:255" json:"-"`
EmailHash string `gorm:"size:64;index" json:"email_hash"`
Website string `gorm:"size:255" json:"website,omitempty"`
Content string `gorm:"type:text;not null" json:"content"`
IsPrivate bool `gorm:"default:false;index" json:"is_private"`
Status int `gorm:"default:0;index" json:"status"`
IPAddress string `gorm:"size:64" json:"-"`
UserAgent string `gorm:"size:512" json:"-"`
Article Article `gorm:"foreignKey:ArticleID" json:"-"`
}
// TableName 覆盖 GORM 默认的表名。
func (Comment) TableName() string {
return "comments"
}
// HashEmail 返回小写并去除空格后的邮箱地址的 md5 哈希值。
// 这是 Gravatar 所期望的格式。
func HashEmail(email string) string {
sum := md5.Sum([]byte(strings.ToLower(strings.TrimSpace(email))))
return hex.EncodeToString(sum[:])
}
// GravatarURL 根据此评论的邮箱哈希返回 Gravatar 头像 URL。
// 当没有对应的 Gravatar 头像时,回退到 "identicon" 默认头像。
func (c *Comment) GravatarURL(size int) string {
if size <= 0 {
size = 64
}
hash := c.EmailHash
if hash == "" {
hash = HashEmail(c.Email)
}
return fmt.Sprintf("https://www.gravatar.com/avatar/%s?s=%d&d=identicon", hash, size)
}
// MaskedEmail 返回把本地部分部分遮盖后的邮箱,供后台列表展示:
// 既能提示身份,又不暴露完整地址。
func (c *Comment) MaskedEmail() string {
email := c.Email
at := strings.LastIndex(email, "@")
if at <= 0 {
return email
}
local := email[:at]
host := email[at:]
if len(local) <= 2 {
return string(local[0]) + "***" + host
}
return string(local[0]) + "***" + string(local[len(local)-1]) + host
}
// AuthorInitial 返回 AuthorName 的首个大写字符,用作禁用 Gravatar 时
// 的文本头像占位符。名称为空时返回 "?"。
func (c *Comment) AuthorInitial() string {
name := strings.TrimSpace(c.AuthorName)
if name == "" {
return "?"
}
r := []rune(name)
return strings.ToUpper(string(r[0]))
}