spark-server/internal/module/square/model/write.go
2026-09-22 17:07:26 +08:00

226 lines
5.8 KiB
Go

package model
import (
"context"
"time"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"spark-server/internal/module/square/entity"
"spark-server/internal/pkg/errs"
)
func (m *Repo) CreatePost(ctx context.Context, post *entity.Post, audience []int64) error {
if post == nil || post.ID <= 0 || post.UserID <= 0 {
return errs.ErrInvalidArgument
}
db, err := m.session(ctx)
if err != nil {
return err
}
row := postFrom(post)
if row.CreatedAt.IsZero() {
row.CreatedAt = time.Now()
}
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(row).Error; err != nil {
return mapDBErr(err)
}
for _, peer := range audience {
if err := tx.Create(&Audience{PostID: post.ID, PeerID: peer}).Error; err != nil {
return mapDBErr(err)
}
}
return nil
})
}
func (m *Repo) SoftDeletePost(ctx context.Context, userID, postID int64) (*entity.Post, error) {
db, err := m.session(ctx)
if err != nil {
return nil, err
}
var row Post
err = db.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("id = ? AND user_id = ?", postID, userID).Take(&row).Error; err != nil {
return mapDBErr(err)
}
if row.Status == int16(entity.StatusDeleted) {
return errs.ErrNotFound
}
res := tx.Model(&row).Update("status", entity.StatusDeleted)
if res.Error != nil {
return mapDBErr(res.Error)
}
if res.RowsAffected == 0 {
return errs.ErrNotFound
}
row.Status = int16(entity.StatusDeleted)
return nil
})
if err != nil {
return nil, err
}
return row.Entity(), nil
}
func (m *Repo) AddLike(ctx context.Context, like *entity.Like) (bool, error) {
if like == nil || like.PostID <= 0 || like.UserID <= 0 {
return false, errs.ErrInvalidArgument
}
db, err := m.session(ctx)
if err != nil {
return false, err
}
row := Like{PostID: like.PostID, UserID: like.UserID, CreatedAt: time.Now()}
if like.CreatedAtMS > 0 {
row.CreatedAt = time.UnixMilli(like.CreatedAtMS)
}
created := false
err = db.Transaction(func(tx *gorm.DB) error {
res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "post_id"}, {Name: "user_id"}},
DoNothing: true,
}).Create(&row)
if res.Error != nil {
return mapDBErr(res.Error)
}
if res.RowsAffected == 0 {
return nil
}
created = true
return bumpCount(tx, like.PostID, "like_count", 1)
})
return created, err
}
func (m *Repo) RemoveLike(ctx context.Context, postID, userID int64) error {
db, err := m.session(ctx)
if err != nil {
return err
}
return db.Transaction(func(tx *gorm.DB) error {
res := tx.Where("post_id = ? AND user_id = ?", postID, userID).Delete(&Like{})
if res.Error != nil {
return mapDBErr(res.Error)
}
if res.RowsAffected == 0 {
return errs.ErrNotFound
}
return bumpCount(tx, postID, "like_count", -1)
})
}
func (m *Repo) AddComment(ctx context.Context, c *entity.Comment) error {
if c == nil || c.ID <= 0 || c.PostID <= 0 || c.UserID <= 0 {
return errs.ErrInvalidArgument
}
db, err := m.session(ctx)
if err != nil {
return err
}
row := commentFrom(c)
if row.CreatedAt.IsZero() {
row.CreatedAt = time.Now()
}
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(row).Error; err != nil {
return mapDBErr(err)
}
return bumpCount(tx, c.PostID, "comment_count", 1)
})
}
func (m *Repo) SoftDeleteComment(ctx context.Context, id int64) error {
db, err := m.session(ctx)
if err != nil {
return err
}
return db.Transaction(func(tx *gorm.DB) error {
var row Comment
if err := tx.Where("id = ?", id).Take(&row).Error; err != nil {
return mapDBErr(err)
}
if row.Status == int16(entity.StatusDeleted) {
return errs.ErrNotFound
}
if err := tx.Model(&row).Update("status", entity.StatusDeleted).Error; err != nil {
return mapDBErr(err)
}
return bumpCount(tx, row.PostID, "comment_count", -1)
})
}
func (m *Repo) CreateNotices(ctx context.Context, rows []*entity.Notice) error {
if len(rows) == 0 {
return nil
}
db, err := m.session(ctx)
if err != nil {
return err
}
now := time.Now()
for _, n := range rows {
row := Notice{
ID: n.ID, ToUserID: n.ToUserID, FromUserID: n.FromUserID, Kind: int16(n.Kind),
PostID: n.PostID, CommentID: n.CommentID, CreatedAt: now,
}
if n.CreatedAtMS > 0 {
row.CreatedAt = time.UnixMilli(n.CreatedAtMS)
}
if err := db.Create(&row).Error; err != nil {
return mapDBErr(err)
}
}
return nil
}
func (m *Repo) MarkNoticesRead(ctx context.Context, userID, cursor int64) error {
db, err := m.session(ctx)
if err != nil {
return err
}
q := db.Model(&Notice{}).Where("to_user_id = ? AND read_at IS NULL", userID)
if cursor > 0 {
q = q.Where("id <= ?", cursor)
}
return mapDBErr(q.Update("read_at", time.Now()).Error)
}
func (m *Repo) AdvanceNoticeRead(ctx context.Context, userID, noticeID int64) error {
if userID <= 0 || noticeID <= 0 {
return errs.ErrInvalidArgument
}
db, err := m.session(ctx)
if err != nil {
return err
}
cur, err := m.GetNoticeState(ctx, userID)
if err != nil {
return err
}
if cur.LastReadNoticeID >= noticeID {
return nil
}
cur.LastReadNoticeID = noticeID
cur.UpdatedAtMS = time.Now().UnixMilli()
row := NoticeState{
UserID: cur.UserID, LastReadNoticeID: cur.LastReadNoticeID,
UpdatedAt: time.UnixMilli(cur.UpdatedAtMS),
}
return mapDBErr(db.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "user_id"}},
DoUpdates: clause.AssignmentColumns([]string{"last_read_notice_id", "updated_at"}),
}).Create(&row).Error)
}
func bumpCount(tx *gorm.DB, postID int64, col string, delta int) error {
expr := col + " + ?"
if delta < 0 {
expr = "CASE WHEN " + col + " + ? < 0 THEN 0 ELSE " + col + " + ? END"
return mapDBErr(tx.Model(&Post{}).Where("id = ?", postID).Update(col, gorm.Expr(expr, delta, delta)).Error)
}
return mapDBErr(tx.Model(&Post{}).Where("id = ?", postID).Update(col, gorm.Expr(expr, delta)).Error)
}