Initial commit: 网络验证平台

This commit is contained in:
Admin
2026-04-27 17:22:56 +08:00
commit afe67d704e
780 changed files with 88960 additions and 0 deletions
+427
View File
@@ -0,0 +1,427 @@
package app
import (
"net/http"
"strings"
"time"
"verification-platform-backend/internal/database"
"verification-platform-backend/internal/model"
"verification-platform-backend/internal/service"
"verification-platform-backend/pkg/jwt"
"verification-platform-backend/pkg/response"
"github.com/dop251/goja"
"github.com/gin-gonic/gin"
)
func SetupDynamicRoutes(r *gin.RouterGroup) {
dynamicCode := r.Group("/dynamic-code")
{
dynamicCode.POST("/:key/execute", handleExecuteDynamicCode)
}
}
func handleExecuteDynamicCode(c *gin.Context) {
var token string
authHeader := c.GetHeader("Authorization")
if authHeader != "" {
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) == 2 && parts[0] == "Bearer" {
token = parts[1]
}
}
if token == "" {
token = c.Query("token")
}
if token == "" {
response.Error(c, http.StatusUnauthorized, "Authorization header is required")
return
}
claims, err := jwt.ParseToken(token)
if err != nil {
response.Error(c, http.StatusUnauthorized, "Invalid token")
return
}
if time.Now().Unix() > claims.ExpiresAt.Unix() {
response.Error(c, http.StatusUnauthorized, "Token expired")
return
}
c.Set("user_id", claims.UserID)
c.Set("username", claims.Username)
c.Set("role", claims.Role)
appKey := c.Param("appKey")
key := c.Param("key")
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 appUser model.AppUser
if err := database.DB.Where("id = ? AND application_id = ?", claims.UserID, app.ID).First(&appUser).Error; err != nil {
response.Error(c, http.StatusForbidden, "无权访问该应用的动态代码")
return
}
var dynamicCode model.DynamicCode
if err := database.DB.Where("application_id = ? AND key = ?", app.ID, key).First(&dynamicCode).Error; err != nil {
response.Error(c, 404, "动态代码不存在")
return
}
if dynamicCode.Status != "active" {
response.Error(c, 400, "动态代码未启用")
return
}
var req struct {
Params map[string]interface{} `json:"params"`
UserID *uint `json:"user_id,omitempty"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.Error(c, 400, "参数错误")
return
}
startTime := time.Now()
vm := goja.New()
appData := map[string]interface{}{
"id": app.ID,
"name": app.Name,
"description": app.Description,
"app_key": app.AppKey,
"status": app.Status,
"billing_type": app.BillingType,
"deduction_mode": app.DeductionMode,
"deduction_type": app.DeductionType,
"deduction_interval": app.DeductionInterval,
"deduction_unit": app.DeductionUnit,
"deduction_amount": app.DeductionAmount,
"enable_trial": app.EnableTrial,
"trial_balance": app.TrialBalance,
"trial_days": app.TrialDays,
"enable_free_period": app.EnableFreePeriod,
"free_period_type": app.FreePeriodType,
"free_period_start": app.FreePeriodStart,
"free_period_end": app.FreePeriodEnd,
"free_period_weekdays": app.FreePeriodWeekdays,
"free_period_start_time": app.FreePeriodStartTime,
"free_period_end_time": app.FreePeriodEndTime,
"max_devices": app.MaxDevices,
"bind_type": app.BindType,
"multi_open": app.MultiOpen,
"multi_open_mode": app.MultiOpenMode,
"max_instances": app.MaxInstances,
"login_policy": app.LoginPolicy,
"max_attempts": app.MaxAttempts,
"lock_duration": app.LockDuration,
"heartbeat_interval": app.HeartbeatInterval,
"heartbeat_timeout": app.HeartbeatTimeout,
"change_limit": app.ChangeLimit,
"change_interval": app.ChangeInterval,
"change_exceed_action": app.ChangeExceedAction,
"change_deduct_amount": app.ChangeDeductAmount,
}
if err := vm.Set("app", appData); err != nil {
response.Error(c, 500, "应用数据设置失败")
return
}
var constants []model.CloudConstant
database.DB.Where("app_id = ? AND status = ?", app.ID, "active").Find(&constants)
constantsData := make(map[string]interface{})
for _, c := range constants {
constantsData[c.Key] = c.Value
}
if err := vm.Set("constants", constantsData); err != nil {
response.Error(c, 500, "云端常量设置失败")
return
}
var cloudVariables []model.CloudVariable
database.DB.Where("app_id = ? AND status = ?", app.ID, "active").Find(&cloudVariables)
appVariables := make(map[string]interface{})
for _, v := range cloudVariables {
appVariables[v.Key] = v.DefaultValue
}
if err := vm.Set("appVariables", appVariables); err != nil {
response.Error(c, 500, "云端变量设置失败")
return
}
userData := map[string]interface{}{}
subscription := map[string]interface{}{}
userVariables := map[string]interface{}{}
devices := []map[string]interface{}{}
if req.UserID != nil {
var user model.AppUser
if err := database.DB.Where("id = ? AND application_id = ?", *req.UserID, app.ID).First(&user).Error; err == nil {
userData = map[string]interface{}{
"id": user.ID,
"username": user.Username,
"email": user.Email,
"status": user.Status,
"device_id": user.DeviceID,
"avatar": user.Avatar,
"created_at": user.CreatedAt,
"last_login_at": user.LastLoginAt,
}
subscription = map[string]interface{}{
"balance": user.Balance,
"is_trial_user": user.IsTrialUser,
"trial_start_at": user.TrialStartAt,
"trial_end_at": user.TrialEndAt,
"expiry_at": user.ExpiryAt,
"is_expired": user.ExpiryAt != nil && user.ExpiryAt.Before(time.Now()),
"is_lifetime": user.Balance == -1,
"days_remaining": calculateDaysRemaining(user.ExpiryAt, user.Balance),
}
var userDevices []model.UserDevice
database.DB.Where("user_id = ? AND application_id = ?", user.ID, app.ID).Find(&userDevices)
for _, d := range userDevices {
devices = append(devices, map[string]interface{}{
"id": d.ID,
"device_id": d.DeviceID,
"device_name": d.DeviceName,
"device_type": d.DeviceType,
"status": d.Status,
"created_at": d.CreatedAt,
})
}
var userVars []model.UserVariable
database.DB.Where("user_id = ? AND app_id = ?", user.ID, app.ID).Find(&userVars)
for _, v := range userVars {
userVariables[v.VarName] = v.VarValue
}
}
}
if err := vm.Set("user", userData); err != nil {
response.Error(c, 500, "用户数据设置失败")
return
}
if err := vm.Set("subscription", subscription); err != nil {
response.Error(c, 500, "订阅数据设置失败")
return
}
if err := vm.Set("userVariables", userVariables); err != nil {
response.Error(c, 500, "用户变量设置失败")
return
}
if err := vm.Set("devices", devices); err != nil {
response.Error(c, 500, "设备数据设置失败")
return
}
for k, v := range req.Params {
if err := vm.Set(k, v); err != nil {
response.Error(c, 500, "参数设置失败")
return
}
}
if err := vm.Set("params", req.Params); err != nil {
response.Error(c, 500, "参数设置失败")
return
}
value, err := vm.RunString("(function() { " + dynamicCode.Code + " })()")
if err != nil {
response.Error(c, 400, "代码执行错误: "+err.Error())
return
}
executionTime := time.Since(startTime).Milliseconds()
result := value.Export()
if actionMap, ok := result.(map[string]interface{}); ok {
if action, hasAction := actionMap["action"]; hasAction {
switch action {
case "extend_time":
if err := handleExtendTime(app.ID, actionMap); err != nil {
response.Error(c, 500, "执行加时操作失败: "+err.Error())
return
}
case "deduct_points":
if err := handleDeductPoints(app.ID, actionMap); err != nil {
response.Error(c, 500, "执行扣点操作失败: "+err.Error())
return
}
case "update_user_variable":
appID := app.ID
if err := handleUpdateUserVariable(&appID, req.UserID, actionMap); err != nil {
response.Error(c, 500, "更新用户变量失败: "+err.Error())
return
}
case "update_app_variable":
if err := handleUpdateAppVariable(app.ID, actionMap); err != nil {
response.Error(c, 500, "更新应用变量失败: "+err.Error())
return
}
}
}
}
response.Success(c, gin.H{
"result": result,
"execution_time": executionTime,
})
}
func calculateDaysRemaining(expiryAt *time.Time, balance float64) int {
if balance == -1 {
return -1
}
if expiryAt == nil {
return 0
}
remaining := int(time.Until(*expiryAt).Hours() / 24)
if remaining < 0 {
return 0
}
return remaining
}
func handleExtendTime(appID uint, actionMap map[string]interface{}) error {
userID, ok := actionMap["user_id"].(float64)
if !ok {
return nil
}
days, ok := actionMap["days"].(float64)
if !ok {
return nil
}
var user model.AppUser
if err := database.DB.Where("id = ? AND application_id = ?", uint(userID), appID).First(&user).Error; err != nil {
return err
}
if user.Balance == -1 {
return nil
}
user.Balance += float64(days)
return database.DB.Save(&user).Error
}
func handleDeductPoints(appID uint, actionMap map[string]interface{}) error {
userID, ok := actionMap["user_id"].(float64)
if !ok {
return nil
}
points, ok := actionMap["points"].(float64)
if !ok {
return nil
}
var user model.AppUser
if err := database.DB.Where("id = ? AND application_id = ?", uint(userID), appID).First(&user).Error; err != nil {
return err
}
if user.Balance == -1 {
return nil
}
user.Balance -= points
if user.Balance < 0 {
user.Balance = 0
}
return database.DB.Save(&user).Error
}
func handleUpdateUserVariable(appID *uint, userID *uint, actionMap map[string]interface{}) error {
if userID == nil {
return nil
}
varName, ok := actionMap["name"].(string)
if !ok {
return nil
}
varValue, ok := actionMap["value"].(string)
if !ok {
if v, ok := actionMap["value"]; ok {
varValue = toString(v)
} else {
return nil
}
}
var userVar model.UserVariable
if err := database.DB.Where("user_id = ? AND app_id = ? AND var_name = ?", *userID, appID, varName).First(&userVar).Error; err != nil {
userVar = model.UserVariable{
UserID: *userID,
AppID: *appID,
VarName: varName,
VarValue: varValue,
}
return database.DB.Create(&userVar).Error
}
userVar.VarValue = varValue
return database.DB.Save(&userVar).Error
}
func handleUpdateAppVariable(appID uint, actionMap map[string]interface{}) error {
varName, ok := actionMap["name"].(string)
if !ok {
return nil
}
varValue, ok := actionMap["value"].(string)
if !ok {
if v, ok := actionMap["value"]; ok {
varValue = toString(v)
} else {
return nil
}
}
var appVar model.CloudVariable
if err := database.DB.Where("app_id = ? AND key = ?", appID, varName).First(&appVar).Error; err != nil {
return err
}
appVar.DefaultValue = varValue
return database.DB.Save(&appVar).Error
}
func toString(v interface{}) string {
switch val := v.(type) {
case string:
return val
case float64:
return string(rune(int(val)))
case int:
return string(rune(val))
default:
return ""
}
}