feat(script_engine): script_engine 初始化提交(元景项目-初始迭代,开发产线产出)
This commit is contained in:
commit
50eb34bee5
9
.gitignore
vendored
Normal file
9
.gitignore
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.egg-info/
|
||||
build/
|
||||
dist/
|
||||
*.swp
|
||||
*.swo
|
||||
models/mysql.ddl.sql
|
||||
wwwroot/script_engine/
|
||||
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# script_engine 模块
|
||||
|
||||
逻辑编程(脚本/规则引擎)模块:管理逻辑脚本/规则表 `script_engine`,提供脚本执行与语法校验接口。
|
||||
|
||||
## 功能
|
||||
- `script_engine` 表 CRUD:脚本名称、编码、所属世界/场景/绑定实体、脚本类型、触发事件、脚本内容、状态。
|
||||
- `api/execute_script.dspy`:按 `script_type` 选择执行器执行脚本内容,返回 `{success, result}`。
|
||||
- `api/validate_script.dspy`:按 `script_type` 做语法/格式校验,返回 `{valid, message}`。
|
||||
|
||||
## 数据表
|
||||
- `script_engine`(逻辑脚本表),脚本内容 `content` 使用 `text` 字段。
|
||||
|
||||
## 脚本类型(appcodes script_type)
|
||||
- `0` = Python 脚本(受限命名空间执行,通过 `result` 变量返回结果)
|
||||
- `1` = 规则(JSON)(解析 JSON 作为结果)
|
||||
- `2` = 表达式(eval 求值)
|
||||
|
||||
## 挂载
|
||||
宿主应用入口 `init()` 中调用 `load_script_engine()`。
|
||||
|
||||
## 安装
|
||||
```bash
|
||||
pip install .
|
||||
```
|
||||
7
__init__.py
Normal file
7
__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
"""script_engine 模块 —— 逻辑编程(脚本/规则引擎)。
|
||||
|
||||
通过 load_script_engine() 挂载到宿主应用。
|
||||
"""
|
||||
from .script_engine.init import load_script_engine
|
||||
|
||||
__all__ = ['load_script_engine']
|
||||
21
init/data.json
Normal file
21
init/data.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"appcodes": [
|
||||
{
|
||||
"parentid": "script_type",
|
||||
"parentname": "脚本类型",
|
||||
"items": [
|
||||
{"k": "0", "v": "Python脚本"},
|
||||
{"k": "1", "v": "规则(JSON)"},
|
||||
{"k": "2", "v": "表达式"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"parentid": "script_status",
|
||||
"parentname": "脚本状态",
|
||||
"items": [
|
||||
{"k": "0", "v": "禁用"},
|
||||
{"k": "1", "v": "启用"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
30
json/script_engine.json
Normal file
30
json/script_engine.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"tblname": "script_engine",
|
||||
"title": "逻辑脚本",
|
||||
"params": {
|
||||
"sortby": ["updated_at desc"],
|
||||
"editable": {
|
||||
"new_data_url": "{{entire_url('../api/script_engine_create.dspy')}}",
|
||||
"update_data_url": "{{entire_url('../api/script_engine_update.dspy')}}",
|
||||
"delete_data_url": "{{entire_url('../api/script_engine_delete.dspy')}}"
|
||||
},
|
||||
"browserfields": {
|
||||
"exclouded": ["id", "content"],
|
||||
"alters": {
|
||||
"world_id": {"uitype": "code", "dataurl": "{{entire_url('../api/get_search_world_id.dspy')}}"},
|
||||
"scene_id": {"uitype": "code", "dataurl": "{{entire_url('../api/get_search_scene_id.dspy')}}"},
|
||||
"entity_id": {"uitype": "code", "dataurl": "{{entire_url('../api/get_search_entity_id.dspy')}}"},
|
||||
"script_type": {"uitype": "code", "dataurl": "{{entire_url('../api/get_search_script_type.dspy')}}"},
|
||||
"status": {"uitype": "code", "dataurl": "{{entire_url('../api/get_search_status.dspy')}}"}
|
||||
}
|
||||
},
|
||||
"editexclouded": ["id", "created_at", "updated_at"],
|
||||
"data_filter": {
|
||||
"AND": [
|
||||
{"field": "name", "op": "LIKE", "var": "name"},
|
||||
{"field": "world_id", "op": "=", "var": "world_id"},
|
||||
{"field": "entity_id", "op": "=", "var": "entity_id"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
28
models/script.json
Normal file
28
models/script.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"summary": [{"name": "script", "title": "脚本定义表", "primary": ["id"], "catelog": "entity"}],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "world_id", "title": "所属世界ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "name", "title": "脚本名称", "type": "str", "length": 255, "nullable": "no"},
|
||||
{"name": "code", "title": "脚本编码", "type": "str", "length": 64, "nullable": "no"},
|
||||
{"name": "script_type", "title": "脚本类型", "type": "str", "length": 16, "nullable": "no", "default": "lua"},
|
||||
{"name": "bind_type", "title": "绑定对象类型", "type": "str", "length": 16, "nullable": "no", "default": "world"},
|
||||
{"name": "bind_id", "title": "绑定对象ID", "type": "str", "length": 32, "nullable": "yes"},
|
||||
{"name": "trigger_event", "title": "触发事件", "type": "str", "length": 64, "nullable": "yes"},
|
||||
{"name": "content", "title": "脚本内容", "type": "text", "nullable": "yes"},
|
||||
{"name": "enabled", "title": "是否启用", "type": "str", "length": 1, "nullable": "no", "default": "0"},
|
||||
{"name": "created_by", "title": "创建人ID", "type": "str", "length": 32, "nullable": "yes"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp", "nullable": "no"},
|
||||
{"name": "updated_at", "title": "更新时间", "type": "timestamp", "nullable": "no"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_script_code", "idxtype": "unique", "idxfields": ["world_id", "code"]},
|
||||
{"name": "idx_script_world", "idxtype": "index", "idxfields": ["world_id"]},
|
||||
{"name": "idx_script_bind", "idxtype": "index", "idxfields": ["bind_type", "bind_id"]}
|
||||
],
|
||||
"codes": [
|
||||
{"field": "world_id", "table": "world", "valuefield": "id", "textfield": "name"},
|
||||
{"field": "script_type", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='script_type'"},
|
||||
{"field": "bind_type", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='bind_type'"}
|
||||
]
|
||||
}
|
||||
36
models/script_engine.json
Normal file
36
models/script_engine.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"summary": [
|
||||
{
|
||||
"name": "script_engine",
|
||||
"title": "逻辑脚本表",
|
||||
"primary": ["id"],
|
||||
"catelog": "entity"
|
||||
}
|
||||
],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "world_id", "title": "所属世界", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "scene_id", "title": "所属场景", "type": "str", "length": 32, "nullable": "yes"},
|
||||
{"name": "entity_id", "title": "绑定实体", "type": "str", "length": 32, "nullable": "yes"},
|
||||
{"name": "name", "title": "脚本名称", "type": "str", "length": 255, "nullable": "no"},
|
||||
{"name": "code", "title": "脚本编码", "type": "str", "length": 64, "nullable": "no"},
|
||||
{"name": "script_type", "title": "脚本类型", "type": "str", "length": 16, "nullable": "no", "default": "0"},
|
||||
{"name": "trigger_event", "title": "触发事件", "type": "str", "length": 64, "nullable": "yes"},
|
||||
{"name": "content", "title": "脚本内容", "type": "text", "nullable": "no"},
|
||||
{"name": "status", "title": "状态", "type": "str", "length": 16, "nullable": "no", "default": "0"},
|
||||
{"name": "created_at", "title": "创建时间", "type": "timestamp", "nullable": "no"},
|
||||
{"name": "updated_at", "title": "更新时间", "type": "timestamp", "nullable": "yes"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_script_code", "idxtype": "unique", "idxfields": ["code"]},
|
||||
{"name": "idx_script_world", "idxtype": "index", "idxfields": ["world_id"]},
|
||||
{"name": "idx_script_entity", "idxtype": "index", "idxfields": ["entity_id"]}
|
||||
],
|
||||
"codes": [
|
||||
{"field": "world_id", "table": "world", "valuefield": "id", "textfield": "name"},
|
||||
{"field": "scene_id", "table": "scene", "valuefield": "id", "textfield": "name"},
|
||||
{"field": "entity_id", "table": "entity", "valuefield": "id", "textfield": "name"},
|
||||
{"field": "script_type", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='script_type'"},
|
||||
{"field": "status", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='script_status'"}
|
||||
]
|
||||
}
|
||||
24
models/script_exec_log.json
Normal file
24
models/script_exec_log.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"summary": [{"name": "script_exec_log", "title": "脚本执行日志表", "primary": ["id"], "catelog": "relation"}],
|
||||
"fields": [
|
||||
{"name": "id", "title": "主键ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "script_id", "title": "脚本ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "world_id", "title": "所属世界ID", "type": "str", "length": 32, "nullable": "no"},
|
||||
{"name": "status", "title": "执行状态", "type": "str", "length": 16, "nullable": "no", "default": "success"},
|
||||
{"name": "input", "title": "输入参数", "type": "text", "nullable": "yes"},
|
||||
{"name": "output", "title": "输出结果", "type": "text", "nullable": "yes"},
|
||||
{"name": "error", "title": "错误信息", "type": "text", "nullable": "yes"},
|
||||
{"name": "duration_ms", "title": "耗时(毫秒)", "type": "int", "nullable": "yes"},
|
||||
{"name": "executed_by", "title": "执行人ID", "type": "str", "length": 32, "nullable": "yes"},
|
||||
{"name": "created_at", "title": "执行时间", "type": "timestamp", "nullable": "no"}
|
||||
],
|
||||
"indexes": [
|
||||
{"name": "idx_script_elog_script", "idxtype": "index", "idxfields": ["script_id"]},
|
||||
{"name": "idx_script_elog_world", "idxtype": "index", "idxfields": ["world_id"]}
|
||||
],
|
||||
"codes": [
|
||||
{"field": "script_id", "table": "script", "valuefield": "id", "textfield": "name"},
|
||||
{"field": "world_id", "table": "world", "valuefield": "id", "textfield": "name"},
|
||||
{"field": "status", "table": "appcodes_kv", "valuefield": "k", "textfield": "v", "cond": "parentid='exec_status'"}
|
||||
]
|
||||
}
|
||||
1
models/script_log.json
Normal file
1
models/script_log.json
Normal file
@ -0,0 +1 @@
|
||||
script_log 表定义。
|
||||
13
pyproject.toml
Normal file
13
pyproject.toml
Normal file
@ -0,0 +1,13 @@
|
||||
[build-system]
|
||||
requires = ["setuptools>=45", "wheel"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "script_engine"
|
||||
version = "1.0.0"
|
||||
requires-python = ">=3.8"
|
||||
dependencies = ["sqlor", "bricks_for_python"]
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["."]
|
||||
include = ["script_engine*"]
|
||||
18
script_engine/__init__.py
Normal file
18
script_engine/__init__.py
Normal file
@ -0,0 +1,18 @@
|
||||
"""script_engine 包 —— 逻辑编程(脚本/规则引擎)模块实现。"""
|
||||
from .init import (
|
||||
load_script_engine,
|
||||
execute_script,
|
||||
validate_script,
|
||||
create_script,
|
||||
update_script,
|
||||
delete_script,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'load_script_engine',
|
||||
'execute_script',
|
||||
'validate_script',
|
||||
'create_script',
|
||||
'update_script',
|
||||
'delete_script',
|
||||
]
|
||||
134
script_engine/init.py
Normal file
134
script_engine/init.py
Normal file
@ -0,0 +1,134 @@
|
||||
"""script_engine 模块初始化。
|
||||
|
||||
逻辑编程(脚本/规则引擎)领域模块,通过 load_script_engine() 挂载到宿主应用,
|
||||
向 ServerEnv 注册业务函数:
|
||||
- execute_script(script_id, context_json) 执行脚本
|
||||
- validate_script(content, script_type) 校验脚本语法
|
||||
- create_script / update_script / delete_script CRUD 业务逻辑
|
||||
"""
|
||||
import json as _json
|
||||
|
||||
from appPublic.uniqueID import getID
|
||||
from appPublic.timeUtils import curDateString
|
||||
from ahserver.serverenv import ServerEnv
|
||||
from sqlor.dbpools import DBPools
|
||||
|
||||
_MODULE = 'script_engine'
|
||||
|
||||
|
||||
def _dbname():
|
||||
"""取模块库名(禁止硬编码,由宿主应用 get_module_dbname 决定)。"""
|
||||
return ServerEnv().get_module_dbname(_MODULE)
|
||||
|
||||
|
||||
async def _get_script(sor, script_id):
|
||||
rows = await sor.sqlExe(
|
||||
'SELECT * FROM script_engine WHERE id = ${id}$', {'id': script_id})
|
||||
return rows[0] if rows else None
|
||||
|
||||
|
||||
async def execute_script(script_id, context_json=None):
|
||||
"""执行指定脚本(逻辑编程运行时)。"""
|
||||
if not script_id:
|
||||
return {'success': False, 'error': '缺少 script_id 参数'}
|
||||
|
||||
context = {}
|
||||
if context_json:
|
||||
if isinstance(context_json, str):
|
||||
try:
|
||||
context = _json.loads(context_json)
|
||||
except Exception:
|
||||
context = {}
|
||||
elif isinstance(context_json, dict):
|
||||
context = context_json
|
||||
|
||||
db = DBPools()
|
||||
async with db.sqlorContext(_dbname()) as sor:
|
||||
script = await _get_script(sor, script_id)
|
||||
if not script:
|
||||
return {'success': False, 'error': '脚本不存在: %s' % script_id}
|
||||
script_type = script.get('script_type') or '0'
|
||||
content = script.get('content') or ''
|
||||
try:
|
||||
if script_type == '0':
|
||||
loc = {'context': context, 'result': None}
|
||||
exec(content, {'__builtins__': {}}, loc)
|
||||
return {'success': True, 'result': loc.get('result')}
|
||||
if script_type == '1':
|
||||
return {'success': True, 'result': _json.loads(content)}
|
||||
val = eval(content, {'__builtins__': {}}, {'context': context})
|
||||
return {'success': True, 'result': val}
|
||||
except Exception as e:
|
||||
return {'success': False, 'error': '%s: %s' % (type(e).__name__, e)}
|
||||
|
||||
|
||||
async def validate_script(content, script_type='0'):
|
||||
"""校验脚本语法。"""
|
||||
script_type = script_type or '0'
|
||||
content = content or ''
|
||||
try:
|
||||
if script_type == '0':
|
||||
compile(content, '<script>', 'exec')
|
||||
return {'valid': True, 'message': 'Python 脚本语法正确'}
|
||||
if script_type == '1':
|
||||
_json.loads(content or '{}')
|
||||
return {'valid': True, 'message': 'JSON 规则格式正确'}
|
||||
compile(content or '0', '<expr>', 'eval')
|
||||
return {'valid': True, 'message': '表达式语法正确'}
|
||||
except Exception as e:
|
||||
return {'valid': False, 'message': '%s: %s' % (type(e).__name__, e)}
|
||||
|
||||
|
||||
async def create_script(ns):
|
||||
"""新建脚本。"""
|
||||
code = ns.get('code') or ''
|
||||
db = DBPools()
|
||||
async with db.sqlorContext(_dbname()) as sor:
|
||||
if code:
|
||||
rows = await sor.sqlExe(
|
||||
'SELECT id FROM script_engine WHERE code = ${code}$', {'code': code})
|
||||
if rows:
|
||||
return {'success': False, 'error': '脚本编码已存在'}
|
||||
ns['id'] = getID()
|
||||
now = curDateString()
|
||||
ns['created_at'] = now
|
||||
ns['updated_at'] = now
|
||||
ns.setdefault('status', '0')
|
||||
ns.setdefault('script_type', '0')
|
||||
await sor.C('script_engine', ns)
|
||||
return {'success': True, 'id': ns['id']}
|
||||
|
||||
|
||||
async def update_script(ns):
|
||||
"""更新脚本。"""
|
||||
rid = ns.get('id')
|
||||
if not rid:
|
||||
return {'success': False, 'error': '缺少 id 参数'}
|
||||
upd = {k: v for k, v in ns.items() if k != 'id'}
|
||||
upd['id'] = rid
|
||||
upd['updated_at'] = curDateString()
|
||||
db = DBPools()
|
||||
async with db.sqlorContext(_dbname()) as sor:
|
||||
await sor.U('script_engine', upd)
|
||||
return {'success': True, 'id': rid}
|
||||
|
||||
|
||||
async def delete_script(ns):
|
||||
"""删除脚本。"""
|
||||
rid = ns.get('id')
|
||||
if not rid:
|
||||
return {'success': False, 'error': '缺少 id 参数'}
|
||||
db = DBPools()
|
||||
async with db.sqlorContext(_dbname()) as sor:
|
||||
await sor.D('script_engine', {'id': rid})
|
||||
return {'success': True, 'id': rid}
|
||||
|
||||
|
||||
def load_script_engine():
|
||||
"""挂载 script_engine 模块到 ServerEnv。"""
|
||||
env = ServerEnv()
|
||||
env.execute_script = execute_script
|
||||
env.validate_script = validate_script
|
||||
env.create_script = create_script
|
||||
env.update_script = update_script
|
||||
env.delete_script = delete_script
|
||||
65
scripts/load_path.py
Normal file
65
scripts/load_path.py
Normal file
@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""script_engine 模块 RBAC 权限注册(显式路径,禁用通配符)。"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
MODULE = 'script_engine'
|
||||
|
||||
PATHS_LOGINED = [
|
||||
'/{0}'.format(MODULE),
|
||||
'/{0}/index.ui'.format(MODULE),
|
||||
'/{0}/script_engine'.format(MODULE),
|
||||
'/{0}/script_engine/index.ui'.format(MODULE),
|
||||
'/{0}/script_engine/get_script_engine.dspy'.format(MODULE),
|
||||
'/{0}/script_engine/add_script_engine.dspy'.format(MODULE),
|
||||
'/{0}/script_engine/update_script_engine.dspy'.format(MODULE),
|
||||
'/{0}/script_engine/delete_script_engine.dspy'.format(MODULE),
|
||||
'/{0}/api/execute_script.dspy'.format(MODULE),
|
||||
'/{0}/api/validate_script.dspy'.format(MODULE),
|
||||
'/{0}/api/script_engine_create.dspy'.format(MODULE),
|
||||
'/{0}/api/script_engine_update.dspy'.format(MODULE),
|
||||
'/{0}/api/script_engine_delete.dspy'.format(MODULE),
|
||||
'/{0}/api/get_search_world_id.dspy'.format(MODULE),
|
||||
'/{0}/api/get_search_scene_id.dspy'.format(MODULE),
|
||||
'/{0}/api/get_search_entity_id.dspy'.format(MODULE),
|
||||
'/{0}/api/get_search_script_type.dspy'.format(MODULE),
|
||||
'/{0}/api/get_search_status.dspy'.format(MODULE),
|
||||
]
|
||||
|
||||
PATHS_ANY = []
|
||||
|
||||
|
||||
def find_sage_root():
|
||||
cur = os.path.dirname(os.path.abspath(__file__))
|
||||
for _ in range(6):
|
||||
if os.path.isdir(os.path.join(cur, 'wwwroot')) and os.path.isdir(os.path.join(cur, 'py3', 'bin')):
|
||||
return cur
|
||||
parent = os.path.dirname(cur)
|
||||
if parent == cur:
|
||||
break
|
||||
cur = parent
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
sage_root = find_sage_root()
|
||||
if not sage_root:
|
||||
print('[{0}] Sage root not found, skip RBAC registration'.format(MODULE))
|
||||
return
|
||||
sys.path.insert(0, sage_root)
|
||||
try:
|
||||
from set_role_perm import set_role_perm
|
||||
except ImportError:
|
||||
print('[{0}] set_role_perm not found, skip RBAC registration'.format(MODULE))
|
||||
return
|
||||
for path in PATHS_ANY:
|
||||
set_role_perm(path, 'any')
|
||||
for path in PATHS_LOGINED:
|
||||
set_role_perm(path, 'logined')
|
||||
print('[{0}] registered {1} any + {2} logined paths'.format(
|
||||
MODULE, len(PATHS_ANY), len(PATHS_LOGINED)))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
33
skill/SKILL.md
Normal file
33
skill/SKILL.md
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
name: script_engine
|
||||
description: 逻辑编程(脚本/规则引擎)模块——脚本表 CRUD + execute_script/validate_script 接口,通过 load_script_engine() 挂载。
|
||||
---
|
||||
|
||||
# script_engine 模块
|
||||
|
||||
## 概述
|
||||
逻辑编程(脚本/规则引擎)模块。管理 `script_engine` 表(脚本/规则),提供脚本执行与语法校验接口。依赖 world、scene、entity 模块。
|
||||
|
||||
## 数据模型
|
||||
表 `script_engine`:`id`、`world_id`(→world)、`scene_id`(→scene)、`entity_id`(→entity)、`name`、`code`(unique)、`script_type`、`trigger_event`、`content`(text)、`status`、`created_at`、`updated_at`。
|
||||
|
||||
## 关键接口
|
||||
- `api/execute_script.dspy`:输入 `script_id` / `context_json`(可选),按 `script_type` 选择执行器执行 `content`,返回 `{success, result}`。
|
||||
- `api/validate_script.dspy`:输入 `content` / `script_type`,语法/格式校验,返回 `{valid, message}`。
|
||||
|
||||
## 脚本类型编码(appcodes script_type)
|
||||
- `0` = Python脚本(受限命名空间,通过 `result` 变量返回结果)
|
||||
- `1` = 规则(JSON)(解析 JSON 作为结果)
|
||||
- `2` = 表达式(eval 求值)
|
||||
|
||||
## 状态编码(appcodes script_status)
|
||||
- `0` = 禁用、`1` = 启用
|
||||
|
||||
## 挂载
|
||||
宿主应用入口 `init()` 调用 `load_script_engine()`,向 ServerEnv 注册 `execute_script` / `validate_script` / `create_script` / `update_script` / `delete_script`。
|
||||
|
||||
## 陷阱
|
||||
- 脚本执行使用受限命名空间(`__builtins__={}`),Python 脚本须通过 `result` 变量返回结果。
|
||||
- `content` 为 `text` 字段:列表页隐藏(`browserfields.exclouded`),编辑页显示。
|
||||
- `code` 唯一索引,create 时校验重复。
|
||||
- 取库名统一 `ServerEnv().get_module_dbname('script_engine')`,禁止硬编码 DBNAME。
|
||||
4
wwwroot/api/execute_script.dspy
Normal file
4
wwwroot/api/execute_script.dspy
Normal file
@ -0,0 +1,4 @@
|
||||
script_id = params_kw.get('script_id')
|
||||
context_json = params_kw.get('context_json')
|
||||
result = await execute_script(script_id, context_json)
|
||||
return result
|
||||
8
wwwroot/api/get_search_entity_id.dspy
Normal file
8
wwwroot/api/get_search_entity_id.dspy
Normal file
@ -0,0 +1,8 @@
|
||||
result = [{'value': '', 'text': '全部'}]
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'entity') as sor:
|
||||
rows = await sor.sqlExe('select id as value, name as text from entity order by name', {})
|
||||
result = [{'value': '', 'text': '全部'}] + list(rows)
|
||||
except Exception as e:
|
||||
debug('get_search_entity_id error: %s' % e)
|
||||
return result
|
||||
8
wwwroot/api/get_search_scene_id.dspy
Normal file
8
wwwroot/api/get_search_scene_id.dspy
Normal file
@ -0,0 +1,8 @@
|
||||
result = [{'value': '', 'text': '全部'}]
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'scene') as sor:
|
||||
rows = await sor.sqlExe('select id as value, name as text from scene order by name', {})
|
||||
result = [{'value': '', 'text': '全部'}] + list(rows)
|
||||
except Exception as e:
|
||||
debug('get_search_scene_id error: %s' % e)
|
||||
return result
|
||||
9
wwwroot/api/get_search_script_type.dspy
Normal file
9
wwwroot/api/get_search_script_type.dspy
Normal file
@ -0,0 +1,9 @@
|
||||
result = [{'value': '', 'text': '全部'}]
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'appbase') as sor:
|
||||
rows = await sor.sqlExe(
|
||||
"select k as value, v as text from appcodes_kv where parentid='script_type' order by k", {})
|
||||
result = [{'value': '', 'text': '全部'}] + list(rows)
|
||||
except Exception as e:
|
||||
debug('get_search_script_type error: %s' % e)
|
||||
return result
|
||||
9
wwwroot/api/get_search_status.dspy
Normal file
9
wwwroot/api/get_search_status.dspy
Normal file
@ -0,0 +1,9 @@
|
||||
result = [{'value': '', 'text': '全部'}]
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'appbase') as sor:
|
||||
rows = await sor.sqlExe(
|
||||
"select k as value, v as text from appcodes_kv where parentid='script_status' order by k", {})
|
||||
result = [{'value': '', 'text': '全部'}] + list(rows)
|
||||
except Exception as e:
|
||||
debug('get_search_status error: %s' % e)
|
||||
return result
|
||||
8
wwwroot/api/get_search_world_id.dspy
Normal file
8
wwwroot/api/get_search_world_id.dspy
Normal file
@ -0,0 +1,8 @@
|
||||
result = [{'value': '', 'text': '全部'}]
|
||||
try:
|
||||
async with get_sor_context(request._run_ns, 'world') as sor:
|
||||
rows = await sor.sqlExe('select id as value, name as text from world order by name', {})
|
||||
result = [{'value': '', 'text': '全部'}] + list(rows)
|
||||
except Exception as e:
|
||||
debug('get_search_world_id error: %s' % e)
|
||||
return result
|
||||
9
wwwroot/api/script_engine_create.dspy
Normal file
9
wwwroot/api/script_engine_create.dspy
Normal file
@ -0,0 +1,9 @@
|
||||
ns = params_kw.copy()
|
||||
for k, v in ns.items():
|
||||
if v == 'NaN' or v == 'null':
|
||||
ns[k] = None
|
||||
for k in list(ns.keys()):
|
||||
if k.endswith('_text'):
|
||||
ns.pop(k, None)
|
||||
result = await create_script(ns)
|
||||
return result
|
||||
3
wwwroot/api/script_engine_delete.dspy
Normal file
3
wwwroot/api/script_engine_delete.dspy
Normal file
@ -0,0 +1,3 @@
|
||||
ns = params_kw.copy()
|
||||
result = await delete_script(ns)
|
||||
return result
|
||||
9
wwwroot/api/script_engine_update.dspy
Normal file
9
wwwroot/api/script_engine_update.dspy
Normal file
@ -0,0 +1,9 @@
|
||||
ns = params_kw.copy()
|
||||
for k, v in ns.items():
|
||||
if v == 'NaN' or v == 'null':
|
||||
ns[k] = None
|
||||
for k in list(ns.keys()):
|
||||
if k.endswith('_text'):
|
||||
ns.pop(k, None)
|
||||
result = await update_script(ns)
|
||||
return result
|
||||
4
wwwroot/api/validate_script.dspy
Normal file
4
wwwroot/api/validate_script.dspy
Normal file
@ -0,0 +1,4 @@
|
||||
content = params_kw.get('content')
|
||||
script_type = params_kw.get('script_type', '0')
|
||||
result = await validate_script(content, script_type)
|
||||
return result
|
||||
18
wwwroot/index.ui
Normal file
18
wwwroot/index.ui
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"widgettype": "VBox",
|
||||
"options": {"width": "100%", "height": "100%", "padding": "20px"},
|
||||
"subwidgets": [
|
||||
{"widgettype": "Text", "options": {"label": "逻辑编程", "fontSize": "24px"}},
|
||||
{"widgettype": "ResponsableBox", "options": {"gap": "16px", "minWidth": "250px"}, "subwidgets": [
|
||||
{"widgettype": "VBox", "options": {"backgroundColor": "#FFFFFF", "padding": "20px", "cursor": "pointer"},
|
||||
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.script_engine_content",
|
||||
"options": {"url": "{{entire_url('/script_engine/script_engine')}}"}, "mode": "replace"}],
|
||||
"subwidgets": [{"widgettype": "Text", "options": {"label": "脚本列表", "fontSize": "16px"}}]},
|
||||
{"widgettype": "VBox", "options": {"backgroundColor": "#FFFFFF", "padding": "20px", "cursor": "pointer"},
|
||||
"binds": [{"wid": "self", "event": "click", "actiontype": "urlwidget", "target": "app.script_engine_content",
|
||||
"options": {"url": "{{entire_url('/script_engine/script_engine/add_script_engine.dspy')}}"}, "mode": "replace"}],
|
||||
"subwidgets": [{"widgettype": "Text", "options": {"label": "新建脚本", "fontSize": "16px"}}]}
|
||||
]},
|
||||
{"widgettype": "VBox", "id": "app.script_engine_content", "options": {"width": "100%", "flex": "1", "marginTop": "20px"}}
|
||||
]
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user