# 动态动作 ( Action)

强大的动态动作子系统是本系统的核心功能之一,通过动态动作,用户可以在系统中定义各类操作。

# 定义

动态动作使用 Dynamic Action 对象注入系统,可通过页面菜单 Development > Dynamic actions 在界面直接创建该对象,并输入 Action 的执行代码,亦可通过 CSV 文件导入 Action 的定义。

创建 Action 对象的字段详述如下:

名称(Name): 名称,不为空,唯一
显示名称(Label): 界面显示的 Label
帮助信息(Description(helpText)): 描述,同时用于在前台显示给用户的帮助信息
所属组织(Organization): 该 Action 所属的组织 
图标(Icon): 显示图标
启用逻辑(Enable logic): 启用的动态逻辑,代码中可使用的注入变量和返回值约定参见下述文档
核心逻辑(Core logic): 具体执行的动态逻辑,只对 groovy 类型的 Action 生效,代码中可使用的注入变量和返回值约定参见下述文档
模式(Mode): Action 的模式,详见下述文档
启用异步处理(async): 是否为异步 Action, 异步 Action 会在后台执行,不会阻塞前台界面,执行完成后,会发送系统消息给用户
支持结果微调(Support result tuning): 是否支持结果微调
确认类型(Confirm Type): 在前台执行前如何显示确认或者提示信息给用户
现实提示信息(Confirm message): 在前台执行前显示的确认和提示信息文本
启用角色(Enable for roles): 对哪些角色用户启用
1
2
3
4
5
6
7
8
9
10
11
12
13

# 适用范围(Action 模式)

当前支持的模式如下:

  • OBJECT_SINGLE 可在某一个 Object 之上运行
  • OBJECT_SINGLE_MULTIPLE 可在某一个或者多个 Object 之上运行
  • OBJECT_MULTIPLE 可在多个 Object 之上运行
  • CLASS_LEVEL 可作用于某种 Domain, 而不是具体的对象

# 提示信息显示模式

当前支持的提示信息显示模式如下:

  • NO_CONFIRM 无需提示,用户在前台点击 Action 链接后,直接运行 Action。
  • NO_POPUP_NO_CONFIRM 不弹出运行的 Popover 控件,点击后,直接调用后台 API 运行 Action, 完成后,刷新 Action 列表。
  • DISPLAY_CONFIRM 在前台显示一个确认控件,用户需点击该确认控件的确认按钮后,才 会执行该 Action。

# 是否支持结果微调

定义 Action 时,可以设定 Action 执行过程中,是否支持结果微调,本功能主要用于 Action 调用大语言模型请求时的结果微调。

# Action 与 Domain 的关联

Dynamic Action 本身并不与 Domain 直接关联,通过 DynamicActionDomainClass 对象关联,该对象包含如下字段:

  • dynamicAction: Dynamic Action 对象
  • domainClass: Domain 对象
  • displaySequence: 该 Action 在界面上的显示顺序, displaySequence 值越小,显示越靠前
  • group: DynamicActionGroup 对象,表示该 Action 在界面上的分组,同一分组 Action 会被放在同一个下拉菜单中显示。

# 启用逻辑

系统会在返回 Action 列表给前台前,运行 Action 定义的 Enable Logic 代码, 以决定该 Action 针对当前场景是否启用,Enable Logic 逻辑在系统中,以 Logic Type 为 DYNAMIC_ACTION_ENABLE_LOGIC 类型的 DynamicLogic 进行定义。

# 注入变量

代码运行时,系统会注入如下的上下文变量

变量名称 变量类型 描述
userContext grails.plugin.springsecurity.userdetails.GrailsUser 当前操作的用户信息
application grails.core.GrailsApplication 当前的 grails 应用上下文
action tech.muyan.dynamic.action.DynamicAction 运行的 action 定义
objectIds List<java.lang.Long> 目标 domain 对象 id 列表
objects List<? extends GormEntity> 目标 domain 对象列表
objectType tech.muyan.DomainClass 目标 domain 对象的类型信息
parameters Map<String, Object> 用户前台输入的 action 运行参数(仅 core Logic 可用)
ownerInfo tech.muyan.importexport.OwnerInfo 当前显示列表的关联主对象
log Closure<?> 用于打印执行日志的 log 闭包

关于显示图标的说明请参考 图标说明 章节

ownerInfo 参数说明: 如果当前的显示和执行上下文是在显示某个主对象的关联对象的列表时, ownerInfo 参数会包含如下信息:

  • ownerId: 主对象的 id
  • ownerClassName: 主对象的类型
  • columnNameInOwnerClass: 关联对象在主对象中的属性名

举例说明: 如果当前显示的是某个 Order 对象的 OrderItem 列表,那么 ownerInfo 参数会包含如下信息:

  • ownerId: 与当前选中的 OrderItem 对象关联的那个 Order 对象的 id
  • ownerClassName: Order 对象的类型
  • columnNameInOwnerClass: OrderItem 对象在 Order 对象中的属性名

提示

parameters 参数只在 core logic 执行中才会被注入,在 enable Logic 的执行中,不 会被注入,因为 enable Logic 逻辑是用来判断 Action 是否可用的,不需要用户输入参数。

注意

对于选中多条记录显示 Action 列表,或者执行 Action 的场景,注入到执行代码中的 objects 是所选中对象的列表,

在某一条对象上显示 Action 列表,或者执行 Action 时,传递的 objects 参数也是一个数组,数组中只有一个元素。

注意

以下场景下,运行 Enable Logic 代码,获取可用 Action 列表时,注入的 objectIds 和 objects 对象为空,在实现 Enable Logic 时,需注意兼容此种场景。

  • 用户进入列表页面,前台获取可用的 CLASS_LEVEL Action 列表时;

针对选中多条记录运行 Action 的场景,系统会显示经 Enable Logic 逻辑判断后,所有 OBJECT_SINGLE, OBJECT_SINGLE_MULTIPLE 和 OBJECT_MULTIPLE 模式下可用 Action 的全集。

提示

在 CSV 文件中导入 Action 定义时,Description 字段需使用 helpText 作为列名称

# 返回结果

Enable Logic 运行后的返回结果的结构如下

// 表示该 action 或 task 或 widget 是否启用
// Indicates whether this action or task or widget is enabled
[result: true | false]
1
2

# 运行及结果返回

执行结果保存在对象 DynamicActionExecRecord 中,动作执行的可能状态有:

  • NOT_START 执行未开始
  • RUNNING 执行中
  • SUCCESS 执行成功
  • FAILED 执行失败
  • SUCCESS_WITH_WARNING 执行完成但有警告

# GROOVY_CODE 动作

# 注入变量

GROOVY_CODE 类型的 Action, 执行的具体逻辑以一个 Type 为 DYNAMIC_ACTION_LOGIC 的 Dynamic Logic 进行定义,执行时,系统会注入如下的上下文变量

变量名称 变量类型 描述
userContext grails.plugin.springsecurity.userdetails.GrailsUser 当前操作的用户信息
application grails.core.GrailsApplication 当前的 grails 应用上下文
action tech.muyan.dynamic.action.DynamicAction 当前待判断的 action 定义
object <? extends GormEntity> 当前待判断的 domain 对象
objectType tech.muyan.DomainClass 当前待判断的 domain 对象的类型信息
classLoader java.lang.ClassLoader 运行客制化代码的调用方 ClassLoader
log Closure<?> 用于打印执行日志的 log 闭包

# 返回结果

返回结果的约定请参考章节 执行结果返回约定 章节。

# OS_COMMAND 及 LLM_ENGINE 动作

# 命令替换

针对 CORE_LOGIC 执行引擎为 OS_COMMANDLLM_ENGINE 类型逻辑定义的 Action, 执行时,系统默认会注入如下规则,替换执行的命令,也可以在Core Logic 中,定义一 个 preLogic, 在 preLogic 中,返回替换上下文。

占位符 替换目标
${username} 用户的用户名
${roles} 用户的角色列表,逗号分隔
${object} Domain Object 的 JSON 序列化字符串
${parameterName} 前台传入的用户输入参数

提示

对于日期类型的参数,在做参数替换时,会使用 "yyyy-MM-dd HH:mm:ss z" 格式来格式化 用户输入的参数。

# 结果保存

OS_COMMAND 类型的 Action 执行完成后,系统会自动捕获命令行输出作为结果,具体规则 如下:

  1. 如果该 Dynamic Action 的 Core Logic 定义了 Post Logic, 则以 Post Logic 执行的 结果作为返回值,具体 Post Logic 的运行情况参考章节 后处理

  2. 如果该 Dynamic Action 的 Core Logic 没有定义 Post Logic, 则遵循如下的返回逻辑:

  3. 如果命令的 exit code 为0, 且在执行过程中,执行框架没有捕获任何异常,则执行结 果为 SUCCESS

  4. 如果命令的 exit code 为非0, 且在执行过程中,执行框架没有捕获任何异常,则执行 结果为 SUCCESS_WITH_WARNING

  5. 重定向到标准输出的 SystemError 流的输出会被捕获到执行结果的 execLog 中,作为 执行日志。

  6. 重定向到标准输出的 SystemOut 流的输出会被捕获到执行结果的 execResult 中,作为 执行结果。

# 后处理

针对 OS_COMMANDLLM_ENGINE 类型的 Action, 其 CORE_LOGIC 中,可以关联后处 理逻辑,用来处理外部命令执行的结果,系统会将 Core Logic 的执行结果连同如下变量, 传递给后处理逻辑:

变量名称 变量类型 描述
userContext grails.plugin.springsecurity.userdetails.GrailsUser 当前操作的用户信息
application grails.core.GrailsApplication 当前的 grails 应用上下文
action tech.muyan.dynamic.action.DynamicAction 当前待判断的 action 定义
object <? extends GormEntity> 当前待判断的 domain 对象
objectType tech.muyan.DomainClass 当前待判断的 domain 对象的类型信息
stdout java.lang.String 外部命令运行的标准输出流
stderr java.lang.String 外部命令运行的标准错误输出流
classLoader java.lang.ClassLoader 运行客制化代码的调用方 ClassLoader
log Closure<?> 用于打印执行日志的 log 闭包

后处理逻辑返回结果的约定请参考章节 执行结果返回约定 章节。

# 执行结果返回约定

Action 执行的返回结果的约定如下:

  1. 如代码正常执行返回,无警告或错误,则执行代码需要返回一个如下结构的结果对象
// execResult 为字符类型
// execResult is of string type
// redirect 为字符类型
// redirect is of string type
[
  execResult: "执行的结果/Execution result",
  redirect: "执行后的跳转页面/Page to redirect to after execution",
  //类型为tech.muyan.storage.StorageFieldValue
  //Type is tech.muyan.storage.StorageFieldValue
  download: "执行后返回前端的文件/File returned to the frontend after execution"
]
1
2
3
4
5
6
7
8
9
10
  1. 如代码运行过程中,希望将该 Action 的状态设置为成功但有警告,则执行代码需要抛 出 tech.muyan.exception.CustomLogicWarningException,如果希望同时返回执行结 果,则可以调用 CustomLogicWarningException 的构造函数 CustomLogicWarningException(String message, Object data),将 data 作为结果 传递给执行框架,执行框架会将该信息保存到执行结果字段 execResult 中。

  2. 如果代码运行过程中,抛出了任何非 tech.muyan.exception.CustomLogicWarningException 类型的异常,则系统会将该 Action 的执行结果标记为失败,且将该异常的 getMessage() 方法返回值或 getMessage() 返回值为空时,toString() 方法返回值记录在执行的 Log 中

# 前台跳转实现

如果希望代码运行完成后,在前台,跳转到指定页面,则需要在返回结果的 Map 中,包括 key 为 redirect, value 为 String 的元素。

# 文件下载实现

如果希望代码运行完成后,返回文件给用户下载,则需要在返回结果的 Map 中,包括 key 为 download, value 为 tech.muyan.storage.StorageFieldValue 类型的元素。

# 可用 extInfo 列表

如下是 DynamicAction 可用的 extInfo 列表,用于控制 DynamicAction 显示及执行的行为

{
  /** 前台是否显示该 Action 的 label, 默认为 true, 如果只希望显示 Action 图标,可设置 displayLabel 为 false */
  /** Whether to display the label of this Action in the frontend, default is true. If you only want to display the Action icon, you can set displayLabel to false */
  "displayLabel"?: true | false,
  /** 是否在执行该 Action 后,刷新当前页面, 默认为 true, 如果 action 执行不更新显示数据,可以无需刷新页面 */
  /** Whether to refresh the current page after executing this Action, default is true. If the action execution does not update display data, you can choose not to refresh the page */
  "refreshPage"?: true | false
}
1
2
3
4
5
6
7
Last Updated: 2024/9/29 02:33:14