# FAQ 常见问题
# 目标读者
本文档的目标读者为:本系统的开发和实施人员
# 如何在客制化代码中手动注入 service 定义
在项目实施过程中,为了代码重用,可能将某些通用逻辑以 service 形式在系统中定义,在客制化的 groovy 代码中,可以方便的查找到 service 定义并调用其相关方法。
参考如下的代码,从 bean 的注册表中按名称或类型查找 service 并调用。按名称查找时,名称是 Service 类名首字母小写,例如 AuthorityService 对应 authorityService。
import tech.muyan.BeanHelper
import tech.muyan.security.AuthorityService
// 按名称查找
AuthorityService authorityService = BeanHelper.getBean("authorityService")
// 或按类型查找
AuthorityService sameService = BeanHelper.getBean(AuthorityService)
2
3
4
5
6
7
8
# 表单中定义了默认过滤器,但是没有生效
当前版本的前端不读取动态过滤器(DynamicFilter),列表页面不会显示任何过滤器,标记为默认(isDefault)的过滤器也不会自动应用,详见 动态过滤。
如果想让列表默认带上过滤条件,请在表单 extInfo 中设置 listForm.searchConditions,见 默认过滤条件。
# 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)来设置缓存数据,返回该 key 之前保存的值(之前没有则返回 null),RegistryHelper.memoryRemove(key)来移除缓存数据。
# 后端启动正常,但前端无法登录或接口返回 401
平台的配置写在 docker-compose.yml 的环境变量里,请检查:
runtime/proxy/conf.d/default.conf中X-Muyan-Tenant的值是否与后端的TENANT_ID一致。不一致时后端返回Tenant xxx not found in system,见 更改默认租户。- 修改或新增
JWT_SECRET后,之前签发的令牌全部失效,需要重新登录;如果浏览器仍带着旧令牌,请按下一条清理浏览器缓存。JWT_SECRET至少 32 字节,太短时后端无法启动。
# 系统前后端上线后,前端请求后端接口报错、用户无法登录或白屏
请尝试如下操作
- 在 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, HasComment<Feedback> {
// 其他字段省略
List<StorageFieldValue> attachments
static hasMany = [attachments: StorageFieldValue]
static constraints = {
// 其他约束省略
attachments nullable: true
}
}
2
3
4
5
6
7
8
9
10
11
12
# 在 One to many 关联关系中,无法保存关联关系
一个可用的定义可参考 DynamicMenu.groovy,其中定义了指向自身的 parent 字段、以及反向引用的 children 字段
具体 GORM 的文档可参考 GORM Associations (opens new window)
class DynamicMenu implements MultiTenant<DynamicMenu>,
Auditable, Serializable, RenderableObject {
// 其他字段省略
// 在 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 接口
当前版本平台只提供一个固定的 WebSocket 入口,前端经 nginx 连接的地址是 /api/websocket/route,不能再注册新的 WebSocket 路径。要处理新的消息类型,在插件中继承 tech.muyan.api.websocket.MuyanWebSocketComponent,按 topic 接收和推送消息,见 插件开发 · WebSocket。