Files
lunar-mini/server/internal/service/wish.go
T
gouki fe5bed0668
Publish Mini Program Dev Version / publish (push) Failing after 11s
Build and Publish Server / build (push) Successful in 2m54s
feat(wish-tree): 许愿树页面微调七项
1. 背景图 aspectFill 撑满全屏,挂载点坐标按裁剪比例重算,便签不再落在图外
2. 写下心愿按钮单行不换行,缩小尺寸
3. 移除页面内许愿树标题/数量/最近心愿,点击便签弹出便签式详情卡
4. 默认请求100条:最新50条挂树,其余以弹幕形式缓缓飘过
5. MAKE A WISH 下方增加顶级位置轮播(购买后24小时内展示),点击弹出金色便签详情,与普通便签区分
6. 多棵树分屏轮播(swiper),背景图从服务器获取,带指示点
7. 后端:WishTree新增image字段,Wish新增isTop运行时标记,图片用go:embed内嵌随二进制部署,/images路由提供访问
2026-08-09 20:58:32 +00:00

214 lines
5.4 KiB
Go

package service
import (
"errors"
"fmt"
"math/rand"
"time"
"github.com/gouki/lunar-server/internal/config"
"github.com/gouki/lunar-server/internal/model"
"gorm.io/gorm"
)
// WishService 许愿服务
type WishService struct {
db *gorm.DB
}
// NewWishService 创建许愿服务
func NewWishService() *WishService {
return &WishService{
db: config.GetDB(),
}
}
// GetWishTrees 获取许愿树列表
func (s *WishService) GetWishTrees() ([]*model.WishTree, error) {
var trees []*model.WishTree
if err := s.db.Where("status = 1").Order("sort ASC, id ASC").Find(&trees).Error; err != nil {
return nil, err
}
return trees, nil
}
// GetWishTree 获取许愿树
func (s *WishService) GetWishTree(treeID uint) (*model.WishTree, error) {
var tree model.WishTree
if err := s.db.First(&tree, treeID).Error; err != nil {
return nil, err
}
return &tree, nil
}
// GetWishTreeWithWishes 获取许愿树及其许愿
func (s *WishService) GetWishTreeWithWishes(treeID uint) (*model.WishTree, []*model.Wish, error) {
tree, err := s.GetWishTree(treeID)
if err != nil {
return nil, nil, err
}
var wishes []*model.Wish
if err := s.db.Where("tree_id = ? AND status = 1", treeID).
Order("position DESC, created_at DESC").
Limit(tree.MaxWishes).
Find(&wishes).Error; err != nil {
return nil, nil, err
}
return tree, wishes, nil
}
// CreateWish 创建许愿
func (s *WishService) CreateWish(wish *model.Wish) error {
// 检查许愿树是否存在
tree, err := s.GetWishTree(wish.TreeID)
if err != nil {
return fmt.Errorf("wish tree not found: %w", err)
}
// 检查是否超过最大许愿数
var count int64
s.db.Model(&model.Wish{}).Where("tree_id = ? AND status = 1", wish.TreeID).Count(&count)
if count >= int64(tree.MaxWishes) {
// 如果是付费许愿,覆盖最旧的免费许愿
if wish.Type == "paid" {
var oldestFreeWish model.Wish
if err := s.db.Where("tree_id = ? AND type = 'free' AND status = 1", wish.TreeID).
Order("created_at ASC").
First(&oldestFreeWish).Error; err == nil {
// 删除最旧的免费许愿
s.db.Model(&oldestFreeWish).Update("status", 0)
}
} else {
return errors.New("wish tree is full")
}
}
// 设置位置
if wish.Type == "paid" {
// 付费许愿位置靠前
wish.Position = 100
} else {
// 免费许愿位置随机
wish.Position = rand.Intn(50)
}
return s.db.Create(wish).Error
}
// GetTreeWishes 获取指定树的许愿列表(最新100条,标记顶级位置)
func (s *WishService) GetTreeWishes(treeID uint) ([]*model.Wish, error) {
var wishes []*model.Wish
if err := s.db.Where("tree_id = ? AND status = 1", treeID).
Order("created_at DESC").
Limit(100).
Find(&wishes).Error; err != nil {
return nil, err
}
// 计算顶级位置:高位置付费许愿且购买后24小时内
for _, w := range wishes {
w.IsTop = w.Position >= 100 && time.Since(w.CreatedAt) <= 24*time.Hour
}
return wishes, nil
}
// GetWishList 获取许愿列表
func (s *WishService) GetWishList(treeID uint, page, pageSize int) ([]*model.Wish, int64, error) {
var wishes []*model.Wish
var total int64
query := s.db.Model(&model.Wish{}).Where("tree_id = ? AND status = 1", treeID)
if err := query.Count(&total).Error; err != nil {
return nil, 0, err
}
if err := query.Order("position DESC, created_at DESC").
Offset((page - 1) * pageSize).
Limit(pageSize).
Find(&wishes).Error; err != nil {
return nil, 0, err
}
return wishes, total, nil
}
// GetWishByID 根据ID获取许愿
func (s *WishService) GetWishByID(id uint) (*model.Wish, error) {
var wish model.Wish
if err := s.db.First(&wish, id).Error; err != nil {
return nil, err
}
return &wish, nil
}
// DeleteWish 删除许愿
func (s *WishService) DeleteWish(id uint) error {
return s.db.Model(&model.Wish{}).Where("id = ?", id).Update("status", 0).Error
}
// CreateRobotWish 创建机器人许愿
func (s *WishService) CreateRobotWish(treeID uint) error {
// 机器人许愿内容库
robotWishes := []string{
"愿世界和平,人人幸福",
"祝所有人心想事成",
"愿健康常伴左右",
"祝事业蒸蒸日上",
"愿爱情甜蜜美满",
"祝学业进步,考试顺利",
"愿财源广进,富贵吉祥",
"祝家庭和睦,幸福美满",
"愿旅途平安,一路顺风",
"祝梦想成真,前程似锦",
}
// 随机选择一条
content := robotWishes[rand.Intn(len(robotWishes))]
wish := &model.Wish{
UserID: 0, // 机器人用户ID为0
TreeID: treeID,
Content: content,
Type: "free",
IsRobot: true,
Status: 1,
}
return s.db.Create(wish).Error
}
// GetWishProducts 获取许愿商品列表
func (s *WishService) GetWishProducts() ([]*model.WishProduct, error) {
var products []*model.WishProduct
if err := s.db.Where("status = 1").Order("price ASC").Find(&products).Error; err != nil {
return nil, err
}
return products, nil
}
// GetWishProductByID 根据ID获取许愿商品
func (s *WishService) GetWishProductByID(id uint) (*model.WishProduct, error) {
var product model.WishProduct
if err := s.db.First(&product, id).Error; err != nil {
return nil, err
}
return &product, nil
}
// StartRobotWishJob 启动机器人许愿定时任务
func (s *WishService) StartRobotWishJob() {
ticker := time.NewTicker(time.Hour * 2) // 每2小时执行一次
go func() {
for range ticker.C {
// 随机决定是否发布许愿
if rand.Intn(100) < 30 { // 30%概率
s.CreateRobotWish(1) // 默认许愿树ID为1
}
}
}()
}