内置特性
# 后端插件开发
# 读取配置
插件JSON配置名称不可更改,同时必须定义顶级根节点,建议以插件名作为顶级根节点,否则当加载多个插件,读取节点时,存在同名将会覆盖。比如读取如下根节点下Url值
底层暴露IIotConfiguration
接口,将其通过构造函数注入使用,使用方式与IConfiguration
接口等同
# 事件总线
引入事件总线设施从而降低代码耦合或削峰等,当前底层仅支持本地事件总线。
# 本地事件总线
所有事件总线定义以及订阅都放在IntegrationEvents目录
Events目录为事件定义,如下我们模拟定义添加用户后给用户发送邮件事件
EventHandlers
目录为事件处理即订阅事件,实现ILocalEventHandler
本地事件处理接口,其泛型类为对应要接收的事件
最后在Configure
方法中通过底层暴露的本地事件总线接口,订阅当添加用户后,给用户发送邮件事件处理
# 全局订阅
首次加载时,立即全局订阅事件处理
# 局部订阅
我们也可以在具体某仓储接口实现中,按需局部订阅事件,比如当添加用户成功后才去订阅事件处理
紧接着在控制器构造函数中注入本地事件总线接口
最后,添加用户成功后,发布给用户发送邮件事件
# 跨库订阅
一般来讲,事件发布和订阅所属事件类型应为相同类型,但在插件化中存在事件发布和订阅处于不同库场景,此时事件发布和订阅类型不同,但属性一致,为避免代码耦合即解耦,底层设施额外扩展此使用场景。
场景假设:在插件库IoTCenter.Module.Test1发布触发发送用户邮件事件,而在IoTCenter.Module.Test2订阅用户发送邮件事件处理
如下截图:IoTCenter.Module.Test1类库中发布用户邮件事件
此类库中定义发布事件类,基础设施暴露EventName
特性,通过EventName
特性指定具体事件名称
如下截图:IoTCenter.Module.Test2类库中订阅用户邮件事件处理
此类库中同样使用EventName
特性指定事件名称,与发布事件类型名称必须一致,同时在此特性上第二个参数必须显式标识为true
注:建议以插件名称简写作为事件名称前缀,否则,若存在同名事件名称将抛出异常
# 定时作业
利用https://github.com/atifaziz/NCrontab (opens new window)库实现计划作业表达式,包含秒级定义即支持6部分表达式定义(底层作业滚动周期为10s,所以自定义实现定时作业应>=10s)
# 使用示例
底层暴露IBackgroundTask
接口,通过BackgroundTask
特性配置Corn表达式
定时作业以单例形式注入
# 使用注意
在实际使用过程中,可能会出现定时任务仅运行一次或不运行情况,基于底层定时任务实现逻辑,列举如下三点使用注意事项
- 插件定时任务以【插件程序集名称+定时任务类名】作为计划名称,所以各插件中计划名称不可重复
DoWorkAsync
方法调用由底层根据滚动周期自动调用,切不可在其方法中再次使用While,否则会完全阻止其他任务调用DoWorkAsync
方法建议尽可能使用异步方法,否则若业务逻辑耗时太长,会阻塞其他任务调用
# 数据转换对象(DTO)
对数据进行DTO,存在各式各样的包,基于此,底层统一规范使用AutoMapper来进行映射,关于AutoMapper这里不再做详细介绍,若不熟悉,请自行查阅相关资料并了解,为考虑到不同场景或简化等原因,底层提供多种映射使用方式
我们使用如下源目标和和映射目标作为示例演示
# 映射配置
底层暴露IAutoMapperConfig
接口,创建映射配置类实现该接口,在此类中全局配置插件库中所有映射,配置类名称推荐以【插件名+ AutoMapperConfig】命名。
AutoMapper
对于属性忽略映射,使用如下
.ForMember(d => d.Id, o => o.Ignore())
为进一步简化书写,底层额外扩展封装Ignore
方法,使用如下
.Ignore(d => d.Id)
例如,如下所示,我们仅配置映射属性Name
,但忽略映射Id
# 映射方式(一)
底层通过对外暴露IObjectMapping
接口来进行对象映射,如下以构造函数注入使用
调用接口中的Map
方法完成从源目标到映射目标的数据映射,此方式乃AutoMapper
内置映射使用方式
同样,我们通过Postman来模拟验证测试下
# 映射方式(二)
上述为AutoMapper
框架默认映射方式,但底层额外基于object
提供了扩展方法,更加方便简洁化,所以我们不再通过IObjectMapping
接口进行映射
比如上述通过源对象CreateUserDTO
映射到目标对象UserTest,导入命名空间【IoTCenterCore.AutoMapper】,然后调用MapTo
扩展方法,我们可修改为如下
调用MapToList
方法将映射源集合映射到目标集合
# 国际化语言
# 前端使用说明
# 翻译包说明
我们以设备列表为例,各个插件翻译包统一存储在en-US和zh-CN目录下中的Front目录,其命名规则为插件名.页面名(如:ganwei-iotcenter-equip-lists.equipListsIot.json)
Json文件配置示例:(ganwei-iotcenter-equip-lists.equipListsIot.json),前端约定命名格式:
以常规组件为前缀key,内部根据页面自行定义,统一为驼峰命名,
示例:(输入框)
{
“input”:{
“inputSearchEquip”:”搜索设备名称”
}
}
# 基于公共外框使用说明
在登录界面进行切换(需下载最新登录包)
若不启用国际语言切换,可在外框中config.json配置
# 各模块插件包处理
以设备列表(ganwei-base-equip-lists)为例,在App.vue
中:
import moduleObserve from 'gw-base-utils/moduleObserve'
mixins: [moduleObserve],
Index.js
入口文件使用
import i18n from 'gw-base-utils/i18n.js'
页面使用:设备列表为例子,其他一样使用方式
# 基于自身外框使用说明
# 外框修改
- 在外框
jumpIframePro
中引入
- 外框
main.js
中引入中英文
import i18n from 'gw-base-utils/i18n.js'
基于自身登录页面(Login.vue)配置说明:
import changeLanguage from 'gw-base-utils-plus/changeLanguage'
mixins: [changeLanguage],
this.showLangSelect = data.showLangSelect;
if (!this.showLangSelect) {
window.localStorage.languageType = 'zh-CN'
}
await this.initLanguage();
<el-select v-if="showLangSelect" v-model="languageSelected" @change="langChange" class="languageSelect">
<template slot="prefix">
<i class="iconfont icon-gw-icon-diqiu"></i>
</template>
<el-option v-for="(item,index) in langOptions" :label="item.name" :value="item.value" :key="index">
</el-option>
</el-select>
样式参考:login.scss
/deep/ .languageSelect {
position: fixed;
right: 30px;
top: 30px;
width: 150px;
.el-input {
width: 150px;
}
.el-input__prefix {
display: flex;
justify-content: center;
align-items: center;
}
}
# 各模块插件包处理
以设备列表(ganwei-base-equip-lists)为例,在App.vue中:
import moduleObserve from 'gw-base-utils/moduleObserve'
mixins: [moduleObserve],
Index.js
入口文件使用
import i18n from 'gw-base-utils/i18n.js'
页面使用:以设备列表为例,其他一样使用方式
# APi请求处理
let languageType = window.localStorage.languageType
if (languageType && languageType !== 'zh-CN') {
config.url = config.url + config.url.includes('?') ? '&' : '?' + 'culture=' + languageType
}
# 后端使用说明
# 公共翻译和插件翻译
公共翻译和插件翻译
前端页面内容和相关操作等提示都由后台统一返回,我们将翻译分为公共翻译和插件翻译两部分。如下截图插件作为国际化插件入口,其Localization目录作为全局国际化公共翻译
zh-CN文件:公共中文翻译
en-US文件:公共英文翻译
指定插件国际化翻译目录如下:
# zh-CN目录
Back:后台插件中文翻译
Front:前端UI中文翻译
# en-US目录
Back:后台插件英文翻译
Front:前端UI英文翻译
注1:前端文件命名约定【前端插件名.子菜单名.json】
注2:后台插件命名约定【插件名.json】
# 使用示例
我们以如下截图设备列表中文翻译为例,作进一步详细说明
- 前端UI
- 后端响应
翻译模板json数组内容:
msgid “填写翻译键” msgstr “填写翻译值”
使用.NET Core框架所提供【IStringLocalizer<T>】构造函数注入,进行翻译。
private readonly IStringLocalizer<T> _stringLocalizer;
# 公共翻译内容
建议翻译键以【common_】作为前缀,其他单词通过下划线隔开
若需响应参数不正确或参数为空或不能为空等,对应公共翻译键如_stringLocalizer["common_parameter_is_incorrect"]
# 插件翻译内容
建议翻译键以【插件名简称_】作为前缀,其他单词通过下划线隔开
比如翻译设备不存在,_stringLocalizer["equip_config_device_not_exist"]