Files

175 lines
5.5 KiB
Go
Raw Permalink Normal View History

2026-08-14 13:17:03 +08:00
package handler
import (
"fmt"
"strconv"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"nl-game-api-gin/internal/database"
"nl-game-api-gin/internal/middleware"
"nl-game-api-gin/internal/model"
"nl-game-api-gin/internal/service"
"nl-game-api-gin/pkg/resp"
)
// vipGameBrief 周免游戏的精简信息VIP页展示用
type vipGameBrief struct {
ID int `json:"id"` // 游戏ID
Code string `json:"code"` // 游戏编码
Name string `json:"name"` // 游戏名称
Icon string `json:"icon"` // emoji 图标
Price int `json:"price"` // 原价(积分)
}
// vipLevelItem VIP等级 + 本周周免批次
type vipLevelItem struct {
model.VipLevel
WeeklyGames []vipGameBrief `json:"weekly_games"` // 该等级本周的周免游戏
}
// VipInfo VIP中心数据我的VIP状态、5个等级的权益与本周周免批次、下次刷新时间
func VipInfo(c *gin.Context) {
uid := middleware.UID(c)
now := time.Now()
var user model.User
if err := database.DB.First(&user, uid).Error; err != nil {
resp.Fail(c, "用户不存在")
return
}
var levels []model.VipLevel
database.DB.Order("level").Find(&levels)
pool := service.VipWeeklyPool(now)
myLevel := service.UserVipLevel(&user, now)
list := make([]vipLevelItem, 0, len(levels))
myName := ""
for _, l := range levels {
batch := service.VipWeeklyBatch(pool, levels, l.Level)
games := make([]vipGameBrief, 0, len(batch))
for _, g := range batch {
games = append(games, vipGameBrief{ID: g.ID, Code: g.Code, Name: g.Name, Icon: g.Icon, Price: g.Price})
}
list = append(list, vipLevelItem{VipLevel: l, WeeklyGames: games})
if l.Level == myLevel {
myName = l.Name
}
}
resp.OK(c, gin.H{
"my": gin.H{
"level": myLevel, // 当前生效等级(过期后为 0
"level_name": myName, // 等级名称
"expire": user.VipExpire, // 到期时间戳(秒)
"active": myLevel > 0, // 是否生效中
},
"refresh_at": service.VipNextRefresh(now).Unix(), // 下次周免刷新时间周五12:00
"levels": list,
})
}
// vipBuyReq 购买VIP请求体
type vipBuyReq struct {
Level int `json:"level" binding:"required,min=1,max=5"` // 目标等级1~5
}
// VipBuy 开通/续费/升级VIP扣积分并更新等级与到期时间30天/次)
// 规则:同等级续费在原到期时间上顺延;升级立即生效、按30天重新计时;不允许降级购买
func VipBuy(c *gin.Context) {
var req vipBuyReq
if err := c.ShouldBindJSON(&req); err != nil {
resp.Fail(c, "参数有误")
return
}
uid := middleware.UID(c)
var target model.VipLevel
if err := database.DB.Where("level = ?", req.Level).First(&target).Error; err != nil {
resp.Fail(c, "VIP等级不存在")
return
}
now := time.Now()
var newExpire int64
var balance int
err := database.DB.Transaction(func(tx *gorm.DB) error {
var user model.User
if err := tx.Set("gorm:query_option", "FOR UPDATE").First(&user, uid).Error; err != nil {
return fmt.Errorf("用户不存在")
}
curLevel := service.UserVipLevel(&user, now)
if curLevel > req.Level {
return fmt.Errorf("当前已是更高等级VIP%d无需降级购买", curLevel)
}
// 计算新到期时间:同级续费顺延,升级/新开从现在起算30天
if curLevel == req.Level {
newExpire = user.VipExpire + 30*86400
} else {
newExpire = now.Unix() + 30*86400
}
// 扣积分并记流水(复用事务,余额不足会报错回滚)
b, err := service.ChangePoints(tx, uid, -target.PricePoints, model.PointTypeVIP, target.ID,
fmt.Sprintf("开通VIP%d·%s30天", target.Level, target.Name))
if err != nil {
return err
}
balance = b
return tx.Model(&model.User{}).Where("id = ?", uid).
Updates(map[string]any{"vip_level": req.Level, "vip_expire": newExpire}).Error
})
if err != nil {
resp.Fail(c, err.Error())
return
}
resp.OK(c, gin.H{"level": req.Level, "expire": newExpire, "balance": balance})
}
// AdminVipLevels 后台VIP等级配置列表
func AdminVipLevels(c *gin.Context) {
var levels []model.VipLevel
database.DB.Order("level").Find(&levels)
resp.OK(c, levels)
}
// adminUpdateVipReq 后台修改VIP等级配置请求体nil=不修改)
type adminUpdateVipReq struct {
Name *string `json:"name"` // 等级名称
PricePoints *int `json:"price_points"` // 30天价格积分
WeeklyFreeQuota *int `json:"weekly_free_quota"` // 周免游戏数量
SigninBonus *int `json:"signin_bonus"` // 签到加成积分
Description *string `json:"description"` // 权益描述
}
// AdminUpdateVipLevel 后台修改某一VIP等级的价格与权益
func AdminUpdateVipLevel(c *gin.Context) {
id, _ := strconv.Atoi(c.Param("id"))
var req adminUpdateVipReq
if err := c.ShouldBindJSON(&req); err != nil {
resp.Fail(c, "参数有误")
return
}
updates := map[string]any{}
if req.Name != nil && *req.Name != "" {
updates["name"] = *req.Name
}
if req.PricePoints != nil && *req.PricePoints >= 0 {
updates["price_points"] = *req.PricePoints
}
if req.WeeklyFreeQuota != nil && *req.WeeklyFreeQuota >= 0 {
updates["weekly_free_quota"] = *req.WeeklyFreeQuota
}
if req.SigninBonus != nil && *req.SigninBonus >= 0 {
updates["signin_bonus"] = *req.SigninBonus
}
if req.Description != nil {
updates["description"] = *req.Description
}
if len(updates) == 0 {
resp.Fail(c, "没有需要修改的内容")
return
}
if err := database.DB.Model(&model.VipLevel{}).Where("id = ?", id).Updates(updates).Error; err != nil {
resp.Fail(c, "保存失败")
return
}
resp.OK(c, nil)
}