Initial commit: 网络验证平台
This commit is contained in:
@@ -0,0 +1,749 @@
|
||||
package extension
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"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 SetupRoutes(r *gin.RouterGroup) {
|
||||
ext := r.Group("/ext")
|
||||
ext.Use(ExtensionAuthMiddleware())
|
||||
{
|
||||
// 用户相关
|
||||
ext.GET("/user/:userId", handleGetUser)
|
||||
ext.GET("/users", handleGetUsers)
|
||||
ext.POST("/user/:userId/recharge", handleRechargeUser)
|
||||
ext.POST("/user/:userId/deduct", handleDeductUser)
|
||||
|
||||
// 用户变量相关
|
||||
ext.GET("/user/:userId/variables", handleGetUserVariables)
|
||||
ext.POST("/user/:userId/variables", handleUpdateUserVariables)
|
||||
|
||||
// 卡密相关
|
||||
ext.GET("/cards", handleGetCards)
|
||||
ext.POST("/cards/generate", handleGenerateCards)
|
||||
ext.GET("/card/:cardId", handleGetCard)
|
||||
|
||||
// 通知相关
|
||||
ext.POST("/notification", handleSendNotification)
|
||||
ext.POST("/notification/batch", handleSendBatchNotification)
|
||||
|
||||
// 应用信息
|
||||
ext.GET("/app/info", handleGetAppInfo)
|
||||
ext.GET("/app/stats", handleGetAppStats)
|
||||
|
||||
// 应用变量相关
|
||||
ext.GET("/app/variables", handleGetAppVariables)
|
||||
ext.POST("/app/variables", handleUpdateAppVariables)
|
||||
}
|
||||
}
|
||||
|
||||
func ExtensionAuthMiddleware() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
accessKey := c.GetHeader("X-Access-Key")
|
||||
signature := c.GetHeader("X-Signature")
|
||||
timestamp := c.GetHeader("X-Timestamp")
|
||||
|
||||
if accessKey == "" || signature == "" || timestamp == "" {
|
||||
response.Error(c, 401, "缺少认证信息")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
ts, err := strconv.ParseInt(timestamp, 10, 64)
|
||||
if err != nil {
|
||||
response.Error(c, 401, "时间戳格式错误")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if time.Now().Unix()-ts > 300 {
|
||||
response.Error(c, 401, "请求已过期")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
var apiKey model.ExtensionAPIKey
|
||||
if err := database.DB.Where("access_key = ? AND status = ?", accessKey, "active").
|
||||
Preload("Application").First(&apiKey).Error; err != nil {
|
||||
response.Error(c, 401, "API密钥无效")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
if apiKey.ExpiresAt != nil && apiKey.ExpiresAt.Before(time.Now()) {
|
||||
response.Error(c, 401, "API密钥已过期")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
bodyBytes, _ := io.ReadAll(c.Request.Body)
|
||||
c.Set("requestBody", bodyBytes)
|
||||
c.Request.Body = io.NopCloser(strings.NewReader(string(bodyBytes)))
|
||||
|
||||
stringToSign := fmt.Sprintf("%s%s%s%s", c.Request.Method, c.Request.URL.Path, timestamp, string(bodyBytes))
|
||||
expectedSignature := generateSignature(apiKey.SecretKey, stringToSign)
|
||||
|
||||
if !hmac.Equal([]byte(signature), []byte(expectedSignature)) {
|
||||
response.Error(c, 401, "签名验证失败")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
c.Set("apiKey", apiKey)
|
||||
c.Set("applicationID", apiKey.ApplicationID)
|
||||
|
||||
var app model.Application
|
||||
if err := database.DB.First(&app, apiKey.ApplicationID).Error; err == nil {
|
||||
c.Set("user_id", app.UserID)
|
||||
c.Set("app_id", apiKey.ApplicationID)
|
||||
|
||||
var user model.User
|
||||
if err := database.DB.Preload("CurrentPackage").First(&user, app.UserID).Error; err == nil {
|
||||
if user.CurrentPackageID != nil {
|
||||
var permission model.PackagePermission
|
||||
if err := database.DB.Where("package_id = ?", user.CurrentPackageID).First(&permission).Error; err == nil {
|
||||
now := time.Now()
|
||||
if user.ApiCallsResetAt == nil || now.Sub(*user.ApiCallsResetAt) >= 24*time.Hour {
|
||||
user.ApiCallsUsed = 0
|
||||
user.ApiCallsResetAt = &now
|
||||
database.DB.Save(&user)
|
||||
}
|
||||
|
||||
if user.ApiCallsUsed >= permission.MaxApiCalls {
|
||||
response.Error(c, 403, fmt.Sprintf("API调用次数已达上限(%d次/天),请升级套餐", permission.MaxApiCalls))
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
database.DB.Model(&apiKey).Update("last_used_at", now)
|
||||
|
||||
c.Next()
|
||||
|
||||
if _, exists := c.Get("app_id"); exists {
|
||||
if userID, exists := c.Get("user_id"); exists {
|
||||
var user model.User
|
||||
if err := database.DB.First(&user, userID).Error; err == nil {
|
||||
user.ApiCallsUsed++
|
||||
database.DB.Save(&user)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateSignature(secretKey, data string) string {
|
||||
h := hmac.New(sha256.New, []byte(secretKey))
|
||||
h.Write([]byte(data))
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
func handleGetUser(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
userID := c.Param("userId")
|
||||
|
||||
var user model.AppUser
|
||||
if err := database.DB.Where("id = ? AND application_id = ?", userID, appID).First(&user).Error; err != nil {
|
||||
response.Error(c, 404, "用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"id": user.ID,
|
||||
"username": user.Username,
|
||||
"email": user.Email,
|
||||
"status": user.Status,
|
||||
"balance": user.Balance,
|
||||
"expiry_at": user.ExpiryAt,
|
||||
"lastLoginAt": user.LastLoginAt,
|
||||
"lastHeartbeatAt": user.LastHeartbeatAt,
|
||||
"isTrialUser": user.IsTrialUser,
|
||||
"createdAt": user.CreatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
func handleGetUsers(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
status := c.Query("status")
|
||||
search := c.Query("search")
|
||||
|
||||
var users []model.AppUser
|
||||
var total int64
|
||||
|
||||
query := database.DB.Model(&model.AppUser{}).Where("application_id = ?", appID)
|
||||
|
||||
if status != "" {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
|
||||
if search != "" {
|
||||
query = query.Where("username LIKE ? OR email LIKE ?", "%"+search+"%", "%"+search+"%")
|
||||
}
|
||||
|
||||
query.Count(&total)
|
||||
|
||||
offset := (page - 1) * pageSize
|
||||
if err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&users).Error; err != nil {
|
||||
response.Error(c, 500, "获取用户列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"users": users,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
"total_page": (total + int64(pageSize) - 1) / int64(pageSize),
|
||||
})
|
||||
}
|
||||
|
||||
func handleRechargeUser(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
userID := c.Param("userId")
|
||||
|
||||
var req struct {
|
||||
Amount int `json:"amount" binding:"required"`
|
||||
Type string `json:"type" binding:"required"` // balance, days
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var app model.Application
|
||||
if err := database.DB.First(&app, appID).Error; err != nil {
|
||||
response.Error(c, 404, "应用不存在")
|
||||
return
|
||||
}
|
||||
|
||||
var user model.AppUser
|
||||
if err := database.DB.Where("id = ? AND application_id = ?", userID, appID).First(&user).Error; err != nil {
|
||||
service.LogVerification(c, &app.ID, nil, "extension_recharge_failed", fmt.Sprintf("扩展API充值失败: 用户不存在 - %s", userID), "", fmt.Errorf("用户不存在"))
|
||||
response.Error(c, 404, "用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
if user.Balance == -1 {
|
||||
service.LogVerification(c, &app.ID, &user.ID, "extension_recharge_failed", fmt.Sprintf("扩展API充值失败: 用户已是永久会员 - %s", user.Username), "", fmt.Errorf("该用户为永久会员,无需充值"))
|
||||
response.Error(c, 400, "该用户为永久会员,无需充值")
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
switch req.Type {
|
||||
case "days":
|
||||
var baseTime time.Time
|
||||
if user.ExpiryAt != nil && user.ExpiryAt.After(now) {
|
||||
baseTime = *user.ExpiryAt
|
||||
} else {
|
||||
baseTime = now
|
||||
}
|
||||
duration := time.Duration(req.Amount) * 24 * time.Hour
|
||||
newExpiry := baseTime.Add(duration)
|
||||
user.ExpiryAt = &newExpiry
|
||||
case "balance":
|
||||
user.Balance += float64(req.Amount)
|
||||
default:
|
||||
service.LogVerification(c, &app.ID, &user.ID, "extension_recharge_failed", fmt.Sprintf("扩展API充值失败: 类型无效 - %s", req.Type), "", fmt.Errorf("充值类型无效"))
|
||||
response.Error(c, 400, "充值类型无效,仅支持days或balance类型")
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.DB.Save(&user).Error; err != nil {
|
||||
service.LogVerification(c, &app.ID, &user.ID, "extension_recharge_failed", fmt.Sprintf("扩展API充值失败: 保存失败 - %s", user.Username), "", err)
|
||||
response.Error(c, 500, "充值失败")
|
||||
return
|
||||
}
|
||||
|
||||
record := model.RechargeRecord{
|
||||
UserID: user.ID,
|
||||
OrderNo: fmt.Sprintf("EXT%d%d", time.Now().Unix(), user.ID),
|
||||
Amount: float64(req.Amount),
|
||||
Status: "success",
|
||||
PaymentType: "extension_api",
|
||||
Remark: req.Description,
|
||||
}
|
||||
database.DB.Create(&record)
|
||||
|
||||
service.LogVerification(c, &app.ID, &user.ID, "extension_recharge", fmt.Sprintf("扩展API充值: 用户%s, 类型:%s, 数量:%d", user.Username, req.Type, req.Amount), "", nil)
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "充值成功",
|
||||
"user": user,
|
||||
})
|
||||
}
|
||||
|
||||
func handleDeductUser(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
userID := c.Param("userId")
|
||||
|
||||
var req struct {
|
||||
Amount int `json:"amount" binding:"required"`
|
||||
Type string `json:"type" binding:"required"` // balance, days
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var app model.Application
|
||||
database.DB.First(&app, appID)
|
||||
|
||||
var user model.AppUser
|
||||
if err := database.DB.Where("id = ? AND application_id = ?", userID, appID).First(&user).Error; err != nil {
|
||||
service.LogVerification(c, &app.ID, nil, "extension_deduct_failed", fmt.Sprintf("扩展API扣费失败: 用户不存在 - %s", userID), "", fmt.Errorf("用户不存在"))
|
||||
response.Error(c, 404, "用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
if user.Balance == -1 {
|
||||
service.LogVerification(c, &app.ID, &user.ID, "extension_deduct_failed", fmt.Sprintf("扩展API扣费失败: 用户已是永久会员 - %s", user.Username), "", fmt.Errorf("该用户为永久会员,无法扣除"))
|
||||
response.Error(c, 400, "该用户为永久会员,无法扣除")
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
|
||||
switch req.Type {
|
||||
case "days":
|
||||
if user.ExpiryAt == nil || user.ExpiryAt.Before(now) {
|
||||
service.LogVerification(c, &app.ID, &user.ID, "extension_deduct_failed", fmt.Sprintf("扩展API扣费失败: 用户订阅已过期 - %s", user.Username), "", fmt.Errorf("用户订阅已过期"))
|
||||
response.Error(c, 400, "用户订阅已过期")
|
||||
return
|
||||
}
|
||||
duration := time.Duration(req.Amount) * 24 * time.Hour
|
||||
newExpiry := user.ExpiryAt.Add(-duration)
|
||||
if newExpiry.Before(now) {
|
||||
newExpiry = now
|
||||
}
|
||||
user.ExpiryAt = &newExpiry
|
||||
case "balance":
|
||||
if user.Balance < float64(req.Amount) {
|
||||
service.LogVerification(c, &app.ID, &user.ID, "extension_deduct_failed", fmt.Sprintf("扩展API扣费失败: 余额不足 - %s", user.Username), "", fmt.Errorf("余额不足"))
|
||||
response.Error(c, 400, "余额不足")
|
||||
return
|
||||
}
|
||||
user.Balance -= float64(req.Amount)
|
||||
default:
|
||||
service.LogVerification(c, &app.ID, &user.ID, "extension_deduct_failed", fmt.Sprintf("扩展API扣费失败: 类型无效 - %s", req.Type), "", fmt.Errorf("扣除类型无效"))
|
||||
response.Error(c, 400, "扣除类型无效,仅支持days或balance类型")
|
||||
return
|
||||
}
|
||||
|
||||
if err := database.DB.Save(&user).Error; err != nil {
|
||||
service.LogVerification(c, &app.ID, &user.ID, "extension_deduct_failed", fmt.Sprintf("扩展API扣费失败: 保存失败 - %s", user.Username), "", err)
|
||||
response.Error(c, 500, "扣除失败")
|
||||
return
|
||||
}
|
||||
|
||||
record := model.ConsumptionRecord{
|
||||
UserID: user.ID,
|
||||
OrderNo: fmt.Sprintf("EXT%d%d", time.Now().Unix(), user.ID),
|
||||
Type: req.Type,
|
||||
Amount: float64(req.Amount),
|
||||
Status: "success",
|
||||
PaymentType: "extension_api",
|
||||
Remark: req.Description,
|
||||
}
|
||||
database.DB.Create(&record)
|
||||
|
||||
service.LogVerification(c, &app.ID, &user.ID, "extension_deduct", fmt.Sprintf("扩展API扣费: 用户%s, 类型:%s, 数量:%d", user.Username, req.Type, req.Amount), "", nil)
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "扣除成功",
|
||||
"user": user,
|
||||
})
|
||||
}
|
||||
|
||||
func handleGetUserVariables(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
userID := c.Param("userId")
|
||||
|
||||
var user model.AppUser
|
||||
if err := database.DB.Where("id = ? AND application_id = ?", userID, appID).First(&user).Error; err != nil {
|
||||
response.Error(c, 404, "用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
var variables []model.CloudVariable
|
||||
if err := database.DB.Where("app_id = ?", appID).Find(&variables).Error; err != nil {
|
||||
response.Error(c, 500, "获取变量定义失败")
|
||||
return
|
||||
}
|
||||
|
||||
var userVariables []model.UserVariable
|
||||
if err := database.DB.Where("user_id = ? AND app_id = ?", userID, appID).Find(&userVariables).Error; err != nil {
|
||||
response.Error(c, 500, "获取用户变量失败")
|
||||
return
|
||||
}
|
||||
|
||||
userVarMap := make(map[string]string)
|
||||
for _, uv := range userVariables {
|
||||
userVarMap[uv.VarName] = uv.VarValue
|
||||
}
|
||||
|
||||
result := make(map[string]interface{})
|
||||
for _, v := range variables {
|
||||
if v.Scope == "app" {
|
||||
result[v.Key] = gin.H{
|
||||
"value": v.DefaultValue,
|
||||
"scope": "app",
|
||||
}
|
||||
} else {
|
||||
value := v.DefaultValue
|
||||
if uv, ok := userVarMap[v.Key]; ok {
|
||||
value = uv
|
||||
}
|
||||
result[v.Key] = gin.H{
|
||||
"value": value,
|
||||
"scope": "user",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.Success(c, result)
|
||||
}
|
||||
|
||||
func handleUpdateUserVariables(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
userID := c.Param("userId")
|
||||
|
||||
var user model.AppUser
|
||||
if err := database.DB.Where("id = ? AND application_id = ?", userID, appID).First(&user).Error; err != nil {
|
||||
response.Error(c, 404, "用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
Variables map[string]string `json:"variables"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
var variables []model.CloudVariable
|
||||
if err := database.DB.Where("app_id = ?", appID).Find(&variables).Error; err != nil {
|
||||
response.Error(c, 500, "获取变量定义失败")
|
||||
return
|
||||
}
|
||||
|
||||
varMap := make(map[string]model.CloudVariable)
|
||||
for _, v := range variables {
|
||||
varMap[v.Key] = v
|
||||
}
|
||||
|
||||
for key, value := range req.Variables {
|
||||
variable, exists := varMap[key]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
|
||||
if variable.Scope == "app" {
|
||||
variable.DefaultValue = value
|
||||
database.DB.Save(&variable)
|
||||
} else {
|
||||
var userVar model.UserVariable
|
||||
if err := database.DB.Where("user_id = ? AND app_id = ? AND var_name = ?", userID, appID, key).First(&userVar).Error; err != nil {
|
||||
userVar = model.UserVariable{
|
||||
UserID: user.ID,
|
||||
AppID: appID,
|
||||
VarName: key,
|
||||
VarValue: value,
|
||||
VarType: variable.VarType,
|
||||
}
|
||||
database.DB.Create(&userVar)
|
||||
} else {
|
||||
userVar.VarValue = value
|
||||
database.DB.Save(&userVar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "更新成功",
|
||||
})
|
||||
}
|
||||
|
||||
func handleGetAppVariables(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
|
||||
var variables []model.CloudVariable
|
||||
if err := database.DB.Where("app_id = ? AND scope = ?", appID, "app").Find(&variables).Error; err != nil {
|
||||
response.Error(c, 500, "获取应用变量失败")
|
||||
return
|
||||
}
|
||||
|
||||
result := make(map[string]string)
|
||||
for _, v := range variables {
|
||||
result[v.Key] = v.DefaultValue
|
||||
}
|
||||
|
||||
response.Success(c, result)
|
||||
}
|
||||
|
||||
func handleUpdateAppVariables(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
|
||||
var req struct {
|
||||
Variables map[string]string `json:"variables"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
for key, value := range req.Variables {
|
||||
var variable model.CloudVariable
|
||||
if err := database.DB.Where("app_id = ? AND key = ? AND scope = ?", appID, key, "app").First(&variable).Error; err == nil {
|
||||
variable.DefaultValue = value
|
||||
database.DB.Save(&variable)
|
||||
}
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "更新成功",
|
||||
})
|
||||
}
|
||||
|
||||
func handleGetCards(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
|
||||
pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20"))
|
||||
status := c.Query("status")
|
||||
cardTypeID := c.Query("card_type_id")
|
||||
|
||||
var cards []model.Card
|
||||
var total int64
|
||||
|
||||
query := database.DB.Model(&model.Card{}).Where("application_id = ?", appID)
|
||||
|
||||
if status != "" {
|
||||
query = query.Where("status = ?", status)
|
||||
}
|
||||
|
||||
if cardTypeID != "" {
|
||||
query = query.Where("card_type_id = ?", cardTypeID)
|
||||
}
|
||||
|
||||
query.Count(&total)
|
||||
|
||||
offset := (page - 1) * pageSize
|
||||
if err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&cards).Error; err != nil {
|
||||
response.Error(c, 500, "获取卡密列表失败")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"cards": cards,
|
||||
"total": total,
|
||||
"page": page,
|
||||
"page_size": pageSize,
|
||||
"total_page": (total + int64(pageSize) - 1) / int64(pageSize),
|
||||
})
|
||||
}
|
||||
|
||||
func handleGenerateCards(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
|
||||
var req struct {
|
||||
CardTypeID uint `json:"card_type_id" binding:"required"`
|
||||
Count int `json:"count" binding:"required"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var cardType model.CardType
|
||||
if err := database.DB.Where("id = ? AND application_id = ?", req.CardTypeID, appID).First(&cardType).Error; err != nil {
|
||||
response.Error(c, 404, "卡密类型不存在")
|
||||
return
|
||||
}
|
||||
|
||||
cards := make([]model.Card, req.Count)
|
||||
for i := 0; i < req.Count; i++ {
|
||||
cardKey := generateCardKey()
|
||||
cards[i] = model.Card{
|
||||
ApplicationID: appID,
|
||||
CardTypeID: req.CardTypeID,
|
||||
CardKey: cardKey,
|
||||
Status: "unused",
|
||||
}
|
||||
}
|
||||
|
||||
if err := database.DB.Create(&cards).Error; err != nil {
|
||||
response.Error(c, 500, "生成卡密失败")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "生成成功",
|
||||
"count": req.Count,
|
||||
"cards": cards,
|
||||
})
|
||||
}
|
||||
|
||||
func handleGetCard(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
cardID := c.Param("cardId")
|
||||
|
||||
var card model.Card
|
||||
if err := database.DB.Where("id = ? AND application_id = ?", cardID, appID).First(&card).Error; err != nil {
|
||||
response.Error(c, 404, "卡密不存在")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, card)
|
||||
}
|
||||
|
||||
func handleSendNotification(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
|
||||
var req struct {
|
||||
UserID uint `json:"user_id" binding:"required"`
|
||||
Title string `json:"title" binding:"required"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var user model.AppUser
|
||||
if err := database.DB.Where("id = ? AND application_id = ?", req.UserID, appID).First(&user).Error; err != nil {
|
||||
response.Error(c, 404, "用户不存在")
|
||||
return
|
||||
}
|
||||
|
||||
notification := model.Announcement{
|
||||
ApplicationID: appID,
|
||||
Title: req.Title,
|
||||
Content: req.Content,
|
||||
Type: req.Type,
|
||||
Status: "active",
|
||||
}
|
||||
|
||||
if err := database.DB.Create(¬ification).Error; err != nil {
|
||||
response.Error(c, 500, "发送通知失败")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "发送成功",
|
||||
"notification": notification,
|
||||
})
|
||||
}
|
||||
|
||||
func handleSendBatchNotification(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
|
||||
var req struct {
|
||||
Title string `json:"title" binding:"required"`
|
||||
Content string `json:"content" binding:"required"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
response.Error(c, 400, "参数错误: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
notification := model.Announcement{
|
||||
ApplicationID: appID,
|
||||
Title: req.Title,
|
||||
Content: req.Content,
|
||||
Type: req.Type,
|
||||
Status: "active",
|
||||
}
|
||||
|
||||
if err := database.DB.Create(¬ification).Error; err != nil {
|
||||
response.Error(c, 500, "发送通知失败")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"message": "发送成功",
|
||||
"notification": notification,
|
||||
})
|
||||
}
|
||||
|
||||
func handleGetAppInfo(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
|
||||
var app model.Application
|
||||
if err := database.DB.First(&app, appID).Error; err != nil {
|
||||
response.Error(c, 404, "应用不存在")
|
||||
return
|
||||
}
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"id": app.ID,
|
||||
"name": app.Name,
|
||||
"description": app.Description,
|
||||
"billing_type": app.BillingType,
|
||||
"encrypt_type": app.EncryptType,
|
||||
"bind_type": app.BindType,
|
||||
"max_devices": app.MaxDevices,
|
||||
"multi_open": app.MultiOpen,
|
||||
"enable_trial": app.EnableTrial,
|
||||
"trial_balance": app.TrialBalance,
|
||||
"status": app.Status,
|
||||
"created_at": app.CreatedAt,
|
||||
})
|
||||
}
|
||||
|
||||
func handleGetAppStats(c *gin.Context) {
|
||||
appID := c.GetUint("applicationID")
|
||||
|
||||
var userCount, activeUserCount, cardCount, usedCardCount int64
|
||||
|
||||
database.DB.Model(&model.AppUser{}).Where("application_id = ?", appID).Count(&userCount)
|
||||
database.DB.Model(&model.AppUser{}).Where("application_id = ? AND status = ?", appID, "active").Count(&activeUserCount)
|
||||
database.DB.Model(&model.Card{}).Where("application_id = ?", appID).Count(&cardCount)
|
||||
database.DB.Model(&model.Card{}).Where("application_id = ? AND status = ?", appID, "used").Count(&usedCardCount)
|
||||
|
||||
response.Success(c, gin.H{
|
||||
"user_count": userCount,
|
||||
"active_user_count": activeUserCount,
|
||||
"card_count": cardCount,
|
||||
"used_card_count": usedCardCount,
|
||||
})
|
||||
}
|
||||
|
||||
func generateCardKey() string {
|
||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
|
||||
b := make([]byte, 16)
|
||||
for i := range b {
|
||||
b[i] = charset[i%len(charset)]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
Reference in New Issue
Block a user