ea8ffb6c74
- 修复订阅模式登录时错误检查余额的问题 - 区分无限余额和永久订阅两种永久会员类型 - 修复动态代码HTTP请求返回值在JS中无法正确访问的问题 - 添加侧边栏滚动位置保持功能 - 移除developer角色相关代码,统一使用admin - 添加缺失的i18n翻译key
224 lines
6.5 KiB
Go
224 lines
6.5 KiB
Go
package app
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
"verification-platform-backend/internal/database"
|
|
"verification-platform-backend/internal/model"
|
|
"verification-platform-backend/internal/service"
|
|
"verification-platform-backend/pkg/response"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func SetupPaymentRoutes(r *gin.RouterGroup) {
|
|
r.POST("/recharge", handleAppRecharge)
|
|
r.POST("/trial", handleAppTrial)
|
|
}
|
|
|
|
func handleAppRecharge(c *gin.Context) {
|
|
appKey := c.Param("appKey")
|
|
var app model.Application
|
|
if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil {
|
|
response.Error(c, 404, "应用不存在")
|
|
return
|
|
}
|
|
|
|
if service.GetApplicationDisabledStatus(app.ID) {
|
|
response.Error(c, 403, "该应用已被禁用")
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
Username string `json:"username"`
|
|
CardKey string `json:"card_key"`
|
|
DeviceID string `json:"device_id"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, 400, "参数错误")
|
|
return
|
|
}
|
|
|
|
if blocked, reason := checkRiskControl(c, app.ID, req.DeviceID, req.Username); blocked {
|
|
response.Error(c, 403, reason)
|
|
return
|
|
}
|
|
|
|
var card model.Card
|
|
if err := database.DB.Preload("CardType").Where("card_key = ?", req.CardKey).First(&card).Error; err != nil {
|
|
service.LogVerification(c, &app.ID, nil, "recharge_failed", fmt.Sprintf("充值失败: 卡密不存在 - %s", req.CardKey), "", fmt.Errorf("卡密不存在"))
|
|
response.Error(c, 404, "卡密不存在")
|
|
return
|
|
}
|
|
|
|
fmt.Printf("[DEBUG] 卡密信息: ID=%d, CardKey=%s, Status=%s, CardTypeID=%d\n", card.ID, card.CardKey, card.Status, card.CardTypeID)
|
|
fmt.Printf("[DEBUG] 卡类信息: ID=%d, Name=%s, Value=%f, Price=%f\n", card.CardType.ID, card.CardType.Name, card.CardType.Value, card.CardType.Price)
|
|
|
|
if card.Status != "unused" {
|
|
service.LogVerification(c, &app.ID, nil, "recharge_failed", fmt.Sprintf("充值失败: 卡密已使用 - %s", req.CardKey), "", fmt.Errorf("卡密已使用"))
|
|
response.Error(c, 400, "卡密已使用")
|
|
return
|
|
}
|
|
|
|
var user model.AppUser
|
|
if err := database.DB.Where("username = ? AND application_id = ?", req.Username, app.ID).First(&user).Error; err != nil {
|
|
service.LogVerification(c, &app.ID, nil, "recharge_failed", fmt.Sprintf("充值失败: 用户不存在 - %s", req.Username), "", fmt.Errorf("用户不存在"))
|
|
response.Error(c, 404, "用户不存在")
|
|
return
|
|
}
|
|
|
|
if user.Balance == -1 {
|
|
service.LogVerification(c, &app.ID, &user.ID, "recharge_failed", fmt.Sprintf("充值失败: 用户已是永久会员 - %s", user.Username), "", fmt.Errorf("该用户已是永久会员,无法再次充值"))
|
|
response.Error(c, 400, "该用户已是永久会员,无法再次充值")
|
|
return
|
|
}
|
|
|
|
fmt.Printf("[DEBUG] 充值前用户信息: ID=%d, Username=%s, Balance=%f, ExpiryAt=%v\n", user.ID, user.Username, user.Balance, user.ExpiryAt)
|
|
|
|
tx := database.DB.Begin()
|
|
|
|
card.Status = "used"
|
|
card.AppUserID = &user.ID
|
|
now := time.Now()
|
|
card.UsedAt = &now
|
|
|
|
if err := tx.Save(&card).Error; err != nil {
|
|
tx.Rollback()
|
|
response.Error(c, 500, "充值失败")
|
|
return
|
|
}
|
|
|
|
user.IsTrialUser = false
|
|
|
|
if card.CardType.Value == -1 {
|
|
if card.CardType.RechargeType == "subscription" {
|
|
permanentExpiry := time.Date(9999, 12, 31, 23, 59, 59, 0, time.UTC)
|
|
user.ExpiryAt = &permanentExpiry
|
|
user.Balance = -1
|
|
} else {
|
|
user.Balance = -1
|
|
user.ExpiryAt = nil
|
|
}
|
|
} else {
|
|
switch card.CardType.RechargeType {
|
|
case "subscription":
|
|
var baseTime time.Time
|
|
if user.ExpiryAt != nil && user.ExpiryAt.After(now) {
|
|
baseTime = *user.ExpiryAt
|
|
} else {
|
|
baseTime = now
|
|
}
|
|
var duration time.Duration
|
|
switch card.CardType.ValueUnit {
|
|
case "minute":
|
|
duration = time.Duration(card.CardType.Value) * time.Minute
|
|
case "hour":
|
|
duration = time.Duration(card.CardType.Value) * time.Hour
|
|
case "day":
|
|
duration = time.Duration(card.CardType.Value) * 24 * time.Hour
|
|
case "month":
|
|
duration = time.Duration(card.CardType.Value) * 30 * 24 * time.Hour
|
|
case "year":
|
|
duration = time.Duration(card.CardType.Value) * 365 * 24 * time.Hour
|
|
default:
|
|
duration = time.Duration(card.CardType.Value) * time.Second
|
|
}
|
|
newExpiry := baseTime.Add(duration)
|
|
user.ExpiryAt = &newExpiry
|
|
case "balance":
|
|
fallthrough
|
|
default:
|
|
user.Balance += card.CardType.Value
|
|
}
|
|
}
|
|
|
|
fmt.Printf("[DEBUG] 充值后用户信息: Balance=%f, ExpiryAt=%v (CardType.Value=%f, BillingType=%s)\n", user.Balance, user.ExpiryAt, card.CardType.Value, app.BillingType)
|
|
|
|
if err := tx.Save(&user).Error; err != nil {
|
|
tx.Rollback()
|
|
response.Error(c, 500, "更新用户信息失败")
|
|
return
|
|
}
|
|
|
|
rechargeRecord := model.RechargeRecord{
|
|
UserID: user.ID,
|
|
OrderNo: generateOrderNo("R"),
|
|
CardID: &card.ID,
|
|
CardCode: card.CardKey,
|
|
Amount: card.CardType.Price,
|
|
Status: "success",
|
|
PaymentType: "card",
|
|
Remark: "卡密充值 - " + card.CardType.Name,
|
|
}
|
|
|
|
if err := tx.Create(&rechargeRecord).Error; err != nil {
|
|
tx.Rollback()
|
|
response.Error(c, 500, "创建充值记录失败")
|
|
return
|
|
}
|
|
|
|
if err := tx.Commit().Error; err != nil {
|
|
response.Error(c, 500, "充值失败")
|
|
return
|
|
}
|
|
|
|
service.LogVerification(c, &app.ID, &user.ID, "recharge", fmt.Sprintf("用户充值: %s, 卡密: %s, 金额: %.2f", user.Username, card.CardKey, card.CardType.Price), "", nil)
|
|
|
|
response.SuccessWithMessage(c, "充值成功", gin.H{
|
|
"message": "充值成功",
|
|
"value": card.CardType.Value,
|
|
})
|
|
}
|
|
|
|
func handleAppTrial(c *gin.Context) {
|
|
appKey := c.Param("appKey")
|
|
var app model.Application
|
|
if err := database.DB.Where("app_key = ?", appKey).First(&app).Error; err != nil {
|
|
response.Error(c, 404, "应用不存在")
|
|
return
|
|
}
|
|
|
|
if service.GetApplicationDisabledStatus(app.ID) {
|
|
response.Error(c, 403, "该应用已被禁用")
|
|
return
|
|
}
|
|
|
|
if !app.EnableTrial {
|
|
response.Error(c, 400, "该应用不支持试用")
|
|
return
|
|
}
|
|
|
|
var req struct {
|
|
UserID uint `json:"user_id"`
|
|
}
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
response.Error(c, 400, "参数错误")
|
|
return
|
|
}
|
|
|
|
var user model.AppUser
|
|
if err := database.DB.First(&user, req.UserID).Error; err != nil {
|
|
response.Error(c, 404, "用户不存在")
|
|
return
|
|
}
|
|
|
|
response.SuccessWithMessage(c, "试用成功", gin.H{
|
|
"message": "试用成功",
|
|
"trial_balance": app.TrialBalance,
|
|
})
|
|
}
|
|
|
|
func generateOrderNo(prefix string) string {
|
|
return prefix + time.Now().Format("20060102150405") + randomString(6)
|
|
}
|
|
|
|
func randomString(length int) string {
|
|
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
|
b := make([]byte, length)
|
|
for i := range b {
|
|
b[i] = charset[time.Now().Nanosecond()%len(charset)]
|
|
time.Sleep(1 * time.Nanosecond)
|
|
}
|
|
return string(b)
|
|
}
|