Files
effekt-interface/common/dataBuilderExecutor.py
2026-05-07 19:21:19 +08:00

58 lines
2.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# encoding: UTF-8
import random
import re
import string
class DataBuilderExecutor(object):
"""造数器同步执行器。
当前版本只做安全的模板渲染和内置随机函数,不执行用户脚本,避免引入任意代码执行风险。
"""
def __init__(self, builder_def, env=None):
# builder_def 对应 data_builder.definition 字段,约定为 JSON 对象。
self.builder_def = builder_def or {}
# 保留 steps 字段,后续扩展 http/db 流程编排时继续复用。
self.steps = self.builder_def.get('steps', [])
self.env = env or {}
# context 是模板变量来源,支持 {{env.xxx}} 和 {{param.xxx}}。
self.context = {'env': self.env}
self.results = []
def execute(self, params=None):
"""执行造数器定义并返回渲染后的 output。"""
params = params or {}
self.context['param'] = params
output = self.builder_def.get('output') or {}
# 如果未配置 output返回基础执行信息方便前端判断定义是否为空。
return self._render_template(output) if output else {'params': params, 'steps': len(self.steps)}
def _render_template(self, obj):
"""递归渲染字符串、字典、数组中的 {{变量}}。"""
if isinstance(obj, str):
return re.sub(r'\{\{([^}]+)\}\}', lambda m: str(self._get_value(m.group(1).strip())), obj)
if isinstance(obj, dict):
return {k: self._render_template(v) for k, v in obj.items()}
if isinstance(obj, list):
return [self._render_template(item) for item in obj]
return obj
def _get_value(self, expr):
"""获取模板表达式的值,支持内置随机函数和点路径取值。"""
if expr.startswith('random_string(') and expr.endswith(')'):
length = int(expr[len('random_string('):-1] or 8)
return ''.join(random.choice(string.ascii_letters + string.digits) for _ in range(length))
if expr == 'random_phone()':
return '1{}{}'.format(random.choice(['3', '5', '7', '8', '9']), ''.join(random.choice(string.digits) for _ in range(9)))
current = self.context
# 点路径示例param.amount、env.base_url。
for part in expr.split('.'):
if isinstance(current, dict):
current = current.get(part)
else:
current = getattr(current, part, None)
if current is None:
return ''
return current