feat: 实现后端业务逻辑和Inertia.js管理后台

- 实现数据库连接和自动迁移
- 实现用户服务(登录、资料、JWT认证)
- 实现许愿服务(创建、列表、机器人许愿)
- 实现订单服务(创建、支付、状态管理)
- 实现JWT认证中间件
- 实现管理后台API(仪表盘、用户、订单、许愿、设置)
- 创建Inertia.js前端页面(Vue3 + Tailwind CSS)
- 修复Go模块依赖问题
This commit is contained in:
gouki
2026-08-06 13:11:59 +00:00
parent 11bfc15141
commit 48255faa5b
19 changed files with 1627 additions and 85 deletions
+151
View File
@@ -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
}
+82 -7
View File
@@ -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
View File
@@ -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
}
}
}()
}