package utils import ( "encoding/json" "fmt" "go.uber.org/zap" "regexp" "strconv" "strings" "beacon/pkg/pb" ) // ParameterProcessor 参数处理器,用于处理activity之间的参数传递 type ParameterProcessor struct { globalVariables map[string]interface{} activityResults map[int64]*ActivityResult } // ActivityResult 用于存储每个activity的执行结果 type ActivityResult struct { StepID int64 `json:"step_id"` StepName string `json:"step_name"` Success bool `json:"success"` Data interface{} `json:"data"` } // NewParameterProcessor 创建新的参数处理器 func NewParameterProcessor(globalVariables map[string]interface{}) *ParameterProcessor { return &ParameterProcessor{ globalVariables: globalVariables, activityResults: make(map[int64]*ActivityResult), } } // AddActivityResult 添加activity执行结果 func (p *ParameterProcessor) AddActivityResult(stepID int64, result *ActivityResult) { p.activityResults[stepID] = result } // ProcessTemplate 处理参数模板,替换其中的变量引用 func (p *ParameterProcessor) ProcessTemplate(template string) (string, error) { if template == "" { return template, nil } result := template // 1. 替换全局变量引用 ${global.key} result = p.replaceGlobalVariables(result) // 2. 替换步骤结果变量引用 ${step.stepId.field} result = p.replaceStepVariables(result) // 3. 替换条件变量引用 ${if.condition:value1:value2} result = p.replaceConditionalVariables(result) // 4. 替换函数调用 ${func:arg1:arg2} result = p.replaceFunctionCalls(result) return result, nil } // replaceGlobalVariables 替换全局变量引用 func (p *ParameterProcessor) replaceGlobalVariables(template string) string { // 使用正则表达式匹配 ${global.key} 格式 re := regexp.MustCompile(`\$\{global\.([^}]+)\}`) return re.ReplaceAllStringFunc(template, func(match string) string { // 提取变量名 matches := re.FindStringSubmatch(match) if len(matches) < 2 { return match // 保持原样 } key := matches[1] if value, exists := p.globalVariables[key]; exists { return fmt.Sprintf("%v", value) } return match // 变量不存在,保持原样 }) } // replaceStepVariables 替换步骤结果变量引用 func (p *ParameterProcessor) replaceStepVariables(template string) string { // 匹配 ${step.stepId.field} 格式 re := regexp.MustCompile(`\$\{step\.(\d+)\.([^}]+)\}`) return re.ReplaceAllStringFunc(template, func(match string) string { matches := re.FindStringSubmatch(match) if len(matches) < 3 { return match } stepIDStr := matches[1] field := matches[2] stepID, err := strconv.ParseInt(stepIDStr, 10, 64) if err != nil { return match } result, exists := p.activityResults[stepID] if !exists { return match } return p.extractFieldValue(result, field) }) } // replaceConditionalVariables 替换条件变量引用 func (p *ParameterProcessor) replaceConditionalVariables(template string) string { // 匹配 ${if.condition:value1:value2} 格式 re := regexp.MustCompile(`\$\{if\.([^:]+):([^:]*):([^}]*)\}`) return re.ReplaceAllStringFunc(template, func(match string) string { matches := re.FindStringSubmatch(match) if len(matches) < 4 { return match } condition := matches[1] valueIfTrue := matches[2] valueIfFalse := matches[3] if p.evaluateCondition(condition) { return valueIfTrue } return valueIfFalse }) } // replaceFunctionCalls 替换函数调用 func (p *ParameterProcessor) replaceFunctionCalls(template string) string { // 匹配 ${func:arg1:arg2} 格式 re := regexp.MustCompile(`\$\{([^:]+):([^}]*)\}`) return re.ReplaceAllStringFunc(template, func(match string) string { matches := re.FindStringSubmatch(match) if len(matches) < 3 { return match } funcName := matches[1] args := matches[2] return p.executeFunction(funcName, args) }) } // extractFieldValue 从activity结果中提取指定字段的值 func (p *ParameterProcessor) extractFieldValue(result *ActivityResult, field string) string { switch result.Data.(type) { case *pb.ApiTestResult: return p.extractApiResultField(result.Data.(*pb.ApiTestResult), field) case *pb.UiTestResult: return p.extractUiResultField(result.Data.(*pb.UiTestResult), field) default: return "" } } // extractApiResultField 从API测试结果中提取字段值 func (p *ParameterProcessor) extractApiResultField(result *pb.ApiTestResult, field string) string { switch field { case "response_body": // 保证输出是 JSON 字符串格式 b, err := json.Marshal(result.ResponseBody) if err == nil { return string(b[1 : len(b)-1]) // 去掉外层引号 } return result.ResponseBody case "actual_status_code": return strconv.Itoa(int(result.ActualStatusCode)) case "success": return strconv.FormatBool(result.BaseResult.Success) case "message": return result.BaseResult.Message default: zap.L().Debug("未知字段", zap.String("field", field), zap.String("ResponseBody", result.ResponseBody)) // 检查是否是响应头 if strings.HasPrefix(field, "headers.") { headerKey := strings.TrimPrefix(field, "headers.") if headerValue, exists := result.Headers[headerKey]; exists { return headerValue } } // 检查是否是JSON字段 if strings.HasPrefix(field, "json.") { jsonField := strings.TrimPrefix(field, "json.") return p.extractJsonField(result.ResponseBody, jsonField) } return "" } } // extractUiResultField 从UI测试结果中提取字段值 func (p *ParameterProcessor) extractUiResultField(result *pb.UiTestResult, field string) string { switch field { case "screenshot_url": return result.ScreenshotUrl case "html_report_url": return result.HtmlReportUrl case "success": return strconv.FormatBool(result.BaseResult.Success) case "message": return result.BaseResult.Message default: return "" } } // extractJsonField 从JSON响应体中提取指定字段 func (p *ParameterProcessor) extractJsonField(responseBody, field string) string { if responseBody == "" { return "" } var data map[string]interface{} if err := json.Unmarshal([]byte(responseBody), &data); err != nil { return "" } // 支持嵌套字段,如 "user.id" keys := strings.Split(field, ".") current := data for i, key := range keys { if i == len(keys)-1 { // 最后一个键 if value, exists := current[key]; exists { return fmt.Sprintf("%v", value) } } else { // 中间键,需要继续深入 if next, exists := current[key]; exists { if nextMap, ok := next.(map[string]interface{}); ok { current = nextMap } else { return "" } } else { return "" } } } return "" } // evaluateCondition 评估条件表达式 func (p *ParameterProcessor) evaluateCondition(condition string) bool { // 支持简单的条件表达式 // 例如:step.123.success, global.environment == "production" // 检查步骤成功状态 if strings.HasSuffix(condition, ".success") { stepIDStr := strings.TrimSuffix(condition, ".success") if strings.HasPrefix(stepIDStr, "step.") { stepIDStr = strings.TrimPrefix(stepIDStr, "step.") stepID, err := strconv.ParseInt(stepIDStr, 10, 64) if err == nil { if result, exists := p.activityResults[stepID]; exists { return result.Success } } } } // 检查全局变量 if strings.HasPrefix(condition, "global.") { key := strings.TrimPrefix(condition, "global.") if value, exists := p.globalVariables[key]; exists { // 转换为布尔值 switch v := value.(type) { case bool: return v case string: return v != "" && v != "false" && v != "0" case int, int64, float64: return fmt.Sprintf("%v", v) != "0" default: return value != nil } } } // 检查相等性 if strings.Contains(condition, "==") { parts := strings.Split(condition, "==") if len(parts) == 2 { left := strings.TrimSpace(parts[0]) right := strings.TrimSpace(strings.Trim(parts[1], `"'`)) // 检查全局变量 if strings.HasPrefix(left, "global.") { key := strings.TrimPrefix(left, "global.") if value, exists := p.globalVariables[key]; exists { return fmt.Sprintf("%v", value) == right } } } } return false } // executeFunction 执行函数调用 func (p *ParameterProcessor) executeFunction(funcName, args string) string { argList := strings.Split(args, ":") switch funcName { case "concat": // 连接字符串 return strings.Join(argList, "") case "upper": // 转换为大写 if len(argList) > 0 { return strings.ToUpper(argList[0]) } case "lower": // 转换为小写 if len(argList) > 0 { return strings.ToLower(argList[0]) } case "substring": // 子字符串 if len(argList) >= 3 { str := argList[0] start, _ := strconv.Atoi(argList[1]) end, _ := strconv.Atoi(argList[2]) if start < len(str) && end <= len(str) && start < end { return str[start:end] } } case "default": // 默认值 if len(argList) >= 2 { value := argList[0] defaultValue := argList[1] if value == "" { return defaultValue } return value } } return "" } // GetAvailableVariables 获取所有可用的变量列表 func (p *ParameterProcessor) GetAvailableVariables() map[string][]string { variables := make(map[string][]string) // 全局变量 var globalVars []string for key := range p.globalVariables { globalVars = append(globalVars, key) } variables["global"] = globalVars // 步骤变量 for stepID, result := range p.activityResults { stepKey := fmt.Sprintf("step.%d", stepID) var stepVars []string switch result.Data.(type) { case *pb.ApiTestResult: stepVars = []string{ "response_body", "actual_status_code", "success", "message", "headers.*", "json.*", } case *pb.UiTestResult: stepVars = []string{ "screenshot_url", "html_report_url", "success", "message", } } variables[stepKey] = stepVars } return variables } // ValidateTemplate 验证模板中的变量引用是否有效 func (p *ParameterProcessor) ValidateTemplate(template string) (bool, []string) { var errors []string // 检查全局变量引用 globalRe := regexp.MustCompile(`\$\{global\.([^}]+)\}`) matches := globalRe.FindAllStringSubmatch(template, -1) for _, match := range matches { if len(match) >= 2 { key := match[1] if _, exists := p.globalVariables[key]; !exists { errors = append(errors, fmt.Sprintf("全局变量 '%s' 不存在", key)) } } } // 检查步骤变量引用 stepRe := regexp.MustCompile(`\$\{step\.(\d+)\.([^}]+)\}`) matches = stepRe.FindAllStringSubmatch(template, -1) for _, match := range matches { if len(match) >= 3 { stepIDStr := match[1] field := match[2] stepID, err := strconv.ParseInt(stepIDStr, 10, 64) if err != nil { errors = append(errors, fmt.Sprintf("无效的步骤ID: %s", stepIDStr)) continue } if _, exists := p.activityResults[stepID]; !exists { errors = append(errors, fmt.Sprintf("步骤 %d 不存在", stepID)) continue } // 检查字段是否有效 if !p.isValidField(stepID, field) { errors = append(errors, fmt.Sprintf("步骤 %d 不支持字段: %s", stepID, field)) } } } return len(errors) == 0, errors } // isValidField 检查字段是否有效 func (p *ParameterProcessor) isValidField(stepID int64, field string) bool { result, exists := p.activityResults[stepID] if !exists { return false } switch result.Data.(type) { case *pb.ApiTestResult: validFields := map[string]bool{ "response_body": true, "actual_status_code": true, "success": true, "message": true, } if validFields[field] { return true } // 检查是否是响应头 if strings.HasPrefix(field, "headers.") { return true } // 检查是否是JSON字段 if strings.HasPrefix(field, "json.") { return true } case *pb.UiTestResult: validFields := map[string]bool{ "screenshot_url": true, "html_report_url": true, "success": true, "message": true, } return validFields[field] } return false }