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 "" } }