数据预警第三方渠道添加示例参考AlarmPushChannel.zip
项目相关文件说明
# 整体说明 预警推送有两种,一种是常规消息推送,一种是含动态接收人的消息推送。由于动态接收人的相关逻辑比较复杂,为了方便后续有项目只需要常规推送,准备了两个demo,分是常规推送渠道demo和包含动态接收人的渠道demo # 前端二开运行 详见 仪表盘 前端二开开发文档说明 [自助仪表盘开发快速入门](https://smartbi.feishu.cn/docs/doccnY0Tuaqlq9WPLksPflegeUh) 1. cd进webpack目录 2. 执行npm install smartbi-ext-env 3. npm run dev (开发调试) npm run build (打包命令) ` 这个项目运行直接使用 npm run dev 即可, 已将 node_modules 一起打包` # 整体结构 ## 前端文件 前端文件在 src\webpack\src\plugins\modifyExtenders\alarm-push 目录下 1. src\webpack\src\plugins\modifyExtenders\alarm-push\index.js 前端整体入口文件,供X二开扫描引用 2. src\webpack\src\plugins\modifyExtenders\alarm-push\AlarmPushExtender.js 渠道扩展点插入定义入口,包含常规渠道`DEMO`, 和 动态接收人 `DYNAMIC_DEMO` 两个渠道 ``` import SmartBIExt from 'smartbi-ext' import SxAlarmPushChannel from './AlarmPushChannel.vue' import SxAlarmDynamicPushChannel from './AlarmDynamicPushChannel.vue' let { AlarmModule: { AlarmEventEnum: { ALARM_PUSH_ON_INIT }, BaseAlarmExtender }, Lang } = SmartBIExt class AlarmPushExtender extends BaseAlarmExtender { constructor() { super() } install () { // 预警推送界面初始化接口 this.on(ALARM_PUSH_ON_INIT, iAlarmPush => { const channel = { id: 'DEMO', // id 即是渠道的类型 icon: 'sx-icon-push-textmessage icon-16', // 图标 label: Lang.$t('pushChannel.pushDemo'), // 标签 tooltip: Lang.$t('pushChannel.pushDemo'), // 提示 comp: SxAlarmPushChannel, // 面板实现Vue,具体的预警面板内容 getDefaultData () { // 添加新的渠道时,初始化的默认数据结构 return { config: { receiverType: 'USER', receivers: [], custom: '' } } }, getDefaultTitle () { // 默认标题 return Lang.$t('pushChannel.pushDemo') } } iAlarmPush.insertChannel(-1, channel) // 插入渠道,-1 标识插入到末尾 }) } } ``` 3. src\webpack\src\plugins\lang.js 前端多语言文件 4. src\webpack\src\assets\alarmpush.css 前端CSS样式文件 5. SmartbiX封装好的相关控件 ``` const { SxAlarmRecipientInput, SxAlarmDynamicRecipient, SettingPanel: { SxInputTextWidget, SxSelectWidget } } = SmartUI ``` a. SxAlarmRecipientInput 预警接收用户控件,封装了打开用户弹窗,选择用户的逻辑 b. SxAlarmDynamicRecipient 预警动态接收人控件,封装了动态接收人的控件实现 c. SxInputTextWidget 符合礼显哥UI规范的输入框控件 d. SxSelectWidget 符合礼显哥UI规范的下拉选择框控件 ## 后端文件 后端文件在 src\java\smartbi\demo 目录下 1. src\java\smartbi\demo\AlarmPushDemoModule.java 后端逻辑注册主文件,注册 渠道订阅器、预警渠道实现代理 ``` // 某些服务器异常情况下,可能会重复注册subscriber,导致接到的消息发送两遍,复现原因场景未知 // 先取消注册,再重新注册 // 通过重写subscriber的equals方法使subscriber移除注册成功 CommonMessageService.getInstance().unsubscribe(demoType, subscriber); CommonMessageService.getInstance().subscribe(demoType, subscriber); AlarmModule.getInstance().addAlarmChannelDelegate("DEMO", new AlarmDemoDelegate()); AlarmModule.getInstance().addAlarmChannelDelegate("DYNAMIC_DEMO", new AlarmDemoDynamicDelegate()); AlarmModule.getInstance().registerAlarmChannelClz("DYNAMIC_DEMO", AlarmDemoChannel.class); ``` 2. src\java\smartbi\demo\errorcode\DemoErrorCode.java 错误码定义文件,用来规范渠道的异常信息,对应多语言文件为 src\web\META-INF\classes\smartbi\demo\errorcode\DemoErrorCode_en.properties、src\web\META-INF\extension_lang_zh_CN.properties、src\web\META-INF\extension_lang_zh_TW.properties # 渠道推送 ## 渠道subscriber 由消息推送模块 CommonMessageService 统一管理接收从预警渠道发出的消息,每个渠道的subscriber自己接收处理 相关文件在 src\java\smartbi\demo\channel\config 目录下 1. src\java\smartbi\demo\channel\subscriber\AlarmPushDemoSubscriber.java 自定义渠道推送的主文件,主要方法为 onMessage。接收的参数应为自己渠道接收的指类型,如IDemoConfig等。该文件在执行时,自己对相关参数进行类型转换,来处理后续逻辑 ``` public class AlarmPushDemoSubscriber implements ISubscriber { @Override public void onMessage(IMessageBody messageBody, IChannelConfig channelConfig, IPublisher publisher) { if (channelConfig == null) { return; } // 类型强转 IDemoConfig demoConfig = (IDemoConfig) channelConfig; try { send(messageBody, demoConfig); } catch (Exception e) { LogManager.getLogger(AlarmPushDemoSubscriber.class).error(e.getMessage(), e); futureException(channelConfig, e); } } } ``` 2. src\java\smartbi\demo\channel\config\DemoConfig.java AlarmPushDemoSubscriber 接收的主要参数,含有渠道推送需要的相关定义,主要由于该类没有实现ICompletableConfig接口,只能能接收处理预警推送消息,无法将处理后的消息结果进行回调处理,返回给预警 3. src\java\smartbi\demo\channel\config\DemoCompletableConfig.java AlarmPushDemoSubscriber 接收的主要参数,含有渠道推送需要的相关定义有实现ICompletableConfig接口,可以通过config.getFuture().complete(DemoResult)将相关推动消息结果进行回调处理,返回给预警 4. src\java\smartbi\demo\channel\config\DemoReceiverType.java 渠道可能会接收多种推送方式,不同的推送方式,处理不同,使用枚举类区别规范化 5. src\java\smartbi\demo\channel\config\DemoMessageBodyExtend.java IMessageBody messageBody 的相关接口提供了基本的推送消息内容。但是不同的渠道对信息的需求不同,这些信息统一xiezaile messageBody exntended中,DemoMessageBodyExtend为和exntended数据结构对应的实体bean 6. src\java\smartbi\demo\channel\config\DemoResult.java 通过IDemoCompletableConfig接口回调给预警的结果,预警接到结果后,对结果中的信息进行解析,将详细信息和错误信息写入预警日志 7. src\java\smartbi\demo\channel\config\DemoResultType.java 渠道推送处理的结果类型,方便预警根据结果类型进行归类处理相信信息和错误信息 8. 将AlarmPushDemoSubscriber 注册给渠道 在 AlarmPushDemoModule中将AlarmPushDemoSubscriber注册给系统,以便能够由系统统一管理渠道的推送消息接收 ``` // 某些服务器异常情况下,可能会重复注册subscriber,导致接到的消息发送两遍,复现原因场景未知 // 先取消注册,再重新注册 // 通过重写subscriber的equals方法使subscriber移除注册成功 CommonMessageService.getInstance().unsubscribe(demoType, subscriber); CommonMessageService.getInstance().subscribe(demoType, subscriber); ``` ## 常规渠道推送 ### 前端实现文件 1. src\webpack\src\plugins\modifyExtenders\alarm-push\AlarmPushChannel.vue 定义常规渠道推送的文件,通过实现主要接口,将结果从预警读取和写入预警 ``` export default { name: 'SxAlarmPushChannel', components: { SxAlarmRecipientInput, SxInputTextWidget }, props: { channel: { type: Object, // 渠道定义 }, }, data () { return { config: { // 接收人相关的配置项 receiverType: 'USER', receivers: [], custom: '' }, ... } }, ... created () { this.intConfig() }, methods: { intConfig () { // 解析传入的channel,初始化 let config = this.channel ? this.channel.config : {} this.config = { receiverType: config.receiverType, receivers: config.receivers || [], custom: config.custom } }, ... // 校验方法,校验当前页面的输入是否合法,由预警系统整体调用 validate () { return new Promise((resolve, reject) => { this.$refs.form.validate((valid) => resolve(valid)) }) }, // 将被父组件 AlarmPushChannel.vue 调用 getContent () { // 返回定义主体内容 return { // config 属性,返回时需要写在自己定义的属性名下 // 返回的结构和在AlarmPushExtender getDefaultData方法的对象结构整体一致 // AlarmPushChannel.vue 会将属性定义复制到 channel 定义中 config: { // 接收人相关的配置项 receiverType: this.config.receiverType, receivers: this.config.receiverType === 'USER' ? this.config.receivers : [], custom: this.config.receiverType === 'CUSTOM' ? this.config.custom : '' } } }, async sync () { // 预警定义变更时,做一些同步动作,主要动态接收人时需要 } } } ``` ### 后端实现文件 后端预警渠道实现文件在 src\java\smartbi\demo\alarm 目录先 #### 渠道定义相关文件 将JavaBean和前端渠道定义channel 的结构一一对应,方便delegate中的逻辑处理 1. src\java\smartbi\demo\alarm\AlarmDemoChannel.java 渠道定义文件,其结构和预警渠道定义一致 2. src\java\smartbi\demo\alarm\AlarmDemoChannelConfig.java 渠道Config文件,其结构和前端config界面文件一致 ### 渠道delegate文件 对接预警渠道处理逻辑的文件,通过实现相关接口逻辑,使得预警系统那边能够根据相关信息进行消息推送 1. src\java\smartbi\demo\alarm\delegate\AlarmDemoDelegate.java ``` /** * 常规的渠道推送delegate */ public class AlarmDemoDelegate implements IAlarmChannelDelegate { @Override public IAlarmConvertor getContentConvertor() { // html 转其他内容的转换器,以预警系统做整体上的转换 // 可以自己在channel的 subsriber中自己做转换,以更好的切合实际 return null; } // 返回渠道接受的config // 系统接收到config后,组装拼接成CommonMessageService需要消息体等,由预警统一进行消息推送处理 @Override public IChannelConfig getChannelConfig(AlarmBO alarmBO, IAlarmChannel channel) { // 转成对应的实体bean,方便后续读取 // channel.toJSON() 为将渠道转成JSON,主要针对没有将渠道对应的JavaBean如AlarmDemoChannel注册给预警系统的情况,如果注册后,可以直接进行类型转换 AlarmDemoChannel demoChannel = JSONUtil.toBean(channel.toJSON(), AlarmDemoChannel.class); DemoCompletableConfig config = new DemoCompletableConfig(); ... return config; } // 对推送后的结构进行包装成IAlarmChannelResult接口对象,供预警统一执行判读推送结果和记录日志 @Override public IAlarmChannelResult getAlarmSendResult(AlarmBO alarmBO, IAlarmChannelConfig config) { IChannelConfig channelConfig = config.getChannelConfig(); // AlarmChannelResult 为产品定义的 IAlarmChannelResult // 渠道有需要也可以自己实现 // 具体处理逻辑见 AlarmDemoDelegate AlarmChannelResult result = new AlarmChannelResult(); ... return result; } ... } ``` 2. 在 AlarmPushDemoModule 中 注册Delegate ``` // 第一个参数 "DEMO" 和在AlarmPushExtender中的渠道ID 一致 AlarmModule.getInstance().addAlarmChannelDelegate("DEMO", new AlarmDemoDelegate()); ``` ## 含动态接收人逻辑的渠道推送 ### 前端实现文件 1. src\webpack\src\plugins\modifyExtenders\alarm-push\AlarmDynamicPushChannel.vue ``` export default { name: 'SxAlarmDynamicPushChannel', components: { SxAlarmRecipientInput, SxInputTextWidget, SxSelectWidget, SxAlarmDynamicRecipient }, props: { channel: { type: Object, }, alarmBusiness: Object }, data () { return { config: { // 接收人相关的配置项 receiverType: 'USER', receivers: [], custom: '', dynamic: { id: '', type: null } }, ... }, computed: { ... fields () { return (this.alarmBusiness.getDataProvider().getFields() || []).map(({ uniqueId: value, label }) => ({ value, label })) } }, watch: { }, created () { this.intConfig() }, methods: { intConfig () { // 解析输入的channel,初始化 let config = this.channel ? this.channel.config : {} let dynamic = config.dynamic || { id: '', type: null } this.config = { receiverType: config.receiverType, receivers: config.receivers || [], custom: config.custom, dynamic: { id: dynamic.id, type: dynamic.type } } }, ... validate () { return new Promise((resolve, reject) => { this.$refs.form.validate((valid) => resolve(valid)) }) }, // 将被父组件 AlarmPushChannel.vue 调用 getContent () { // 返回定义主体内容 return { // config 属性,返回时需要写在自己定义的属性名下 // AlarmPushChannel.vue 会将属性定义复制到 channel 定义中 config: { // 接收人相关的配置项 receiverType: this.config.receiverType, receivers: this.config.receiverType === 'USER' ? this.config.receivers : [], custom: this.config.receiverType === 'CUSTOM' ? this.config.custom : '', dynamic: this.config.receiverType === 'DYNAMIC' ? this.config.dynamic : {}, } } }, ... // 将被父组件 AlarmPushChannel.vue 调用 async sync () { // 前面数据范围字段变更后,可能动态接收人选择的字段已经不存在了 // 这个时候需要清空动态接收人的字段定义 if (this.checkFieldId()) { return } this.$set(this.config.dynamic, 'id', null) }, checkFieldId () { let id = this.config.dynamic.id return this.fields.some(f => f.value === id) } } ``` ### 后端实现文件 对接预警渠道处理逻辑的文件,通过实现相关接口逻辑,使得预警系统那边能够根据相关信息进行消息推送 1. src\java\smartbi\demo\alarm\delegate\AlarmDemoDynamicDelegate.java ``` /** * * 包含动态接收人处理逻辑的delegate */ public class AlarmDemoDynamicDelegate implements IAlarmChannelDelegate, IAlarmChannelDynamicDelegate { // 一般即实现常规渠道推送接口,也实现动态接收人渠道推送接口 @Override public IAlarmConvertor getContentConvertor() { // html 转其他内容的转换器,以预警系统做整体上的转换 // 可以自己在channel的 subsriber中自己做转换,以更好的切合实际 return null; } // 返回渠道接受的config @Override public IChannelConfig getChannelConfig(AlarmBO alarmBO, IAlarmChannel channel) { AlarmDemoChannel demoChannel = (AlarmDemoChannel) channel; DemoCompletableConfig config = new DemoCompletableConfig(); config.setType(ChannelType.valueOf(demoChannel.getType().name())); config.setCustom(demoChannel.getConfig().getCustom()); config.setHtml(true); config.setReceiverType(demoChannel.getConfig().getReceiverType()); config.setRecipients(demoChannel.getConfig().getReceivers().stream().map(reciver -> new AlarmChannelRecipient(RecipientType.valueOf(reciver.getTargettype()), reciver.getTargetid(), reciver.getAlias())) .collect(Collectors.toList())); return config; } @Override public IAlarmChannelResult getAlarmSendResult(AlarmBO alarmBO, IAlarmChannelConfig config) { return new AlarmDemoResultGenerator().generate(config); } // ------------- 下面是动态接收人的处理逻辑 ---------------------- /** * 是否是动态接收人的方式 * 是动态接收人时,由预警系统管理相关逻辑走动态接收人的逻辑 */ @Override public boolean isDynamic(AlarmBO alarmBO, IAlarmChannel channel) { // AlarmDemoChannel 注册给了预警系统,有预警系统统一转成对应的JavaBean,和前端属性一一对应,可以直接进行类型转义 AlarmDemoChannel demoChannel = (AlarmDemoChannel) channel; return DemoReceiverType.DYNAMIC == demoChannel.getConfig().getReceiverType(); } @Override public IAlarmDynamicRecipient getDynamicRecipient(AlarmBO alarmBO, IAlarmChannel channel) { // 返回动态接收人对象 return ((AlarmDemoChannel) channel).getConfig().getDynamic(); } // 动态接收人接口生成config @Override public IChannelConfig getChannelConfig(AlarmBO alarmBO, IAlarmChannel channel, List<IRecipient> recipients) { // 对于动态接收人,实际走的时User类型的渠道退丝攻 // 只是接收人是由预警系统根据执行结果生成的 // 转成对应的实体bean,方便后续读取 AlarmDemoChannel demoChannel = (AlarmDemoChannel) channel; DemoCompletableConfig config = new DemoCompletableConfig(); config.setType(ChannelType.valueOf("DEMO")); config.setCustom(demoChannel.getConfig().getCustom()); config.setHtml(true); // 动态接收人实际走的用户逻辑 config.setReceiverType(DemoReceiverType.USER); // 将接收人设置为本次的接收人 config.setRecipients(recipients); return config; } @Override public IAlarmChannelResult getAlarmDynamicSendResult(AlarmBO alarmBO, IAlarmChannel channel, List<IAlarmChannelDynamicConfig> dynamicConfigs) { // dynamicConfigs 是动态接收人结果config列表 // 一次推送动态接收人,由于动态接收人会触发多次推送,需要将多次推送的结果合并处理后,才是这次渠道的推送结果 return new AlarmDemoResultGenerator().generateDynamicResult(channel, dynamicConfigs); } ``` 2. src\java\smartbi\demo\alarm\delegate\AlarmDemoResultGenerator.java 预警渠道结果生成器,将渠道的返回信息进行解析整理,返回给预警系统 3. 在 AlarmPushDemoModule 中 注册Delegate ``` // 第一个参数 "DYNAMIC_DEMO" 和在AlarmPushExtender中的渠道ID 一致 AlarmModule.getInstance().addAlarmChannelDelegate("DYNAMIC_DEMO", new AlarmDemoDynamicDelegate()); // 将渠道数据结构对应的JavaBean注册给预警,以便后续delegate处理时,不需要每次都使用JSONUtil转成JavaBean。 // 如果想要在delegate直接操作ObjectNode,也可以不用注册 // 操作ObjectNode的工具类 smarbix.util.JSONUtil.java AlarmModule.getInstance().registerAlarmChannelClz("DYNAMIC_DEMO", AlarmDemoChannel.class); ``` 预警推送有两种,一种是常规消息推送,一种是含动态接收人的消息推送。由于动态接收人的相关逻辑比较复杂,为了方便后续有项目只需要常规推送,准备了两个demo,分是常规推送渠道demo和包含动态接收人的渠道demo # 前端二开运行 详见 仪表盘 前端二开开发文档说明 [自助仪表盘开发快速入门](https://smartbi.feishu.cn/docs/doccnY0Tuaqlq9WPLksPflegeUh) 1. cd进webpack目录 2. 执行npm install smartbi-ext-env 3. npm run dev (开发调试) npm run build (打包命令) ` 这个项目运行直接使用 npm run dev 即可, 已将 node_modules 一起打包` # 整体结构 ## 前端文件 前端文件在 src\webpack\src\plugins\modifyExtenders\alarm-push 目录下 1. src\webpack\src\plugins\modifyExtenders\alarm-push\index.js 前端整体入口文件,供X二开扫描引用 2. src\webpack\src\plugins\modifyExtenders\alarm-push\AlarmPushExtender.js 渠道扩展点插入定义入口,包含常规渠道`DEMO`, 和 动态接收人 `DYNAMIC_DEMO` 两个渠道 ``` import SmartBIExt from 'smartbi-ext' import SxAlarmPushChannel from './AlarmPushChannel.vue' import SxAlarmDynamicPushChannel from './AlarmDynamicPushChannel.vue' let { AlarmModule: { AlarmEventEnum: { ALARM_PUSH_ON_INIT }, BaseAlarmExtender }, Lang } = SmartBIExt class AlarmPushExtender extends BaseAlarmExtender { constructor() { super() } install () { // 预警推送界面初始化接口 this.on(ALARM_PUSH_ON_INIT, iAlarmPush => { const channel = { id: 'DEMO', // id 即是渠道的类型 icon: 'sx-icon-push-textmessage icon-16', // 图标 label: Lang.$t('pushChannel.pushDemo'), // 标签 tooltip: Lang.$t('pushChannel.pushDemo'), // 提示 comp: SxAlarmPushChannel, // 面板实现Vue,具体的预警面板内容 getDefaultData () { // 添加新的渠道时,初始化的默认数据结构 return { config: { receiverType: 'USER', receivers: [], custom: '' } } }, getDefaultTitle () { // 默认标题 return Lang.$t('pushChannel.pushDemo') } } iAlarmPush.insertChannel(-1, channel) // 插入渠道,-1 标识插入到末尾 }) } } ``` 3. src\webpack\src\plugins\lang.js 前端多语言文件 4. src\webpack\src\assets\alarmpush.css 前端CSS样式文件 5. SmartbiX封装好的相关控件 ``` const { SxAlarmRecipientInput, SxAlarmDynamicRecipient, SettingPanel: { SxInputTextWidget, SxSelectWidget } } = SmartUI ``` a. SxAlarmRecipientInput 预警接收用户控件,封装了打开用户弹窗,选择用户的逻辑 b. SxAlarmDynamicRecipient 预警动态接收人控件,封装了动态接收人的控件实现 c. SxInputTextWidget 符合礼显哥UI规范的输入框控件 d. SxSelectWidget 符合礼显哥UI规范的下拉选择框控件 ## 后端文件 后端文件在 src\java\smartbi\demo 目录下 1. src\java\smartbi\demo\AlarmPushDemoModule.java 后端逻辑注册主文件,注册 渠道订阅器、预警渠道实现代理 ``` // 某些服务器异常情况下,可能会重复注册subscriber,导致接到的消息发送两遍,复现原因场景未知 // 先取消注册,再重新注册 // 通过重写subscriber的equals方法使subscriber移除注册成功 CommonMessageService.getInstance().unsubscribe(demoType, subscriber); CommonMessageService.getInstance().subscribe(demoType, subscriber); AlarmModule.getInstance().addAlarmChannelDelegate("DEMO", new AlarmDemoDelegate()); AlarmModule.getInstance().addAlarmChannelDelegate("DYNAMIC_DEMO", new AlarmDemoDynamicDelegate()); AlarmModule.getInstance().registerAlarmChannelClz("DYNAMIC_DEMO", AlarmDemoChannel.class); ``` 2. src\java\smartbi\demo\errorcode\DemoErrorCode.java 错误码定义文件,用来规范渠道的异常信息,对应多语言文件为 src\web\META-INF\classes\smartbi\demo\errorcode\DemoErrorCode_en.properties、src\web\META-INF\extension_lang_zh_CN.properties、src\web\META-INF\extension_lang_zh_TW.properties # 渠道推送 ## 渠道subscriber 由消息推送模块 CommonMessageService 统一管理接收从预警渠道发出的消息,每个渠道的subscriber自己接收处理 相关文件在 src\java\smartbi\demo\channel\config 目录下 1. src\java\smartbi\demo\channel\subscriber\AlarmPushDemoSubscriber.java 自定义渠道推送的主文件,主要方法为 onMessage。接收的参数应为自己渠道接收的指类型,如IDemoConfig等。该文件在执行时,自己对相关参数进行类型转换,来处理后续逻辑 ``` public class AlarmPushDemoSubscriber implements ISubscriber { @Override public void onMessage(IMessageBody messageBody, IChannelConfig channelConfig, IPublisher publisher) { if (channelConfig == null) { return; } // 类型强转 IDemoConfig demoConfig = (IDemoConfig) channelConfig; try { send(messageBody, demoConfig); } catch (Exception e) { LogManager.getLogger(AlarmPushDemoSubscriber.class).error(e.getMessage(), e); futureException(channelConfig, e); } } } ``` 2. src\java\smartbi\demo\channel\config\DemoConfig.java AlarmPushDemoSubscriber 接收的主要参数,含有渠道推送需要的相关定义,主要由于该类没有实现ICompletableConfig接口,只能能接收处理预警推送消息,无法将处理后的消息结果进行回调处理,返回给预警 3. src\java\smartbi\demo\channel\config\DemoCompletableConfig.java AlarmPushDemoSubscriber 接收的主要参数,含有渠道推送需要的相关定义有实现ICompletableConfig接口,可以通过config.getFuture().complete(DemoResult)将相关推动消息结果进行回调处理,返回给预警 4. src\java\smartbi\demo\channel\config\DemoReceiverType.java 渠道可能会接收多种推送方式,不同的推送方式,处理不同,使用枚举类区别规范化 5. src\java\smartbi\demo\channel\config\DemoMessageBodyExtend.java IMessageBody messageBody 的相关接口提供了基本的推送消息内容。但是不同的渠道对信息的需求不同,这些信息统一xiezaile messageBody exntended中,DemoMessageBodyExtend为和exntended数据结构对应的实体bean 6. src\java\smartbi\demo\channel\config\DemoResult.java 通过IDemoCompletableConfig接口回调给预警的结果,预警接到结果后,对结果中的信息进行解析,将详细信息和错误信息写入预警日志 7. src\java\smartbi\demo\channel\config\DemoResultType.java 渠道推送处理的结果类型,方便预警根据结果类型进行归类处理相信信息和错误信息 8. 将AlarmPushDemoSubscriber 注册给渠道 在 AlarmPushDemoModule中将AlarmPushDemoSubscriber注册给系统,以便能够由系统统一管理渠道的推送消息接收 ``` // 某些服务器异常情况下,可能会重复注册subscriber,导致接到的消息发送两遍,复现原因场景未知 // 先取消注册,再重新注册 // 通过重写subscriber的equals方法使subscriber移除注册成功 CommonMessageService.getInstance().unsubscribe(demoType, subscriber); CommonMessageService.getInstance().subscribe(demoType, subscriber); ``` ## 常规渠道推送 ### 前端实现文件 1. src\webpack\src\plugins\modifyExtenders\alarm-push\AlarmPushChannel.vue 定义常规渠道推送的文件,通过实现主要接口,将结果从预警读取和写入预警 ``` export default { name: 'SxAlarmPushChannel', components: { SxAlarmRecipientInput, SxInputTextWidget }, props: { channel: { type: Object, // 渠道定义 }, }, data () { return { config: { // 接收人相关的配置项 receiverType: 'USER', receivers: [], custom: '' }, ... } }, ... created () { this.intConfig() }, methods: { intConfig () { // 解析传入的channel,初始化 let config = this.channel ? this.channel.config : {} this.config = { receiverType: config.receiverType, receivers: config.receivers || [], custom: config.custom } }, ... // 校验方法,校验当前页面的输入是否合法,由预警系统整体调用 validate () { return new Promise((resolve, reject) => { this.$refs.form.validate((valid) => resolve(valid)) }) }, // 将被父组件 AlarmPushChannel.vue 调用 getContent () { // 返回定义主体内容 return { // config 属性,返回时需要写在自己定义的属性名下 // 返回的结构和在AlarmPushExtender getDefaultData方法的对象结构整体一致 // AlarmPushChannel.vue 会将属性定义复制到 channel 定义中 config: { // 接收人相关的配置项 receiverType: this.config.receiverType, receivers: this.config.receiverType === 'USER' ? this.config.receivers : [], custom: this.config.receiverType === 'CUSTOM' ? this.config.custom : '' } } }, async sync () { // 预警定义变更时,做一些同步动作,主要动态接收人时需要 } } } ``` ### 后端实现文件 后端预警渠道实现文件在 src\java\smartbi\demo\alarm 目录先 #### 渠道定义相关文件 将JavaBean和前端渠道定义channel 的结构一一对应,方便delegate中的逻辑处理 1. src\java\smartbi\demo\alarm\AlarmDemoChannel.java 渠道定义文件,其结构和预警渠道定义一致 2. src\java\smartbi\demo\alarm\AlarmDemoChannelConfig.java 渠道Config文件,其结构和前端config界面文件一致 ### 渠道delegate文件 对接预警渠道处理逻辑的文件,通过实现相关接口逻辑,使得预警系统那边能够根据相关信息进行消息推送 1. src\java\smartbi\demo\alarm\delegate\AlarmDemoDelegate.java ``` /** * 常规的渠道推送delegate */ public class AlarmDemoDelegate implements IAlarmChannelDelegate { @Override public IAlarmConvertor getContentConvertor() { // html 转其他内容的转换器,以预警系统做整体上的转换 // 可以自己在channel的 subsriber中自己做转换,以更好的切合实际 return null; } // 返回渠道接受的config // 系统接收到config后,组装拼接成CommonMessageService需要消息体等,由预警统一进行消息推送处理 @Override public IChannelConfig getChannelConfig(AlarmBO alarmBO, IAlarmChannel channel) { // 转成对应的实体bean,方便后续读取 // channel.toJSON() 为将渠道转成JSON,主要针对没有将渠道对应的JavaBean如AlarmDemoChannel注册给预警系统的情况,如果注册后,可以直接进行类型转换 AlarmDemoChannel demoChannel = JSONUtil.toBean(channel.toJSON(), AlarmDemoChannel.class); DemoCompletableConfig config = new DemoCompletableConfig(); ... return config; } // 对推送后的结构进行包装成IAlarmChannelResult接口对象,供预警统一执行判读推送结果和记录日志 @Override public IAlarmChannelResult getAlarmSendResult(AlarmBO alarmBO, IAlarmChannelConfig config) { IChannelConfig channelConfig = config.getChannelConfig(); // AlarmChannelResult 为产品定义的 IAlarmChannelResult // 渠道有需要也可以自己实现 // 具体处理逻辑见 AlarmDemoDelegate AlarmChannelResult result = new AlarmChannelResult(); ... return result; } ... } ``` 2. 在 AlarmPushDemoModule 中 注册Delegate ``` // 第一个参数 "DEMO" 和在AlarmPushExtender中的渠道ID 一致 AlarmModule.getInstance().addAlarmChannelDelegate("DEMO", new AlarmDemoDelegate()); ``` ## 含动态接收人逻辑的渠道推送 ### 前端实现文件 1. src\webpack\src\plugins\modifyExtenders\alarm-push\AlarmDynamicPushChannel.vue ``` export default { name: 'SxAlarmDynamicPushChannel', components: { SxAlarmRecipientInput, SxInputTextWidget, SxSelectWidget, SxAlarmDynamicRecipient }, props: { channel: { type: Object, }, alarmBusiness: Object }, data () { return { config: { // 接收人相关的配置项 receiverType: 'USER', receivers: [], custom: '', dynamic: { id: '', type: null } }, ... }, computed: { ... fields () { return (this.alarmBusiness.getDataProvider().getFields() || []).map(({ uniqueId: value, label }) => ({ value, label })) } }, watch: { }, created () { this.intConfig() }, methods: { intConfig () { // 解析输入的channel,初始化 let config = this.channel ? this.channel.config : {} let dynamic = config.dynamic || { id: '', type: null } this.config = { receiverType: config.receiverType, receivers: config.receivers || [], custom: config.custom, dynamic: { id: dynamic.id, type: dynamic.type } } }, ... validate () { return new Promise((resolve, reject) => { this.$refs.form.validate((valid) => resolve(valid)) }) }, // 将被父组件 AlarmPushChannel.vue 调用 getContent () { // 返回定义主体内容 return { // config 属性,返回时需要写在自己定义的属性名下 // AlarmPushChannel.vue 会将属性定义复制到 channel 定义中 config: { // 接收人相关的配置项 receiverType: this.config.receiverType, receivers: this.config.receiverType === 'USER' ? this.config.receivers : [], custom: this.config.receiverType === 'CUSTOM' ? this.config.custom : '', dynamic: this.config.receiverType === 'DYNAMIC' ? this.config.dynamic : {}, } } }, ... // 将被父组件 AlarmPushChannel.vue 调用 async sync () { // 前面数据范围字段变更后,可能动态接收人选择的字段已经不存在了 // 这个时候需要清空动态接收人的字段定义 if (this.checkFieldId()) { return } this.$set(this.config.dynamic, 'id', null) }, checkFieldId () { let id = this.config.dynamic.id return this.fields.some(f => f.value === id) } } ``` ### 后端实现文件 对接预警渠道处理逻辑的文件,通过实现相关接口逻辑,使得预警系统那边能够根据相关信息进行消息推送 1. src\java\smartbi\demo\alarm\delegate\AlarmDemoDynamicDelegate.java ``` /** * * 包含动态接收人处理逻辑的delegate */ public class AlarmDemoDynamicDelegate implements IAlarmChannelDelegate, IAlarmChannelDynamicDelegate { // 一般即实现常规渠道推送接口,也实现动态接收人渠道推送接口 @Override public IAlarmConvertor getContentConvertor() { // html 转其他内容的转换器,以预警系统做整体上的转换 // 可以自己在channel的 subsriber中自己做转换,以更好的切合实际 return null; } // 返回渠道接受的config @Override public IChannelConfig getChannelConfig(AlarmBO alarmBO, IAlarmChannel channel) { AlarmDemoChannel demoChannel = (AlarmDemoChannel) channel; DemoCompletableConfig config = new DemoCompletableConfig(); config.setType(ChannelType.valueOf(demoChannel.getType().name())); config.setCustom(demoChannel.getConfig().getCustom()); config.setHtml(true); config.setReceiverType(demoChannel.getConfig().getReceiverType()); config.setRecipients(demoChannel.getConfig().getReceivers().stream().map(reciver -> new AlarmChannelRecipient(RecipientType.valueOf(reciver.getTargettype()), reciver.getTargetid(), reciver.getAlias())) .collect(Collectors.toList())); return config; } @Override public IAlarmChannelResult getAlarmSendResult(AlarmBO alarmBO, IAlarmChannelConfig config) { return new AlarmDemoResultGenerator().generate(config); } // ------------- 下面是动态接收人的处理逻辑 ---------------------- /** * 是否是动态接收人的方式 * 是动态接收人时,由预警系统管理相关逻辑走动态接收人的逻辑 */ @Override public boolean isDynamic(AlarmBO alarmBO, IAlarmChannel channel) { // AlarmDemoChannel 注册给了预警系统,有预警系统统一转成对应的JavaBean,和前端属性一一对应,可以直接进行类型转义 AlarmDemoChannel demoChannel = (AlarmDemoChannel) channel; return DemoReceiverType.DYNAMIC == demoChannel.getConfig().getReceiverType(); } @Override public IAlarmDynamicRecipient getDynamicRecipient(AlarmBO alarmBO, IAlarmChannel channel) { // 返回动态接收人对象 return ((AlarmDemoChannel) channel).getConfig().getDynamic(); } // 动态接收人接口生成config @Override public IChannelConfig getChannelConfig(AlarmBO alarmBO, IAlarmChannel channel, List<IRecipient> recipients) { // 对于动态接收人,实际走的时User类型的渠道退丝攻 // 只是接收人是由预警系统根据执行结果生成的 // 转成对应的实体bean,方便后续读取 AlarmDemoChannel demoChannel = (AlarmDemoChannel) channel; DemoCompletableConfig config = new DemoCompletableConfig(); config.setType(ChannelType.valueOf("DEMO")); config.setCustom(demoChannel.getConfig().getCustom()); config.setHtml(true); // 动态接收人实际走的用户逻辑 config.setReceiverType(DemoReceiverType.USER); // 将接收人设置为本次的接收人 config.setRecipients(recipients); return config; } @Override public IAlarmChannelResult getAlarmDynamicSendResult(AlarmBO alarmBO, IAlarmChannel channel, List<IAlarmChannelDynamicConfig> dynamicConfigs) { // dynamicConfigs 是动态接收人结果config列表 // 一次推送动态接收人,由于动态接收人会触发多次推送,需要将多次推送的结果合并处理后,才是这次渠道的推送结果 return new AlarmDemoResultGenerator().generateDynamicResult(channel, dynamicConfigs); } ``` 2. src\java\smartbi\demo\alarm\delegate\AlarmDemoResultGenerator.java 预警渠道结果生成器,将渠道的返回信息进行解析整理,返回给预警系统 3. 在 AlarmPushDemoModule 中 注册Delegate ``` // 第一个参数 "DYNAMIC_DEMO" 和在AlarmPushExtender中的渠道ID 一致 AlarmModule.getInstance().addAlarmChannelDelegate("DYNAMIC_DEMO", new AlarmDemoDynamicDelegate()); // 将渠道数据结构对应的JavaBean注册给预警,以便后续delegate处理时,不需要每次都使用JSONUtil转成JavaBean。 // 如果想要在delegate直接操作ObjectNode,也可以不用注册 // 操作ObjectNode的工具类 smarbix.util.JSONUtil.java AlarmModule.getInstance().registerAlarmChannelClz("DYNAMIC_DEMO", AlarmDemoChannel.class); ``` |