feat: 添加许愿树功能和Go后端服务

- 新增许愿树页面(Canvas绘制、许愿条展示)
- 新增许愿详情页面
- 新增许愿创建弹窗(免费/付费两种模式)
- 新增网络请求封装 utils/request.js
- 新增Go后端项目结构(Gin + Inertia.js)
- 新增用户、订单、许愿数据模型
- 新增数据库迁移脚本
- 更新.gitignore补全忽略规则
- 更新.env.local配置(robot=2, 版本1.0.1)
- 添加secrets目录说明文档
This commit is contained in:
gouki
2026-08-06 12:26:29 +00:00
parent 9c178e6d49
commit 11bfc15141
31 changed files with 2025 additions and 1 deletions
+96
View File
@@ -0,0 +1,96 @@
# 祈福小助手后端服务
基于 Go + Gin + Inertia.js 的后端服务。
## 功能模块
- **用户模块** — 微信登录、资料修改、授权管理
- **支付模块** — 微信支付集成、订单管理
- **许愿树** — 许愿发布、付费许愿、机器人自动许愿
- **管理后台** — 基于 Inertia.js 的前后端分离管理界面
## 技术栈
- **后端**: Go 1.22 + Gin + GORM
- **前端**: Inertia.js + React/Vue(待选择)
- **数据库**: MySQL
- **缓存**: Redis
## 快速开始
### 开发环境
```bash
# 安装依赖
go mod download
# 启动开发服务器
./scripts/dev.sh
```
### 生产构建
```bash
# 构建
./scripts/build.sh
# 运行
./bin/lunar-server
```
## 项目结构
```
server/
├── cmd/ # 入口文件
│ └── main.go
├── internal/ # 内部包
│ ├── config/ # 配置
│ ├── handler/ # 处理器
│ ├── middleware/ # 中间件
│ ├── model/ # 数据模型
│ └── service/ # 业务逻辑
├── web/ # Inertia.js 前端
│ ├── src/
│ └── public/
├── migrations/ # 数据库迁移
├── scripts/ # 脚本
└── go.mod
```
## API 接口
### 用户接口
- `POST /api/user/login` — 用户登录
- `POST /api/user/logout` — 用户登出
- `GET /api/user/profile` — 获取用户资料
- `PUT /api/user/profile` — 更新用户资料
- `POST /api/user/auth` — 微信授权
### 支付接口
- `POST /api/pay/create` — 创建订单
- `POST /api/pay/notify` — 支付回调
- `GET /api/pay/status/:orderId` — 查询支付状态
### 订单接口
- `GET /api/order/list` — 订单列表
- `GET /api/order/detail/:id` — 订单详情
- `POST /api/order/cancel/:id` — 取消订单
### 许愿接口
- `GET /api/wish/tree` — 获取许愿树
- `POST /api/wish/create` — 创建许愿
- `GET /api/wish/list` — 许愿列表
- `DELETE /api/wish/:id` — 删除许愿
## 环境变量
参考 `.env.local` 文件配置。
## 数据库
执行 `migrations/001_init.sql` 初始化数据库。
+94
View File
@@ -0,0 +1,94 @@
package main
import (
"log"
"os"
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
"github.com/gouki/lunar-server/internal/config"
"github.com/gouki/lunar-server/internal/handler"
"github.com/gouki/lunar-server/internal/middleware"
)
func main() {
// 加载环境变量
if err := godotenv.Load("../.env.local"); err != nil {
log.Println("No .env.local file found, using system environment")
}
// 初始化配置
cfg := config.Load()
// 设置 Gin 模式
if cfg.Server.Env == "production" {
gin.SetMode(gin.ReleaseMode)
}
// 创建路由
r := gin.Default()
// 中间件
r.Use(middleware.CORS())
r.Use(middleware.Logger())
// 静态文件(Inertia.js 前端构建产物)
r.Static("/build", "./web/public/build")
r.LoadHTMLGlob("web/*.html")
// API 路由
api := r.Group("/api")
{
// 用户相关
user := api.Group("/user")
{
user.POST("/login", handler.UserLogin)
user.POST("/logout", handler.UserLogout)
user.GET("/profile", middleware.Auth(), handler.GetUserProfile)
user.PUT("/profile", middleware.Auth(), handler.UpdateUserProfile)
user.POST("/auth", handler.WechatAuth)
}
// 支付相关
pay := api.Group("/pay")
{
pay.POST("/create", middleware.Auth(), handler.CreateOrder)
pay.POST("/notify", handler.PayNotify)
pay.GET("/status/:orderId", middleware.Auth(), handler.GetPayStatus)
}
// 订单管理
order := api.Group("/order")
{
order.GET("/list", middleware.Auth(), handler.GetOrderList)
order.GET("/detail/:id", middleware.Auth(), handler.GetOrderDetail)
order.POST("/cancel/:id", middleware.Auth(), handler.CancelOrder)
}
// 许愿树
wish := api.Group("/wish")
{
wish.GET("/tree", handler.GetWishTree)
wish.POST("/create", middleware.Auth(), handler.CreateWish)
wish.GET("/list", handler.GetWishList)
wish.DELETE("/:id", middleware.Auth(), handler.DeleteWish)
}
}
// 管理后台(Inertia.js
admin := r.Group("/admin")
{
admin.GET("/", handler.AdminIndex)
admin.GET("/users", handler.AdminUsers)
admin.GET("/orders", handler.AdminOrders)
admin.GET("/wishes", handler.AdminWishes)
admin.GET("/settings", handler.AdminSettings)
}
// 启动服务器
addr := ":" + cfg.Server.Port
log.Printf("Server starting on %s", addr)
if err := r.Run(addr); err != nil {
log.Fatal("Failed to start server:", err)
}
}
+41
View File
@@ -0,0 +1,41 @@
module github.com/gouki/lunar-server
go 1.22
require (
github.com/gin-gonic/gin v1.9.1
github.com/go-inertia/inertia-go v1.0.0
github.com/golang-jwt/jwt/v5 v5.2.0
github.com/go-sql-driver/mysql v1.7.1
github.com/joho/godotenv v1.5.1
github.com/redis/go-redis/v9 v9.4.0
golang.org/x/crypto v0.17.0
gorm.io/gorm v1.25.5
gorm.io/driver/mysql v1.5.2
)
require (
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
+82
View File
@@ -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
}
+42
View File
@@ -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": "系统设置",
})
}
+43
View File
@@ -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,
},
})
}
+39
View File
@@ -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",
},
})
}
+58
View File
@@ -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",
},
})
}
+57
View File
@@ -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,
},
})
}
+46
View File
@@ -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()
}
}
+32
View File
@@ -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"` // 数量
}
+34
View File
@@ -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"`
}
+41
View File
@@ -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"`
}
+26
View File
@@ -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
}
+38
View File
@@ -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
}
+104
View File
@@ -0,0 +1,104 @@
-- 用户表
CREATE TABLE IF NOT EXISTS users (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
open_id VARCHAR(64) NOT NULL UNIQUE COMMENT '微信OpenID',
union_id VARCHAR(64) DEFAULT '' COMMENT '微信UnionID',
nickname VARCHAR(64) DEFAULT '' COMMENT '昵称',
avatar VARCHAR(255) DEFAULT '' COMMENT '头像',
gender TINYINT DEFAULT 0 COMMENT '性别 0:未知 1:男 2:女',
phone VARCHAR(20) DEFAULT '' COMMENT '手机号',
email VARCHAR(100) DEFAULT '' COMMENT '邮箱',
status TINYINT DEFAULT 1 COMMENT '状态 1:正常 0:禁用',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_open_id (open_id),
INDEX idx_status (status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
-- 用户资料表
CREATE TABLE IF NOT EXISTS user_profiles (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
real_name VARCHAR(32) DEFAULT '' COMMENT '真实姓名',
birthday VARCHAR(10) DEFAULT '' COMMENT '生日 YYYY-MM-DD',
birth_time VARCHAR(8) DEFAULT '' COMMENT '出生时间 HH:mm:ss',
gender TINYINT DEFAULT 0 COMMENT '性别',
zodiac VARCHAR(10) DEFAULT '' COMMENT '生肖',
constellation VARCHAR(20) DEFAULT '' COMMENT '星座',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户资料表';
-- 订单表
CREATE TABLE IF NOT EXISTS orders (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
order_no VARCHAR(32) NOT NULL UNIQUE COMMENT '订单号',
user_id BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
type VARCHAR(20) NOT NULL COMMENT '订单类型 wish:许愿 vip:会员',
product_id BIGINT UNSIGNED DEFAULT 0 COMMENT '商品ID',
product_name VARCHAR(100) DEFAULT '' COMMENT '商品名称',
amount INT NOT NULL DEFAULT 0 COMMENT '金额(分)',
status VARCHAR(20) DEFAULT 'pending' COMMENT '状态 pending:待支付 paid:已支付 cancelled:已取消 refunded:已退款',
pay_time TIMESTAMP NULL COMMENT '支付时间',
expire_time TIMESTAMP NULL COMMENT '过期时间',
remark VARCHAR(255) DEFAULT '' COMMENT '备注',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_order_no (order_no),
INDEX idx_user_id (user_id),
INDEX idx_status (status),
INDEX idx_type (type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
-- 许愿树表
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 '描述',
max_wishes INT DEFAULT 100 COMMENT '最大许愿条数',
status TINYINT DEFAULT 1 COMMENT '状态',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='许愿树表';
-- 许愿表
CREATE TABLE IF NOT EXISTS wishes (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT UNSIGNED NOT NULL COMMENT '用户ID',
tree_id BIGINT UNSIGNED NOT NULL COMMENT '许愿树ID',
content VARCHAR(500) NOT NULL COMMENT '许愿内容',
type VARCHAR(20) DEFAULT 'free' COMMENT '类型 free:免费 paid:付费',
position INT DEFAULT 0 COMMENT '位置',
status TINYINT DEFAULT 1 COMMENT '状态 1:正常 0:隐藏/删除',
is_robot TINYINT DEFAULT 0 COMMENT '是否机器人发布',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user_id (user_id),
INDEX idx_tree_id (tree_id),
INDEX idx_status (status),
INDEX idx_type (type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='许愿表';
-- 许愿商品表
CREATE TABLE IF NOT EXISTS wish_products (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL COMMENT '商品名称',
description VARCHAR(255) DEFAULT '' COMMENT '描述',
price INT NOT NULL DEFAULT 0 COMMENT '价格(分)',
duration INT DEFAULT 7 COMMENT '展示时长(天)',
position 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
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='许愿商品表';
-- 插入默认许愿树
INSERT IGNORE INTO wish_trees (id, name, description, max_wishes) VALUES
(1, '祈福许愿树', '许下美好愿望,祈福平安顺遂', 100);
-- 插入默认许愿商品
INSERT IGNORE INTO wish_products (id, name, description, price, duration, position) VALUES
(1, '普通许愿条', '基础许愿条,展示7天', 100, 7, 0),
(2, '精品许愿条', '精品许愿条,展示30天,优先位置', 500, 30, 10),
(3, '至尊许愿条', '至尊许愿条,展示90天,置顶显示', 2000, 90, 100);
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
# 生产环境构建脚本
echo "Building Lunar Server..."
# 创建输出目录
mkdir -p bin
# 编译 Go 后端
echo "Building Go backend..."
go build -o bin/lunar-server cmd/main.go
# 编译前端(如果存在)
if [ -d "web" ]; then
echo "Building frontend..."
cd web
npm install
npm run build
cd ..
fi
echo "Build complete!"
echo "Output: bin/lunar-server"
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
# 开发环境启动脚本
echo "Starting Lunar Server..."
# 检查 .env.local 文件
if [ ! -f "../.env.local" ]; then
echo "Warning: .env.local not found, using default configuration"
fi
# 启动服务器
go run cmd/main.go