# 对象生命周期客制化

系统支持通过客制化的方式,定制对象的生命周期中的各个节点的逻辑,包括对象的创建、 更新、删除、被访问、接口返回数据、被搜索等。

# 创建

系统支持通过客制化的方式,定制某个对象创建时的额外逻辑,可以在如下节点插入客制化逻辑:

  1. 创建保存前:对象已经在内存中创建,所有的对象属性均构建完成,但还没有被保存到数据库中。
  2. 创建保存后:对象的保存方法已经被调用,但事务还没有提交前。

对于如上所述的两种客制化,需要分别创建如下的Dynamic Object Hook对象

Object Type: 选择该客制化逻辑适用的对象类型
Hook Type: 选择 "Before creating" 
Core Logic: 客制化代码,具体代码中可使用的注入变量和返回值约定参见下述文档
1
2
3
Object Type: 选择该客制化逻辑适用的对象类型
Hook Type: 选择 "After creating" 
Core Logic: 客制化代码,具体代码中可使用的注入变量和返回值约定参见下述文档
1
2
3

该客制化代码中,可使用的注入变量如下表所示:

变量名称 变量类型 描述
object <? extends GormEntity> 对象实例
dynamicFieldValues List<tech.muyan.dynamic.field.DynamicFieldValue> 所有动态字段的值
userContext grails.plugin.springsecurity.userdetails.GrailsUser 当前操作的用户信息
application grails.core.GrailsApplication 当前的 grails 应用上下文
log Closure<?> 用于打印执行日志的 log 闭包
hookType tech.muyan.enums.ObjectHookType 当前执行的 object hook 的类型

返回结果及异常请参考下述返回结果及异常处理章节描述。

注意

dynamicFieldDefinition 参数只在创建保存后的客制化点可用

# 更新

系统支持通过客制化的方式,定制某个对象更新时的额外逻辑,可以在如下节点插入客制化逻辑:

  1. 更新前:对象已经在内存中更新,包括动态属性在内的所有的对象属性均已被更新,但还没有被保存到数据库中。
  2. 更新后:对象及所有动态字段的更新方法已经被调用,但事务还没有提交前。

对于上所述的两种客制化,需要分别创建如下的Dynamic Object Hook对象

Object Type: 选择该客制化逻辑适用的对象类型
Hook Type: 选择 "Before updating" 
Core logic: 客制化代码定义,具体代码中可使用的注入变量和返回值约定参见下述文档
1
2
3
Object Type: 选择该客制化逻辑适用的对象类型
Hook Type: 选择 "After updating" 
Core logic: 客制化代码定义,具体代码中可使用的注入变量和返回值约定参见下述文档
1
2
3

该客制化代码中,可使用的注入变量如下表所示:

变量名称 变量类型 描述
oldObject <? extends GormEntity> 更新前的对象实例的拷贝
newObject <? extends GormEntity> 包括动态字段的,更新后的对象实例
userContext grails.plugin.springsecurity.userdetails.GrailsUser 当前操作的用户信息
application grails.core.GrailsApplication 当前的 grails 应用上下文
log Closure<?> 用于打印执行日志的 log 闭包
hookType tech.muyan.enums.ObjectHookType 当前执行的 object hook 的类型

返回结果及异常请参考下述返回结果及异常处理章节描述。

# 删除

系统支持通过客制化的方式,定制某个对象删除时的额外逻辑,可以在如下节点插入客制化逻辑:

  1. 删除前:对象被从数据库中查询出来,对象的删除方法被调用前
  2. 删除后:对象的删除方法被调用后,事务提交前

对于如上所述的两种客制化,需要分别创建如下的Dynamic Object Hook对象

Object Type: 选择该客制化逻辑适用的对象类型
Hook Type: 选择 "Before deleting" 
Core logic: 客制化代码定义,具体代码中可使用的注入变量和返回值约定参见下述文档
1
2
3
Object Type: 选择该客制化逻辑适用的对象类型
Hook Type: 选择 "After deleting" 
Core logic: 客制化代码定义,具体代码中可使用的注入变量和返回值约定参见下述文档
1
2
3

该客制化代码中,可使用的注入变量如下表所示:

变量名称 变量类型 描述
object <? extends GormEntity> 待删除的对象
userContext grails.plugin.springsecurity.userdetails.GrailsUser 当前操作的用户信息
application grails.core.GrailsApplication 当前的 grails 应用上下文
log Closure<?> 用于打印执行日志的 log 闭包
hookType tech.muyan.enums.ObjectHookType 当前执行的 object hook 的类型

返回结果及异常请参考下述返回结果及异常处理章节描述。

# 返回结果及异常处理

在操作前的客制化逻辑中,可以直接修改操作的对象实例参数实现客制化,对对象实例的修 改会被保存到数据库中。

在操作前和操作后的客制化代码的逻辑执行中,均可使用抛出异常的方式中断系统对该对象 的操作过程,详述如下:

  • tech.muyan.exception.CustomLogicInterruptException 或其子类型的异常被捕获 到,则该对象的操作过程会被中断,该操作不会被保存到数据库中,且该异常的 Message 会 被在前台作为错误显示给用户。

  • tech.muyan.exception.CustomLogicWarningException 或其子类型的异常被捕获到, 则该对象的操作过程不会被中断,但该异常的 Message 会被在前台作为警告显示给用户。

提示

在创建保存前的客制化点,对象的 id 字段为空,在创建保存后的客制化点,对象的 id 字 段已经获取到值。

# 接口返回数据客制化

系统支持通过客制化的方式,定制对象 API 接口返回前台的某个对象的数据的结构和值。 创建如下的Dynamic Object Hook对象即可实现本客制化。

Object Type: 选择该客制化逻辑适用的对象类型
Logic Type: 选择 "Object render" 
Core logic: 客制化代码,具体代码中可使用的注入变量和返回值约定参见下述文档
1
2
3
  • 注入变量

该客制化代码中,可使用的注入变量如下表所示:

变量名称 变量类型 描述
object <? extends GormEntity> 接口待返回的对象实例
userContext grails.plugin.springsecurity.userdetails.GrailsUser 当前操作的用户信息
application grails.core.GrailsApplication 当前的 grails 应用上下文
page tech.muyan.enums.CustomRenderPageType 调用该 render 方法的页面
log Closure<?> 用于打印执行日志的 log 闭包
ownerClass java.lang.Class<? extends GormEntity> 对于搜索页面, 搜索字段所属对象
fieldName java.lang.String 对于搜索页面, 搜索字段的字段名
fetchType tech.muyan.enums.FetchType 获取数据的类型

fetchType 参数是根据界面的不同业务场景,选择返回的数据的范围,当前可选值说明如下:

说明
ONLY_LABEL_FIELD 只获取 Label 字段和 id
EXCLUDE_ARRAY_COLUMNS 返回除了一对多和多对多的关联对象外的其他字段值
ALL_COLUMNS 返回所有字段值

page 参数的可选值如下:

说明
RELATED_SEARCH 关联对象列表页面带搜索条件
RELATED 关联对象列表页面带
LIST_SEARCH 主列表页面带搜索条件
LIST 主列表页面
FINDER 搜索页面
FINDER_SEARCH 搜索页面带搜索条件
DETAIL 详情或者编辑页面
SHOW_MULTIPLE Show multiple API 的返回值

注意

在非搜索结果渲染的场景下, 注入的 ownerClass 字段为空,在实现 render API 时,请注意兼容。

  • 返回结果

该客制化代码需要返回的结果为一个 Map 数据类型或者为一个 org.grails.datastore.gorm.GormEntity 对象的实例。

当返回值为 Map 类型时,Map 的 Key 为 "result", Map 的Value 为另外一个 Map, 这个 内层 Map 的 key 为各字段名称,value 为字段的值。系统会向调用该 API 的客户端返回 该 Map 数据的 JSON 表示。

// 该客制化代码需要返回的结果为一个 Map, key 为 result, value 中为 [返回字段名: 字段值] 的 Map 的格式返回结果
[
  result: [
    columnName: columnValue,
    // 对象类型的子字段返回一个只包含 id 的子 map
    objectField: [
      id: objectId
    ],
    // 用于 card list view 进行 HTML 渲染的属性
    "@HTML_CONTENT@": ""
  ]
]
// 或者直接为一个 org.grails.datastore.gorm.GormEntity 对象的实例
return object
1
2
3
4
5
6
7
8
9
10
11
12
13

系统限制

  • 当前该客制化只作用于不包含动态字段的实体类。
  • 卡片列表渲染

在表格渲染界面,用户可以在表格和卡片显示模式之间切换,

特别的,如果 render API 的返回数据中,包含名为 @HTML_CONTENT@ 的属性,则在渲染 card list 视图时,系统会直接在卡片中,渲染该属性的值,而不是以表格形式显示该对象 的详情。

# 对象详情访问

系统支持通过客制化的方式,在对象详情被查看时,调用客制化逻辑,一个比较直观的应用 场景为对象访问的计数器。

当前本客制化的调用点为: 对象通过 /show/<domainId> 接口被访问时

对于本客制化,需要创建如下的 Dynamic Object Hook 对象

Object Type: 选择该客制化逻辑适用的对象类型
Hook Type: 选择 "Object access" 
Code: 客制化代码,具体代码中可使用的注入变量和返回值约定参见下述文档
1
2
3

该客制化代码中,可使用的注入变量如下表所示:

变量名称 变量类型 描述
object <? extends GormEntity> 对象实例
renderedObject <? extends GormEntity> 或 Map<String, Object> Render API 返回的对象实例或包含对象数据的 Map 对象
userContext grails.plugin.springsecurity.userdetails.GrailsUser 当前操作的用户信息
application grails.core.GrailsApplication 当前的 grails 应用上下文
fetchType tech.muyan.enums.FetchType 获取数据的类型
log Closure<?> 用于打印执行日志的 log 闭包
hookType tech.muyan.enums.ObjectHookType.ACCESS 当前执行的 object hook 的类型

提示

本客制化没有返回值,且不应该修改待访问对象。ACCESS 客制化运行不应该修改待渲染对 象的值,而应该仅仅进行诸如对象访问计数等类型的逻辑实现。

提示

本客制化点与 render API 的区别在于, 本客制化点只在对象的详情被请求时才会被调用,对应到系统代码,是 DomainDataController.show 方法被调用时。 而 render API 在各种对象的数据被请求的场景,如对象列表,创建,更新对象后返回数据,搜索返回对象列表等场景,均会被调用。

# 附件存储及返回客制化

为支持病毒扫描,加密,解密等需求,系统支持在写入附件内容和读取附件内容时,调用客 制化代码来进行附件内容的变换。 这种变换通过 StorageFieldValue 对象的 RENDER Api 来实现。

在创建附件时,在 render 客制化代码中,可通过 object.getData() 方法获取原始的附 件文件数据,通过设置同一对象的data 属性,即可将变换后的文件数据注入到保存节点, 并保存在文件存储引擎中。

在读取附件时,在 render 代码中,可通过 object.getData() 方法获取存储在存储引擎 中的文件数据,通过设置同一对象的 data 属性,即可将变换后的文件数据注入到返回数据 的节点,并返回给调用方。

具体客制化 API 的注入参数可参考 接口返回数据客制化 章节。

最后更新: 2024/5/26 15:43:38