feat(wish-tree): 许愿树游戏化改造
Build and Deploy Server / build (push) Failing after 2s

- 前端:全屏Canvas场景,夜空/云朵/草地/大树
- 前端:丝带系统,支持陀螺仪和麦克风交互
- 前端:弹幕许愿展示
- 前端:多棵树切换(祈福树/姻缘树/事业树)
- 后端:新增GET /api/wish/trees获取树列表
- 后端:新增GET /api/wish/tree/:id/wishes获取树许愿
- 后端:WishTree模型新增Type、Sort字段
- 后端:AutoMigrate自动处理表结构变更
- 后端:seedDefaultData自动初始化默认数据
- 数据库:wish_trees表新增type、sort字段
This commit is contained in:
gouki
2026-08-07 12:56:48 +00:00
parent 82c1a8d694
commit 01e67d4bea
6 changed files with 115 additions and 4 deletions
+2
View File
@@ -71,7 +71,9 @@ func main() {
// 许愿相关
wish := api.Group("/wish")
{
wish.GET("/trees", handler.GetWishTrees)
wish.GET("/tree", handler.GetWishTree)
wish.GET("/tree/:id/wishes", handler.GetTreeWishes)
wish.POST("/create", middleware.Auth(), handler.CreateWish)
wish.GET("/list", handler.GetWishList)
wish.GET("/:id", handler.GetWishDetail)
+40 -2
View File
@@ -38,7 +38,7 @@ func InitDB(cfg *Config) error {
// autoMigrate 自动迁移数据库表
func autoMigrate() error {
return DB.AutoMigrate(
if err := DB.AutoMigrate(
&model.User{},
&model.UserProfile{},
&model.Order{},
@@ -46,7 +46,45 @@ func autoMigrate() error {
&model.WishTree{},
&model.Wish{},
&model.WishProduct{},
)
); err != nil {
return err
}
// 初始化默认数据
return seedDefaultData()
}
// seedDefaultData 初始化默认数据
func seedDefaultData() error {
// 检查是否已有许愿树数据
var count int64
DB.Model(&model.WishTree{}).Count(&count)
if count > 0 {
return nil // 已有数据,跳过
}
// 插入默认许愿树
trees := []model.WishTree{
{Name: "祈福树", Description: "许下美好愿望,祈福平安顺遂", Type: "pine", MaxWishes: 100, Sort: 0, Status: 1},
{Name: "姻缘树", Description: "祈求姻缘美满,爱情甜蜜", Type: "sakura", MaxWishes: 50, Sort: 1, Status: 1},
{Name: "事业树", Description: "祈愿事业顺利,步步高升", Type: "bamboo", MaxWishes: 80, Sort: 2, Status: 1},
}
if err := DB.Create(&trees).Error; err != nil {
return err
}
// 插入默认许愿商品
products := []model.WishProduct{
{Name: "普通许愿条", Description: "基础许愿条,展示7天", Price: 100, Duration: 7, Position: 0, Status: 1},
{Name: "精品许愿条", Description: "精品许愿条,展示30天,优先位置", Price: 500, Duration: 30, Position: 10, Status: 1},
{Name: "至尊许愿条", Description: "至尊许愿条,展示90天,置顶显示", Price: 2000, Duration: 90, Position: 100, Status: 1},
}
if err := DB.Create(&products).Error; err != nil {
return err
}
log.Println("Default data seeded successfully")
return nil
}
// GetDB 获取数据库连接
+44
View File
@@ -9,6 +9,27 @@ import (
"github.com/gouki/lunar-server/internal/service"
)
// GetWishTrees 获取许愿树列表
func GetWishTrees(c *gin.Context) {
wishService := service.NewWishService()
trees, err := wishService.GetWishTrees()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"msg": "获取许愿树列表失败",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "success",
"data": gin.H{
"list": trees,
},
})
}
// GetWishTree 获取许愿树
func GetWishTree(c *gin.Context) {
treeID, _ := strconv.Atoi(c.DefaultQuery("treeId", "1"))
@@ -95,6 +116,29 @@ func CreateWish(c *gin.Context) {
})
}
// GetTreeWishes 获取指定树的许愿列表
func GetTreeWishes(c *gin.Context) {
treeID, _ := strconv.Atoi(c.Param("id"))
wishService := service.NewWishService()
wishes, err := wishService.GetTreeWishes(uint(treeID))
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"code": 500,
"msg": "获取许愿列表失败",
})
return
}
c.JSON(http.StatusOK, gin.H{
"code": 0,
"msg": "success",
"data": gin.H{
"list": wishes,
},
})
}
// GetWishList 获取许愿列表
func GetWishList(c *gin.Context) {
treeID, _ := strconv.Atoi(c.DefaultQuery("treeId", "1"))
+2
View File
@@ -23,7 +23,9 @@ type WishTree struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:50" json:"name"`
Description string `gorm:"size:255" json:"description"`
Type string `gorm:"size:20;default:'pine'" json:"type"` // pine:松树 sakura:樱花 bamboo:竹子
MaxWishes int `gorm:"default:100" json:"maxWishes"` // 最大许愿条数
Sort int `gorm:"default:0" json:"sort"` // 排序
Status int `gorm:"default:1" json:"status"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
+21
View File
@@ -23,6 +23,15 @@ func NewWishService() *WishService {
}
}
// 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
@@ -88,6 +97,18 @@ func (s *WishService) CreateWish(wish *model.Wish) error {
return s.db.Create(wish).Error
}
// GetTreeWishes 获取指定树的许愿列表
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("position DESC, created_at DESC").
Limit(100).
Find(&wishes).Error; err != nil {
return nil, err
}
return wishes, nil
}
// GetWishList 获取许愿列表
func (s *WishService) GetWishList(treeID uint, page, pageSize int) ([]*model.Wish, int64, error) {
var wishes []*model.Wish
+6 -2
View File
@@ -56,7 +56,9 @@ CREATE TABLE IF NOT EXISTS wish_trees (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL COMMENT '名称',
description VARCHAR(255) DEFAULT '' COMMENT '描述',
type VARCHAR(20) DEFAULT 'pine' COMMENT '树类型 pine:松树 sakura:樱花 bamboo:竹子',
max_wishes INT DEFAULT 100 COMMENT '最大许愿条数',
sort INT DEFAULT 0 COMMENT '排序',
status TINYINT DEFAULT 1 COMMENT '状态',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
@@ -94,8 +96,10 @@ CREATE TABLE IF NOT EXISTS wish_products (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='许愿商品表';
-- 插入默认许愿树
INSERT IGNORE INTO wish_trees (id, name, description, max_wishes) VALUES
(1, '祈福许愿', '许下美好愿望,祈福平安顺遂', 100);
INSERT IGNORE INTO wish_trees (id, name, description, type, max_wishes, sort) VALUES
(1, '祈福树', '许下美好愿望,祈福平安顺遂', 'pine', 100, 0),
(2, '姻缘树', '祈求姻缘美满,爱情甜蜜', 'sakura', 50, 1),
(3, '事业树', '祈愿事业顺利,步步高升', 'bamboo', 80, 2);
-- 插入默认许愿商品
INSERT IGNORE INTO wish_products (id, name, description, price, duration, position) VALUES