spark-server/internal/module/square/model/post.go
DESKTOP-L8OUL9F\gfl0034 53426af122 feat: 多广场 channel、音乐公开 API 与订阅 package_name
支持 square/squarenovel/squaresteps/squarehealth 实例与 category 筛选;音乐增加免登录 grouped/track;订阅套餐按 package_name 区分;补 migration 0030/0031 与 OpenAPI。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-30 09:02:28 +08:00

218 lines
7.8 KiB
Go

package model
import (
"time"
"spark-server/internal/module/square/entity"
)
// Post maps table square_posts.
type Post struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement:false;comment:Snowflake 帖 id"`
UserID int64 `gorm:"column:user_id;comment:作者 users.id"`
Channel string `gorm:"column:channel;comment:广场实例"`
Category string `gorm:"column:category;comment:内容标签"`
Kind int16 `gorm:"column:kind;comment:1纯文 2图 3视频"`
Text string `gorm:"column:text;comment:正文"`
Content []byte `gorm:"column:content;type:varbinary(16384);comment:序列化 JSON 字节"`
POILat float64 `gorm:"column:poi_lat;comment:地点纬度"`
POILng float64 `gorm:"column:poi_lng;comment:地点经度"`
POIName string `gorm:"column:poi_name;comment:地点名称"`
POIAddress string `gorm:"column:poi_address;comment:地点地址"`
Visibility int16 `gorm:"column:visibility;comment:0公开 1私密 2白名单 3黑名单"`
AuditStatus int16 `gorm:"column:audit_status;comment:0待审 1通过 2拒绝"`
AuditNote string `gorm:"column:audit_note;comment:拒绝原因"`
AuditedAt *time.Time `gorm:"column:audited_at;comment:审核时间"`
Status int16 `gorm:"column:status;comment:0正常 1已删"`
Source int16 `gorm:"column:source;comment:0用户 1机器人海外媒体RSS"`
SourceURL string `gorm:"column:source_url;comment:原文 URL"`
ExpiresAt *time.Time `gorm:"column:expires_at;comment:机器人帖过期时间"`
LikeCount int `gorm:"column:like_count;comment:点赞数"`
CommentCount int `gorm:"column:comment_count;comment:评论数"`
CreatedAt time.Time `gorm:"column:created_at;comment:发帖时间"`
}
func (Post) TableName() string { return "t_square_posts" }
func (r *Post) Entity() *entity.Post {
if r == nil {
return nil
}
ch := entity.Channel(r.Channel)
if ch == "" {
ch = entity.ChannelSquare
}
p := &entity.Post{
ID: r.ID, UserID: r.UserID, Channel: ch, Category: entity.Category(r.Category),
Kind: entity.Kind(r.Kind), Text: r.Text, Content: r.Content,
POI: entity.POI{Lat: r.POILat, Lng: r.POILng, Name: r.POIName, Address: r.POIAddress},
Visibility: entity.Visibility(r.Visibility), AuditStatus: entity.AuditStatus(r.AuditStatus),
Status: entity.Status(r.Status), Source: entity.Source(r.Source), SourceURL: r.SourceURL,
LikeCount: r.LikeCount, CommentCount: r.CommentCount, CreatedAtMS: r.CreatedAt.UnixMilli(),
}
if r.ExpiresAt != nil {
p.ExpiresAtMS = r.ExpiresAt.UnixMilli()
}
return p
}
func postFrom(p *entity.Post) *Post {
if p == nil {
return nil
}
ch := string(p.Channel)
if ch == "" {
ch = string(entity.ChannelSquare)
}
row := &Post{
ID: p.ID, UserID: p.UserID, Channel: ch, Category: string(p.Category),
Kind: int16(p.Kind), Text: p.Text, Content: p.Content,
POILat: p.POI.Lat, POILng: p.POI.Lng, POIName: p.POI.Name, POIAddress: p.POI.Address,
Visibility: int16(p.Visibility), AuditStatus: int16(p.AuditStatus), Status: int16(p.Status),
Source: int16(p.Source), SourceURL: p.SourceURL, LikeCount: p.LikeCount, CommentCount: p.CommentCount,
}
if p.CreatedAtMS > 0 {
row.CreatedAt = time.UnixMilli(p.CreatedAtMS)
}
if p.ExpiresAtMS > 0 {
t := time.UnixMilli(p.ExpiresAtMS)
row.ExpiresAt = &t
}
if p.AuditStatus == entity.AuditApproved {
t := row.CreatedAt
if t.IsZero() {
t = time.Now()
}
row.AuditedAt = &t
}
return row
}
// BotLink maps t_square_bot_links (RSS URL 去重).
type BotLink struct {
URLHash string `gorm:"column:url_hash;primaryKey;comment:URL SHA-256 hex"`
URL string `gorm:"column:url;comment:原文 URL"`
PostID int64 `gorm:"column:post_id;comment:帖 id"`
CreatedAt time.Time `gorm:"column:created_at;comment:入库时间"`
}
func (BotLink) TableName() string { return "t_square_bot_links" }
// Audience maps square_audience.
type Audience struct {
PostID int64 `gorm:"column:post_id;primaryKey;comment:帖 id"`
PeerID int64 `gorm:"column:peer_id;primaryKey;comment:好友 uid"`
}
func (Audience) TableName() string { return "t_square_audience" }
// Like maps square_likes.
type Like struct {
PostID int64 `gorm:"column:post_id;primaryKey;comment:帖 id"`
UserID int64 `gorm:"column:user_id;primaryKey;comment:点赞人"`
CreatedAt time.Time `gorm:"column:created_at;comment:点赞时间"`
}
func (Like) TableName() string { return "t_square_likes" }
func (r *Like) Entity() *entity.Like {
if r == nil {
return nil
}
return &entity.Like{PostID: r.PostID, UserID: r.UserID, CreatedAtMS: r.CreatedAt.UnixMilli()}
}
// Comment maps square_comments.
type Comment struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement:false;comment:Snowflake 评论 id"`
PostID int64 `gorm:"column:post_id;comment:帖 id"`
UserID int64 `gorm:"column:user_id;comment:评论人"`
ReplyToID int64 `gorm:"column:reply_to_id;comment:父评论 id"`
ReplyToUID int64 `gorm:"column:reply_to_uid;comment:被回复用户"`
Text string `gorm:"column:text;comment:评论文本"`
Status int16 `gorm:"column:status;comment:0正常 1已删"`
CreatedAt time.Time `gorm:"column:created_at;comment:评论时间"`
}
func (Comment) TableName() string { return "t_square_comments" }
func (r *Comment) Entity() *entity.Comment {
if r == nil {
return nil
}
return &entity.Comment{
ID: r.ID, PostID: r.PostID, UserID: r.UserID, ReplyToID: r.ReplyToID, ReplyToUID: r.ReplyToUID,
Text: r.Text, Status: entity.Status(r.Status), CreatedAtMS: r.CreatedAt.UnixMilli(),
}
}
func commentFrom(c *entity.Comment) *Comment {
if c == nil {
return nil
}
row := &Comment{
ID: c.ID, PostID: c.PostID, UserID: c.UserID, ReplyToID: c.ReplyToID, ReplyToUID: c.ReplyToUID,
Text: c.Text, Status: int16(c.Status),
}
if c.CreatedAtMS > 0 {
row.CreatedAt = time.UnixMilli(c.CreatedAtMS)
}
return row
}
// Notice maps square_notices.
type Notice struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement:false;comment:Snowflake 通知 id"`
ToUserID int64 `gorm:"column:to_user_id;comment:接收人"`
Channel string `gorm:"column:channel;comment:广场实例"`
FromUserID int64 `gorm:"column:from_user_id;comment:发起人"`
Kind int16 `gorm:"column:kind;comment:1赞 2评论 3回复"`
PostID int64 `gorm:"column:post_id;comment:帖 id"`
CommentID int64 `gorm:"column:comment_id;comment:评论 id"`
ReadAt *time.Time `gorm:"column:read_at;comment:已读时间"`
CreatedAt time.Time `gorm:"column:created_at;comment:通知时间"`
}
func (Notice) TableName() string { return "t_square_notices" }
func (r *Notice) Entity() *entity.Notice {
if r == nil {
return nil
}
ch := entity.Channel(r.Channel)
if ch == "" {
ch = entity.ChannelSquare
}
n := &entity.Notice{
ID: r.ID, ToUserID: r.ToUserID, Channel: ch, FromUserID: r.FromUserID, Kind: entity.NoticeKind(r.Kind),
PostID: r.PostID, CommentID: r.CommentID, CreatedAtMS: r.CreatedAt.UnixMilli(),
}
if r.ReadAt != nil {
n.ReadAtMS = r.ReadAt.UnixMilli()
}
return n
}
// NoticeState maps square_notice_state.
type NoticeState struct {
UserID int64 `gorm:"column:user_id;primaryKey;comment:用户 id"`
Channel string `gorm:"column:channel;primaryKey;comment:广场实例"`
LastReadNoticeID int64 `gorm:"column:last_read_notice_id;comment:已读水位"`
UpdatedAt time.Time `gorm:"column:updated_at;comment:更新时间"`
}
func (NoticeState) TableName() string { return "t_square_notice_state" }
func (r *NoticeState) Entity() *entity.NoticeState {
if r == nil {
return nil
}
ch := entity.Channel(r.Channel)
if ch == "" {
ch = entity.ChannelSquare
}
return &entity.NoticeState{
UserID: r.UserID, Channel: ch, LastReadNoticeID: r.LastReadNoticeID, UpdatedAtMS: r.UpdatedAt.UnixMilli(),
}
}