Files
backend/internal/repository/vote_repo.go
lafay 05c7f8a5eb
All checks were successful
Build Backend / build (push) Successful in 19m52s
Build Backend / build-docker (push) Successful in 1m29s
feat(api): add message segments support for posts and comments
Add rich message segment support enabling structured content (text, images, votes, mentions) in posts and comments. Includes segments field in models, DTOs, handlers, and services. Also adds @mention notification processing and migrates vote data to embedded segments.
2026-04-23 22:29:34 +08:00

161 lines
4.7 KiB
Go

package repository
import (
"errors"
"with_you/internal/model"
"gorm.io/gorm"
)
// VoteRepository 投票仓储接口
type VoteRepository interface {
CreateOptions(postID string, options []string) error
GetOptionsByPostID(postID string) ([]model.VoteOption, error)
Vote(postID, userID, optionID string) error
Unvote(postID, userID string) error
GetUserVote(postID, userID string) (*model.UserVote, error)
UpdateOption(optionID, content string) error
DeleteOptionsByPostID(postID string) error
CountVotesByOption(postID, optionID string) (int, error)
}
// voteRepository 投票仓储实现
type voteRepository struct {
db *gorm.DB
}
// NewVoteRepository 创建投票仓储
func NewVoteRepository(db *gorm.DB) VoteRepository {
return &voteRepository{db: db}
}
// CreateOptions 批量创建投票选项(使用批量插入,避免 N+1 问题)
func (r *voteRepository) CreateOptions(postID string, options []string) error {
if len(options) == 0 {
return nil
}
// 构建批量插入的选项切片
voteOptions := make([]model.VoteOption, len(options))
for i, content := range options {
voteOptions[i] = model.VoteOption{
PostID: postID,
Content: content,
SortOrder: i,
}
}
// 使用 GORM 的 Create 方法批量插入
return r.db.CreateInBatches(voteOptions, 100).Error
}
// GetOptionsByPostID 获取帖子的所有投票选项
func (r *voteRepository) GetOptionsByPostID(postID string) ([]model.VoteOption, error) {
var options []model.VoteOption
err := r.db.Where("post_id = ?", postID).Order("sort_order ASC").Find(&options).Error
return options, err
}
// Vote 用户投票
func (r *voteRepository) Vote(postID, userID, optionID string) error {
return r.db.Transaction(func(tx *gorm.DB) error {
// 检查用户是否已投票
var existing model.UserVote
err := tx.Where("post_id = ? AND user_id = ?", postID, userID).First(&existing).Error
if err == nil {
// 已经投票,返回错误
return errors.New("user already voted")
}
if err != gorm.ErrRecordNotFound {
return err
}
// 验证选项是否属于该帖子
var option model.VoteOption
if err := tx.Where("id = ? AND post_id = ?", optionID, postID).First(&option).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return errors.New("invalid option")
}
return err
}
// 创建投票记录
if err := tx.Create(&model.UserVote{
PostID: postID,
UserID: userID,
OptionID: optionID,
}).Error; err != nil {
return err
}
// 原子增加选项投票数
return tx.Model(&model.VoteOption{}).Where("id = ?", optionID).
UpdateColumn("votes_count", gorm.Expr("votes_count + 1")).Error
})
}
// Unvote 取消投票
func (r *voteRepository) Unvote(postID, userID string) error {
return r.db.Transaction(func(tx *gorm.DB) error {
// 获取用户的投票记录
var userVote model.UserVote
err := tx.Where("post_id = ? AND user_id = ?", postID, userID).First(&userVote).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil // 没有投票记录,直接返回
}
return err
}
// 删除投票记录
result := tx.Where("post_id = ? AND user_id = ?", postID, userID).Delete(&model.UserVote{})
if result.Error != nil {
return result.Error
}
if result.RowsAffected > 0 {
// 原子减少选项投票数
return tx.Model(&model.VoteOption{}).Where("id = ?", userVote.OptionID).
UpdateColumn("votes_count", gorm.Expr("votes_count - 1")).Error
}
return nil
})
}
// GetUserVote 获取用户在指定帖子的投票
func (r *voteRepository) GetUserVote(postID, userID string) (*model.UserVote, error) {
var userVote model.UserVote
err := r.db.Where("post_id = ? AND user_id = ?", postID, userID).First(&userVote).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
return nil, err
}
return &userVote, nil
}
// UpdateOption 更新选项内容
func (r *voteRepository) UpdateOption(optionID, content string) error {
return r.db.Model(&model.VoteOption{}).Where("id = ?", optionID).
Update("content", content).Error
}
// DeleteOptionsByPostID 删除帖子的所有投票选项
func (r *voteRepository) DeleteOptionsByPostID(postID string) error {
return r.db.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("post_id = ?", postID).Delete(&model.UserVote{}).Error; err != nil {
return err
}
return tx.Where("post_id = ?", postID).Delete(&model.VoteOption{}).Error
})
}
// CountVotesByOption 统计指定选项的投票数
func (r *voteRepository) CountVotesByOption(postID, optionID string) (int, error) {
var count int64
err := r.db.Model(&model.UserVote{}).Where("post_id = ? AND option_id = ?", postID, optionID).Count(&count).Error
return int(count), err
}