1. 页面概览

1.1 是什么

「函数注册」页面(页面内标题为 本体函数)是 LightFoundry 的 OntologyFunction(本体函数)注册与测试工作台,V5 Stage 5(B7-2)交付。页面 hint 给出定位:「OntologyFunction · 服务端业务函数(工作流条件 / 校验 / 编辑态派生值)」。

在 Foundry 中,工作流条件、数据校验、编辑态派生值这些场景需要「可复用的计算逻辑」:例如折后价 = 原价 × (1 - 折扣率)、满减金额计算、订单状态拼接等。如果每次都把表达式写死在各处,会重复且难以维护。本体函数就是把这类逻辑声明式注册到本体的函数表(fon_functions)中:一个函数 = 一个稳定的 name(api_name)+ 参数声明 params(JSON)+ 函数体 body(表达式),可被工作流条件/校验/编辑态派生值引用,也可以被其他函数递归调用(深度 ≤ 5)。

函数体使用表达式引擎语法(workflow.EvalExpression):{{param}} 引用参数、fn_name(arg1, arg2) 函数调用、+ - * / 运算、比较与逻辑运算,内置函数(len/abs/round/min/max/coalesce/if/ifnull/concat/upper/lower/now/date_trunc)与用户函数可混用。页面提供完整的函数列表、编辑器与测试面板——测试面板按 params 声明动态生成参数输入框,输入参数后即时求值(POST /ontology/functions/:name/test)。

1.2 核心价值

维度说明
声明式注册用 JSON 声明参数、用表达式写函数体,无需写 Go/Python 代码即可扩展业务计算
稳定标识name 为不可变的 api_name(^[a-z][a-z0-9_]*$),被引用后不可改名
参数类型化params 声明 name/type/required,测试面板按声明动态生成输入框
即时求值「运行求值」把输入参数 POST 到 /ontology/functions/:name/test,秒级验证函数逻辑
递归复用用户函数可被其他函数 body 以 fn_xxx(...) 调用(递归深度 ≤ 5,防死循环)
灰度开关pure(纯函数)与 enabled(启用)开关控制函数是否可被表达式调用

1.3 一句话总结

把「工作流条件 / 校验 / 派生值」要用的计算逻辑注册成带名字的本体函数,用表达式写函数体、用 JSON 声明参数,然后在测试面板里即时求值验证,之后即可被其他函数与表达式调用。

2. 访问入口

2.1 路由与菜单

2.2 认证与权限

2.3 端口与 API 前缀

3. 界面布局

页面为「左窄右宽」两栏布局(grid 380px + 1fr):

┌────────────────────────────────────────────────────────────────┐
│ 本体函数(页头 + hint:OntologyFunction · 服务端业务函数)          │
│ [alert 操作结果提示条(可关闭)]                                   │
├───────────────────┬────────────────────────────────────────────┤
│ 函数列表(左)      │ 编辑器(v-if editing)                        │
│ (共 N 个)        │  编辑函数/新建函数 · name 为稳定标识            │
│ [+ 新建函数]       │  函数名 name * | 返回类型 return_type          │
│ 表格:             │  描述 | 参数 params(JSON 数组)               │
│  名称(描述)        │  函数体 body *(表达式)                       │
│  类型(return/pure) │  [纯函数(pure)] [启用(enabled)]               │
│  状态(启用/禁用)   │  [保存] [取消]                                │
│  操作(编辑|测试|删除)│ ──────────────────────────────────────────  │
│                   │ 测试面板(v-else-if testing)                  │
│                   │  测试函数 {name} [关闭]                        │
│                   │  参数输入框(按 params 动态生成)                │
│                   │  [运行求值] [清空] 结果:<code>                 │
│                   │ ──────────────────────────────────────────  │
│                   │ 空态(v-else):选择左侧函数进行编辑或测试,      │
│                   │  或点击「+ 新建函数」注册本体函数。              │
└───────────────────┴────────────────────────────────────────────┘

各板块职责:

4. 交互元素详解

4.1 函数列表(左栏)

元素含义操作效果触发后端调用
「+ 新建函数」按钮打开新建表单编辑中点击则收起表单;否则重置表单并打开编辑器,标题「新建函数」无(打开表单)
名称列函数名(api_name)粗体展示 name;描述超 32 字截断加省略号
类型列return_type + purereturn_type 显示 tag-blue(缺省显示 any);pure 显示 tag-green,否则灰色 impure
状态列enabled 开关启用(term-approved)/ 禁用(term-deprecated)徽标
「编辑」按钮打开编辑表单回填函数字段(params 格式化 JSON),标题「编辑函数」,name 输入框禁用无(打开表单)
「测试」按钮打开测试面板关闭编辑器,按 params 生成参数输入框无(打开面板)
「删除」按钮删除函数confirm 二次确认「确定删除函数"{name}"吗?引用它的计算属性将无法求值。」后删除DELETE /ontology/functions/:id

4.2 编辑器(右侧)

元素含义必填与默认值操作效果触发后端调用
函数名 name *稳定标识(api_name)必填;小写字母开头的小写字母/数字/下划线;编辑时输入框禁用新建时提交 name;编辑时不可改名(后端校验「function name(api_name)为稳定标识,不可修改」)写入 name
返回类型 return_type函数返回值类型可空,placeholder 如 number / text / bool类型标签展示用写入 return_type
描述函数用途说明可空列表展示(32 字截断)写入 description
参数 params(JSON 数组,可选)参数声明可空;必填时须为 JSON 数组,如 [{"name":"amount","type":"number","required":true}]保存时前端先 JSON.parse 校验,非法提示「params JSON 非法」不提交写入 params
函数体 body *(表达式)表达式逻辑必填;placeholder 如 {{amount}} * (1 - {{rate}} / 100)保存时后端做语法检查(CheckExpressionSyntax),不可编译则拒绝写入 body
纯函数(pure)是否纯函数checkbox,默认勾选纯函数可被任意表达式引用写入 pure(1/0)
启用(enabled)是否可被调用checkbox,默认勾选禁用后引擎注销该函数,调用被拒绝写入 enabled(1/0)
「保存」按钮提交busy 时显示「提交中...」并禁用新建提示「函数创建成功 id=xxx」/ 更新提示「函数已更新」POST /ontology/functionsPUT /ontology/functions/:id
「取消」按钮关闭编辑器丢弃编辑内容

4.3 测试面板(右侧)

元素含义操作效果触发后端调用
参数输入框按 params 声明动态生成每个参数一行 label(name + type tag + 必填 tag-red)+ 输入框;placeholder 按类型:number→数字、bool→true/false、其他→文本收集参数值
「运行求值」按钮提交求值testingBusy 时显示「求值中...」;结果展示 结果:<code>…</code>,失败红色展示 errorPOST /ontology/functions/:name/test(body {args: {...}}
「清空」按钮重置清空全部参数、结果与错误
「关闭」链接按钮关闭测试面板退出测试状态

4.4 参数值转换规则(前端 runTest)

5. 后端关联

5.1 API 客户端

5.2 端点表

方法路径请求体说明
GET/ontology/functions函数列表(按 name 字典序)
POST/ontology/functionsFunctionInput创建函数(name 正则/冲突/重名/body 语法检查后落库并注册进表达式引擎)
GET/ontology/functions/:id函数详情
PUT/ontology/functions/:idFunctionInput更新(name 稳定标识不可改;body 语法检查)
DELETE/ontology/functions/:id删除并从表达式引擎注销
POST/ontology/functions/:name/test{args: {参数名: 值}}按参数名填充 vars 对 body 求值

5.3 响应结构

函数对象(functions 数组元素):

{ "id": "...", "name": "discount_amount", "description": "折扣后金额",
  "params": [ {"name":"amount","type":"number","required":true},
              {"name":"rate","type":"number","required":false} ],
  "return_type": "number", "body": "{{amount}} * (1 - {{rate}} / 100)",
  "pure": 1, "enabled": 1, "created_at": "...", "updated_at": "..." }

测试求值响应(data 含 name 与 result):

{ "code": 0, "data": { "name": "discount_amount", "result": 85 } }

5.4 关联模块表

后端包职责
products/foundry/ontology/function_rest.goREST 端点、Test 请求解析({args})、统一响应
products/foundry/ontology/function.goFunctionService:CRUD、校验(name 正则/内置函数冲突/params 结构)、Test 求值、用户函数注册与递归求值
products/foundry/ontology/models_function.gofon_functions 表模型、FunctionParam、AutoMigrate
products/foundry/ontology/validate.goobjectNamePattern 等校验正则与错误结构
products/foundry/workflow表达式引擎:EvalExpression / CheckExpressionSyntax / RegisterUserFunction / MaxUserFunctionDepth

5.5 关键机制

6. 核心流程详解

6.1 主流程:注册函数 → 即时求值 → 发布复用

  1. 新建函数:点「+ 新建函数」,填写函数名 name(小写字母开头)、返回类型、描述、参数 JSON 与函数体表达式,勾选 pure/enabled,点「保存」。前端先校验 params JSON 合法性,后端校验 name 正则与 body 语法,成功提示「函数创建成功 id=xxx」。
  2. 编辑函数:列表点「编辑」回填字段;name 输入框禁用(稳定标识不可改),仅可改 description/params/return_type/body/pure/enabled;保存提示「函数已更新」。
  3. 即时求值:列表点「测试」打开测试面板,按 params 声明填入参数(可选参数留空按 null 处理),点「运行求值」提交 POST /ontology/functions/:name/test,页面展示 结果:<code>…</code>
  4. 发布复用:保存并启用后函数即注册进表达式引擎,可被工作流条件/校验/编辑态派生值以及其他函数 body 以 fn_xxx(...) 调用。

6.2 函数体表达式要点

6.3 删除流程与影响

点「删除」弹出 confirm 弹窗「确定删除函数"{name}"吗?引用它的计算属性将无法求值。」——确认后 DELETE /ontology/functions/:id,后端从表达式引擎注销该函数;引用它的计算属性将无法求值,删除前应确认没有其他表达式在引用。

6.4 参数必选语义

7. 权限与安全

8. 常见问题与排错

问题一:保存函数时报「name 须为小写字母开头的小写字母/数字/下划线」或「body 表达式不可编译」

现象:保存被拒绝并提示 name 或 body 相关错误。

原因:name 不符合 ^[a-z][a-z0-9_]*$(如大写开头、含横杠),或 body 表达式语法错误(括号不匹配、变量引用拼写错、函数名不存在)。

排查步骤:检查 name 是否全小写字母/数字/下划线;检查 body 中 {{参数名}} 是否与 params 声明一致;先用测试面板构造最小表达式验证语法;保存后看 alert 具体错误。

问题二:测试面板「运行求值」显示红色错误「必选参数 "x" 未提供」

现象:求值失败,提示必选参数未提供。

原因:params 声明了 required=true 的参数但未填写。

排查步骤:找到红色「必填」标签的参数并填入;或编辑函数把该参数 required 改为 false;确认测试面板参数输入框确实由 params 声明生成。

问题三:函数保存成功但被其他函数以 fn_xxx(...) 调用时报「用户函数 "x" 不存在」或「已禁用」

现象:调用方求值失败。

原因:调用时机早于注册、函数被删除/禁用,或表达式引擎注册表未刷新。

排查步骤:确认被调函数仍在列表且状态为「启用」;测试面板每次求值前后端会 registerUserFunctions 刷新注册表;若删除后仍有引用,需一并修正引用方的 body。

问题四:测试结果为 null 或与预期不符

现象:求值结果为空或数值不对。

原因:可选参数留空被按 null 处理、bool 输入不是 true/1、number 输入含非数字字符、表达式逻辑与预期不符。

排查步骤:核对参数类型——bool 输入 true1、number 只填数字;用 coalesce 给可能为 null 的参数兜底;逐步简化 body 缩小范围。

问题五:进入页面函数列表一直显示「加载中...」或「暂无函数(后端可能未就绪)。」

现象:列表加载不出来或为空。

原因GET /ontology/functions 失败(401/网络/后端未启动)或列表为空。

排查步骤:检查浏览器 Network 里 /api/v1/ontology/functions 的状态码;401 则重新登录;确认 foundry 后端 18081 已启动;后端未就绪时页面有 alert 错误提示且不白屏(防御式取值)。

9. 已知缺陷与边界

边界说明
name 不可改name 为稳定标识,创建后不可修改(更新接口强制校验)
表达式能力仅支持运算/比较/逻辑/内置函数/用户函数,不支持循环、数组迭代、外部数据源访问
递归深度用户函数互相调用深度 ≤ 5(MaxUserFunctionDepth),超出报错
SQL 翻译层计算属性 Formula 引用用户函数时 semantic mapper 降级为 NULL+标注,不在 SQL 层实现用户函数
参数类型type 仅 text/number/bool/json 等在测试面板做转换;date/datetime 未单独转换(按文本提交)
删除联动删除函数不校验被引用方,引用它的计算属性/表达式将无法求值(仅前端 confirm 提示)
EditRecorder 派生本批不接入执行器(无「Action 写回后计算派生属性值」钩子),后续有派生值钩子时复用 evalUserFunction