feat: 添加许愿树功能和Go后端服务
- 新增许愿树页面(Canvas绘制、许愿条展示) - 新增许愿详情页面 - 新增许愿创建弹窗(免费/付费两种模式) - 新增网络请求封装 utils/request.js - 新增Go后端项目结构(Gin + Inertia.js) - 新增用户、订单、许愿数据模型 - 新增数据库迁移脚本 - 更新.gitignore补全忽略规则 - 更新.env.local配置(robot=2, 版本1.0.1) - 添加secrets目录说明文档
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Server ServerConfig
|
||||
Database DatabaseConfig
|
||||
Redis RedisConfig
|
||||
JWT JWTConfig
|
||||
Wechat WechatConfig
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
Port string
|
||||
Env string
|
||||
}
|
||||
|
||||
type DatabaseConfig struct {
|
||||
Host string
|
||||
Port string
|
||||
User string
|
||||
Password string
|
||||
Name string
|
||||
}
|
||||
|
||||
type RedisConfig struct {
|
||||
Host string
|
||||
Port string
|
||||
Password string
|
||||
DB int
|
||||
}
|
||||
|
||||
type JWTConfig struct {
|
||||
Secret string
|
||||
}
|
||||
|
||||
type WechatConfig struct {
|
||||
AppID string
|
||||
AppSecret string
|
||||
PayKey string
|
||||
MchID string
|
||||
}
|
||||
|
||||
func Load() *Config {
|
||||
return &Config{
|
||||
Server: ServerConfig{
|
||||
Port: getEnv("SERVER_PORT", "8080"),
|
||||
Env: getEnv("SERVER_ENV", "development"),
|
||||
},
|
||||
Database: DatabaseConfig{
|
||||
Host: getEnv("DB_HOST", "localhost"),
|
||||
Port: getEnv("DB_PORT", "3306"),
|
||||
User: getEnv("DB_USER", "root"),
|
||||
Password: getEnv("DB_PASSWORD", ""),
|
||||
Name: getEnv("DB_NAME", "lunar"),
|
||||
},
|
||||
Redis: RedisConfig{
|
||||
Host: getEnv("REDIS_HOST", "localhost"),
|
||||
Port: getEnv("REDIS_PORT", "6379"),
|
||||
Password: getEnv("REDIS_PASSWORD", ""),
|
||||
DB: 0,
|
||||
},
|
||||
JWT: JWTConfig{
|
||||
Secret: getEnv("JWT_SECRET", "your-secret-key"),
|
||||
},
|
||||
Wechat: WechatConfig{
|
||||
AppID: getEnv("MINI_APP_ID", ""),
|
||||
AppSecret: getEnv("MINI_APP_SECRET", ""),
|
||||
PayKey: getEnv("WECHAT_PAY_APIKEY", ""),
|
||||
MchID: getEnv("WECHAT_PAY_MCHID", ""),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value := os.Getenv(key); value != "" {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// AdminIndex 管理后台首页
|
||||
func AdminIndex(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{
|
||||
"title": "管理后台",
|
||||
})
|
||||
}
|
||||
|
||||
// AdminUsers 用户管理
|
||||
func AdminUsers(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{
|
||||
"title": "用户管理",
|
||||
})
|
||||
}
|
||||
|
||||
// AdminOrders 订单管理
|
||||
func AdminOrders(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{
|
||||
"title": "订单管理",
|
||||
})
|
||||
}
|
||||
|
||||
// AdminWishes 许愿管理
|
||||
func AdminWishes(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{
|
||||
"title": "许愿管理",
|
||||
})
|
||||
}
|
||||
|
||||
// AdminSettings 系统设置
|
||||
func AdminSettings(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{
|
||||
"title": "系统设置",
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// GetOrderList 获取订单列表
|
||||
func GetOrderList(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"list": []interface{}{},
|
||||
"total": 0,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetOrderDetail 获取订单详情
|
||||
func GetOrderDetail(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"id": id,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// CancelOrder 取消订单
|
||||
func CancelOrder(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"id": id,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// CreateOrder 创建订单
|
||||
func CreateOrder(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"orderId": "example-order-id",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// PayNotify 支付回调
|
||||
func PayNotify(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": "SUCCESS",
|
||||
"msg": "OK",
|
||||
})
|
||||
}
|
||||
|
||||
// GetPayStatus 获取支付状态
|
||||
func GetPayStatus(c *gin.Context) {
|
||||
orderId := c.Param("orderId")
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"orderId": orderId,
|
||||
"status": "pending",
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// UserLogin 用户登录
|
||||
func UserLogin(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"token": "example-token",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// UserLogout 用户登出
|
||||
func UserLogout(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
})
|
||||
}
|
||||
|
||||
// GetUserProfile 获取用户资料
|
||||
func GetUserProfile(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"id": 1,
|
||||
"nickname": "用户昵称",
|
||||
"avatar": "",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateUserProfile 更新用户资料
|
||||
func UpdateUserProfile(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
})
|
||||
}
|
||||
|
||||
// WechatAuth 微信授权
|
||||
func WechatAuth(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"openid": "example-openid",
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// GetWishTree 获取许愿树
|
||||
func GetWishTree(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"tree": gin.H{
|
||||
"id": 1,
|
||||
"name": "许愿树",
|
||||
"wishes": []interface{}{},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// CreateWish 创建许愿
|
||||
func CreateWish(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"id": 1,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetWishList 获取许愿列表
|
||||
func GetWishList(c *gin.Context) {
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"list": []interface{}{},
|
||||
"total": 0,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteWish 删除许愿
|
||||
func DeleteWish(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"id": id,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// CORS 跨域中间件
|
||||
func CORS() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
|
||||
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// Logger 日志中间件
|
||||
func Logger() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// Auth 认证中间件
|
||||
func Auth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
token := c.GetHeader("Authorization")
|
||||
if token == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"code": 401,
|
||||
"msg": "unauthorized",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Order 订单模型
|
||||
type Order struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
OrderNo string `gorm:"uniqueIndex;size:32" json:"orderNo"` // 订单号
|
||||
UserID uint `gorm:"index" json:"userId"`
|
||||
Type string `gorm:"size:20" json:"type"` // wish:许愿 vip:会员
|
||||
ProductID uint `json:"productId"`
|
||||
ProductName string `gorm:"size:100" json:"productName"`
|
||||
Amount int `json:"amount"` // 金额(分)
|
||||
Status string `gorm:"size:20;default:'pending'" json:"status"` // pending:待支付 paid:已支付 cancelled:已取消 refunded:已退款
|
||||
PayTime *time.Time `json:"payTime"`
|
||||
ExpireTime *time.Time `json:"expireTime"`
|
||||
Remark string `gorm:"size:255" json:"remark"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// OrderItem 订单项
|
||||
type OrderItem struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
OrderID uint `gorm:"index" json:"orderId"`
|
||||
ProductID uint `json:"productId"`
|
||||
Name string `gorm:"size:100" json:"name"`
|
||||
Price int `json:"price"` // 单价(分)
|
||||
Quantity int `json:"quantity"` // 数量
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// User 用户模型
|
||||
type User struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
OpenID string `gorm:"uniqueIndex;size:64" json:"openId"`
|
||||
UnionID string `gorm:"size:64" json:"unionId"`
|
||||
Nickname string `gorm:"size:64" json:"nickname"`
|
||||
Avatar string `gorm:"size:255" json:"avatar"`
|
||||
Gender int `gorm:"default:0" json:"gender"` // 0:未知 1:男 2:女
|
||||
Phone string `gorm:"size:20" json:"phone"`
|
||||
Email string `gorm:"size:100" json:"email"`
|
||||
Status int `gorm:"default:1" json:"status"` // 1:正常 0:禁用
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// UserProfile 用户资料扩展
|
||||
type UserProfile struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
UserID uint `gorm:"index" json:"userId"`
|
||||
RealName string `gorm:"size:32" json:"realName"`
|
||||
Birthday string `gorm:"size:10" json:"birthday"` // YYYY-MM-DD
|
||||
BirthTime string `gorm:"size:8" json:"birthTime"` // HH:mm:ss
|
||||
Gender int `gorm:"default:0" json:"gender"`
|
||||
Zodiac string `gorm:"size:10" json:"zodiac"` // 生肖
|
||||
Constellation string `gorm:"size:20" json:"constellation"` // 星座
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Wish 许愿模型
|
||||
type Wish struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
UserID uint `gorm:"index" json:"userId"`
|
||||
TreeID uint `gorm:"index" json:"treeId"` // 许愿树ID
|
||||
Content string `gorm:"size:500" json:"content"` // 许愿内容
|
||||
Type string `gorm:"size:20;default:'free'" json:"type"` // free:免费 paid:付费
|
||||
Position int `gorm:"default:0" json:"position"` // 位置(用于排序/覆盖)
|
||||
Status int `gorm:"default:1" json:"status"` // 1:正常 0:隐藏/删除
|
||||
IsRobot bool `gorm:"default:false" json:"isRobot"` // 是否机器人发布
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// WishTree 许愿树模型
|
||||
type WishTree struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"size:50" json:"name"`
|
||||
Description string `gorm:"size:255" json:"description"`
|
||||
MaxWishes int `gorm:"default:100" json:"maxWishes"` // 最大许愿条数
|
||||
Status int `gorm:"default:1" json:"status"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// WishProduct 许愿商品(付费许愿)
|
||||
type WishProduct struct {
|
||||
ID uint `gorm:"primaryKey" json:"id"`
|
||||
Name string `gorm:"size:50" json:"name"` // 商品名称
|
||||
Description string `gorm:"size:255" json:"description"`
|
||||
Price int `json:"price"` // 价格(分)
|
||||
Duration int `json:"duration"` // 展示时长(天)
|
||||
Position int `json:"position"` // 优先位置
|
||||
Status int `gorm:"default:1" json:"status"`
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/gouki/lunar-server/internal/model"
|
||||
)
|
||||
|
||||
// UserService 用户服务
|
||||
type UserService struct{}
|
||||
|
||||
// GetUserByOpenID 根据OpenID获取用户
|
||||
func (s *UserService) GetUserByOpenID(openID string) (*model.User, error) {
|
||||
// TODO: 实现数据库查询
|
||||
return &model.User{}, nil
|
||||
}
|
||||
|
||||
// CreateUser 创建用户
|
||||
func (s *UserService) CreateUser(user *model.User) error {
|
||||
// TODO: 实现数据库创建
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateUser 更新用户
|
||||
func (s *UserService) UpdateUser(user *model.User) error {
|
||||
// TODO: 实现数据库更新
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"github.com/gouki/lunar-server/internal/model"
|
||||
)
|
||||
|
||||
// WishService 许愿服务
|
||||
type WishService struct{}
|
||||
|
||||
// GetWishTree 获取许愿树
|
||||
func (s *WishService) GetWishTree(treeID uint) (*model.WishTree, error) {
|
||||
// TODO: 实现数据库查询
|
||||
return &model.WishTree{}, nil
|
||||
}
|
||||
|
||||
// CreateWish 创建许愿
|
||||
func (s *WishService) CreateWish(wish *model.Wish) error {
|
||||
// TODO: 实现数据库创建
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetWishList 获取许愿列表
|
||||
func (s *WishService) GetWishList(treeID uint, page, pageSize int) ([]*model.Wish, int64, error) {
|
||||
// TODO: 实现数据库查询
|
||||
return []*model.Wish{}, 0, nil
|
||||
}
|
||||
|
||||
// DeleteWish 删除许愿
|
||||
func (s *WishService) DeleteWish(id uint) error {
|
||||
// TODO: 实现数据库删除
|
||||
return nil
|
||||
}
|
||||
|
||||
// CreateRobotWish 创建机器人许愿
|
||||
func (s *WishService) CreateRobotWish(treeID uint) error {
|
||||
// TODO: 实现机器人自动许愿
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user