Files
backend/internal/repository/vote_repo.go
lafay 2f584c1f2c
All checks were successful
Build Backend / build (push) Successful in 5m10s
Build Backend / build-docker (push) Successful in 2m22s
This is a breaking change that renames the entire project from "Carrot BBS" to "WithYou".
The Go module name has been changed from `carrot_bbs` to `with_you`, which requires
all import paths to be updated throughout the codebase and by external consumers.

BREAKING CHANGE: Module name changed from carrot_bbs to with_you. All imports
and dependencies must be updated from carrot_bbs/* to with_you/*.
2026-04-22 16:01:59 +08:00

156 lines
4.4 KiB
Go

package repository
import (
"with_you/internal/model"
"errors"
"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
}
// 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
})
}