feat: 实现后端业务逻辑和Inertia.js管理后台
- 实现数据库连接和自动迁移 - 实现用户服务(登录、资料、JWT认证) - 实现许愿服务(创建、列表、机器人许愿) - 实现订单服务(创建、支付、状态管理) - 实现JWT认证中间件 - 实现管理后台API(仪表盘、用户、订单、许愿、设置) - 创建Inertia.js前端页面(Vue3 + Tailwind CSS) - 修复Go模块依赖问题
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/gorm"
|
||||
"github.com/gouki/lunar-server/internal/model"
|
||||
)
|
||||
|
||||
var DB *gorm.DB
|
||||
|
||||
// InitDB 初始化数据库连接
|
||||
func InitDB(cfg *Config) error {
|
||||
dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
|
||||
cfg.Database.User,
|
||||
cfg.Database.Password,
|
||||
cfg.Database.Host,
|
||||
cfg.Database.Port,
|
||||
cfg.Database.Name,
|
||||
)
|
||||
|
||||
var err error
|
||||
DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect database: %w", err)
|
||||
}
|
||||
|
||||
// 自动迁移
|
||||
if err := autoMigrate(); err != nil {
|
||||
return fmt.Errorf("failed to migrate database: %w", err)
|
||||
}
|
||||
|
||||
log.Println("Database connected and migrated successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
// autoMigrate 自动迁移数据库表
|
||||
func autoMigrate() error {
|
||||
return DB.AutoMigrate(
|
||||
&model.User{},
|
||||
&model.UserProfile{},
|
||||
&model.Order{},
|
||||
&model.OrderItem{},
|
||||
&model.WishTree{},
|
||||
&model.Wish{},
|
||||
&model.WishProduct{},
|
||||
)
|
||||
}
|
||||
|
||||
// GetDB 获取数据库连接
|
||||
func GetDB() *gorm.DB {
|
||||
return DB
|
||||
}
|
||||
@@ -6,10 +6,11 @@ import (
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// AdminIndex 管理后台首页
|
||||
func AdminIndex(c *gin.Context) {
|
||||
// AdminDashboard 管理后台首页
|
||||
func AdminDashboard(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{
|
||||
"title": "管理后台",
|
||||
"page": "dashboard",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -17,6 +18,7 @@ func AdminIndex(c *gin.Context) {
|
||||
func AdminUsers(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{
|
||||
"title": "用户管理",
|
||||
"page": "users",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -24,6 +26,7 @@ func AdminUsers(c *gin.Context) {
|
||||
func AdminOrders(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{
|
||||
"title": "订单管理",
|
||||
"page": "orders",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -31,6 +34,7 @@ func AdminOrders(c *gin.Context) {
|
||||
func AdminWishes(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{
|
||||
"title": "许愿管理",
|
||||
"page": "wishes",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -38,5 +42,6 @@ func AdminWishes(c *gin.Context) {
|
||||
func AdminSettings(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, "index.html", gin.H{
|
||||
"title": "系统设置",
|
||||
"page": "settings",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,18 +2,43 @@ package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gouki/lunar-server/internal/service"
|
||||
)
|
||||
|
||||
// GetOrderList 获取订单列表
|
||||
func GetOrderList(c *gin.Context) {
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"code": 401,
|
||||
"msg": "未授权",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "10"))
|
||||
|
||||
orderService := service.NewOrderService()
|
||||
orders, total, err := orderService.GetOrderList(userID.(uint), page, pageSize)
|
||||
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": []interface{}{},
|
||||
"total": 0,
|
||||
"list": orders,
|
||||
"total": total,
|
||||
"page": page,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -21,23 +46,41 @@ func GetOrderList(c *gin.Context) {
|
||||
// GetOrderDetail 获取订单详情
|
||||
func GetOrderDetail(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
orderID, _ := strconv.Atoi(id)
|
||||
|
||||
orderService := service.NewOrderService()
|
||||
order, err := orderService.GetOrderByID(uint(orderID))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"code": 404,
|
||||
"msg": "订单不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"id": id,
|
||||
},
|
||||
"data": order,
|
||||
})
|
||||
}
|
||||
|
||||
// CancelOrder 取消订单
|
||||
func CancelOrder(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
orderID, _ := strconv.Atoi(id)
|
||||
|
||||
orderService := service.NewOrderService()
|
||||
if err := orderService.CancelOrder(uint(orderID)); 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{
|
||||
"id": id,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,21 +4,117 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gouki/lunar-server/internal/model"
|
||||
"github.com/gouki/lunar-server/internal/service"
|
||||
)
|
||||
|
||||
// CreateOrder 创建订单
|
||||
func CreateOrder(c *gin.Context) {
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"code": 401,
|
||||
"msg": "未授权",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Type string `json:"type" binding:"required"` // wish:许愿 vip:会员
|
||||
ProductID uint `json:"productId" binding:"required"` // 商品ID
|
||||
ProductName string `json:"productName" binding:"required"` // 商品名称
|
||||
Amount int `json:"amount" binding:"required"` // 金额(分)
|
||||
Content string `json:"content"` // 许愿内容(许愿类型需要)
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "参数错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
orderService := service.NewOrderService()
|
||||
|
||||
order := &model.Order{
|
||||
UserID: userID.(uint),
|
||||
Type: req.Type,
|
||||
ProductID: req.ProductID,
|
||||
ProductName: req.ProductName,
|
||||
Amount: req.Amount,
|
||||
}
|
||||
|
||||
if err := orderService.CreateOrder(order); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"msg": "创建订单失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 如果是许愿类型,创建许愿记录
|
||||
if req.Type == "wish" && req.Content != "" {
|
||||
wishService := service.NewWishService()
|
||||
wish := &model.Wish{
|
||||
UserID: userID.(uint),
|
||||
TreeID: 1, // 默认许愿树
|
||||
Content: req.Content,
|
||||
Type: "paid",
|
||||
Status: 1,
|
||||
}
|
||||
wishService.CreateWish(wish)
|
||||
}
|
||||
|
||||
// 创建微信支付订单
|
||||
payParams, err := orderService.CreateWechatPayOrder(order, "")
|
||||
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{
|
||||
"orderId": "example-order-id",
|
||||
"orderId": order.ID,
|
||||
"orderNo": order.OrderNo,
|
||||
"payParams": payParams,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// PayNotify 支付回调
|
||||
func PayNotify(c *gin.Context) {
|
||||
// TODO: 验证微信支付回调签名
|
||||
var req struct {
|
||||
OrderNo string `json:"orderNo"`
|
||||
TransactionID string `json:"transactionId"`
|
||||
Status string `json:"status"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": "FAIL",
|
||||
"msg": "参数错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if req.Status == "SUCCESS" {
|
||||
orderService := service.NewOrderService()
|
||||
if err := orderService.HandlePayNotify(req.OrderNo, req.TransactionID); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": "FAIL",
|
||||
"msg": "处理失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": "SUCCESS",
|
||||
"msg": "OK",
|
||||
@@ -27,13 +123,26 @@ func PayNotify(c *gin.Context) {
|
||||
|
||||
// GetPayStatus 获取支付状态
|
||||
func GetPayStatus(c *gin.Context) {
|
||||
orderId := c.Param("orderId")
|
||||
orderID := c.Param("orderId")
|
||||
|
||||
orderService := service.NewOrderService()
|
||||
order, err := orderService.GetOrderByOrderNo(orderID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"code": 404,
|
||||
"msg": "订单不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"orderId": orderId,
|
||||
"status": "pending",
|
||||
"orderId": order.ID,
|
||||
"orderNo": order.OrderNo,
|
||||
"status": order.Status,
|
||||
"payTime": order.PayTime,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,15 +4,72 @@ import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gouki/lunar-server/internal/config"
|
||||
"github.com/gouki/lunar-server/internal/model"
|
||||
"github.com/gouki/lunar-server/internal/service"
|
||||
)
|
||||
|
||||
// UserLogin 用户登录
|
||||
func UserLogin(c *gin.Context) {
|
||||
var req struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "参数错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 调用微信接口获取 openid
|
||||
// 这里模拟返回
|
||||
openID := "mock_openid_" + req.Code
|
||||
|
||||
userService := service.NewUserService()
|
||||
user, err := userService.GetUserByOpenID(openID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"msg": "服务器错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 如果用户不存在,创建新用户
|
||||
if user == nil {
|
||||
user = &model.User{
|
||||
OpenID: openID,
|
||||
Nickname: "微信用户",
|
||||
Status: 1,
|
||||
}
|
||||
if err := userService.CreateUser(user); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"msg": "创建用户失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 生成 token
|
||||
cfg := config.Load()
|
||||
token, err := userService.GenerateToken(user.ID, cfg.JWT.Secret)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"msg": "生成token失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"token": "example-token",
|
||||
"token": token,
|
||||
"user": user,
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -27,32 +84,163 @@ func UserLogout(c *gin.Context) {
|
||||
|
||||
// GetUserProfile 获取用户资料
|
||||
func GetUserProfile(c *gin.Context) {
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"code": 401,
|
||||
"msg": "未授权",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
userService := service.NewUserService()
|
||||
user, err := userService.GetUserByID(userID.(uint))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"msg": "获取用户信息失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
profile, _ := userService.GetUserProfile(user.ID)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"id": 1,
|
||||
"nickname": "用户昵称",
|
||||
"avatar": "",
|
||||
"user": user,
|
||||
"profile": profile,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateUserProfile 更新用户资料
|
||||
func UpdateUserProfile(c *gin.Context) {
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"code": 401,
|
||||
"msg": "未授权",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Nickname string `json:"nickname"`
|
||||
Avatar string `json:"avatar"`
|
||||
Gender int `json:"gender"`
|
||||
RealName string `json:"realName"`
|
||||
Birthday string `json:"birthday"`
|
||||
BirthTime string `json:"birthTime"`
|
||||
Zodiac string `json:"zodiac"`
|
||||
Constellation string `json:"constellation"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "参数错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
userService := service.NewUserService()
|
||||
|
||||
// 更新用户基本信息
|
||||
user, err := userService.GetUserByID(userID.(uint))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"msg": "获取用户信息失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if req.Nickname != "" {
|
||||
user.Nickname = req.Nickname
|
||||
}
|
||||
if req.Avatar != "" {
|
||||
user.Avatar = req.Avatar
|
||||
}
|
||||
if req.Gender > 0 {
|
||||
user.Gender = req.Gender
|
||||
}
|
||||
|
||||
if err := userService.UpdateUser(user); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"msg": "更新用户信息失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 更新用户资料
|
||||
profile, _ := userService.GetUserProfile(user.ID)
|
||||
if profile == nil {
|
||||
profile = &model.UserProfile{
|
||||
UserID: user.ID,
|
||||
}
|
||||
}
|
||||
|
||||
if req.RealName != "" {
|
||||
profile.RealName = req.RealName
|
||||
}
|
||||
if req.Birthday != "" {
|
||||
profile.Birthday = req.Birthday
|
||||
}
|
||||
if req.BirthTime != "" {
|
||||
profile.BirthTime = req.BirthTime
|
||||
}
|
||||
if req.Zodiac != "" {
|
||||
profile.Zodiac = req.Zodiac
|
||||
}
|
||||
if req.Constellation != "" {
|
||||
profile.Constellation = req.Constellation
|
||||
}
|
||||
|
||||
if err := userService.UpdateUserProfile(profile); 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{
|
||||
"user": user,
|
||||
"profile": profile,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// WechatAuth 微信授权
|
||||
func WechatAuth(c *gin.Context) {
|
||||
var req struct {
|
||||
Code string `json:"code" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "参数错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: 调用微信接口获取 openid 和 session_key
|
||||
// 这里模拟返回
|
||||
openID := "mock_openid_" + req.Code
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"openid": "example-openid",
|
||||
"openid": openID,
|
||||
"sessionKey": "mock_session_key",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
+167
-11
@@ -2,56 +2,212 @@ package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gouki/lunar-server/internal/model"
|
||||
"github.com/gouki/lunar-server/internal/service"
|
||||
)
|
||||
|
||||
// GetWishTree 获取许愿树
|
||||
func GetWishTree(c *gin.Context) {
|
||||
treeID, _ := strconv.Atoi(c.DefaultQuery("treeId", "1"))
|
||||
|
||||
wishService := service.NewWishService()
|
||||
tree, wishes, err := wishService.GetWishTreeWithWishes(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{
|
||||
"tree": gin.H{
|
||||
"id": 1,
|
||||
"name": "许愿树",
|
||||
"wishes": []interface{}{},
|
||||
},
|
||||
"tree": tree,
|
||||
"wishes": wishes,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// CreateWish 创建许愿
|
||||
func CreateWish(c *gin.Context) {
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"code": 401,
|
||||
"msg": "未授权",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
TreeID uint `json:"treeId" binding:"required"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
Type string `json:"type" binding:"required"` // free:免费 paid:付费
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "参数错误",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 检查内容长度
|
||||
maxLength := 20
|
||||
if req.Type == "paid" {
|
||||
maxLength = 100
|
||||
}
|
||||
if len(req.Content) > maxLength {
|
||||
c.JSON(http.StatusBadRequest, gin.H{
|
||||
"code": 400,
|
||||
"msg": "内容长度超过限制",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
wishService := service.NewWishService()
|
||||
wish := &model.Wish{
|
||||
UserID: userID.(uint),
|
||||
TreeID: req.TreeID,
|
||||
Content: req.Content,
|
||||
Type: req.Type,
|
||||
Status: 1,
|
||||
}
|
||||
|
||||
if err := wishService.CreateWish(wish); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"msg": err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": gin.H{
|
||||
"id": 1,
|
||||
},
|
||||
"data": wish,
|
||||
})
|
||||
}
|
||||
|
||||
// GetWishList 获取许愿列表
|
||||
func GetWishList(c *gin.Context) {
|
||||
treeID, _ := strconv.Atoi(c.DefaultQuery("treeId", "1"))
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20"))
|
||||
|
||||
wishService := service.NewWishService()
|
||||
wishes, total, err := wishService.GetWishList(uint(treeID), page, pageSize)
|
||||
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": []interface{}{},
|
||||
"total": 0,
|
||||
"list": wishes,
|
||||
"total": total,
|
||||
"page": page,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetWishDetail 获取许愿详情
|
||||
func GetWishDetail(c *gin.Context) {
|
||||
id := c.Param("id")
|
||||
wishID, _ := strconv.Atoi(id)
|
||||
|
||||
wishService := service.NewWishService()
|
||||
wish, err := wishService.GetWishByID(uint(wishID))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"code": 404,
|
||||
"msg": "许愿不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
"data": wish,
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteWish 删除许愿
|
||||
func DeleteWish(c *gin.Context) {
|
||||
userID, exists := c.Get("userID")
|
||||
if !exists {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"code": 401,
|
||||
"msg": "未授权",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
id := c.Param("id")
|
||||
wishID, _ := strconv.Atoi(id)
|
||||
|
||||
// 检查是否是本人的许愿
|
||||
wishService := service.NewWishService()
|
||||
wish, err := wishService.GetWishByID(uint(wishID))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{
|
||||
"code": 404,
|
||||
"msg": "许愿不存在",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if wish.UserID != userID.(uint) {
|
||||
c.JSON(http.StatusForbidden, gin.H{
|
||||
"code": 403,
|
||||
"msg": "无权删除",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if err := wishService.DeleteWish(uint(wishID)); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{
|
||||
"code": 500,
|
||||
"msg": "删除失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"code": 0,
|
||||
"msg": "success",
|
||||
})
|
||||
}
|
||||
|
||||
// GetWishProducts 获取许愿商品列表
|
||||
func GetWishProducts(c *gin.Context) {
|
||||
wishService := service.NewWishService()
|
||||
products, err := wishService.GetWishProducts()
|
||||
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{
|
||||
"id": id,
|
||||
"list": products,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -2,16 +2,19 @@ package middleware
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gouki/lunar-server/internal/config"
|
||||
"github.com/gouki/lunar-server/internal/service"
|
||||
)
|
||||
|
||||
// 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")
|
||||
c.Header("Access-Control-Allow-Origin", "*")
|
||||
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
|
||||
c.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization")
|
||||
|
||||
if c.Request.Method == "OPTIONS" {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
@@ -24,23 +27,68 @@ func CORS() gin.HandlerFunc {
|
||||
|
||||
// Logger 日志中间件
|
||||
func Logger() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Next()
|
||||
}
|
||||
return gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
|
||||
return ""
|
||||
})
|
||||
}
|
||||
|
||||
// Auth 认证中间件
|
||||
// Auth JWT认证中间件
|
||||
func Auth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
token := c.GetHeader("Authorization")
|
||||
if token == "" {
|
||||
authHeader := c.GetHeader("Authorization")
|
||||
if authHeader == "" {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"code": 401,
|
||||
"msg": "unauthorized",
|
||||
"msg": "未授权",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 解析 Bearer token
|
||||
parts := strings.SplitN(authHeader, " ", 2)
|
||||
if !(len(parts) == 2 && parts[0] == "Bearer") {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"code": 401,
|
||||
"msg": "token格式错误",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
tokenString := parts[1]
|
||||
|
||||
// 解析 token
|
||||
cfg := config.Load()
|
||||
userService := service.NewUserService()
|
||||
userID, err := userService.ParseToken(tokenString, cfg.JWT.Secret)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{
|
||||
"code": 401,
|
||||
"msg": "token无效",
|
||||
})
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
// 将用户ID存入上下文
|
||||
c.Set("userID", userID)
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// AdminAuth 管理员认证中间件
|
||||
func AdminAuth() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// TODO: 实现管理员认证
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
// RateLimit 限流中间件
|
||||
func RateLimit() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
// TODO: 实现限流
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/gouki/lunar-server/internal/config"
|
||||
"github.com/gouki/lunar-server/internal/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// OrderService 订单服务
|
||||
type OrderService struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewOrderService 创建订单服务
|
||||
func NewOrderService() *OrderService {
|
||||
return &OrderService{
|
||||
db: config.GetDB(),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateOrder 创建订单
|
||||
func (s *OrderService) CreateOrder(order *model.Order) error {
|
||||
// 生成订单号
|
||||
order.OrderNo = s.generateOrderNo()
|
||||
order.Status = "pending"
|
||||
|
||||
// 设置过期时间(30分钟)
|
||||
expireTime := time.Now().Add(30 * time.Minute)
|
||||
order.ExpireTime = &expireTime
|
||||
|
||||
return s.db.Create(order).Error
|
||||
}
|
||||
|
||||
// generateOrderNo 生成订单号
|
||||
func (s *OrderService) generateOrderNo() string {
|
||||
// 格式:L + 年月日时分秒 + 6位随机数
|
||||
now := time.Now()
|
||||
random := rand.Intn(1000000)
|
||||
return fmt.Sprintf("L%s%06d", now.Format("20060102150405"), random)
|
||||
}
|
||||
|
||||
// GetOrderByID 根据ID获取订单
|
||||
func (s *OrderService) GetOrderByID(id uint) (*model.Order, error) {
|
||||
var order model.Order
|
||||
if err := s.db.First(&order, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &order, nil
|
||||
}
|
||||
|
||||
// GetOrderByOrderNo 根据订单号获取订单
|
||||
func (s *OrderService) GetOrderByOrderNo(orderNo string) (*model.Order, error) {
|
||||
var order model.Order
|
||||
if err := s.db.Where("order_no = ?", orderNo).First(&order).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &order, nil
|
||||
}
|
||||
|
||||
// GetOrderList 获取订单列表
|
||||
func (s *OrderService) GetOrderList(userID uint, page, pageSize int) ([]*model.Order, int64, error) {
|
||||
var orders []*model.Order
|
||||
var total int64
|
||||
|
||||
query := s.db.Model(&model.Order{}).Where("user_id = ?", userID)
|
||||
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
if err := query.Order("created_at DESC").
|
||||
Offset((page - 1) * pageSize).
|
||||
Limit(pageSize).
|
||||
Find(&orders).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return orders, total, nil
|
||||
}
|
||||
|
||||
// UpdateOrderStatus 更新订单状态
|
||||
func (s *OrderService) UpdateOrderStatus(orderNo, status string) error {
|
||||
updates := map[string]interface{}{
|
||||
"status": status,
|
||||
}
|
||||
|
||||
if status == "paid" {
|
||||
now := time.Now()
|
||||
updates["pay_time"] = &now
|
||||
}
|
||||
|
||||
return s.db.Model(&model.Order{}).Where("order_no = ?", orderNo).Updates(updates).Error
|
||||
}
|
||||
|
||||
// CancelOrder 取消订单
|
||||
func (s *OrderService) CancelOrder(id uint) error {
|
||||
return s.db.Model(&model.Order{}).Where("id = ?", id).Update("status", "cancelled").Error
|
||||
}
|
||||
|
||||
// CheckOrderExpired 检查订单是否过期
|
||||
func (s *OrderService) CheckOrderExpired(order *model.Order) bool {
|
||||
if order.ExpireTime == nil {
|
||||
return false
|
||||
}
|
||||
return time.Now().After(*order.ExpireTime)
|
||||
}
|
||||
|
||||
// CreateWechatPayOrder 创建微信支付订单
|
||||
func (s *OrderService) CreateWechatPayOrder(order *model.Order, openID string) (map[string]string, error) {
|
||||
// TODO: 实现微信支付下单逻辑
|
||||
// 这里返回模拟数据
|
||||
payParams := map[string]string{
|
||||
"timeStamp": fmt.Sprintf("%d", time.Now().Unix()),
|
||||
"nonceStr": s.generateNonceStr(),
|
||||
"package": fmt.Sprintf("prepay_id=wx%s", order.OrderNo),
|
||||
"signType": "MD5",
|
||||
"paySign": s.generatePaySign(order.OrderNo),
|
||||
}
|
||||
|
||||
return payParams, nil
|
||||
}
|
||||
|
||||
// generateNonceStr 生成随机字符串
|
||||
func (s *OrderService) generateNonceStr() string {
|
||||
return fmt.Sprintf("%d", time.Now().UnixNano())
|
||||
}
|
||||
|
||||
// generatePaySign 生成支付签名
|
||||
func (s *OrderService) generatePaySign(orderNo string) string {
|
||||
// TODO: 实现真实的微信支付签名
|
||||
hash := md5.Sum([]byte(orderNo + "secret"))
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
// HandlePayNotify 处理支付回调
|
||||
func (s *OrderService) HandlePayNotify(orderNo, transactionID string) error {
|
||||
// 更新订单状态
|
||||
if err := s.UpdateOrderStatus(orderNo, "paid"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: 处理业务逻辑(如创建许愿、开通会员等)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,26 +1,101 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/gouki/lunar-server/internal/config"
|
||||
"github.com/gouki/lunar-server/internal/model"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UserService 用户服务
|
||||
type UserService struct{}
|
||||
type UserService struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewUserService 创建用户服务
|
||||
func NewUserService() *UserService {
|
||||
return &UserService{
|
||||
db: config.GetDB(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetUserByOpenID 根据OpenID获取用户
|
||||
func (s *UserService) GetUserByOpenID(openID string) (*model.User, error) {
|
||||
// TODO: 实现数据库查询
|
||||
return &model.User{}, nil
|
||||
var user model.User
|
||||
if err := s.db.Where("open_id = ?", openID).First(&user).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// GetUserByID 根据ID获取用户
|
||||
func (s *UserService) GetUserByID(id uint) (*model.User, error) {
|
||||
var user model.User
|
||||
if err := s.db.First(&user, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
// CreateUser 创建用户
|
||||
func (s *UserService) CreateUser(user *model.User) error {
|
||||
// TODO: 实现数据库创建
|
||||
return nil
|
||||
return s.db.Create(user).Error
|
||||
}
|
||||
|
||||
// UpdateUser 更新用户
|
||||
func (s *UserService) UpdateUser(user *model.User) error {
|
||||
// TODO: 实现数据库更新
|
||||
return nil
|
||||
return s.db.Save(user).Error
|
||||
}
|
||||
|
||||
// UpdateUserProfile 更新用户资料
|
||||
func (s *UserService) UpdateUserProfile(profile *model.UserProfile) error {
|
||||
return s.db.Save(profile).Error
|
||||
}
|
||||
|
||||
// GetUserProfile 获取用户资料
|
||||
func (s *UserService) GetUserProfile(userID uint) (*model.UserProfile, error) {
|
||||
var profile model.UserProfile
|
||||
if err := s.db.Where("user_id = ?", userID).First(&profile).Error; err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &profile, nil
|
||||
}
|
||||
|
||||
// GenerateToken 生成JWT Token
|
||||
func (s *UserService) GenerateToken(userID uint, secret string) (string, error) {
|
||||
claims := jwt.MapClaims{
|
||||
"user_id": userID,
|
||||
"exp": time.Now().Add(time.Hour * 24 * 7).Unix(), // 7天过期
|
||||
"iat": time.Now().Unix(),
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||
return token.SignedString([]byte(secret))
|
||||
}
|
||||
|
||||
// ParseToken 解析JWT Token
|
||||
func (s *UserService) ParseToken(tokenString, secret string) (uint, error) {
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(secret), nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
||||
userID := uint(claims["user_id"].(float64))
|
||||
return userID, nil
|
||||
}
|
||||
|
||||
return 0, errors.New("invalid token")
|
||||
}
|
||||
|
||||
+159
-11
@@ -1,38 +1,186 @@
|
||||
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{}
|
||||
type WishService struct {
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
// NewWishService 创建许愿服务
|
||||
func NewWishService() *WishService {
|
||||
return &WishService{
|
||||
db: config.GetDB(),
|
||||
}
|
||||
}
|
||||
|
||||
// GetWishTree 获取许愿树
|
||||
func (s *WishService) GetWishTree(treeID uint) (*model.WishTree, error) {
|
||||
// TODO: 实现数据库查询
|
||||
return &model.WishTree{}, nil
|
||||
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 {
|
||||
// TODO: 实现数据库创建
|
||||
return nil
|
||||
// 检查许愿树是否存在
|
||||
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
|
||||
}
|
||||
|
||||
// GetWishList 获取许愿列表
|
||||
func (s *WishService) GetWishList(treeID uint, page, pageSize int) ([]*model.Wish, int64, error) {
|
||||
// TODO: 实现数据库查询
|
||||
return []*model.Wish{}, 0, nil
|
||||
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 {
|
||||
// TODO: 实现数据库删除
|
||||
return nil
|
||||
return s.db.Model(&model.Wish{}).Where("id = ?", id).Update("status", 0).Error
|
||||
}
|
||||
|
||||
// CreateRobotWish 创建机器人许愿
|
||||
func (s *WishService) CreateRobotWish(treeID uint) error {
|
||||
// TODO: 实现机器人自动许愿
|
||||
return nil
|
||||
// 机器人许愿内容库
|
||||
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
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user