spark-server/internal/module/square/model/bot.go
DESKTOP-L8OUL9F\gfl0034 715ace7ac6 feat(square): 广场 Bot/RSS 发布与媒体校验
支持广场机器人发帖与媒体类型检查,并补充相关迁移与 i18n。
2026-09-24 23:07:06 +08:00

104 lines
2.5 KiB
Go

package model
import (
"context"
"crypto/sha256"
"encoding/hex"
"strings"
"time"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"spark-server/internal/module/square/entity"
"spark-server/internal/pkg/errs"
)
// CreateBotPost inserts a bot RSS post and claims its source URL atomically.
func (m *Repo) CreateBotPost(ctx context.Context, post *entity.Post) error {
if post == nil || post.ID <= 0 || post.UserID <= 0 {
return errs.ErrInvalidArgument
}
url := strings.TrimSpace(post.SourceURL)
if url == "" {
return errs.ErrInvalidArgument
}
post.SourceURL = url
if post.Source == 0 {
post.Source = entity.SourceBotRSS
}
db, err := m.session(ctx)
if err != nil {
return err
}
row := postFrom(post)
if row.CreatedAt.IsZero() {
row.CreatedAt = time.Now()
}
hash := urlHash(url)
return db.Transaction(func(tx *gorm.DB) error {
link := BotLink{URLHash: hash, URL: url, PostID: post.ID, CreatedAt: row.CreatedAt}
res := tx.Clauses(clause.OnConflict{DoNothing: true}).Create(&link)
if res.Error != nil {
return mapDBErr(res.Error)
}
if res.RowsAffected == 0 {
return errs.ErrDuplicate
}
if err := tx.Create(row).Error; err != nil {
return mapDBErr(err)
}
return nil
})
}
// SoftDeleteExpiredBotPosts marks expired bot posts deleted; returns deleted rows for media GC.
func (m *Repo) SoftDeleteExpiredBotPosts(ctx context.Context, nowMS int64, limit int) ([]*entity.Post, error) {
if limit <= 0 {
limit = 100
}
if nowMS <= 0 {
nowMS = time.Now().UnixMilli()
}
db, err := m.session(ctx)
if err != nil {
return nil, err
}
deadline := time.UnixMilli(nowMS)
var rows []Post
err = db.Transaction(func(tx *gorm.DB) error {
q := tx.Where(
"source = ? AND status = ? AND expires_at IS NOT NULL AND expires_at <= ?",
entity.SourceBotRSS, entity.StatusNormal, deadline,
).Order("expires_at ASC").Limit(limit)
if err := q.Find(&rows).Error; err != nil {
return mapDBErr(err)
}
if len(rows) == 0 {
return nil
}
ids := make([]int64, 0, len(rows))
for i := range rows {
ids = append(ids, rows[i].ID)
}
res := tx.Model(&Post{}).Where("id IN ? AND status = ?", ids, entity.StatusNormal).
Update("status", entity.StatusDeleted)
if res.Error != nil {
return mapDBErr(res.Error)
}
for i := range rows {
rows[i].Status = int16(entity.StatusDeleted)
}
return nil
})
if err != nil {
return nil, err
}
return collectPosts(rows), nil
}
func urlHash(url string) string {
sum := sha256.Sum256([]byte(strings.TrimSpace(url)))
return hex.EncodeToString(sum[:])
}