package app import ( "encoding/json" "fmt" "net/http" "strings" "time" "verification-platform-backend/internal/database" "verification-platform-backend/internal/model" "verification-platform-backend/internal/service" "verification-platform-backend/internal/utils/httputil" "verification-platform-backend/internal/utils/smtputil" "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 } httpClient := httputil.NewHTTPClient(10 * time.Second) httpObj := map[string]interface{}{ "get": func(url string, headers map[string]interface{}) *httputil.HTTPResponse { convertedHeaders := make(map[string]string) for k, v := range headers { convertedHeaders[k] = fmt.Sprintf("%v", v) } return httpClient.Get(url, convertedHeaders) }, "post": func(url string, headers map[string]interface{}, body interface{}) *httputil.HTTPResponse { convertedHeaders := make(map[string]string) for k, v := range headers { convertedHeaders[k] = fmt.Sprintf("%v", v) } return httpClient.Post(url, convertedHeaders, body) }, } if err := vm.Set("http", httpObj); err != nil { response.Error(c, 500, "HTTP对象设置失败") return } dbObj := map[string]interface{}{ "getRecords": func(variableKey string, page, pageSize int) map[string]interface{} { return getRecords(app.ID, req.UserID, variableKey, page, pageSize) }, "deleteRecord": func(variableKey string, recordID uint) map[string]interface{} { return deleteRecord(app.ID, req.UserID, variableKey, recordID) }, "deleteRecords": func(variableKey string, recordIDs []uint) map[string]interface{} { return deleteRecords(app.ID, req.UserID, variableKey, recordIDs) }, } if err := vm.Set("db", dbObj); err != nil { response.Error(c, 500, "数据库对象设置失败") return } smtpObj := map[string]interface{}{ "send": func(config map[string]interface{}, to []string, subject, body string, html bool) *smtputil.SMTPResult { smtpConfig := smtputil.SMTPConfig{ Host: getString(config, "host"), Port: getInt(config, "port", 587), Username: getString(config, "username"), Password: getString(config, "password"), From: getString(config, "from"), UseTLS: getBool(config, "use_tls", true), } client := smtputil.NewSMTPClient(smtpConfig, 30*time.Second) return client.Send(smtputil.EmailMessage{ To: to, Subject: subject, Body: body, HTML: html, }) }, } if err := vm.Set("smtp", smtpObj); err != nil { response.Error(c, 500, "SMTP对象设置失败") 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 } case "add_record": if err := handleAddRecord(app.ID, req.UserID, 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 "" } } func handleAddRecord(appID uint, userID *uint, actionMap map[string]interface{}) error { variableKey, ok := actionMap["variable_key"].(string) if !ok { return fmt.Errorf("缺少variable_key参数") } recordData, ok := actionMap["record_data"] if !ok { return fmt.Errorf("缺少record_data参数") } var variable model.CloudVariable if err := database.DB.Where("app_id = ? AND key = ?", appID, variableKey).First(&variable).Error; err != nil { return fmt.Errorf("云端变量不存在: %s", variableKey) } if variable.VarType != "stream" { return fmt.Errorf("该变量不是记录类型") } dataBytes, err := json.Marshal(recordData) if err != nil { return fmt.Errorf("序列化记录数据失败: %v", err) } record := model.CloudVariableRecord{ CloudVariableID: variable.ID, Data: string(dataBytes), } if variable.Scope == "user" && userID != nil { appUserID := *userID record.AppUserID = &appUserID } if err := database.DB.Create(&record).Error; err != nil { return fmt.Errorf("创建记录失败: %v", err) } if variable.MaxRecords > 0 { var total int64 database.DB.Model(&model.CloudVariableRecord{}).Where("cloud_variable_id = ?", variable.ID).Count(&total) if int(total) > variable.MaxRecords { deleteCount := int(total) - variable.MaxRecords var oldRecords []model.CloudVariableRecord database.DB.Where("cloud_variable_id = ?", variable.ID). Order("created_at ASC"). Limit(deleteCount). Find(&oldRecords) for _, oldRecord := range oldRecords { database.DB.Delete(&oldRecord) } } } return nil } func getRecords(appID uint, userID *uint, variableKey string, page, pageSize int) map[string]interface{} { result := map[string]interface{}{ "success": false, "records": []interface{}{}, "total": 0, "page": page, "pageSize": pageSize, } var variable model.CloudVariable if err := database.DB.Where("app_id = ? AND key = ?", appID, variableKey).First(&variable).Error; err != nil { result["error"] = "云端变量不存在" return result } if variable.VarType != "stream" { result["error"] = "该变量不是记录类型" return result } if page < 1 { page = 1 } if pageSize < 1 || pageSize > 100 { pageSize = 20 } var total int64 query := database.DB.Model(&model.CloudVariableRecord{}).Where("cloud_variable_id = ?", variable.ID) if variable.Scope == "user" && userID != nil { query = query.Where("app_user_id = ?", *userID) } query.Count(&total) var records []model.CloudVariableRecord offset := (page - 1) * pageSize if err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&records).Error; err != nil { result["error"] = "查询记录失败" return result } var recordList []interface{} for _, record := range records { var data interface{} if err := json.Unmarshal([]byte(record.Data), &data); err == nil { recordList = append(recordList, map[string]interface{}{ "id": record.ID, "data": data, "created_at": record.CreatedAt, }) } } result["success"] = true result["records"] = recordList result["total"] = total return result } func deleteRecord(appID uint, userID *uint, variableKey string, recordID uint) map[string]interface{} { result := map[string]interface{}{ "success": false, } var variable model.CloudVariable if err := database.DB.Where("app_id = ? AND key = ?", appID, variableKey).First(&variable).Error; err != nil { result["error"] = "云端变量不存在" return result } if variable.VarType != "stream" { result["error"] = "该变量不是记录类型" return result } query := database.DB.Where("id = ? AND cloud_variable_id = ?", recordID, variable.ID) if variable.Scope == "user" && userID != nil { query = query.Where("app_user_id = ?", *userID) } if err := query.Delete(&model.CloudVariableRecord{}).Error; err != nil { result["error"] = "删除记录失败" return result } result["success"] = true return result } func deleteRecords(appID uint, userID *uint, variableKey string, recordIDs []uint) map[string]interface{} { result := map[string]interface{}{ "success": false, "deleted": 0, } var variable model.CloudVariable if err := database.DB.Where("app_id = ? AND key = ?", appID, variableKey).First(&variable).Error; err != nil { result["error"] = "云端变量不存在" return result } if variable.VarType != "stream" { result["error"] = "该变量不是记录类型" return result } query := database.DB.Where("id IN ? AND cloud_variable_id = ?", recordIDs, variable.ID) if variable.Scope == "user" && userID != nil { query = query.Where("app_user_id = ?", *userID) } deleteResult := query.Delete(&model.CloudVariableRecord{}) if deleteResult.Error != nil { result["error"] = "删除记录失败" return result } result["success"] = true result["deleted"] = deleteResult.RowsAffected return result } func getString(m map[string]interface{}, key string) string { if v, ok := m[key]; ok { if s, ok := v.(string); ok { return s } } return "" } func getInt(m map[string]interface{}, key string, defaultValue int) int { if v, ok := m[key]; ok { switch val := v.(type) { case int: return val case float64: return int(val) } } return defaultValue } func getBool(m map[string]interface{}, key string, defaultValue bool) bool { if v, ok := m[key]; ok { if b, ok := v.(bool); ok { return b } } return defaultValue }