# FAQ 常见问题
# 目标读者
本文档的目标读者为:本系统的开发和实施人员
# 为什么枚举字段被系统识别为对象字段?
请确认枚举字段的包名称中,包含 enums
部分,比如
package tech.muyan.dynamic.task.enums // --> 包名称中含 enums 部分,可以正确识别
Enum DynamicTaskType {
RUN_AT_STARTUP('RUN_AT_STARTUP'),
CRON_TASK('CRON_TASK'),
SCHEDULE_TASK('SCHEDULE_TASK'),
;
String value
}
2
3
4
5
6
7
8
9
10
11
可以被正常识别,而下述枚举会被识别为对象类型
package tech.muyan.dynamic.task // --> 包名称中不含 enums 部分,无法正确识别
enum DynamicTaskType {
RUN_AT_STARTUP('RUN_AT_STARTUP'),
CRON_TASK('CRON_TASK'),
SCHEDULE_TASK('SCHEDULE_TASK'),
;
String value
}
2
3
4
5
6
7
8
9
10
11
# 如何在客制化代码中, 手动注入 service 定义
在项目实施过程中,为了代码重用,可能将某些通用,逻辑以 service 形式在系统中定义, 在客制化的 groovy 代码中,可以方便的查找到 service 定义并调用其相关方法。
参考如下的代码, 从 bean 的注册表中根据名称手动查找到 service 定义, 其中 authorityService 是 service 的名称, 对应于 Service 定义的 class 名称, 首字母小写.
import tech.muyan.BeanHelper
import grails.core.GrailsApplication
GrailsApplication grailsApplication = (application as GrailsApplication)
AuthorityService authorityService = BeanHelper.getBean(grailsapplication, "authorityService")
2
3
4
5
# 表单中定义了默认过滤器, 但是没有生效
请确认, 作为过滤器条件的字段, 包含在表单字段中, 如果作为过滤器条件的字段, 没有返回前台, 则该字段无法作为过滤器条件使用.
# extInfo 字段无法成功通过 csv 导入
请确认, extInfo 字段(使用JSON 类型存储在数据库中) 在导入的 CSV 中, 如果内容包含引号("), 是使用两个双引号("")来转义, 而不是使用反斜杠(\) 来进行转义的
# 在 Dynamic Logic 执行间共享全局配置、连接、或进行数据缓存
在某些场景下,可能需要在多次 Dynamic Logic 执行中共享一些数据,典型场景比如:
- 保存并共享一些计算较耗费资源的缓存数据
- 需要在全局共享的一些配置或资源,如第三方系统的有状态连接,Kafka, Message Queue 等的连接等的管理
- 不同 Dynamic Logic 的执行之间,可能存在业务逻辑上的先后顺序,且无法避免的,可以将之前步骤的运行结果进行缓存。
当前系统提供了 tech.muyan.helper.RegistryHelper
类来进行全局共享数据的管理,可以通过
RegistryHelper.memoryGet(key)
来获取缓存数据,RegistryHelper.memoryPut(key, value)
来设置缓存数据,并返回当前已经保存的数据,RegistryHelper.memoryRemove(key)
来移除缓存数据。
# 用户打开 Dashboard, Widget 中报错
报错截图如下:
先确认:
- 请确认不是在 windows 系统下打包的 jar 文件,当前 grails 有 bug 导致 windows 下生成的后端部署包中的 gson 文件编译生成的 class 文件名称错误,从而导致所有的 枚举字段传递到前端的值不对,
解决方法:
- 从 Github action 页面找到待部署的 commit SHA 对应的 action 运行, 直接下载 action 运行生成的部署包
- 在 MacOS 或者 Linux 下打包后端应用
# 后端启动正常但前端连接显示 401 无法连接
请检查 application.yml 文件是否正确,常见的问题包括因为修改导致的缩进或其他格式 问题,进而导致相关配置没有被系统正确读入。可以尝试将 application.yml 文件恢复为 template repository 中的或其他人的原始版本进行尝试
# 系统前后端上线后,前端请求后端接口报错或者用户无法登陆,白屏幕等
请尝试如下操作
- 在 web 服务器端,删除或者强制刷新 nginx 缓存
- 在浏览器端,清空前端
localStorage
中的条目 - 在浏览器端,删除或者强制刷新浏览器缓存
# Domain 中包含可空的 primitive 类型字段,其值为空时对象保存失败
请在 Domain 定义中,将可空的 primitive 类型字段定义为对应的包装类型,如 int
改
为 Integer
,double
改为 Double
, boolean
改为 Boolean
等。
# Domain 中包含的字段在前端没有显示
请检查该字段是否在 Domain 定义的 constraints
中有定义
static constraints = {
attachments nullable: true
}
2
3
如下是一个示例配置:
class Feedback implements Serializable, MultiTenant<Feedback>,
Auditable, Stampable<Feedback>, HasComment<Feedback> {
List<StorageFieldValue> attachments
static hasMany = [attachments: StorageFieldValue]
}
2
3
4
5
# 在 One to many 关联关系中,无法保存关联关系
一个可用的定义可参考 DynamicMenu.groovy, 其中定义了指向自身的 parent 字段、以及反向引用的 children 字段
具体 GORM 的文档可参考 GORM Associations (opens new window)
class DynamicMenu implements MultiTenant<DynamicMenu>,
Auditable, Stampable<DynamicMenu>, Serializable {
// 其他字段省略
// 在 Many 端指向 One 端的字段
DynamicMenu parent
// 在 One 端指向 Many 端的字段
static hasMany = [children: DynamicMenu]
// 请注意不能显式使用 List<DynamicMenu> children 形式定义 children 字段
// 否则会导致 GORM 无法保存 parent 字段
static constraints = {
// 其他约束省略
parent nullable: true
children nullable: true
}
static mapping = {
// 不需要定义 children 字段的 mapping, 该字段只是作为反向引用使用
parent index: 'dynamic_menu_parent_idx'
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 如何定义新的 Websocket 接口
- 实现接口
tech.muyan.websocket.WebSocketService
- 实现方法
abstract String getWebSocketPath()
返回 websocket 的路径 - 实现方法
void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception
处理 websocket 消息