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 := api.Group("/wish")
{ {
wish.GET("/trees", handler.GetWishTrees)
wish.GET("/tree", handler.GetWishTree) wish.GET("/tree", handler.GetWishTree)
wish.GET("/tree/:id/wishes", handler.GetTreeWishes)
wish.POST("/create", middleware.Auth(), handler.CreateWish) wish.POST("/create", middleware.Auth(), handler.CreateWish)
wish.GET("/list", handler.GetWishList) wish.GET("/list", handler.GetWishList)
wish.GET("/:id", handler.GetWishDetail) wish.GET("/:id", handler.GetWishDetail)
+40 -2
View File
@@ -38,7 +38,7 @@ func InitDB(cfg *Config) error {
// autoMigrate 自动迁移数据库表 // autoMigrate 自动迁移数据库表
func autoMigrate() error { func autoMigrate() error {
return DB.AutoMigrate( if err := DB.AutoMigrate(
&model.User{}, &model.User{},
&model.UserProfile{}, &model.UserProfile{},
&model.Order{}, &model.Order{},
@@ -46,7 +46,45 @@ func autoMigrate() error {
&model.WishTree{}, &model.WishTree{},
&model.Wish{}, &model.Wish{},
&model.WishProduct{}, &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 获取数据库连接 // GetDB 获取数据库连接
+44
View File
@@ -9,6 +9,27 @@ import (
"github.com/gouki/lunar-server/internal/service" "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 获取许愿树 // GetWishTree 获取许愿树
func GetWishTree(c *gin.Context) { func GetWishTree(c *gin.Context) {
treeID, _ := strconv.Atoi(c.DefaultQuery("treeId", "1")) 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 获取许愿列表 // GetWishList 获取许愿列表
func GetWishList(c *gin.Context) { func GetWishList(c *gin.Context) {
treeID, _ := strconv.Atoi(c.DefaultQuery("treeId", "1")) treeID, _ := strconv.Atoi(c.DefaultQuery("treeId", "1"))
+2
View File
@@ -23,7 +23,9 @@ type WishTree struct {
ID uint `gorm:"primaryKey" json:"id"` ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:50" json:"name"` Name string `gorm:"size:50" json:"name"`
Description string `gorm:"size:255" json:"description"` 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"` // 最大许愿条数 MaxWishes int `gorm:"default:100" json:"maxWishes"` // 最大许愿条数
Sort int `gorm:"default:0" json:"sort"` // 排序
Status int `gorm:"default:1" json:"status"` Status int `gorm:"default:1" json:"status"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"` 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 获取许愿树 // GetWishTree 获取许愿树
func (s *WishService) GetWishTree(treeID uint) (*model.WishTree, error) { func (s *WishService) GetWishTree(treeID uint) (*model.WishTree, error) {
var tree model.WishTree var tree model.WishTree
@@ -88,6 +97,18 @@ func (s *WishService) CreateWish(wish *model.Wish) error {
return s.db.Create(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 获取许愿列表 // GetWishList 获取许愿列表
func (s *WishService) GetWishList(treeID uint, page, pageSize int) ([]*model.Wish, int64, error) { func (s *WishService) GetWishList(treeID uint, page, pageSize int) ([]*model.Wish, int64, error) {
var wishes []*model.Wish 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, id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL COMMENT '名称', name VARCHAR(50) NOT NULL COMMENT '名称',
description VARCHAR(255) DEFAULT '' COMMENT '描述', description VARCHAR(255) DEFAULT '' COMMENT '描述',
type VARCHAR(20) DEFAULT 'pine' COMMENT '树类型 pine:松树 sakura:樱花 bamboo:竹子',
max_wishes INT DEFAULT 100 COMMENT '最大许愿条数', max_wishes INT DEFAULT 100 COMMENT '最大许愿条数',
sort INT DEFAULT 0 COMMENT '排序',
status TINYINT DEFAULT 1 COMMENT '状态', status TINYINT DEFAULT 1 COMMENT '状态',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE 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='许愿商品表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='许愿商品表';
-- 插入默认许愿树 -- 插入默认许愿树
INSERT IGNORE INTO wish_trees (id, name, description, max_wishes) VALUES INSERT IGNORE INTO wish_trees (id, name, description, type, max_wishes, sort) VALUES
(1, '祈福许愿', '许下美好愿望,祈福平安顺遂', 100); (1, '祈福树', '许下美好愿望,祈福平安顺遂', 'pine', 100, 0),
(2, '姻缘树', '祈求姻缘美满,爱情甜蜜', 'sakura', 50, 1),
(3, '事业树', '祈愿事业顺利,步步高升', 'bamboo', 80, 2);
-- 插入默认许愿商品 -- 插入默认许愿商品
INSERT IGNORE INTO wish_products (id, name, description, price, duration, position) VALUES INSERT IGNORE INTO wish_products (id, name, description, price, duration, position) VALUES