151 lines
4.2 KiB
Go
151 lines
4.2 KiB
Go
package service
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"errors"
|
|
"math/big"
|
|
|
|
"git.iioio.com/freefire/jiang13-forum/model"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
var (
|
|
ErrLotteryAlreadyDrawn = errors.New("已开奖")
|
|
ErrLotteryNotEnough = errors.New("参与人数不足")
|
|
)
|
|
|
|
// PostLotteryView 帖内抽奖视图
|
|
type PostLotteryView struct {
|
|
WinnerCount int `json:"winner_count"`
|
|
Status string `json:"status"`
|
|
ParticipantCount int `json:"participant_count"`
|
|
Winners []PostLotteryWinnerView `json:"winners,omitempty"`
|
|
}
|
|
|
|
type PostLotteryWinnerView struct {
|
|
UserID uint `json:"user_id"`
|
|
Username string `json:"username"`
|
|
Nickname string `json:"nickname"`
|
|
CommentID uint `json:"comment_id"`
|
|
}
|
|
|
|
// InitPostLottery 初始化抽奖帖
|
|
func InitPostLottery(postID uint, winnerCount int) error {
|
|
if winnerCount < 1 || winnerCount > 20 {
|
|
return errors.New("开奖人数需 1-20")
|
|
}
|
|
return model.DB.Model(&model.Post{}).Where("id = ?", postID).Updates(map[string]interface{}{
|
|
"lottery_winner_count": winnerCount,
|
|
"lottery_status": model.PostLotteryStatusOpen,
|
|
}).Error
|
|
}
|
|
|
|
// GetPostLotteryView 获取抽奖视图
|
|
func GetPostLotteryView(post *model.Post) (*PostLotteryView, error) {
|
|
if post == nil || post.PostType != model.PostTypeLottery {
|
|
return nil, nil
|
|
}
|
|
participants, err := lotteryParticipants(post.ID, post.UserID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
view := &PostLotteryView{
|
|
WinnerCount: post.LotteryWinnerCount,
|
|
Status: post.LotteryStatus,
|
|
ParticipantCount: len(participants),
|
|
}
|
|
if post.LotteryStatus == model.PostLotteryStatusDrawn {
|
|
var winners []model.PostLotteryWinner
|
|
model.DB.Preload("User").Where("post_id = ?", post.ID).Find(&winners)
|
|
for _, w := range winners {
|
|
view.Winners = append(view.Winners, PostLotteryWinnerView{
|
|
UserID: w.UserID, Username: w.User.Username, Nickname: w.User.Nickname,
|
|
CommentID: w.CommentID,
|
|
})
|
|
}
|
|
}
|
|
return view, nil
|
|
}
|
|
|
|
func lotteryParticipants(postID, authorID uint) ([]model.Comment, error) {
|
|
var comments []model.Comment
|
|
err := model.DB.Where("post_id = ? AND status = ? AND user_id <> ?", postID, model.ContentStatusPublished, authorID).
|
|
Order("id ASC").Find(&comments).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
seen := map[uint]bool{}
|
|
var unique []model.Comment
|
|
for _, c := range comments {
|
|
if seen[c.UserID] {
|
|
continue
|
|
}
|
|
seen[c.UserID] = true
|
|
unique = append(unique, c)
|
|
}
|
|
return unique, nil
|
|
}
|
|
|
|
// DrawPostLottery 开奖
|
|
func DrawPostLottery(postID, operatorID uint, isAdmin bool) (*PostLotteryView, error) {
|
|
var post model.Post
|
|
if err := model.DB.First(&post, postID).Error; err != nil {
|
|
return nil, ErrPostNotFound
|
|
}
|
|
if post.PostType != model.PostTypeLottery {
|
|
return nil, errors.New("非抽奖帖")
|
|
}
|
|
if !isAdmin && post.UserID != operatorID {
|
|
return nil, ErrPermissionDenied
|
|
}
|
|
if post.LotteryStatus == model.PostLotteryStatusDrawn {
|
|
return nil, ErrLotteryAlreadyDrawn
|
|
}
|
|
participants, err := lotteryParticipants(postID, post.UserID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
need := post.LotteryWinnerCount
|
|
if need < 1 {
|
|
need = 1
|
|
}
|
|
if len(participants) < need {
|
|
return nil, ErrLotteryNotEnough
|
|
}
|
|
picked := randomPickComments(participants, need)
|
|
err = model.DB.Transaction(func(tx *gorm.DB) error {
|
|
for _, c := range picked {
|
|
w := model.PostLotteryWinner{PostID: postID, UserID: c.UserID, CommentID: c.ID}
|
|
if err := tx.Create(&w).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return tx.Model(&post).Update("lottery_status", model.PostLotteryStatusDrawn).Error
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
post.LotteryStatus = model.PostLotteryStatusDrawn
|
|
return GetPostLotteryView(&post)
|
|
}
|
|
|
|
func randomPickComments(comments []model.Comment, n int) []model.Comment {
|
|
pool := append([]model.Comment{}, comments...)
|
|
out := make([]model.Comment, 0, n)
|
|
for i := 0; i < n && len(pool) > 0; i++ {
|
|
idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(pool))))
|
|
if err != nil {
|
|
idx = big.NewInt(0)
|
|
}
|
|
j := int(idx.Int64())
|
|
out = append(out, pool[j])
|
|
pool = append(pool[:j], pool[j+1:]...)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// DeleteLotteryData 删帖清理
|
|
func DeleteLotteryData(tx *gorm.DB, postID uint) {
|
|
tx.Where("post_id = ?", postID).Delete(&model.PostLotteryWinner{})
|
|
}
|