适配业务
This commit is contained in:
@@ -40,43 +40,111 @@ def validate_response_is_true(response_ctx: APIResponseContext, stage_ctx: dict)
|
||||
return ValidationResult(passed=False, message=f"Expected response data to be true, but it was '{response_data.get('data')}'.")
|
||||
|
||||
def validate_resource_details(response_ctx: APIResponseContext, stage_ctx: dict) -> ValidationResult:
|
||||
"""Validates the details of a resource against the context."""
|
||||
"""验证资源详情,支持READ(对象)和LIST(数组)两种响应格式"""
|
||||
pk_name = stage_ctx.get("pk_name")
|
||||
pk_value = stage_ctx.get("pk_value")
|
||||
payload = stage_ctx.get("current_payload")
|
||||
use_list_for_read = stage_ctx.get("use_list_for_read", False)
|
||||
|
||||
response_data = _get_value_by_path(response_ctx.json_content, "data")
|
||||
if not isinstance(response_data, dict):
|
||||
return ValidationResult(passed=False, message=f"Response 'data' field is not a JSON object. Got: {response_data}")
|
||||
|
||||
# Check if all fields from the payload exist in the response and match
|
||||
if use_list_for_read:
|
||||
# LIST响应:data是数组,需要在数组中找到匹配的资源
|
||||
if not isinstance(response_data, list):
|
||||
return ValidationResult(passed=False, message=f"LIST响应的'data'字段应该是数组。实际: {type(response_data)}")
|
||||
|
||||
# 在数组中查找匹配所有主键的资源
|
||||
identity_id_list = stage_ctx.get("identity_id_list", [pk_name])
|
||||
matching_resource = None
|
||||
|
||||
for item in response_data:
|
||||
if isinstance(item, dict):
|
||||
# 检查是否所有主键都匹配
|
||||
all_match = True
|
||||
for pk_field in identity_id_list:
|
||||
if pk_field in payload and item.get(pk_field) != payload[pk_field]:
|
||||
all_match = False
|
||||
break
|
||||
|
||||
if all_match:
|
||||
matching_resource = item
|
||||
break
|
||||
|
||||
if not matching_resource:
|
||||
return ValidationResult(passed=False, message=f"在LIST响应中未找到匹配的资源,主键: {identity_id_list}")
|
||||
|
||||
# 验证找到的资源
|
||||
resource_to_validate = matching_resource
|
||||
else:
|
||||
# READ响应:data是对象
|
||||
if not isinstance(response_data, dict):
|
||||
return ValidationResult(passed=False, message=f"READ响应的'data'字段应该是对象。实际: {type(response_data)}")
|
||||
|
||||
resource_to_validate = response_data
|
||||
|
||||
# 验证资源字段
|
||||
for key, expected_value in payload.items():
|
||||
if key not in response_data:
|
||||
return ValidationResult(passed=False, message=f"Field '{key}' from payload not found in response.")
|
||||
if response_data[key] != expected_value:
|
||||
return ValidationResult(passed=False, message=f"Field '{key}' mismatch. Expected '{expected_value}', got '{response_data[key]}'.")
|
||||
if key not in resource_to_validate:
|
||||
return ValidationResult(passed=False, message=f"字段'{key}'在响应中不存在。")
|
||||
if resource_to_validate[key] != expected_value:
|
||||
return ValidationResult(passed=False, message=f"字段'{key}'不匹配。期望: '{expected_value}', 实际: '{resource_to_validate[key]}'。")
|
||||
|
||||
return ValidationResult(passed=True, message="Resource details successfully validated against payload.")
|
||||
operation_type = "LIST查询" if use_list_for_read else "READ查询"
|
||||
return ValidationResult(passed=True, message=f"资源详情验证成功({operation_type})。")
|
||||
|
||||
|
||||
def validate_resource_details_after_update(response_ctx: APIResponseContext, stage_ctx: dict) -> ValidationResult:
|
||||
"""Validates the details of a resource against the *update* payload from the context."""
|
||||
"""验证更新后的资源详情,支持READ(对象)和LIST(数组)两种响应格式"""
|
||||
pk_name = stage_ctx.get("pk_name")
|
||||
pk_value = stage_ctx.get("pk_value")
|
||||
payload = stage_ctx.get("update_payload") # Use the specific update payload
|
||||
payload = stage_ctx.get("update_payload") # 使用更新负载
|
||||
use_list_for_read = stage_ctx.get("use_list_for_read", False)
|
||||
|
||||
response_data = _get_value_by_path(response_ctx.json_content, "data")
|
||||
if not isinstance(response_data, dict):
|
||||
return ValidationResult(passed=False, message=f"Response 'data' field is not a JSON object. Got: {response_data}")
|
||||
|
||||
# Check if all fields from the payload exist in the response and match
|
||||
if use_list_for_read:
|
||||
# LIST响应:data是数组,需要在数组中找到匹配的资源
|
||||
if not isinstance(response_data, list):
|
||||
return ValidationResult(passed=False, message=f"LIST响应的'data'字段应该是数组。实际: {type(response_data)}")
|
||||
|
||||
# 在数组中查找匹配所有主键的资源
|
||||
identity_id_list = stage_ctx.get("identity_id_list", [pk_name])
|
||||
matching_resource = None
|
||||
|
||||
for item in response_data:
|
||||
if isinstance(item, dict):
|
||||
# 检查是否所有主键都匹配
|
||||
all_match = True
|
||||
for pk_field in identity_id_list:
|
||||
if pk_field in payload and item.get(pk_field) != payload[pk_field]:
|
||||
all_match = False
|
||||
break
|
||||
|
||||
if all_match:
|
||||
matching_resource = item
|
||||
break
|
||||
|
||||
if not matching_resource:
|
||||
return ValidationResult(passed=False, message=f"在LIST响应中未找到匹配的更新资源,主键: {identity_id_list}")
|
||||
|
||||
# 验证找到的资源
|
||||
resource_to_validate = matching_resource
|
||||
else:
|
||||
# READ响应:data是对象
|
||||
if not isinstance(response_data, dict):
|
||||
return ValidationResult(passed=False, message=f"READ响应的'data'字段应该是对象。实际: {type(response_data)}")
|
||||
|
||||
resource_to_validate = response_data
|
||||
|
||||
# 验证更新后的资源字段
|
||||
for key, expected_value in payload.items():
|
||||
if key not in response_data:
|
||||
return ValidationResult(passed=False, message=f"Field '{key}' from update_payload not found in response.")
|
||||
if response_data[key] != expected_value:
|
||||
return ValidationResult(passed=False, message=f"Field '{key}' mismatch. Expected '{expected_value}' from update_payload, got '{response_data[key]}'.")
|
||||
if key not in resource_to_validate:
|
||||
return ValidationResult(passed=False, message=f"更新字段'{key}'在响应中不存在。")
|
||||
if resource_to_validate[key] != expected_value:
|
||||
return ValidationResult(passed=False, message=f"更新字段'{key}'不匹配。期望: '{expected_value}', 实际: '{resource_to_validate[key]}'。")
|
||||
|
||||
return ValidationResult(passed=True, message="Resource details successfully validated against update_payload.")
|
||||
operation_type = "LIST查询" if use_list_for_read else "READ查询"
|
||||
return ValidationResult(passed=True, message=f"更新后资源详情验证成功({operation_type})。")
|
||||
|
||||
|
||||
def validate_resource_is_deleted(response_ctx: APIResponseContext, stage_ctx: dict) -> ValidationResult:
|
||||
@@ -174,7 +242,21 @@ class DmsCrudScenarioStage(BaseAPIStage):
|
||||
delete_op = current_scenario['delete']
|
||||
pk_name = next(iter(delete_op.request_body['content']['application/json']['schema']['properties']['data']['items']['properties']))
|
||||
|
||||
# 获取完整的主键列表
|
||||
identity_id_list = getattr(create_op, 'identity_id_list', [])
|
||||
if not identity_id_list:
|
||||
identity_id_list = [pk_name] if pk_name else []
|
||||
|
||||
# 为主要主键生成值
|
||||
pk_value = str(uuid.uuid4())
|
||||
|
||||
# 为所有主键生成值
|
||||
all_pk_values = {}
|
||||
for pk_field in identity_id_list:
|
||||
if pk_field == pk_name:
|
||||
all_pk_values[pk_field] = pk_value
|
||||
else:
|
||||
all_pk_values[pk_field] = self._generate_default_key_value(pk_field, {"type": "string"})
|
||||
|
||||
# 使用测试框架的数据生成器生成完整有效的请求负载
|
||||
# from ddms_compliance_suite.utils.schema_utils import DataGenerator
|
||||
@@ -186,23 +268,76 @@ class DmsCrudScenarioStage(BaseAPIStage):
|
||||
if 'application/json' in content and 'schema' in content['application/json']:
|
||||
create_schema = content['application/json']['schema']
|
||||
|
||||
# 生成创建请求负载
|
||||
data_generator = DataGenerator(logger_param=self.logger)
|
||||
# 生成创建请求负载 - 优先使用LLM智能生成
|
||||
create_payload = all_pk_values.copy() # 包含所有主键的基础负载
|
||||
|
||||
if create_schema:
|
||||
# 生成基于模式的数据
|
||||
generated_data = data_generator.generate_data_from_schema(create_schema)
|
||||
# 确保主键字段存在且被正确设置
|
||||
if isinstance(generated_data, dict) and 'data' in generated_data and isinstance(generated_data['data'], list) and len(generated_data['data']) > 0:
|
||||
generated_data['data'][0][pk_name] = pk_value
|
||||
create_payload = generated_data['data'][0]
|
||||
# 尝试使用LLM智能生成数据(如果可用)
|
||||
if self.llm_service:
|
||||
self.logger.info(f"使用LLM为CRUD Stage生成智能测试数据,端点: {create_op.path}")
|
||||
|
||||
# 构建针对业务规则的提示
|
||||
business_rules_prompt = self._build_business_rules_prompt(create_schema, pk_name, pk_value)
|
||||
|
||||
try:
|
||||
llm_generated_data = self.llm_service.generate_data_from_schema(
|
||||
create_schema,
|
||||
prompt_instruction=business_rules_prompt,
|
||||
max_tokens=1024,
|
||||
temperature=0.1
|
||||
)
|
||||
|
||||
if llm_generated_data and isinstance(llm_generated_data, dict):
|
||||
# 处理LLM生成的数据结构
|
||||
if 'data' in llm_generated_data and isinstance(llm_generated_data['data'], list) and len(llm_generated_data['data']) > 0:
|
||||
create_payload = llm_generated_data['data'][0]
|
||||
elif 'data' in llm_generated_data and isinstance(llm_generated_data['data'], dict):
|
||||
create_payload = llm_generated_data['data']
|
||||
else:
|
||||
create_payload = llm_generated_data
|
||||
|
||||
# 确保所有主键字段正确设置
|
||||
for pk_field, pk_val in all_pk_values.items():
|
||||
create_payload[pk_field] = pk_val
|
||||
self.logger.info(f"LLM成功生成智能测试数据: {create_payload}")
|
||||
else:
|
||||
self.logger.warning("LLM生成的数据格式不符合预期,回退到传统数据生成")
|
||||
raise ValueError("LLM数据格式无效")
|
||||
|
||||
except Exception as e:
|
||||
self.logger.warning(f"LLM数据生成失败: {e},回退到传统数据生成")
|
||||
# 回退到传统数据生成
|
||||
data_generator = DataGenerator(logger_param=self.logger)
|
||||
generated_data = data_generator.generate_data_from_schema(create_schema, context_name="create_payload", llm_service=self.llm_service)
|
||||
if isinstance(generated_data, dict) and 'data' in generated_data and isinstance(generated_data['data'], list) and len(generated_data['data']) > 0:
|
||||
# 设置所有主键字段
|
||||
for pk_field, pk_val in all_pk_values.items():
|
||||
generated_data['data'][0][pk_field] = pk_val
|
||||
create_payload = generated_data['data'][0]
|
||||
elif isinstance(generated_data, dict):
|
||||
# 设置所有主键字段
|
||||
for pk_field, pk_val in all_pk_values.items():
|
||||
generated_data[pk_field] = pk_val
|
||||
create_payload = generated_data
|
||||
else:
|
||||
# 如果生成的数据结构不符合预期,使用基本负载
|
||||
self.logger.warning("Generated data structure was not as expected. Falling back to a minimal payload.")
|
||||
create_payload = { pk_name: pk_value }
|
||||
# 使用传统数据生成器(但仍然传递LLM服务以便在内部尝试使用)
|
||||
self.logger.info("LLM服务不可用,使用传统数据生成器")
|
||||
data_generator = DataGenerator(logger_param=self.logger)
|
||||
generated_data = data_generator.generate_data_from_schema(create_schema, context_name="create_payload", llm_service=None)
|
||||
if isinstance(generated_data, dict) and 'data' in generated_data and isinstance(generated_data['data'], list) and len(generated_data['data']) > 0:
|
||||
# 设置所有主键字段
|
||||
for pk_field, pk_val in all_pk_values.items():
|
||||
generated_data['data'][0][pk_field] = pk_val
|
||||
create_payload = generated_data['data'][0]
|
||||
elif isinstance(generated_data, dict):
|
||||
# 设置所有主键字段
|
||||
for pk_field, pk_val in all_pk_values.items():
|
||||
generated_data[pk_field] = pk_val
|
||||
create_payload = generated_data
|
||||
else:
|
||||
self.logger.warning("Generated data structure was not as expected. Falling back to a minimal payload.")
|
||||
else:
|
||||
# 如果没有模式,使用基本负载
|
||||
self.logger.warning("No create schema found. Falling back to a minimal payload.")
|
||||
create_payload = { pk_name: pk_value }
|
||||
|
||||
# 更新负载基于创建负载,但修改描述字段
|
||||
update_payload = copy.deepcopy(create_payload)
|
||||
@@ -216,13 +351,43 @@ class DmsCrudScenarioStage(BaseAPIStage):
|
||||
stage_context["scenario_endpoints"] = current_scenario
|
||||
|
||||
# Pre-build the delete body to avoid key-templating issues later
|
||||
# Per user request, the delete body should be an array of PK values
|
||||
stage_context["delete_request_body"] = {"data": [pk_value]}
|
||||
# 构建删除请求体,支持多主键的对象列表
|
||||
delete_request_body = self._build_delete_request_body(current_scenario, pk_name, pk_value, create_payload)
|
||||
stage_context["delete_request_body"] = delete_request_body
|
||||
|
||||
# 为查询步骤准备参数(单主键用READ,多主键用LIST)
|
||||
if len(identity_id_list) > 1:
|
||||
# 多主键:使用LIST操作,准备查询过滤条件
|
||||
list_filter_payload = self._build_list_filter_payload(identity_id_list, all_pk_values)
|
||||
stage_context["use_list_for_read"] = True
|
||||
stage_context["list_filter_payload"] = list_filter_payload
|
||||
self.logger.info(f"多主键场景,使用LIST操作代替READ,过滤条件: {list_filter_payload}")
|
||||
else:
|
||||
# 单主键:使用READ操作
|
||||
read_path_params = {"id": pk_value}
|
||||
stage_context["use_list_for_read"] = False
|
||||
stage_context["read_path_params"] = read_path_params
|
||||
self.logger.info(f"单主键场景,使用READ操作,路径参数: {read_path_params}")
|
||||
|
||||
def get_api_spec_for_operation(self, lookup_key: str, *args, **kwargs) -> Optional[Endpoint]:
|
||||
"""
|
||||
Resolves a lookup key like "CREATE" to the actual endpoint for the current scenario.
|
||||
"""
|
||||
# 处理动态的VERIFY_READ操作
|
||||
if lookup_key == "VERIFY_READ":
|
||||
# 从stage_context中获取是否使用LIST代替READ
|
||||
stage_context = kwargs.get('stage_context', {})
|
||||
use_list_for_read = stage_context.get('use_list_for_read', False)
|
||||
|
||||
if use_list_for_read:
|
||||
# 多主键场景:使用LIST操作
|
||||
lookup_key = "LIST"
|
||||
self.logger.info("多主键场景:VERIFY_READ使用LIST操作")
|
||||
else:
|
||||
# 单主键场景:使用READ操作
|
||||
lookup_key = "READ"
|
||||
self.logger.info("单主键场景:VERIFY_READ使用READ操作")
|
||||
|
||||
op_map = {
|
||||
"CREATE": "create", "READ": "read", "UPDATE": "update",
|
||||
"DELETE": "delete", "LIST": "list"
|
||||
@@ -230,7 +395,7 @@ class DmsCrudScenarioStage(BaseAPIStage):
|
||||
op_type = op_map.get(lookup_key)
|
||||
if not op_type:
|
||||
return None
|
||||
|
||||
|
||||
scenario = self.scenarios[self.current_scenario_index]
|
||||
return scenario.get(op_type)
|
||||
|
||||
@@ -246,10 +411,11 @@ class DmsCrudScenarioStage(BaseAPIStage):
|
||||
outputs_to_context={}
|
||||
),
|
||||
StageStepDefinition(
|
||||
name="Step 2: Read Resource to Verify Creation",
|
||||
endpoint_spec_lookup_key="READ",
|
||||
name="Step 2: Verify Resource Creation",
|
||||
endpoint_spec_lookup_key="VERIFY_READ", # 动态选择READ或LIST
|
||||
request_overrides={
|
||||
"path_params": {"id": "{{stage_context.pk_value}}"}
|
||||
"path_params": "{{stage_context.read_path_params}}",
|
||||
"request_body": "{{stage_context.list_filter_payload}}"
|
||||
},
|
||||
response_assertions=[validate_resource_details]
|
||||
),
|
||||
@@ -262,10 +428,11 @@ class DmsCrudScenarioStage(BaseAPIStage):
|
||||
response_assertions=[validate_response_is_true]
|
||||
),
|
||||
StageStepDefinition(
|
||||
name="Step 4: Read Resource to Verify Update",
|
||||
endpoint_spec_lookup_key="READ",
|
||||
name="Step 4: Verify Resource Update",
|
||||
endpoint_spec_lookup_key="VERIFY_READ", # 动态选择READ或LIST
|
||||
request_overrides={
|
||||
"path_params": {"id": "{{stage_context.pk_value}}"}
|
||||
"path_params": "{{stage_context.read_path_params}}",
|
||||
"request_body": "{{stage_context.list_filter_payload}}"
|
||||
},
|
||||
response_assertions=[validate_resource_details_after_update]
|
||||
),
|
||||
@@ -300,4 +467,260 @@ class DmsCrudScenarioStage(BaseAPIStage):
|
||||
# get resource name from first op
|
||||
op_id = next(iter(scenario.values())).operation_id
|
||||
resource_name = op_id.split('_', 1)[1]
|
||||
stage_result.description += f" (Scenario for: {resource_name})"
|
||||
stage_result.description += f" (Scenario for: {resource_name})"
|
||||
|
||||
return stage_result
|
||||
|
||||
def _build_business_rules_prompt(self, schema: Dict[str, Any], pk_name: str, pk_value: str) -> str:
|
||||
"""构建包含业务规则的LLM提示"""
|
||||
|
||||
# 分析schema中的业务规则
|
||||
business_rules = []
|
||||
|
||||
def analyze_properties(properties: Dict[str, Any], path: str = ""):
|
||||
"""递归分析属性中的业务规则"""
|
||||
for prop_name, prop_schema in properties.items():
|
||||
current_path = f"{path}.{prop_name}" if path else prop_name
|
||||
|
||||
# 检查枚举值
|
||||
if 'enum' in prop_schema:
|
||||
enum_values = prop_schema['enum']
|
||||
business_rules.append(f"字段 '{prop_name}' 只能取值: {enum_values}")
|
||||
|
||||
# 检查特殊字段的业务规则
|
||||
if prop_name == 'bsflag':
|
||||
business_rules.append(f"字段 'bsflag' 是删除标识,只能是 1(正常数据)或 -5(废弃数据)")
|
||||
|
||||
# 检查日期字段
|
||||
if prop_schema.get('type') == 'date' or prop_schema.get('format') == 'date':
|
||||
business_rules.append(f"字段 '{prop_name}' 是日期字段,需要使用合理的日期值")
|
||||
|
||||
# 检查必需字段
|
||||
if prop_name in schema.get('required', []):
|
||||
business_rules.append(f"字段 '{prop_name}' 是必需字段,不能为空")
|
||||
|
||||
# 检查字符串长度限制
|
||||
if prop_schema.get('type') == 'string':
|
||||
if 'maxLength' in prop_schema:
|
||||
business_rules.append(f"字段 '{prop_name}' 最大长度为 {prop_schema['maxLength']}")
|
||||
if 'minLength' in prop_schema:
|
||||
business_rules.append(f"字段 '{prop_name}' 最小长度为 {prop_schema['minLength']}")
|
||||
|
||||
# 检查数值范围
|
||||
if prop_schema.get('type') in ['number', 'integer']:
|
||||
if 'minimum' in prop_schema:
|
||||
business_rules.append(f"字段 '{prop_name}' 最小值为 {prop_schema['minimum']}")
|
||||
if 'maximum' in prop_schema:
|
||||
business_rules.append(f"字段 '{prop_name}' 最大值为 {prop_schema['maximum']}")
|
||||
|
||||
# 递归处理嵌套对象
|
||||
if prop_schema.get('type') == 'object' and 'properties' in prop_schema:
|
||||
analyze_properties(prop_schema['properties'], current_path)
|
||||
|
||||
# 处理数组中的对象
|
||||
if prop_schema.get('type') == 'array' and 'items' in prop_schema:
|
||||
items_schema = prop_schema['items']
|
||||
if items_schema.get('type') == 'object' and 'properties' in items_schema:
|
||||
analyze_properties(items_schema['properties'], f"{current_path}[]")
|
||||
|
||||
# 分析根级属性
|
||||
if 'properties' in schema:
|
||||
analyze_properties(schema['properties'])
|
||||
|
||||
# 处理数组类型的schema
|
||||
if schema.get('type') == 'array' and 'items' in schema:
|
||||
items_schema = schema['items']
|
||||
if 'properties' in items_schema:
|
||||
analyze_properties(items_schema['properties'])
|
||||
|
||||
# 构建提示文本
|
||||
prompt = f"""请为DMS数据管理系统生成符合业务规则的测试数据。
|
||||
|
||||
主键信息:
|
||||
- 主键字段: {pk_name}
|
||||
- 主键值: {pk_value}
|
||||
|
||||
业务规则约束:
|
||||
"""
|
||||
|
||||
if business_rules:
|
||||
for i, rule in enumerate(business_rules, 1):
|
||||
prompt += f"{i}. {rule}\n"
|
||||
else:
|
||||
prompt += "- 无特殊业务规则约束\n"
|
||||
|
||||
prompt += """
|
||||
数据生成要求:
|
||||
1. 严格遵守上述业务规则约束
|
||||
2. 生成真实、合理的测试数据
|
||||
3. 日期字段使用当前日期或合理的历史日期
|
||||
4. 字符串字段使用有意义的中文内容
|
||||
5. 数值字段使用合理的数值范围
|
||||
6. 确保所有必需字段都有值
|
||||
|
||||
请生成一个完整的JSON对象,包含所有必要的字段和合理的测试数据。"""
|
||||
|
||||
return prompt
|
||||
|
||||
def _build_read_path_params(self, identity_id_list: List[str], all_pk_values: Dict[str, str]) -> Dict[str, str]:
|
||||
"""构建READ步骤的路径参数"""
|
||||
|
||||
if not identity_id_list or len(identity_id_list) <= 1:
|
||||
# 单主键:使用传统的id参数
|
||||
primary_pk_value = next(iter(all_pk_values.values())) if all_pk_values else ""
|
||||
return {"id": primary_pk_value}
|
||||
else:
|
||||
# 多主键:使用所有主键作为路径参数
|
||||
path_params = {}
|
||||
for pk_field in identity_id_list:
|
||||
if pk_field in all_pk_values:
|
||||
path_params[pk_field] = all_pk_values[pk_field]
|
||||
else:
|
||||
# 如果缺少某个主键值,生成默认值
|
||||
path_params[pk_field] = self._generate_default_key_value(pk_field, {"type": "string"})
|
||||
self.logger.warning(f"READ路径参数缺少主键 {pk_field},使用默认值: {path_params[pk_field]}")
|
||||
|
||||
self.logger.info(f"构建多主键READ路径参数: {path_params}")
|
||||
return path_params
|
||||
|
||||
def _build_list_filter_payload(self, identity_id_list: List[str], all_pk_values: Dict[str, str]) -> Dict[str, Any]:
|
||||
"""构建LIST操作的过滤条件,用于多主键场景的查询,使用简化的单条件模式"""
|
||||
|
||||
# 选择第一个可用的主键作为过滤条件(简化模式)
|
||||
filter_key = None
|
||||
filter_value = None
|
||||
|
||||
for pk_field in identity_id_list:
|
||||
if pk_field in all_pk_values:
|
||||
filter_key = pk_field
|
||||
filter_value = all_pk_values[pk_field]
|
||||
break
|
||||
|
||||
# 构建LIST请求体,使用固定的简化模式
|
||||
if filter_key and filter_value:
|
||||
list_payload = {
|
||||
"isSearchCount": True,
|
||||
"query": {
|
||||
"fields": [],
|
||||
"filter": {
|
||||
"logic": "AND",
|
||||
"realValue": [],
|
||||
"subFilter": [
|
||||
{
|
||||
"key": filter_key,
|
||||
"logic": "AND",
|
||||
"realValue": [filter_value],
|
||||
"subFilter": [],
|
||||
"symbol": "="
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
self.logger.info(f"构建LIST过滤条件,使用主键 {filter_key}={filter_value}")
|
||||
else:
|
||||
# 没有可用的过滤条件,返回基本查询
|
||||
list_payload = {
|
||||
"isSearchCount": True,
|
||||
"query": {
|
||||
"fields": [],
|
||||
"filter": {
|
||||
"logic": "AND",
|
||||
"realValue": [],
|
||||
"subFilter": []
|
||||
}
|
||||
}
|
||||
}
|
||||
self.logger.warning("没有可用的主键过滤条件,将返回所有数据")
|
||||
|
||||
return list_payload
|
||||
|
||||
def _build_delete_request_body(self, scenario: Dict[str, Any], pk_name: str, pk_value: str, create_payload: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""构建删除请求体,根据identityId列表长度决定格式"""
|
||||
|
||||
delete_op = scenario.get('delete')
|
||||
if not delete_op or not isinstance(delete_op, DMSEndpoint):
|
||||
# 回退到简单的主键值列表
|
||||
self.logger.warning("无法获取删除操作信息,使用简单主键值列表")
|
||||
return {"data": [pk_value]}
|
||||
|
||||
# 获取identityId列表
|
||||
identity_id_list = getattr(delete_op, 'identity_id_list', [])
|
||||
|
||||
if not identity_id_list:
|
||||
self.logger.warning("删除操作没有identityId信息,使用简单主键值列表")
|
||||
return {"data": [pk_value]}
|
||||
|
||||
# 根据identityId列表长度判断删除格式
|
||||
if len(identity_id_list) > 1:
|
||||
# 多主键:使用对象列表
|
||||
self.logger.info(f"检测到多主键删除操作,主键字段: {identity_id_list}")
|
||||
return self._build_multi_key_delete_body(identity_id_list, pk_name, pk_value, create_payload)
|
||||
else:
|
||||
# 单主键:使用字符串列表
|
||||
self.logger.info(f"检测到单主键删除操作,主键字段: {identity_id_list[0]}")
|
||||
return {"data": [pk_value]}
|
||||
|
||||
def _build_multi_key_delete_body(self, identity_id_list: List[str], primary_pk_name: str, primary_pk_value: str, create_payload: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""构建多主键的删除请求体"""
|
||||
|
||||
# 构建删除对象,包含所有主键字段
|
||||
delete_object = {}
|
||||
|
||||
# 设置所有主键字段
|
||||
for pk_field in identity_id_list:
|
||||
if pk_field == primary_pk_name:
|
||||
# 主要主键使用传入的值
|
||||
delete_object[pk_field] = primary_pk_value
|
||||
elif pk_field in create_payload:
|
||||
# 从创建负载中提取其他主键
|
||||
delete_object[pk_field] = create_payload[pk_field]
|
||||
self.logger.debug(f"从创建负载中提取主键字段: {pk_field} = {create_payload[pk_field]}")
|
||||
else:
|
||||
# 为缺失的主键生成默认值
|
||||
default_value = self._generate_default_key_value(pk_field, {"type": "string"})
|
||||
delete_object[pk_field] = default_value
|
||||
self.logger.debug(f"为删除对象生成默认主键值: {pk_field} = {default_value}")
|
||||
|
||||
# 支持批量删除:生成多个删除对象
|
||||
delete_objects = [delete_object]
|
||||
|
||||
# 可以添加第二个对象用于测试批量删除
|
||||
if len(identity_id_list) > 1:
|
||||
second_object = delete_object.copy()
|
||||
# 修改非主要主键的值来创建第二个对象
|
||||
for pk_field in identity_id_list:
|
||||
if pk_field != primary_pk_name and isinstance(second_object[pk_field], str):
|
||||
original_value = second_object[pk_field]
|
||||
if original_value.endswith('1'):
|
||||
second_object[pk_field] = original_value[:-1] + '2'
|
||||
else:
|
||||
second_object[pk_field] = original_value + '_2'
|
||||
|
||||
delete_objects.append(second_object)
|
||||
self.logger.info(f"生成批量删除对象,共{len(delete_objects)}个,主键字段: {identity_id_list}")
|
||||
|
||||
return {
|
||||
"version": "1.0.0",
|
||||
"data": delete_objects
|
||||
}
|
||||
|
||||
def _generate_default_key_value(self, field_name: str, field_schema: Dict[str, Any]) -> str:
|
||||
"""为主键字段生成默认值"""
|
||||
|
||||
field_type = field_schema.get('type', 'string')
|
||||
|
||||
if field_type == 'string':
|
||||
# 根据字段名生成语义化的值
|
||||
if 'project' in field_name.lower():
|
||||
return f"项目{uuid.uuid4().hex[:4]}"
|
||||
elif 'survey' in field_name.lower():
|
||||
return f"工区{uuid.uuid4().hex[:4]}"
|
||||
elif 'site' in field_name.lower():
|
||||
return f"站点{uuid.uuid4().hex[:4]}"
|
||||
else:
|
||||
return f"{field_name}_{uuid.uuid4().hex[:8]}"
|
||||
elif field_type in ['number', 'integer']:
|
||||
return 1
|
||||
else:
|
||||
return f"default_{field_name}"
|
||||
Reference in New Issue
Block a user